Merge branch 'dev' of ssh://git.commumedia.org:2222/alex/headquarter into dev
This commit is contained in:
@@ -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();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user