"""Instance administration (SPEC §6.1). The backend seam for the admin panel. Today it carries only the registration gate — the first account on a fresh install becomes the administrator and closes signup behind itself, and this is how that gets reopened. Further instance-wide options land here as the panel grows. """ from __future__ import annotations import datetime as dt from typing import Annotated from fastapi import APIRouter, Depends from sqlalchemy.ext.asyncio import AsyncSession from app.db import get_session from app.identity.deps import CurrentAdmin, require_csrf from app.identity.schemas import AdminSettingsIn, AdminSettingsOut from app.identity.service import app_settings from app.models import AppSettings router = APIRouter(prefix="/admin", tags=["admin"]) DbSession = Annotated[AsyncSession, Depends(get_session)] def _out(row: AppSettings) -> AdminSettingsOut: return AdminSettingsOut( registration_open=row.registration_open, default_quota_bytes=row.default_quota_bytes, ) @router.get("/settings", response_model=AdminSettingsOut) async def read_settings(admin: CurrentAdmin, db: DbSession) -> AdminSettingsOut: return _out(await app_settings(db)) @router.put("/settings", response_model=AdminSettingsOut, dependencies=[Depends(require_csrf)]) async def write_settings( body: AdminSettingsIn, admin: CurrentAdmin, db: DbSession ) -> AdminSettingsOut: """Merge the supplied fields into the settings row. Changing ``default_quota_bytes`` affects users created afterwards only — nothing here rewrites an existing ``users.quota_bytes``. """ row = await app_settings(db, for_update=True) if body.registration_open is not None: row.registration_open = body.registration_open if body.default_quota_bytes is not None: row.default_quota_bytes = body.default_quota_bytes row.updated_at = dt.datetime.now(dt.UTC) await db.commit() return _out(row)