"""System administration (spec sections 29, 41, 46). Broker status and restart through the control sidecar. The restart primitive is SIGTERM to PID 1 in the broker's (shared) PID namespace — the broker shuts down gracefully (persistence saved) and Docker's `restart: unless-stopped` policy brings the container back. No Docker socket is involved (spec section 29). """ from __future__ import annotations from typing import Annotated from fastapi import APIRouter, Depends, Request from sqlalchemy.orm import Session from app import audit from app.db import get_db from app.deps import CurrentUser, require_roles from app.models import ManagementRole from app.routers.broker import _call_control, restart_required router = APIRouter(prefix="/api/v1/system", tags=["system"]) BrokerAdmin = Annotated[CurrentUser, Depends(require_roles(ManagementRole.BROKER_ADMIN))] AnyReader = Annotated[ CurrentUser, Depends( require_roles( ManagementRole.BROKER_ADMIN, ManagementRole.OPERATOR, ManagementRole.AUDITOR, ) ), ] DbDep = Annotated[Session, Depends(get_db)] @router.get("/status") def system_status(db: DbDep, _user: AnyReader): """Broker process state + pending-restart flag (spec section 46). Used by the sidebar indicator (polls lightly) and the System page. The control call is allowed to fail — an unreachable sidecar is a status, not an error. """ try: control = _call_control("/status", method="GET") broker_alive = bool(control.get("broker_pid_alive")) except Exception: # noqa: BLE001 - status endpoint must not 500 broker_alive = False return { "broker_alive": broker_alive, "restart_required": restart_required(db), } @router.post("/broker/restart") def restart_broker(request: Request, db: DbDep, user: BrokerAdmin): """Restart the broker via the control sidecar (spec section 29). The sidecar acknowledges and then SIGTERMs PID 1; Docker restarts the container. The pending-restart flag is cleared here — the config on disk is what the broker will boot with, so listener changes take effect by definition once the restart completes. """ from app.routers.broker import _set_restart_required response = _call_control("/restart") _set_restart_required(db, False) audit.record( db, "BROKER_RESTARTED", request=request, user=user.username, object_type="broker", object_id="mosquitto", result="initiated", ) return {"status": "restarting", "broker_alive": False}