"""Operation audit log table.

Revision ID: 0008_add_admin_audit_log
Revises: 0007_add_crawl_job
Create Date: 2026-04-29

Captured by a FastAPI middleware for every state-changing admin call.
GET reads are NOT logged (would dominate volume + don't change state).
Body bytes are NEVER stored verbatim — only sha256(body)[:16] digest.

Indexes cover the two common queries:
  • timeline view ordered by created_at
  • "what did user X do" filter by actor + created_at
  • action-class drill-down (site.create / target.run / article.bulk_delete)
"""
from alembic import op
import sqlalchemy as sa


revision = "0008_add_admin_audit_log"
down_revision = "0007_add_crawl_job"
branch_labels = None
depends_on = None


def upgrade() -> None:
    op.create_table(
        "admin_audit_log",
        sa.Column("id", sa.BigInteger, primary_key=True, autoincrement=True),
        sa.Column("created_at", sa.DateTime, server_default=sa.func.now()),
        sa.Column("actor", sa.String(64)),
        sa.Column("actor_ip", sa.String(64)),
        sa.Column("method", sa.String(8), nullable=False),
        sa.Column("path", sa.String(500), nullable=False),
        sa.Column("status_code", sa.Integer),
        sa.Column("duration_ms", sa.Integer),
        sa.Column("payload_digest", sa.String(32)),
        sa.Column("action", sa.String(64)),
        sa.Column("resource_type", sa.String(32)),
        sa.Column("resource_id", sa.String(128)),
    )
    op.create_index("ix_audit_created_at", "admin_audit_log", ["created_at"])
    op.create_index("ix_audit_actor", "admin_audit_log", ["actor"])
    op.create_index("ix_audit_action", "admin_audit_log", ["action"])
    op.create_index(
        "ix_audit_actor_created", "admin_audit_log",
        ["actor", "created_at"],
    )


def downgrade() -> None:
    op.drop_index("ix_audit_actor_created", table_name="admin_audit_log")
    op.drop_index("ix_audit_action", table_name="admin_audit_log")
    op.drop_index("ix_audit_actor", table_name="admin_audit_log")
    op.drop_index("ix_audit_created_at", table_name="admin_audit_log")
    op.drop_table("admin_audit_log")
