import uuid from datetime import datetime from sqlalchemy import JSON, DateTime, String from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.sql import func from app.core.database import Base from app.core.sql_types import GUID as Uuid class AuditLog(Base): __tablename__ = "audit_logs" id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4) trace_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False, index=True) # Indexed for ``WHERE actor_user_id = :id`` lookups on the audit # router (admin "actions by user X" view) — without it the table # full-scans as audit grows. No FK to identity_persons because # audit must outlive person rows. actor_user_id: Mapped[uuid.UUID | None] = mapped_column(Uuid, index=True) actor_type: Mapped[str] = mapped_column(String(16), nullable=False) action: Mapped[str] = mapped_column(String(64), nullable=False, index=True) resource_type: Mapped[str | None] = mapped_column(String(64)) resource_id: Mapped[str | None] = mapped_column(String(128)) status: Mapped[str] = mapped_column(String(16), nullable=False) detail: Mapped[dict | None] = mapped_column(JSON) ip_address: Mapped[str | None] = mapped_column(String(45)) user_agent: Mapped[str | None] = mapped_column(String(512)) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), index=True )