feat: enhance WebSocket connection handling with reconnect logic and state management
All checks were successful
CI / test (push) Successful in 6s
All checks were successful
CI / test (push) Successful in 6s
This commit is contained in:
42
src/ui.py
42
src/ui.py
@@ -61,8 +61,10 @@ def monitoring_page_html() -> str:
|
||||
token: localStorage.getItem("screenjob_token") || "",
|
||||
jobs: [],
|
||||
selectedJobId: null,
|
||||
ws: null
|
||||
ws: null,
|
||||
wsReconnectTimer: null
|
||||
};
|
||||
const manuallyClosedSockets = new WeakSet();
|
||||
tokenInput.value = state.token;
|
||||
|
||||
function authHeaders() {
|
||||
@@ -119,15 +121,26 @@ def monitoring_page_html() -> str:
|
||||
}
|
||||
|
||||
function pushEventLine(obj) {
|
||||
if (!obj || !obj.job_id || !obj.event_type) return;
|
||||
const line = document.createElement("div");
|
||||
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);
|
||||
while (eventsEl.childNodes.length > 400) {
|
||||
eventsEl.removeChild(eventsEl.lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleWsReconnect() {
|
||||
if (state.wsReconnectTimer || !state.token) return;
|
||||
state.wsReconnectTimer = setTimeout(() => {
|
||||
state.wsReconnectTimer = null;
|
||||
connectWs();
|
||||
}, 1200);
|
||||
}
|
||||
|
||||
function updateLatestVisualFromEvent(ev) {
|
||||
if (!ev || ev.event_type !== "visual_update") return;
|
||||
if (!state.selectedJobId || ev.job_id !== state.selectedJobId) return;
|
||||
@@ -164,16 +177,17 @@ def monitoring_page_html() -> str:
|
||||
}
|
||||
|
||||
function connectWs() {
|
||||
if (state.ws) {
|
||||
try { state.ws.close(); } catch (_) {}
|
||||
}
|
||||
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 ws = new WebSocket(`${scheme}://${location.host}/ws?token=${encodeURIComponent(state.token)}`);
|
||||
state.ws = ws;
|
||||
ws.onmessage = async (event) => {
|
||||
try {
|
||||
const payload = JSON.parse(event.data);
|
||||
if (!payload || payload.event_type === "connected") return;
|
||||
pushEventLine(payload);
|
||||
updateLatestVisualFromEvent(payload);
|
||||
if (!state.selectedJobId || payload.job_id === state.selectedJobId) {
|
||||
@@ -185,7 +199,14 @@ def monitoring_page_html() -> str:
|
||||
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() {
|
||||
@@ -197,6 +218,15 @@ def monitoring_page_html() -> str:
|
||||
async function connect() {
|
||||
state.token = tokenInput.value.trim();
|
||||
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();
|
||||
connectWs();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user