from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, Request from sqlalchemy import desc, select from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import get_db, require_admin from app.core.security import now_utc from app.models.audit import AuditLog from app.models.challenge import Challenge from app.models.device import Device from app.models.user import User from app.schemas.api import AuditOut, ChallengeOut, DeviceOut, UserOut from app.services.audit import audit router = APIRouter(prefix="/api/admin", tags=["admin"], dependencies=[Depends(require_admin)]) @router.get("/users", response_model=list[UserOut]) async def users(session: AsyncSession = Depends(get_db)): return (await session.scalars(select(User).order_by(User.username))).all() @router.get("/devices", response_model=list[DeviceOut]) async def devices(session: AsyncSession = Depends(get_db)): return (await session.scalars(select(Device).order_by(desc(Device.created_at)))).all() @router.post("/devices/{device_id}/revoke") async def revoke_device(device_id: UUID, request: Request, session: AsyncSession = Depends(get_db)): device = await session.get(Device, device_id) if not device: raise HTTPException(status_code=404, detail="Device not found") device.is_revoked = True device.revoked_at = now_utc() await audit( session, "device.revoked", actor="admin", target_type="device", target_id=str(device.id), ip_address=request.client.host if request.client else None, ) await session.commit() return {"status": "revoked"} @router.get("/audit", response_model=list[AuditOut]) async def audit_logs(limit: int = 100, session: AsyncSession = Depends(get_db)): limit = min(max(limit, 1), 500) return (await session.scalars(select(AuditLog).order_by(desc(AuditLog.created_at)).limit(limit))).all() @router.get("/challenges", response_model=list[ChallengeOut]) async def challenges(limit: int = 100, session: AsyncSession = Depends(get_db)): limit = min(max(limit, 1), 500) return (await session.scalars(select(Challenge).order_by(desc(Challenge.created_at)).limit(limit))).all()