"""Setup router. Exposes the ``POST /api/setup`` endpoint for the one-time first-run configuration wizard. Once setup has been completed, subsequent calls return ``409 Conflict``. """ from __future__ import annotations import structlog from fastapi import APIRouter, HTTPException, status from app.dependencies import DbDep from app.models.setup import SetupRequest, SetupResponse, SetupStatusResponse, SetupTimezoneResponse from app.services import setup_service log: structlog.stdlib.BoundLogger = structlog.get_logger() router = APIRouter(prefix="/api/setup", tags=["setup"]) @router.get( "", response_model=SetupStatusResponse, summary="Check whether setup has been completed", ) async def get_setup_status(db: DbDep) -> SetupStatusResponse: """Return whether the initial setup wizard has been completed. Returns: :class:`~app.models.setup.SetupStatusResponse` with ``completed`` set to ``True`` if setup is done, ``False`` otherwise. """ done = await setup_service.is_setup_complete(db) return SetupStatusResponse(completed=done) @router.post( "", response_model=SetupResponse, status_code=status.HTTP_201_CREATED, summary="Run the initial setup wizard", ) async def post_setup(body: SetupRequest, db: DbDep) -> SetupResponse: """Persist the initial BanGUI configuration. Args: body: Setup request payload validated by Pydantic. db: Injected aiosqlite connection. Returns: :class:`~app.models.setup.SetupResponse` on success. Raises: HTTPException: 409 if setup has already been completed. """ if await setup_service.is_setup_complete(db): raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Setup has already been completed.", ) await setup_service.run_setup( db, master_password=body.master_password, database_path=body.database_path, fail2ban_socket=body.fail2ban_socket, timezone=body.timezone, session_duration_minutes=body.session_duration_minutes, ) return SetupResponse() @router.get( "/timezone", response_model=SetupTimezoneResponse, summary="Return the configured IANA timezone", ) async def get_timezone(db: DbDep) -> SetupTimezoneResponse: """Return the IANA timezone configured during the initial setup wizard. The frontend uses this to convert UTC timestamps to the local time zone chosen by the administrator. Returns: :class:`~app.models.setup.SetupTimezoneResponse` with ``timezone`` set to the stored IANA identifier (e.g. ``"UTC"`` or ``"Europe/Berlin"``), defaulting to ``"UTC"`` if unset. """ tz = await setup_service.get_timezone(db) return SetupTimezoneResponse(timezone=tz)