Merge branch 'dev' of ssh://git.commumedia.org:2222/alex/headquarter into dev

This commit is contained in:
Fusion
2026-05-24 23:21:28 +02:00
+27 -3
View File
@@ -56,6 +56,7 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
});
const lastPingRef = useRef<number>(0);
const heartbeatCheckRef = useRef<number | null>(null);
const isUnmountingRef = useRef(false);
const calculateFontSize = useCallback(() => {
if (!isMobile) return fontSize;
@@ -70,10 +71,12 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
const wsHost = apiUrl.replace(/^https?:\/\//, "").replace(/\/+$/, "");
const wsUrl = `${wsProtocol}//${wsHost}/ws/tool-instances/${instanceId}/terminal`;
console.log(`[Terminal WS] Connecting to ${wsUrl} (attempt ${reconnectAttemptsRef.current + 1}/${RECONNECT_ATTEMPTS + 1})`);
const ws = new WebSocket(wsUrl);
wsRef.current = ws;
ws.onopen = () => {
console.log(`[Terminal WS] Connected successfully`);
setStatus("connected");
setError(null);
reconnectAttemptsRef.current = 0;
@@ -94,9 +97,10 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
}
heartbeatCheckRef.current = window.setInterval(() => {
const elapsed = Date.now() - lastPingRef.current;
console.log(`[Terminal WS] Heartbeat check: lastPing=${elapsed}ms ago`);
if (elapsed > 60000) {
// No ping for 60 seconds, connection may be dead
console.warn("Terminal heartbeat timeout, reconnecting...");
console.warn("[Terminal WS] Heartbeat timeout (>60s), closing connection");
ws.close(4000, "Heartbeat timeout");
}
}, 30000);
@@ -137,6 +141,7 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
} else if (msg.type === "ping") {
// Respond with pong and update last ping time
lastPingRef.current = Date.now();
console.log(`[Terminal WS] Received ping, sending pong`);
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: "pong" }));
}
@@ -148,6 +153,7 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
};
ws.onclose = (event) => {
console.log(`[Terminal WS] Connection closed: code=${event.code}, reason="${event.reason}", wasClean=${event.wasClean}, attempts=${reconnectAttemptsRef.current}`);
setStatus("disconnected");
// Clean up heartbeat check
@@ -163,19 +169,30 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
if (reconnectAttemptsRef.current < RECONNECT_ATTEMPTS) {
reconnectAttemptsRef.current++;
const delay = RECONNECT_DELAY_BASE * Math.pow(2, reconnectAttemptsRef.current - 1);
console.log(`[Terminal WS] Will retry in ${delay}ms (attempt ${reconnectAttemptsRef.current}/${RECONNECT_ATTEMPTS})`);
setTimeout(() => {
if (isUnmountingRef.current) {
console.log(`[Terminal WS] Component unmounting, skipping reconnect`);
return;
}
if (document.visibilityState !== "hidden") {
connectWebSocket();
} else {
console.log(`[Terminal WS] Tab hidden, skipping reconnect`);
}
}, delay);
} else {
console.log(`[Terminal WS] Max reconnection attempts (${RECONNECT_ATTEMPTS}) reached`);
}
} else if (event.code === 4000) {
// Server closed old connection for concurrent connection - don't reconnect
// The new connection is already established
console.log(`[Terminal WS] Server closed old connection (concurrent/heartbeat)`);
}
};
ws.onerror = () => {
ws.onerror = (error) => {
console.error(`[Terminal WS] Error event fired`, error);
setStatus("error");
setError("WebSocket error");
};
@@ -358,7 +375,9 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
// Visibility API for reconnection
const handleVisibilityChange = () => {
console.log(`[Terminal WS] Visibility changed to: ${document.visibilityState}, wsState=${ws?.readyState}`);
if (document.visibilityState === "visible" && ws && ws.readyState !== WebSocket.OPEN) {
console.log(`[Terminal WS] Tab visible, resetting reconnect attempts and reconnecting`);
reconnectAttemptsRef.current = 0;
connectWebSocket();
}
@@ -366,6 +385,7 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
document.addEventListener("visibilitychange", handleVisibilityChange);
return () => {
isUnmountingRef.current = true;
clearTimeout(resizeTimeout);
clearTimeout(windowResizeTimeout);
clearTimeout(headerHideTimeout);
@@ -373,7 +393,11 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
window.removeEventListener("resize", handleWindowResize);
document.removeEventListener("visibilitychange", handleVisibilityChange);
if (ws) {
ws.close();
ws.close(1000, "Component unmounting");
}
if (heartbeatCheckRef.current) {
window.clearInterval(heartbeatCheckRef.current);
heartbeatCheckRef.current = null;
}
term.dispose();
};