"""Audit log service — centralized logging for all operations.""" from typing import Optional, List, Dict, Any from sqlalchemy import select, func from sqlalchemy.ext.asyncio import AsyncSession from app.models.audit_log import AuditLog class AuditLogService: @staticmethod async def log( db: AsyncSession, user_id: int, action: str, target_type: str, target_id: Optional[int] = None, detail: Optional[str] = None, ip_address: Optional[str] = None, ) -> AuditLog: """Create an audit log entry.""" entry = AuditLog( user_id=user_id, action=action, target_type=target_type, target_id=target_id, detail=detail[:500] if detail else None, ip_address=ip_address, ) db.add(entry) # Don't commit — let the caller's transaction handle it await db.flush() return entry @staticmethod async def list_logs( db: AsyncSession, page: int = 1, page_size: int = 20, user_id: Optional[int] = None, action: Optional[str] = None, target_type: Optional[str] = None, ) -> Dict[str, Any]: """List audit logs with pagination and filters.""" stmt = select(AuditLog) if user_id: stmt = stmt.where(AuditLog.user_id == user_id) if action: stmt = stmt.where(AuditLog.action == action) if target_type: stmt = stmt.where(AuditLog.target_type == target_type) total_stmt = select(func.count()).select_from(stmt.subquery()) total = (await db.execute(total_stmt)).scalar() stmt = stmt.order_by(AuditLog.created_at.desc()) stmt = stmt.offset((page - 1) * page_size).limit(page_size) result = await db.execute(stmt) logs = result.scalars().all() return { "total": total, "page": page, "page_size": page_size, "items": [ { "id": log.id, "user_id": log.user_id, "action": log.action, "target_type": log.target_type, "target_id": log.target_id, "detail": log.detail, "ip_address": log.ip_address, "created_at": log.created_at.isoformat() if log.created_at else None, } for log in logs ], }