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:
47
src/health_run.py
Normale Datei
47
src/health_run.py
Normale Datei
@@ -0,0 +1,47 @@
|
||||
"""In-Memory-Status für den manuellen Health-Run des Portals.
|
||||
|
||||
Pure Funktionen ohne DB und IO, damit die Zustandsübergänge unit-testbar sind.
|
||||
Der Status lebt nur im Prozess. Nach einem Neustart ist er wieder idle, ein
|
||||
dabei abgebrochener Lauf hinterlässt höchstens einen partiellen Stand in
|
||||
source_health_checks, den der nächste Lauf archiviert und überschreibt
|
||||
(identisch zum Verhalten des Monitor-Nachtjobs um 04:00 Uhr).
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def new_state() -> dict:
|
||||
return {
|
||||
"running": False,
|
||||
"started_at": None,
|
||||
"finished_at": None,
|
||||
"result": None,
|
||||
"error": None,
|
||||
}
|
||||
|
||||
|
||||
def _now_iso(now=None) -> str:
|
||||
return now if now is not None else datetime.now().isoformat(timespec="seconds")
|
||||
|
||||
|
||||
def try_start(state: dict, now=None) -> bool:
|
||||
"""Setzt den Lauf auf running. False, wenn schon einer läuft."""
|
||||
if state.get("running"):
|
||||
return False
|
||||
state["running"] = True
|
||||
state["started_at"] = _now_iso(now)
|
||||
state["finished_at"] = None
|
||||
state["result"] = None
|
||||
state["error"] = None
|
||||
return True
|
||||
|
||||
|
||||
def mark_finished(state: dict, result: dict, now=None) -> None:
|
||||
state["running"] = False
|
||||
state["finished_at"] = _now_iso(now)
|
||||
state["result"] = result
|
||||
|
||||
|
||||
def mark_failed(state: dict, error: str, now=None) -> None:
|
||||
state["running"] = False
|
||||
state["finished_at"] = _now_iso(now)
|
||||
state["error"] = error
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren