perf(sources): Quellen-Health Tab schneller (Payload-Slim + 60s-Cache)
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.
Dieser Commit ist enthalten in:
@@ -578,10 +578,9 @@ async def get_health(
|
|||||||
|
|
||||||
cursor = await db.execute("""
|
cursor = await db.execute("""
|
||||||
SELECT
|
SELECT
|
||||||
h.id, h.source_id, s.name, s.domain, s.url, s.source_type,
|
h.source_id, s.name, s.domain, s.tenant_id, s.language,
|
||||||
s.tenant_id, s.category, s.language, s.bias,
|
|
||||||
o.name AS org_name,
|
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
|
FROM source_health_checks h
|
||||||
JOIN sources s ON s.id = h.source_id
|
JOIN sources s ON s.id = h.source_id
|
||||||
LEFT JOIN organizations o ON o.id = s.tenant_id
|
LEFT JOIN organizations o ON o.id = s.tenant_id
|
||||||
|
|||||||
@@ -708,7 +708,7 @@
|
|||||||
|
|
||||||
<script src="/static/js/app.js?v=20260509d"></script>
|
<script src="/static/js/app.js?v=20260509d"></script>
|
||||||
<script src="/static/js/sources.js?v=20260509d"></script>
|
<script src="/static/js/sources.js?v=20260509d"></script>
|
||||||
<script src="/static/js/source-health.js?v=20260509d"></script>
|
<script src="/static/js/source-health.js?v=20260509e"></script>
|
||||||
<script src="/static/js/audit.js?v=20260509d"></script>
|
<script src="/static/js/audit.js?v=20260509d"></script>
|
||||||
<div id="toastContainer" class="toast-container" aria-live="polite" aria-atomic="true"></div>
|
<div id="toastContainer" class="toast-container" aria-live="polite" aria-atomic="true"></div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ let suggestionsCache = [];
|
|||||||
let healthFilters = { status: "", check_type: "", org: "all" };
|
let healthFilters = { status: "", check_type: "", org: "all" };
|
||||||
let healthHistoryCache = [];
|
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 = {
|
const CHECK_TYPE_LABELS = {
|
||||||
reachability: "Erreichbarkeit",
|
reachability: "Erreichbarkeit",
|
||||||
@@ -38,7 +43,15 @@ function setupHealthTab() {
|
|||||||
document.addEventListener("DOMContentLoaded", setupHealthTab);
|
document.addEventListener("DOMContentLoaded", setupHealthTab);
|
||||||
|
|
||||||
// --- Health-Daten laden ---
|
// --- 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 {
|
try {
|
||||||
const [health, suggestions, history] = await Promise.all([
|
const [health, suggestions, history] = await Promise.all([
|
||||||
API.get("/api/sources/health"),
|
API.get("/api/sources/health"),
|
||||||
@@ -48,6 +61,7 @@ async function loadHealthData() {
|
|||||||
healthData = health;
|
healthData = health;
|
||||||
suggestionsCache = suggestions;
|
suggestionsCache = suggestions;
|
||||||
healthHistoryCache = history || [];
|
healthHistoryCache = history || [];
|
||||||
|
healthDataCache = { health, suggestions, history: history || [], ts: Date.now() };
|
||||||
renderHealthDashboard();
|
renderHealthDashboard();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Health-Daten laden fehlgeschlagen:", err);
|
console.error("Health-Daten laden fehlgeschlagen:", err);
|
||||||
@@ -289,7 +303,7 @@ async function handleSuggestion(id, accept) {
|
|||||||
if (result.action) {
|
if (result.action) {
|
||||||
showToast("Ergebnis: " + result.action, "success");
|
showToast("Ergebnis: " + result.action, "success");
|
||||||
}
|
}
|
||||||
loadHealthData();
|
loadHealthData(true);
|
||||||
// Grundquellen-Liste auch aktualisieren
|
// Grundquellen-Liste auch aktualisieren
|
||||||
if (typeof loadGlobalSources === "function") loadGlobalSources();
|
if (typeof loadGlobalSources === "function") loadGlobalSources();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -371,7 +385,7 @@ async function runHealthCheck() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadHealthData();
|
loadHealthData(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
progressEl.innerHTML = '<span class="text-danger">Fehler: ' + esc(err.message) + '</span>';
|
progressEl.innerHTML = '<span class="text-danger">Fehler: ' + esc(err.message) + '</span>';
|
||||||
} finally {
|
} finally {
|
||||||
@@ -419,7 +433,7 @@ async function searchFix(btn) {
|
|||||||
msg += `\n\nKosten: $${result.cost_usd.toFixed(2)}`;
|
msg += `\n\nKosten: $${result.cost_usd.toFixed(2)}`;
|
||||||
}
|
}
|
||||||
showToast(msg, "info");
|
showToast(msg, "info");
|
||||||
loadHealthData();
|
loadHealthData(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast("Fehler: " + err.message, "error");
|
showToast("Fehler: " + err.message, "error");
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren