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:
@@ -924,6 +924,10 @@ input[type="date"].filter-select { padding: 6px 10px; }
|
||||
.health-badge-btn { cursor: pointer; }
|
||||
.health-badge-btn:hover { filter: brightness(1.25); text-decoration: underline; }
|
||||
|
||||
/* "Jetzt pruefen"-Knopf in der Stats-Leiste */
|
||||
.health-run-item { display: inline-flex; align-items: center; gap: 8px; }
|
||||
.health-run-item #healthRunInfo { font-size: 12px; }
|
||||
|
||||
/* Checkbox-Spalte: nur sichtbar bei Herkunfts-Filter "Kundenquellen" */
|
||||
.select-col { display: none; }
|
||||
table.show-select .select-col { display: table-cell; }
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<title>AegisSight Monitor-Verwaltung</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
||||
<link rel="apple-touch-icon" href="/static/favicon.svg">
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=20260725i">
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=20260725j">
|
||||
<script>(function(){var t=localStorage.getItem('portal_theme');if(t==='light')document.documentElement.setAttribute('data-theme','light');try{var a=JSON.parse(localStorage.getItem('osint_a11y')||'{}');Object.keys(a).forEach(function(k){if(a[k])document.documentElement.setAttribute('data-a11y-'+k,'true');});}catch(e){}})()</script>
|
||||
|
||||
<style>
|
||||
@@ -1006,7 +1006,7 @@
|
||||
|
||||
<script src="/static/js/a11y.js?v=20260725a"></script>
|
||||
<script src="/static/js/app.js?v=20260725s"></script>
|
||||
<script src="/static/js/sources.js?v=20260725c"></script>
|
||||
<script src="/static/js/sources.js?v=20260725d"></script>
|
||||
<script src="/static/js/aufgaben.js?v=20260725a"></script>
|
||||
<script src="/static/js/x-scraper.js?v=20260522a"></script>
|
||||
<script src="/static/js/audit.js?v=20260509d"></script>
|
||||
|
||||
@@ -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