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:
@@ -15,6 +15,7 @@ from auth import get_current_admin
|
||||
from database import db_dependency, get_db
|
||||
from audit import log_action, get_client_ip
|
||||
from source_meta import get_meta
|
||||
from health_run import new_state, try_start, mark_finished, mark_failed
|
||||
from config import DB_PATH
|
||||
from shared.source_rules import (
|
||||
discover_source,
|
||||
@@ -847,6 +848,52 @@ async def get_health_history(
|
||||
return [dict(row) for row in await cursor.fetchall()]
|
||||
|
||||
|
||||
# Status des manuellen Health-Runs. In-Memory: nach einem Neustart wieder idle,
|
||||
# ein dabei abgebrochener Lauf hinterlässt höchstens einen partiellen
|
||||
# source_health_checks-Stand, den der nächste Lauf archiviert und heilt.
|
||||
_HEALTH_RUN_STATE = new_state()
|
||||
|
||||
|
||||
async def _health_run_background():
|
||||
"""Manueller Health-Run, exakt nach dem Vorbild des Monitor-Nachtjobs
|
||||
daily_source_health_check (eigene Connection, Checks + Vorschläge)."""
|
||||
from shared.services.source_health import run_health_checks
|
||||
from shared.services.source_suggester import generate_suggestions
|
||||
|
||||
db = await get_db()
|
||||
try:
|
||||
result = await run_health_checks(db)
|
||||
suggestions = await generate_suggestions(db)
|
||||
mark_finished(_HEALTH_RUN_STATE, {**result, "suggestions": suggestions})
|
||||
logger.info(
|
||||
"Manueller Health-Run: %s geprüft, %s Probleme, %s neue Vorschläge",
|
||||
result.get("checked"), result.get("issues"), suggestions,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("Manueller Health-Run fehlgeschlagen: %s", e, exc_info=True)
|
||||
mark_failed(_HEALTH_RUN_STATE, str(e))
|
||||
finally:
|
||||
await db.close()
|
||||
|
||||
|
||||
@router.post("/health/run", status_code=202)
|
||||
async def start_health_run(
|
||||
background_tasks: BackgroundTasks,
|
||||
admin: dict = Depends(get_current_admin),
|
||||
):
|
||||
"""Health-Check + Vorschläge manuell anstoßen (läuft im Hintergrund weiter)."""
|
||||
if not try_start(_HEALTH_RUN_STATE):
|
||||
raise HTTPException(status_code=409, detail="Es läuft bereits eine Prüfung")
|
||||
background_tasks.add_task(_health_run_background)
|
||||
return {"status": "started", "started_at": _HEALTH_RUN_STATE["started_at"]}
|
||||
|
||||
|
||||
@router.get("/health/run-status")
|
||||
async def health_run_status(admin: dict = Depends(get_current_admin)):
|
||||
"""Aktueller Zustand des manuellen Health-Runs (Polling-Ziel des Frontends)."""
|
||||
return _HEALTH_RUN_STATE
|
||||
|
||||
|
||||
@router.get("/{source_id}/health")
|
||||
async def get_source_health(
|
||||
source_id: int,
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren