Files
Random-image-api/backend/app/api/admin/audit.py
T
2026-02-22 18:58:55 +08:00

84 lines
2.5 KiB
Python

from __future__ import annotations
import json
from typing import Any
import sqlalchemy as sa
from fastapi import APIRouter, Depends, Request
from app.api.admin.deps import get_admin_claims
from app.core.errors import ApiError, ErrorCode
from app.core.request_id import get_or_create_request_id
from app.db.models.admin_audit import AdminAudit
from app.db.session import create_sessionmaker
router = APIRouter()
@router.get("/audit")
async def list_admin_audit(
request: Request,
limit: int = 50,
cursor: str | None = None,
_claims: dict[str, Any] = Depends(get_admin_claims),
) -> dict[str, Any]:
_ = _claims
if limit < 1 or limit > 200:
raise ApiError(code=ErrorCode.BAD_REQUEST, message="Unsupported limit", status_code=400)
cursor_i: int | None = None
cursor_raw = (cursor or "").strip()
if cursor_raw:
if not cursor_raw.isdigit():
raise ApiError(code=ErrorCode.BAD_REQUEST, message="Unsupported cursor", status_code=400)
cursor_i = int(cursor_raw)
if cursor_i <= 0:
raise ApiError(code=ErrorCode.BAD_REQUEST, message="Unsupported cursor", status_code=400)
rid = get_or_create_request_id(request)
engine = request.app.state.engine
Session = create_sessionmaker(engine)
stmt = sa.select(AdminAudit).order_by(AdminAudit.id.desc()).limit(limit + 1)
if cursor_i is not None:
stmt = stmt.where(AdminAudit.id < cursor_i)
async with Session() as session:
rows = (await session.execute(stmt)).scalars().all()
items_rows = rows[:limit]
next_cursor = int(items_rows[-1].id) if len(rows) > limit and items_rows else None
def _parse_detail(text: str | None) -> dict[str, Any] | None:
raw = str(text or "").strip()
if not raw:
return None
try:
data = json.loads(raw)
except Exception:
return {"raw": raw}
return data if isinstance(data, dict) else {"value": data}
items = [
{
"id": str(row.id),
"created_at": row.created_at,
"actor": row.actor,
"action": row.action,
"resource": row.resource,
"record_id": row.record_id,
"request_id": row.request_id,
"detail_json": _parse_detail(row.detail_json),
}
for row in items_rows
]
return {
"ok": True,
"items": items,
"next_cursor": str(next_cursor) if next_cursor is not None else "",
"request_id": rid,
}