From 309bfbbc196fd9c6d2f12cc3e92999b42bb3df21 Mon Sep 17 00:00:00 2001 From: Prateek Date: Sat, 7 Mar 2026 20:28:00 +0530 Subject: [PATCH] fix: DirectTerminal WebSocket URL behind reverse proxy When the AO dashboard is served behind a reverse proxy (e.g. Caddy -> FastAPI -> Next.js), the browser cannot reach the direct terminal WebSocket server on port 14801. The connection attempt to wss://hostname:14801/ws times out, leaving terminals stuck at 'Connecting...'. Fix: detect when running on a standard port (443/80, indicating a reverse proxy) and use a path-based WebSocket endpoint (/ao-terminal-ws) instead of the direct port. This allows the proxy to forward WebSocket connections to the terminal server. Supports three modes: 1. NEXT_PUBLIC_TERMINAL_WS_PATH env var (explicit path override) 2. Auto-detect reverse proxy (standard port -> /ao-terminal-ws) 3. Direct port access (dev mode, non-standard port -> :14801) --- packages/web/src/components/DirectTerminal.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/web/src/components/DirectTerminal.tsx b/packages/web/src/components/DirectTerminal.tsx index 5a5a51b12..4bb2caf92 100644 --- a/packages/web/src/components/DirectTerminal.tsx +++ b/packages/web/src/components/DirectTerminal.tsx @@ -184,10 +184,23 @@ export function DirectTerminal({ fit.fit(); // WebSocket URL (stable across reconnects) + // When accessed via reverse proxy (HTTPS on standard port), use path-based + // WebSocket endpoint instead of direct port access. const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; const hostname = window.location.hostname; - const port = process.env.NEXT_PUBLIC_DIRECT_TERMINAL_PORT ?? "14801"; - const wsUrl = `${protocol}//${hostname}:${port}/ws?session=${encodeURIComponent(sessionId)}`; + const proxyWsPath = process.env.NEXT_PUBLIC_TERMINAL_WS_PATH; + let wsUrl: string; + if (proxyWsPath) { + // Path-based proxy (e.g. /ao-terminal-ws) + wsUrl = `${protocol}//${hostname}${proxyWsPath}?session=${encodeURIComponent(sessionId)}`; + } else if (window.location.port === "" || window.location.port === "443" || window.location.port === "80") { + // Behind reverse proxy on standard port — use path-based endpoint + wsUrl = `${protocol}//${hostname}/ao-terminal-ws?session=${encodeURIComponent(sessionId)}`; + } else { + // Direct access (dev mode) — use direct port + const port = process.env.NEXT_PUBLIC_DIRECT_TERMINAL_PORT ?? "14801"; + wsUrl = `${protocol}//${hostname}:${port}/ws?session=${encodeURIComponent(sessionId)}`; + } // ── Preserve selection while terminal receives output ──────── // xterm.js clears the selection on every terminal.write(). We