From cf54ff8b45e9bc213ce52a6f1143609327d9d8d2 Mon Sep 17 00:00:00 2001 From: matitos Date: Mon, 21 Sep 2026 15:57:50 +0000 Subject: [PATCH] Don't let a failing initial fetch block the socket/polling fallback onMounted awaited the initial REST calls before setting up the socket and the 6s polling fallback, so any rejected request aborted the handler and left the badge stuck on "connecting". Set up the socket and fallback first, make the initial loads best-effort, and log [ws] connect/open/close for debugging. --- frontend/src/App.vue | 25 ++++++++++++++++--------- frontend/src/api.js | 3 +++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 6c57c73..1055e1c 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -159,14 +159,11 @@ async function ensureFreshBuild() { } } -onMounted(async () => { +onMounted(() => { ensureFreshBuild() - await Promise.all([ - loadStrategies(), - loadPresets(), - api.getStatus().then((s) => (status.value = s)), - refreshAll(), - ]) + + // Set up the socket and the polling fallback first, so a failing initial + // fetch can never prevent them (that left the badge stuck on "connecting"). socket = api.openSocket({ onMessage: handleMessage, onOpen: () => { @@ -185,9 +182,19 @@ onMounted(async () => { graceTimer = setTimeout(() => { if (!connected.value) pollingFallback.value = true }, 6000) + + // Initial loads are best-effort; the intervals below keep retrying. + api.getStatus().then((s) => (status.value = s)).catch(() => {}) + refreshAll().catch(() => {}) + loadStrategies().catch(() => {}) + loadPresets().catch(() => {}) + // Poll fast enough that the UI feels live even when it is the only channel. - pollTimer = setInterval(refreshAll, 3000) - statusTimer = setInterval(() => api.getStatus().then((s) => (status.value = s)), 3000) + pollTimer = setInterval(() => refreshAll().catch(() => {}), 3000) + statusTimer = setInterval( + () => api.getStatus().then((s) => (status.value = s)).catch(() => {}), + 3000, + ) }) onUnmounted(() => { diff --git a/frontend/src/api.js b/frontend/src/api.js index e20c073..80f680e 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -48,6 +48,7 @@ export function openSocket({ onMessage, onOpen, onClose, onError }) { if (closed) return let opened = false let reported = false + console.debug('[ws] connecting to', url) socket = new WebSocket(url) // Report at most one failure per attempt (Safari often fires error *and* @@ -72,11 +73,13 @@ export function openSocket({ onMessage, onOpen, onClose, onError }) { opened = true clearTimeout(openTimeout) attempt = 0 + console.debug('[ws] open') onOpen?.() } socket.onmessage = (event) => onMessage?.(JSON.parse(event.data)) socket.onclose = (event) => { clearTimeout(openTimeout) + console.debug('[ws] close', event.code, event.reason) onClose?.() if (!opened) { // 1008 = policy violation (token rejected); 1006 = abnormal close.