feat: enhance WebSocket connection handling with reconnect logic and state management
All checks were successful
CI / test (push) Successful in 6s

This commit is contained in:
Space-Banane
2026-05-27 18:41:30 +02:00
parent 336ef97c49
commit 375c1073ec

View File

@@ -61,8 +61,10 @@ def monitoring_page_html() -> str:
token: localStorage.getItem("screenjob_token") || "", token: localStorage.getItem("screenjob_token") || "",
jobs: [], jobs: [],
selectedJobId: null, selectedJobId: null,
ws: null ws: null,
wsReconnectTimer: null
}; };
const manuallyClosedSockets = new WeakSet();
tokenInput.value = state.token; tokenInput.value = state.token;
function authHeaders() { function authHeaders() {
@@ -119,15 +121,26 @@ def monitoring_page_html() -> str:
} }
function pushEventLine(obj) { function pushEventLine(obj) {
if (!obj || !obj.job_id || !obj.event_type) return;
const line = document.createElement("div"); const line = document.createElement("div");
line.className = "border-b border-slate-800 pb-1"; line.className = "border-b border-slate-800 pb-1";
line.textContent = `[${obj.ts}] ${obj.job_id} step=${obj.step} ${obj.event_type} ${JSON.stringify(obj.payload || {})}`; const ts = obj.ts || "-";
const step = (obj.step ?? "-");
line.textContent = `[${ts}] ${obj.job_id} step=${step} ${obj.event_type} ${JSON.stringify(obj.payload || {})}`;
eventsEl.prepend(line); eventsEl.prepend(line);
while (eventsEl.childNodes.length > 400) { while (eventsEl.childNodes.length > 400) {
eventsEl.removeChild(eventsEl.lastChild); eventsEl.removeChild(eventsEl.lastChild);
} }
} }
function scheduleWsReconnect() {
if (state.wsReconnectTimer || !state.token) return;
state.wsReconnectTimer = setTimeout(() => {
state.wsReconnectTimer = null;
connectWs();
}, 1200);
}
function updateLatestVisualFromEvent(ev) { function updateLatestVisualFromEvent(ev) {
if (!ev || ev.event_type !== "visual_update") return; if (!ev || ev.event_type !== "visual_update") return;
if (!state.selectedJobId || ev.job_id !== state.selectedJobId) return; if (!state.selectedJobId || ev.job_id !== state.selectedJobId) return;
@@ -164,16 +177,17 @@ def monitoring_page_html() -> str:
} }
function connectWs() { function connectWs() {
if (state.ws) {
try { state.ws.close(); } catch (_) {}
}
if (!state.token) return; if (!state.token) return;
if (state.ws && (state.ws.readyState === WebSocket.OPEN || state.ws.readyState === WebSocket.CONNECTING)) {
return;
}
const scheme = location.protocol === "https:" ? "wss" : "ws"; const scheme = location.protocol === "https:" ? "wss" : "ws";
const ws = new WebSocket(`${scheme}://${location.host}/ws?token=${encodeURIComponent(state.token)}`); const ws = new WebSocket(`${scheme}://${location.host}/ws?token=${encodeURIComponent(state.token)}`);
state.ws = ws; state.ws = ws;
ws.onmessage = async (event) => { ws.onmessage = async (event) => {
try { try {
const payload = JSON.parse(event.data); const payload = JSON.parse(event.data);
if (!payload || payload.event_type === "connected") return;
pushEventLine(payload); pushEventLine(payload);
updateLatestVisualFromEvent(payload); updateLatestVisualFromEvent(payload);
if (!state.selectedJobId || payload.job_id === state.selectedJobId) { if (!state.selectedJobId || payload.job_id === state.selectedJobId) {
@@ -185,7 +199,14 @@ def monitoring_page_html() -> str:
console.error(err); console.error(err);
} }
}; };
ws.onclose = () => setTimeout(connectWs, 1200); ws.onclose = () => {
if (state.ws === ws) state.ws = null;
if (manuallyClosedSockets.has(ws)) {
manuallyClosedSockets.delete(ws);
return;
}
scheduleWsReconnect();
};
} }
async function fullRefresh() { async function fullRefresh() {
@@ -197,6 +218,15 @@ def monitoring_page_html() -> str:
async function connect() { async function connect() {
state.token = tokenInput.value.trim(); state.token = tokenInput.value.trim();
localStorage.setItem("screenjob_token", state.token); localStorage.setItem("screenjob_token", state.token);
if (state.ws) {
manuallyClosedSockets.add(state.ws);
try { state.ws.close(); } catch (_) {}
state.ws = null;
}
if (state.wsReconnectTimer) {
clearTimeout(state.wsReconnectTimer);
state.wsReconnectTimer = null;
}
await fullRefresh(); await fullRefresh();
connectWs(); connectWs();
} }