"""Wartungs-Endpoints: Update-Badge, OS-/Engine-Update, Reboot, Restart, Logs. v3-Umbau P1 (28.08.2026): Das Sudo-Passwort ist hier ersatzlos entfallen. Auf der Box gemessen — `sudo -n true` läuft durch, weil `/etc/sudoers` den Dienst-Nutzer mit `NOPASSWD: ALL` führt. Das Passwort wurde also nie gebraucht, lag aber im `localStorage` des Browsers und reiste bei jedem mutierenden Request mit. Sollte die sudoers-Zeile je fallen, meldet `services.maintenance` sauber `password_required` statt still zu scheitern — das ist dann ein Konfigurations-Signal und nichts, was man mit einem im Browser geparkten Geheimnis übertüncht. """ from fastapi import APIRouter, HTTPException from pydantic import BaseModel from services import geheimnisse, maintenance router = APIRouter(prefix="/api") class RestartReq(BaseModel): service: str class GeheimnisReq(BaseModel): schluessel: str wert: str | None = None @router.get("/maintenance/updates") def updates() -> dict: return maintenance.updates() @router.get("/maintenance/update-details") def update_details(kind: str) -> dict: if kind not in ("os", "engine", "swap", "hermes"): raise HTTPException(400, "Unbekannte Update-Art.") return maintenance.update_details(kind) @router.post("/maintenance/check-updates") def check_updates() -> dict: res = maintenance.check_updates_job() if isinstance(res, dict) and not res.get("ok", True): return res return res @router.post("/maintenance/os-update") def os_update() -> dict: res = maintenance.os_update_job() if isinstance(res, dict) and not res.get("ok", True): return res return res @router.post("/maintenance/engine-update") def engine_update() -> dict: res = maintenance.engine_update_job() if not res: raise HTTPException(400, "Kein Engine-Update-Befehl gesetzt (MC_ENGINE_UPDATE_CMD).") return res @router.post("/maintenance/swap-update") def swap_update() -> dict: res = maintenance.swap_update_job() if not res: raise HTTPException(400, "Kein Router-Update-Befehl gesetzt (MC_SWAP_UPDATE_CMD).") return res @router.post("/maintenance/hermes-update") def hermes_update() -> dict: return maintenance.hermes_update_job() @router.post("/maintenance/update-all") def update_all() -> dict: return maintenance.update_all_job() @router.post("/maintenance/reboot") def reboot() -> dict: return maintenance.reboot() @router.post("/maintenance/restart") def restart(body: RestartReq) -> dict: return maintenance.restart_service(body.service) @router.get("/maintenance/logs") def logs(service: str, lines: int = 200) -> dict: return maintenance.logs(service, lines) @router.get("/maintenance/geheimnisse") def geheimnisse_status() -> dict: """Nur der Zustand — ob ein Token gesetzt ist, niemals sein Wert.""" return geheimnisse.status() @router.post("/maintenance/geheimnisse") def geheimnisse_setzen(body: GeheimnisReq) -> dict: """Setzt (oder löscht bei leerem Wert) ein Geheimnis in der Box-Ablage.""" if not geheimnisse.setzen(body.schluessel, body.wert): raise HTTPException(400, f"Geheimnis '{body.schluessel}' konnte nicht gespeichert werden.") return {"ok": True, **geheimnisse.status()}