Files
2026-09-19 18:08:02 +02:00

80 lines
2.6 KiB
Python

"""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}