From f6af21e6cb2d834fbf871fd731a7fdb31d986f2d Mon Sep 17 00:00:00 2001 From: claude-dev Date: Sat, 9 May 2026 12:33:30 +0000 Subject: [PATCH] perf(sources): Quellen-Health Tab schneller (Payload-Slim + 60s-Cache) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tab "Quellen Health" lädt deutlich schneller: 1. /api/sources/health: SELECT reduziert auf nur die im Frontend wirklich gerenderten Felder. Weg sind: h.id, s.url, s.source_type, s.category, s.bias, h.details, h.checked_at. Response-Größe sinkt damit von ~198 KB auf grob die Hälfte (bei 519 Health-Checks) ohne UI-Verlust. 2. source-health.js: 60-Sekunden In-Memory-Cache fürs loadHealthData. Tab hin und her klicken ist damit instant statt jedes Mal voller Reload + Render der 519 Tabellen-Zeilen. Bei Mutationen (Vorschlag annehmen/ablehnen, run-stream beendet, search-fix) wird mit loadHealthData(true) der Cache umgangen, damit frische Daten gezeigt werden. 3. dashboard.html: Cache-Buster für source-health.js auf 20260509e gebumpt. --- src/routers/sources.py | 5 ++--- src/static/dashboard.html | 2 +- src/static/js/source-health.js | 22 ++++++++++++++++++---- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/routers/sources.py b/src/routers/sources.py index dd96608..3d7f737 100644 --- a/src/routers/sources.py +++ b/src/routers/sources.py @@ -578,10 +578,9 @@ async def get_health( cursor = await db.execute(""" SELECT - h.id, h.source_id, s.name, s.domain, s.url, s.source_type, - s.tenant_id, s.category, s.language, s.bias, + h.source_id, s.name, s.domain, s.tenant_id, s.language, o.name AS org_name, - h.check_type, h.status, h.message, h.details, h.checked_at + h.check_type, h.status, h.message FROM source_health_checks h JOIN sources s ON s.id = h.source_id LEFT JOIN organizations o ON o.id = s.tenant_id diff --git a/src/static/dashboard.html b/src/static/dashboard.html index 61b4d41..e4e7a8c 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -708,7 +708,7 @@ - +
diff --git a/src/static/js/source-health.js b/src/static/js/source-health.js index 6684da4..5c45cc4 100644 --- a/src/static/js/source-health.js +++ b/src/static/js/source-health.js @@ -6,6 +6,11 @@ let suggestionsCache = []; let healthFilters = { status: "", check_type: "", org: "all" }; let healthHistoryCache = []; +// 60-Sekunden-Cache, damit Tab-Wechsel nicht jedes Mal die volle Antwort neu lädt. +// Bei Mutationen (Vorschlag annehmen/ablehnen, run-stream, search-fix) wird mit force=true neu geladen. +let healthDataCache = { health: null, suggestions: null, history: null, ts: 0 }; +const HEALTH_CACHE_TTL_MS = 60000; + const CHECK_TYPE_LABELS = { reachability: "Erreichbarkeit", @@ -38,7 +43,15 @@ function setupHealthTab() { document.addEventListener("DOMContentLoaded", setupHealthTab); // --- Health-Daten laden --- -async function loadHealthData() { +async function loadHealthData(force = false) { + const now = Date.now(); + if (!force && healthDataCache.health && (now - healthDataCache.ts) < HEALTH_CACHE_TTL_MS) { + healthData = healthDataCache.health; + suggestionsCache = healthDataCache.suggestions; + healthHistoryCache = healthDataCache.history; + renderHealthDashboard(); + return; + } try { const [health, suggestions, history] = await Promise.all([ API.get("/api/sources/health"), @@ -48,6 +61,7 @@ async function loadHealthData() { healthData = health; suggestionsCache = suggestions; healthHistoryCache = history || []; + healthDataCache = { health, suggestions, history: history || [], ts: Date.now() }; renderHealthDashboard(); } catch (err) { console.error("Health-Daten laden fehlgeschlagen:", err); @@ -289,7 +303,7 @@ async function handleSuggestion(id, accept) { if (result.action) { showToast("Ergebnis: " + result.action, "success"); } - loadHealthData(); + loadHealthData(true); // Grundquellen-Liste auch aktualisieren if (typeof loadGlobalSources === "function") loadGlobalSources(); } catch (err) { @@ -371,7 +385,7 @@ async function runHealthCheck() { } } - loadHealthData(); + loadHealthData(true); } catch (err) { progressEl.innerHTML = 'Fehler: ' + esc(err.message) + ''; } finally { @@ -419,7 +433,7 @@ async function searchFix(btn) { msg += `\n\nKosten: $${result.cost_usd.toFixed(2)}`; } showToast(msg, "info"); - loadHealthData(); + loadHealthData(true); } catch (err) { showToast("Fehler: " + err.message, "error"); } finally {