feat(sources): Knopf Jetzt pruefen startet den Health-Check aus dem Portal
- POST /api/sources/health/run (202, bei laufendem Run 409) stoesst den Lauf als BackgroundTask an: eigene DB-Connection, run_health_checks + generate_suggestions aus den geteilten Diensten, exakt nach dem Vorbild des Monitor-Nachtjobs daily_source_health_check. Kein Code-Duplikat. - GET /api/sources/health/run-status liefert den In-Memory-Status (health_run.py, pure Funktionen mit Doppelstart-Sperre, unit-getestet). Nach einem Neustart ist der Status idle, ein partieller Stand wird vom naechsten Lauf archiviert und ueberschrieben (wie beim Nachtjob). - Frontend: Knopf Jetzt pruefen + Zuletzt geprueft in der Stats-Leiste der Quellenliste, Polling alle 4 s, Abschluss-Toast mit geprueft/ Probleme/neue Vorschlaege, danach Liste + Aufgaben-Badge neu laden. Beim Oeffnen des Reiters wird ein laufender Run aus anderer Session erkannt und weiterverfolgt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -114,6 +114,7 @@ async function loadUnifiedSources() {
|
||||
}
|
||||
renderUnifiedStats(stats);
|
||||
filterUnifiedSources();
|
||||
checkHealthRunOnLoad();
|
||||
} catch (err) {
|
||||
console.error("Quellenliste laden fehlgeschlagen:", err);
|
||||
}
|
||||
@@ -194,7 +195,75 @@ function renderUnifiedStats(stats) {
|
||||
if (h.warnings) parts.push(`<span class="sources-stat-item health-warning"><span class="sources-stat-value">${h.warnings}</span> Warnungen</span>`);
|
||||
if (h.ok) parts.push(`<span class="sources-stat-item health-ok"><span class="sources-stat-value">${h.ok}</span> OK</span>`);
|
||||
|
||||
const lastCheck = stats.last_check ? formatDateTime(stats.last_check) : "noch nie";
|
||||
parts.push(`<span class="sources-stat-item health-run-item"><button class="btn btn-secondary btn-small" id="runHealthCheckBtn" onclick="startHealthRun()">Jetzt prüfen</button><span class="text-secondary" id="healthRunInfo">Zuletzt geprüft ${lastCheck}</span></span>`);
|
||||
|
||||
bar.innerHTML = parts.join("");
|
||||
if (healthRunPollTimer) setHealthRunButton(true);
|
||||
}
|
||||
|
||||
// --- Manueller Health-Run (Knopf in der Stats-Leiste) ---
|
||||
let healthRunPollTimer = null;
|
||||
|
||||
async function startHealthRun() {
|
||||
try {
|
||||
await API.post("/api/sources/health/run", {});
|
||||
setHealthRunButton(true);
|
||||
beginHealthRunPolling();
|
||||
showToast("Prüfung gestartet. Das kann einige Minuten dauern.", "info");
|
||||
} catch (err) {
|
||||
if (String(err.message || "").includes("bereits")) {
|
||||
showToast("Es läuft bereits eine Prüfung.", "warning");
|
||||
setHealthRunButton(true);
|
||||
beginHealthRunPolling();
|
||||
} else {
|
||||
showToast("Start fehlgeschlagen. " + err.message, "error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function beginHealthRunPolling() {
|
||||
if (healthRunPollTimer) return;
|
||||
healthRunPollTimer = setInterval(pollHealthRunStatus, 4000);
|
||||
}
|
||||
|
||||
async function pollHealthRunStatus() {
|
||||
try {
|
||||
const st = await API.get("/api/sources/health/run-status");
|
||||
if (st.running) { setHealthRunButton(true); return; }
|
||||
// Lauf beendet
|
||||
if (healthRunPollTimer) { clearInterval(healthRunPollTimer); healthRunPollTimer = null; }
|
||||
setHealthRunButton(false);
|
||||
if (st.result) {
|
||||
const r = st.result;
|
||||
showToast(`Health-Check abgeschlossen. ${r.checked} geprüft, ${r.issues} Probleme, ${r.suggestions} neue Vorschläge.`, "success");
|
||||
} else if (st.error) {
|
||||
showToast("Health-Check fehlgeschlagen. " + st.error, "error");
|
||||
} else {
|
||||
showToast("Prüfung beendet, Ergebnis unbekannt (Server wurde neu gestartet).", "info");
|
||||
}
|
||||
loadUnifiedSources();
|
||||
if (typeof refreshTasksBadge === "function") refreshTasksBadge();
|
||||
} catch (_) { /* nächster Tick versucht es erneut */ }
|
||||
}
|
||||
|
||||
function setHealthRunButton(running) {
|
||||
const btn = document.getElementById("runHealthCheckBtn");
|
||||
if (!btn) return;
|
||||
btn.disabled = running;
|
||||
btn.textContent = running ? "Prüfung läuft…" : "Jetzt prüfen";
|
||||
}
|
||||
|
||||
// Beim Öffnen des Quellen-Reiters einmal nachsehen, ob (z.B. aus einer anderen
|
||||
// Session) gerade ein Lauf aktiv ist, und dann das Polling aufnehmen.
|
||||
async function checkHealthRunOnLoad() {
|
||||
try {
|
||||
const st = await API.get("/api/sources/health/run-status");
|
||||
if (st.running) {
|
||||
setHealthRunButton(true);
|
||||
beginHealthRunPolling();
|
||||
}
|
||||
} catch (_) { /* unkritisch */ }
|
||||
}
|
||||
|
||||
function renderUnifiedSources(sources) {
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren