"""Programmatic API keys. Only admins may create, list, or revoke keys. A key authenticates via the ``X-API-Key`` header and acts as the admin who created it. The raw key is returned exactly once, at creation; afterwards only its prefix and hash are stored. """ from fastapi import APIRouter, Depends, Request from sqlalchemy import select from sqlalchemy.orm import Session as DbSession from .. import auth from ..auth import require_admin from ..database import get_db from ..errors import APIError from ..models import Admin, ApiKey from ..schemas import ApiKeyCreate, ApiKeyCreated, ApiKeyOut router = APIRouter(prefix="/apikeys", tags=["apikeys"]) @router.get("", response_model=list[ApiKeyOut]) def list_api_keys(_: Admin = Depends(require_admin), db: DbSession = Depends(get_db)): return db.execute(select(ApiKey).order_by(ApiKey.id.desc())).scalars().all() @router.post("", response_model=ApiKeyCreated, status_code=201) def create_api_key( payload: ApiKeyCreate, request: Request, admin: Admin = Depends(require_admin), db: DbSession = Depends(get_db), ): raw = auth.generate_api_key() key = ApiKey( name=payload.name, key_prefix=raw[:8], key_hash=auth.hash_api_key(raw), created_by=admin.username, ) db.add(key) db.commit() db.refresh(key) auth.record_log( db, username=admin.username, action="create_api_key", detail=f"Created API key '{key.name}' ({key.key_prefix}…)", request=request, ) return ApiKeyCreated(**ApiKeyOut.model_validate(key).model_dump(), key=raw) @router.delete("/{key_id}", status_code=204) def revoke_api_key( key_id: int, request: Request, admin: Admin = Depends(require_admin), db: DbSession = Depends(get_db), ): key = db.get(ApiKey, key_id) if key is None: raise APIError(status_code=404, detail=f"API key '{key_id}' not found") key.revoked = True db.commit() auth.record_log( db, username=admin.username, action="revoke_api_key", detail=f"Revoked API key '{key.name}' ({key.key_prefix}…)", request=request, )