diff --git a/src/aggregator/web/index.html b/src/aggregator/web/index.html index a511fb8..a6f14eb 100644 --- a/src/aggregator/web/index.html +++ b/src/aggregator/web/index.html @@ -537,19 +537,77 @@ setTimeout(() => toast.remove(), 3000); } + let usePolling = false; + let pollInterval = null; + + // HTTP fallback to load initial data + async function loadDataViaHTTP() { + try { + const [projectsRes, tasksRes] = await Promise.all([ + fetch('/api/projects'), + fetch('/api/tasks') + ]); + if (projectsRes.ok && tasksRes.ok) { + projects = await projectsRes.json(); + tasks = await tasksRes.json(); + renderProjects(); + renderTasks(); + updateProjectDropdown(); + return true; + } + } catch (e) { + console.error('HTTP load failed:', e); + } + return false; + } + + // Start polling for updates (fallback when WebSocket unavailable) + function startPolling() { + if (pollInterval) return; + usePolling = true; + document.getElementById('status-badge').textContent = 'Polling'; + pollInterval = setInterval(loadDataViaHTTP, 5000); + } + function connectWebSocket() { const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; ws = new WebSocket(`${protocol}//${location.host}`); + // Timeout - if WS doesn't connect in 5s, fall back to HTTP + const wsTimeout = setTimeout(async () => { + if (ws.readyState !== WebSocket.OPEN) { + console.log('WebSocket timeout, falling back to HTTP'); + ws.close(); + await loadDataViaHTTP(); + startPolling(); + } + }, 5000); + ws.onopen = () => { + clearTimeout(wsTimeout); + usePolling = false; + if (pollInterval) { + clearInterval(pollInterval); + pollInterval = null; + } document.getElementById('status-badge').textContent = 'Live'; document.getElementById('status-badge').classList.remove('offline'); }; + ws.onerror = async () => { + clearTimeout(wsTimeout); + console.log('WebSocket error, falling back to HTTP'); + await loadDataViaHTTP(); + startPolling(); + }; + ws.onclose = () => { - document.getElementById('status-badge').textContent = 'Reconnecting...'; + clearTimeout(wsTimeout); + document.getElementById('status-badge').textContent = usePolling ? 'Polling' : 'Reconnecting...'; document.getElementById('status-badge').classList.add('offline'); - setTimeout(connectWebSocket, 3000); + if (!usePolling) { + setTimeout(connectWebSocket, 3000); + } }; ws.onmessage = (event) => {