feat: add HTTP fallback when WebSocket unavailable
WebSocket connections may not work through Cloudflare Tunnel. Added HTTP polling fallback that: - Loads initial data via /api/projects and /api/tasks - Falls back to 5-second polling if WebSocket fails - Shows "Polling" status badge when in fallback mode 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d2a229ade2
commit
0368963bff
|
|
@ -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');
|
||||
if (!usePolling) {
|
||||
setTimeout(connectWebSocket, 3000);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue