feat: add backup dashboard summary endpoint

This commit is contained in:
2026-05-11 21:41:49 +02:00
parent 6b0f2df629
commit 1a4f56cdd9
2 changed files with 80 additions and 2 deletions
@@ -3,13 +3,21 @@
from __future__ import annotations
import logging
import time
from typing import Any
from fastapi import APIRouter, Depends
from media_library_viewer_api.clients.jellyfin import JellyfinClient
from media_library_viewer_api.dependencies import get_jellyfin_client, get_monitoring_poller, get_settings_store, get_user_id
from media_library_viewer_api.dependencies import (
get_jellyfin_client,
get_monitoring_poller,
get_settings_store,
get_user_id,
)
from media_library_viewer_api.models.backups import BackupDashboardSummary
from media_library_viewer_api.services.monitoring_actions import collect_machine_overview
from media_library_viewer_api.services.settings_store import SettingsStore
logger = logging.getLogger(__name__)
@@ -54,7 +62,10 @@ def get_monitoring_overview(
machine.get("id"),
)
rows.append({
"machine": {k: machine.get(k) for k in ("id", "name", "mode", "enabled", "host", "port", "username", "media_root", "path_prefix", "notes")},
"machine": {k: machine.get(k) for k in (
"id", "name", "mode", "enabled", "host", "port", "username",
"media_root", "path_prefix", "notes",
)},
"status": "",
"status_error": str(exc),
"metrics_error": str(exc),
@@ -183,3 +194,44 @@ def get_now_playing(
) -> list[dict[str, Any]]:
"""Backward-compatible alias; returns full activity rows."""
return get_activity(client)
@router.get("/backups")
def get_backup_dashboard(
store: SettingsStore = Depends(get_settings_store),
) -> BackupDashboardSummary:
jobs = store.list_backup_jobs()
total_jobs = len(jobs)
# Calculate 24h success rate
cutoff = int(time.time()) - (24 * 60 * 60)
recent_runs = []
for job in jobs:
runs = store.list_backup_runs(job_id=job["id"], limit=1)
if runs and runs[0]["started_at"] >= cutoff:
recent_runs.append(runs[0])
successful = sum(1 for r in recent_runs if r["status"] == "success")
success_rate = (successful / len(recent_runs) * 100) if recent_runs else 100.0
# Active alerts
alerts = store.list_backup_alerts(acknowledged=False)
active_alerts = len(alerts)
# Last failed
failed_runs = []
for job in jobs:
runs = store.list_backup_runs(job_id=job["id"], status="failure", limit=1)
if runs:
failed_runs.append(runs[0])
last_failed_at = None
if failed_runs:
last_failed_at = max(r["started_at"] for r in failed_runs)
return BackupDashboardSummary(
total_jobs=total_jobs,
success_rate_24h=round(success_rate, 1),
active_alerts=active_alerts,
last_failed_at=last_failed_at,
)
+26
View File
@@ -7,6 +7,32 @@ from media_library_viewer_api.main import app
from media_library_viewer_api.services.settings_store import SettingsStore
def test_dashboard_backups():
with tempfile.TemporaryDirectory() as tmpdir:
db_path = Path(tmpdir) / "test_settings.sqlite"
store = SettingsStore(db_path)
store.init_schema()
# Monkey-patch the global store for this test
import media_library_viewer_api.auth as auth_module
from media_library_viewer_api.services import settings_store
original_store = settings_store._store
settings_store._store = store
auth_module._API_KEY = None
try:
client = TestClient(app)
response = client.get("/api/dashboard/backups")
assert response.status_code == 200
data = response.json()
assert "total_jobs" in data
assert "success_rate_24h" in data
finally:
settings_store._store = original_store
auth_module._API_KEY = None
def test_post_backup_report():
with tempfile.TemporaryDirectory() as tmpdir:
db_path = Path(tmpdir) / "test_settings.sqlite"