From 4ebb0bc9c15232126584061a3afa2b763f53f4c8 Mon Sep 17 00:00:00 2001 From: Fabian Schieder Date: Sun, 1 Mar 2026 14:49:31 +0100 Subject: [PATCH] Refactor server status fetching to use a dynamic endpoint and improve JSON parsing error handling --- assets/js/main.js | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/assets/js/main.js b/assets/js/main.js index 9c9bdc5..5c00330 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -99,6 +99,8 @@ document.querySelectorAll('.card').forEach(card => { const cards = Array.from(document.querySelectorAll('.card[data-status-url]')); if (cards.length === 0) return; + const endpoint = './server_status.php'; + const normalize = (url) => { if (!url) return ''; return String(url).replace(/\/+$/, ''); @@ -106,14 +108,10 @@ document.querySelectorAll('.card').forEach(card => { const parsePossiblyUtf16Json = (text) => { if (!text) return null; - - // If server accidentally delivered UTF-16LE bytes, the browser may decode it with lots of \u0000. - // Remove NULs and common BOM remnants. const cleaned = String(text) - .replace(/^\uFEFF/, '') + .replace(/^?\uFEFF/, '') .replace(/\u0000/g, '') .trim(); - return JSON.parse(cleaned); }; @@ -121,6 +119,7 @@ document.querySelectorAll('.card').forEach(card => { const byUrl = payload && payload.byUrl ? payload.byUrl : {}; const byUrlNormalized = payload && payload.byUrlNormalized ? payload.byUrlNormalized : {}; + let applied = 0; for (const card of cards) { const key = normalize(card.getAttribute('data-status-url')); const svc = byUrl[key] || byUrl[key + '/'] || byUrlNormalized[key] || null; @@ -143,22 +142,29 @@ document.querySelectorAll('.card').forEach(card => { badge.textContent = 'Unbekannt'; badge.title = svc.detail || 'Unbekannt'; } + applied++; } + + console.debug('[server-status] applied badges:', applied); }; const fetchAndApply = async () => { - const res = await fetch('/server_status.php', { cache: 'no-store' }); - if (!res.ok) throw new Error('status_fetch_failed'); + const res = await fetch(endpoint, { cache: 'no-store' }); + if (!res.ok) throw new Error('status_fetch_failed_' + res.status); - // Use text() + robust JSON parse to survive wrong encoding (UTF-16/NULs). const text = await res.text(); - const payload = parsePossiblyUtf16Json(text); + let payload; + try { + payload = parsePossiblyUtf16Json(text); + } catch (e) { + console.warn('[server-status] JSON parse failed, first 120 chars:', text.slice(0, 120)); + throw e; + } if (!payload) throw new Error('status_json_empty'); applyPayload(payload); }; - // Nach ~1s einmal aktualisieren, dann (falls nötig) 2 schnelle Retries. const delays = [1000, 1000, 1500]; for (const d of delays) { await new Promise(r => setTimeout(r, d)); @@ -166,7 +172,7 @@ document.querySelectorAll('.card').forEach(card => { await fetchAndApply(); break; } catch (e) { - // keep unknown and retry + console.warn('[server-status] update failed:', e); } } })();