fix(docker): proxy CLIProxy management panel through dashboard to avoid cross-origin errors

In Docker, the browser cannot directly reach the container-internal CLIProxy
port (8317). This adds a reverse proxy at /cliproxy-local/* that forwards
requests through the dashboard Express server to 127.0.0.1:8317 internally.
The control panel embed now uses same-origin API endpoints for health checks
and the proxy path for the management iframe.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
UserandClaude Opus 4.6 committed 2026-03-30 06:25:14 -07:00
1 parent 5160e1d454
commit 881b061dfe
3 files changed
+100 -20

No files matched your search

+4
View File
@@ -79,6 +79,10 @@ export async function startServer(options: ServerOptions): Promise<ServerInstanc
const { usageRoutes } = await import('./usage-routes');
app.use('/api/usage', usageRoutes);
// CLIProxy local reverse proxy (avoids cross-origin issues in Docker)
const cliproxyLocalProxy = (await import('./routes/cliproxy-local-proxy')).default;
app.use('/cliproxy-local', cliproxyLocalProxy);
// Dev mode: use Vite middleware for HMR
if (options.dev) {
const { createServer: createViteServer } = await import('vite');
@@ -0,0 +1,49 @@
/**
* CLIProxy Local Reverse Proxy
*
* Proxies requests from the dashboard to the local CLIProxy service
* running on 127.0.0.1:8317 inside the same host/container.
*
* This eliminates cross-origin issues when the dashboard and CLIProxy
* run on different ports (e.g., inside Docker containers where the
* browser cannot reach the internal CLIProxy port directly).
*
* Mounted at: /cliproxy-local/* → http://127.0.0.1:{port}/*
*/
import { Router, Request, Response } from 'express';
import http from 'http';
import { CLIPROXY_DEFAULT_PORT } from '../../cliproxy/config/port-manager';
const router = Router();
router.all('/*', (req: Request, res: Response) => {
// Strip the mount prefix — req.url already has it removed by Express
const targetPath = req.url || '/';
const options: http.RequestOptions = {
hostname: '127.0.0.1',
port: CLIPROXY_DEFAULT_PORT,
path: targetPath,
method: req.method,
headers: {
...req.headers,
host: `127.0.0.1:${CLIPROXY_DEFAULT_PORT}`,
},
};
const proxyReq = http.request(options, (proxyRes) => {
res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
proxyRes.pipe(res, { end: true });
});
proxyReq.on('error', () => {
if (!res.headersSent) {
res.status(502).json({ error: 'CLIProxy is not reachable' });
}
});
req.pipe(proxyReq, { end: true });
});
export default router;