from datetime import datetime from typing import Optional from fastapi import APIRouter, Depends, Query from sqlalchemy import select, and_ from sqlalchemy.ext.asyncio import AsyncSession from database.postgres import get_pg_session from shared.orm import AuditLog from auth.dependencies import require_admin_or_above from auth.models import TokenPayload router = APIRouter(prefix="/api/audit-log", tags=["audit-log"]) _MAX_LIMIT = 200 _DEFAULT_LIMIT = 50 @router.get("") async def list_audit_log( actor_id: Optional[str] = Query(None), entity_type: Optional[str] = Query(None), entity_id: Optional[str] = Query(None), action: Optional[str] = Query(None), from_date: Optional[datetime] = Query(None), to_date: Optional[datetime] = Query(None), limit: int = Query(_DEFAULT_LIMIT, ge=1, le=_MAX_LIMIT), offset: int = Query(0, ge=0), _user: TokenPayload = Depends(require_admin_or_above), db: AsyncSession = Depends(get_pg_session), ): filters = [] if actor_id: filters.append(AuditLog.actor_id == actor_id) if entity_type: filters.append(AuditLog.entity_type == entity_type) if entity_id: filters.append(AuditLog.entity_id == entity_id) if action: filters.append(AuditLog.action == action) if from_date: filters.append(AuditLog.occurred_at >= from_date) if to_date: filters.append(AuditLog.occurred_at <= to_date) stmt = ( select(AuditLog) .where(and_(*filters) if filters else True) .order_by(AuditLog.occurred_at.desc()) .offset(offset) .limit(limit) ) result = await db.execute(stmt) rows = result.scalars().all() return { "entries": [ { "id": r.id, "occurred_at": r.occurred_at.isoformat(), "actor_id": r.actor_id, "actor_name": r.actor_name, "action": r.action, "entity_type": r.entity_type, "entity_id": r.entity_id, "entity_label": r.entity_label, "changes": r.changes, "meta": r.meta, } for r in rows ], "limit": limit, "offset": offset, }