Fortschrittsanzeige beim Health-Check (SSE-Streaming)
- Neuer Endpoint /health/run-stream mit Server-Sent Events - Frontend zeigt Fortschrittsbalken: 4/12 + Quellenname + Prozent - Status-Icons pro Quelle (Fehler/Warnung/OK) - Phase Vorschläge wird separat angezeigt - Ergebnis-Zusammenfassung verschwindet nach 5 Sekunden Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -220,26 +220,83 @@ async function handleSuggestion(id, accept) {
|
||||
// --- Health-Check manuell starten ---
|
||||
async function runHealthCheck() {
|
||||
const btn = document.getElementById("runHealthCheckBtn");
|
||||
if (btn) {
|
||||
btn.disabled = true;
|
||||
btn.textContent = "Läuft...";
|
||||
if (!btn) return;
|
||||
btn.disabled = true;
|
||||
|
||||
// Fortschrittsanzeige erstellen
|
||||
let progressEl = document.getElementById("healthProgress");
|
||||
if (!progressEl) {
|
||||
progressEl = document.createElement("div");
|
||||
progressEl.id = "healthProgress";
|
||||
progressEl.style.cssText = "display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius);margin-bottom:16px;font-size:13px;";
|
||||
btn.parentElement.after(progressEl);
|
||||
}
|
||||
progressEl.style.display = "flex";
|
||||
|
||||
function updateProgress(data) {
|
||||
if (data.phase === "check") {
|
||||
const pct = data.total > 0 ? Math.round((data.checked / data.total) * 100) : 0;
|
||||
const statusIcon = data.status === "error" ? "\u2717" : data.status === "warning" ? "\u26A0" : "\u2713";
|
||||
btn.textContent = data.checked + "/" + data.total;
|
||||
progressEl.innerHTML =
|
||||
'<div style="flex:1;">' +
|
||||
'<div style="display:flex;justify-content:space-between;margin-bottom:4px;">' +
|
||||
'<span>' + (data.current ? statusIcon + " " + esc(data.current) : "Starte...") + '</span>' +
|
||||
'<span class="text-secondary">' + pct + '%</span>' +
|
||||
'</div>' +
|
||||
'<div style="height:4px;background:var(--bg-tertiary);border-radius:2px;overflow:hidden;">' +
|
||||
'<div style="height:100%;width:' + pct + '%;background:var(--accent);transition:width 0.3s;"></div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
} else if (data.phase === "suggestions") {
|
||||
progressEl.innerHTML = '<span class="text-secondary">Generiere Vorschl\u00e4ge...</span>';
|
||||
} else if (data.phase === "done") {
|
||||
progressEl.innerHTML = '<span class="text-success">' + data.checked + ' gepr\u00fcft, ' + data.issues + ' Probleme, ' + data.suggestions + ' Vorschl\u00e4ge</span>';
|
||||
setTimeout(function() { progressEl.style.display = "none"; }, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await API.post("/api/sources/health/run");
|
||||
alert(
|
||||
`Health-Check abgeschlossen: ${result.checked} Quellen geprüft, ` +
|
||||
`${result.issues} Probleme gefunden. ` +
|
||||
`${result.suggestions} neue Vorschläge generiert.`,
|
||||
);
|
||||
const headers = { "Content-Type": "application/json" };
|
||||
if (API.token) headers["Authorization"] = "Bearer " + API.token;
|
||||
|
||||
const response = await fetch("/api/sources/health/run-stream", {
|
||||
method: "POST",
|
||||
headers: headers,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("HTTP " + response.status);
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = "";
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
|
||||
const lines = buffer.split("\n");
|
||||
buffer = lines.pop() || "";
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("data: ")) {
|
||||
try {
|
||||
const data = JSON.parse(line.slice(6));
|
||||
updateProgress(data);
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadHealthData();
|
||||
} catch (err) {
|
||||
alert("Fehler: " + err.message);
|
||||
progressEl.innerHTML = '<span class="text-danger">Fehler: ' + esc(err.message) + '</span>';
|
||||
} finally {
|
||||
if (btn) {
|
||||
btn.disabled = false;
|
||||
btn.textContent = "Jetzt prüfen";
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.textContent = "Jetzt pr\u00fcfen";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren