84 lines
2.5 KiB
Python
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,
|
|
}
|
|
|