Admin: add Garmin sync status panel

New /api/admin/garmin-sync (GET) and /api/admin/garmin-sync/run (POST)
endpoints mirror the Strava equivalents, reading _garmin_sync_status.json
per user and exposing a run-now button. Admin page shows the Garmin table
below the Strava one, with auth_error/api_error/ok badges and live polling
while a sync is running.
This commit is contained in:
Davide Scaini
2026-05-16 20:31:02 +02:00
parent 2c69e75842
commit 2d9620c6d1
3 changed files with 176 additions and 0 deletions
+68
View File
@@ -585,6 +585,74 @@ async def admin_strava_sync_run(
return JSONResponse({"ok": True}, status_code=202)
@router.get("/api/admin/garmin-sync")
async def admin_garmin_sync_status(
bincio_session: str | None = Cookie(default=None),
) -> JSONResponse:
"""Return per-user Garmin sync status for the admin panel."""
deps._require_admin(bincio_session)
root = deps._get_data_dir()
users = []
for cf in sorted(root.glob("*/garmin_creds.json")):
user_dir = cf.parent
handle = user_dir.name
run_status: str | None = None
run_imported = 0
run_errors = 0
run_error_message: str | None = None
last_run: str | None = None
status_path = user_dir / "_garmin_sync_status.json"
if status_path.exists():
try:
ss = json.loads(status_path.read_text(encoding="utf-8"))
run_status = ss.get("status")
run_imported = ss.get("imported", 0)
run_errors = ss.get("errors", 0)
run_error_message = ss.get("error_message")
last_run = ss.get("last_run")
except (OSError, json.JSONDecodeError):
pass
users.append({
"handle": handle,
"run_status": run_status,
"run_imported": run_imported,
"run_errors": run_errors,
"run_error_message": run_error_message,
"last_run": last_run,
})
return JSONResponse({"running": deps._garmin_sync_running, "users": users})
@router.post("/api/admin/garmin-sync/run")
async def admin_garmin_sync_run(
bincio_session: str | None = Cookie(default=None),
) -> JSONResponse:
"""Trigger an immediate Garmin sync for all users (admin only)."""
deps._require_admin(bincio_session)
with deps._garmin_sync_lock:
if deps._garmin_sync_running:
raise HTTPException(409, "Sync already running")
deps._garmin_sync_running = True
def _run() -> None:
try:
from bincio.sync_garmin import sync_all
results = sync_all(deps._get_data_dir())
total_new = sum(n for n, _ in results.values())
if total_new > 0:
tasks._site_rebuild_event.set()
except Exception:
log.exception("admin_garmin_sync_run: unexpected error")
finally:
deps._garmin_sync_running = False
threading.Thread(target=_run, daemon=True, name="admin-garmin-sync").start()
return JSONResponse({"ok": True}, status_code=202)
@router.post("/api/admin/users/{handle}/recompute-elevation")
async def admin_recompute_elevation(
handle: str,