fix: 🐛 client timeouts

This commit is contained in:
2026-05-23 15:40:13 +00:00
parent c7ffdf81c2
commit 187a3c7882
3 changed files with 82 additions and 33 deletions
+39 -26
View File
@@ -24,7 +24,7 @@ let ws: WebSocket | null = null;
let term: Terminal | null = null;
let fit: FitAddon | null = null;
let ro: ResizeObserver | null = null;
let pingInterval: NodeJS.Timeout | null = null;
let visibilityHandler: (() => void) | null = null;
function wsUrl(hostId: number): string {
const proto = location.protocol === "https:" ? "wss:" : "ws:";
@@ -37,6 +37,12 @@ function sendResize() {
ws.send(JSON.stringify({ type: "resize", ...dims }));
}
function sendPing() {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: "ping" }));
}
}
function fitAndResize() {
if (!fit || !term || !props.visible) return;
try {
@@ -47,6 +53,25 @@ function fitAndResize() {
}
}
function isControlMessage(raw: string): boolean {
try {
const o = JSON.parse(raw) as { type?: string; conn_id?: string };
if (o.type === "ready" && o.conn_id) {
connId.value = o.conn_id;
status.value = "";
fitAndResize();
term?.focus();
return true;
}
if (o.type === "keepalive" || o.type === "pong") {
return true;
}
} catch {
/* not JSON control traffic */
}
return false;
}
onMounted(async () => {
await nextTick();
if (!termEl.value) return;
@@ -87,29 +112,12 @@ onMounted(async () => {
ws.onopen = () => {
status.value = "Handshaking…";
sendResize();
// Send ping every 60 seconds to keep connection alive
pingInterval = setInterval(() => {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: "ping" }));
}
}, 60000);
};
ws.onmessage = (ev) => {
if (!term) return;
if (typeof ev.data === "string") {
try {
const o = JSON.parse(ev.data) as { type?: string; conn_id?: string };
if (o.type === "ready" && o.conn_id) {
connId.value = o.conn_id;
status.value = "";
fitAndResize();
term.focus();
return;
}
} catch {
/* fall through */
}
if (isControlMessage(ev.data)) return;
term.write(ev.data);
return;
}
@@ -122,22 +130,26 @@ onMounted(async () => {
};
ws.onclose = () => {
if (pingInterval) {
clearInterval(pingInterval);
pingInterval = null;
}
if (!connId.value) {
status.value = "Disconnected";
} else {
status.value = "Session ended";
}
};
visibilityHandler = () => {
if (document.visibilityState === "visible") {
sendPing();
fitAndResize();
}
};
document.addEventListener("visibilitychange", visibilityHandler);
});
onUnmounted(() => {
if (pingInterval) {
clearInterval(pingInterval);
pingInterval = null;
if (visibilityHandler) {
document.removeEventListener("visibilitychange", visibilityHandler);
visibilityHandler = null;
}
ro?.disconnect();
ro = null;
@@ -155,6 +167,7 @@ watch(
await nextTick();
fitAndResize();
term?.focus();
sendPing();
}
},
);