feat(terminal): enhance WebSocket connection handling and port management

- Refactor WebSocket connection logic to dynamically fetch the port from the server, improving flexibility.
- Introduce global variables to manage WebSocket server state and port, allowing for better control and error handling.
- Update terminal drawer component to use the new port fetching mechanism, ensuring consistent connection behavior.
This commit is contained in:
kumarabhirup 2026-03-09 10:54:40 -07:00
parent e49b74c990
commit c084caf78c
No known key found for this signature in database
GPG Key ID: DB7CA2289CAB0167
3 changed files with 39 additions and 5 deletions

View File

@ -0,0 +1,9 @@
import { NextResponse } from "next/server";
import { getTerminalPort } from "@/lib/terminal-server";
export const dynamic = "force-dynamic";
export function GET() {
const port = getTerminalPort();
return NextResponse.json({ port });
}

View File

@ -16,7 +16,7 @@ const MIN_DRAWER_HEIGHT = 180;
const MAX_DRAWER_HEIGHT_RATIO = 0.75;
const DEFAULT_DRAWER_HEIGHT = 280;
const STORAGE_KEY = "dench-terminal-height";
const WS_PORT = 3101;
const DEFAULT_WS_PORT = 3101;
const MAX_TERMINALS = 8;
function maxDrawerHeight(): number {
@ -159,7 +159,7 @@ function TerminalViewport({
termRef.current = terminal;
fitRef.current = fitAddon;
const connectWs = () => {
const connectWs = async () => {
if (disposed) return;
// Fit now that the container has layout dimensions
@ -167,8 +167,16 @@ function TerminalViewport({
const cols = terminal.cols > 0 ? terminal.cols : 80;
const rows = terminal.rows > 0 ? terminal.rows : 24;
let wsPort = DEFAULT_WS_PORT;
try {
const res = await fetch("/api/terminal/port");
const json = await res.json();
if (json.port) wsPort = json.port;
} catch {}
if (disposed) return;
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
const wsUrl = `${protocol}//127.0.0.1:${WS_PORT}`;
const wsUrl = `${protocol}//127.0.0.1:${wsPort}`;
const ws = new WebSocket(wsUrl);
wsRef.current = ws;

View File

@ -11,12 +11,19 @@ interface TerminalSession {
const sessions = new Map<WebSocket, TerminalSession>();
let wss: WebSocketServer | null = null;
let didFixSpawnHelper = false;
const _g = globalThis as unknown as {
__terminalWss?: WebSocketServer;
__terminalDidFixSpawnHelper?: boolean;
__terminalPort?: number;
};
let wss: WebSocketServer | null = _g.__terminalWss ?? null;
let didFixSpawnHelper = _g.__terminalDidFixSpawnHelper ?? false;
function ensureSpawnHelperExecutable() {
if (didFixSpawnHelper || process.platform === "win32") return;
didFixSpawnHelper = true;
_g.__terminalDidFixSpawnHelper = true;
try {
const req = createRequire(import.meta.url);
const pkgPath = req.resolve("node-pty/package.json");
@ -163,16 +170,25 @@ export function startTerminalServer(port: number) {
if (wss) return;
wss = new WebSocketServer({ port, host: "127.0.0.1" });
_g.__terminalWss = wss;
wss.on("connection", handleConnection);
wss.on("listening", () => {
_g.__terminalPort = port;
});
wss.on("error", (err) => {
if ((err as NodeJS.ErrnoException).code === "EADDRINUSE") {
console.warn(`[terminal] Port ${port} in use, retrying on ${port + 1}`);
wss = null;
_g.__terminalWss = undefined;
startTerminalServer(port + 1);
}
});
}
export function getTerminalPort(): number | null {
return _g.__terminalPort ?? null;
}
export function stopTerminalServer() {
if (!wss) return;
for (const session of sessions.values()) {
@ -181,4 +197,5 @@ export function stopTerminalServer() {
sessions.clear();
wss.close();
wss = null;
_g.__terminalWss = undefined;
}