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

119 lines
4.0 KiB
Python

from __future__ import annotations
from typing import Any
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.request_logs_cleanup import (
DEFAULT_REQUEST_LOGS_CHUNK_SIZE,
DEFAULT_REQUEST_LOGS_KEEP_DAYS,
DEFAULT_REQUEST_LOGS_MAX_DELETE_ROWS,
cleanup_request_logs,
preview_request_logs_cleanup,
)
router = APIRouter()
def _parse_bool(value: Any, *, default: bool) -> bool:
if value is None:
return default
if isinstance(value, bool):
return value
if isinstance(value, int) and value in (0, 1):
return bool(value)
if isinstance(value, str):
v = value.strip().lower()
if v in {"1", "true", "yes", "y", "on"}:
return True
if v in {"0", "false", "no", "n", "off"}:
return False
return default
async def _load_cleanup_request_logs_json(request: Request) -> dict[str, Any]:
try:
data = await request.json()
except Exception as exc:
raise ApiError(code=ErrorCode.BAD_REQUEST, message="Invalid JSON body", status_code=400) from exc
if not isinstance(data, dict):
raise ApiError(code=ErrorCode.BAD_REQUEST, message="Invalid JSON body", status_code=400)
keep_days_raw = data.get("keep_days", DEFAULT_REQUEST_LOGS_KEEP_DAYS)
try:
keep_days = int(keep_days_raw)
except Exception as exc:
raise ApiError(code=ErrorCode.BAD_REQUEST, message="Unsupported keep_days", status_code=400) from exc
if keep_days < 0 or keep_days > 36500:
raise ApiError(code=ErrorCode.BAD_REQUEST, message="Unsupported keep_days", status_code=400)
max_delete_rows_raw = data.get("max_delete_rows", DEFAULT_REQUEST_LOGS_MAX_DELETE_ROWS)
try:
max_delete_rows = int(max_delete_rows_raw)
except Exception as exc:
raise ApiError(code=ErrorCode.BAD_REQUEST, message="Unsupported max_delete_rows", status_code=400) from exc
if max_delete_rows < 1 or max_delete_rows > 10_000_000:
raise ApiError(code=ErrorCode.BAD_REQUEST, message="Unsupported max_delete_rows", status_code=400)
chunk_size_raw = data.get("chunk_size", DEFAULT_REQUEST_LOGS_CHUNK_SIZE)
try:
chunk_size = int(chunk_size_raw)
except Exception as exc:
raise ApiError(code=ErrorCode.BAD_REQUEST, message="Unsupported chunk_size", status_code=400) from exc
if chunk_size < 1 or chunk_size > 100_000:
raise ApiError(code=ErrorCode.BAD_REQUEST, message="Unsupported chunk_size", status_code=400)
dry_run = _parse_bool(data.get("dry_run"), default=False)
return {
"keep_days": int(keep_days),
"max_delete_rows": int(max_delete_rows),
"chunk_size": int(chunk_size),
"dry_run": bool(dry_run),
}
@router.post("/maintenance/request-logs/cleanup")
async def cleanup_request_logs_endpoint(
request: Request,
_claims: dict[str, Any] = Depends(get_admin_claims),
) -> dict[str, Any]:
_ = _claims
rid = get_or_create_request_id(request)
cfg = await _load_cleanup_request_logs_json(request)
engine = request.app.state.engine
if bool(cfg["dry_run"]):
preview = await preview_request_logs_cleanup(
engine,
keep_days=int(cfg["keep_days"]),
max_delete_rows=int(cfg["max_delete_rows"]),
)
return {
"ok": True,
"dry_run": True,
"cutoff": preview.cutoff,
"would_delete": int(preview.would_delete),
"has_more": bool(preview.has_more),
"request_id": rid,
}
result = await cleanup_request_logs(
engine,
keep_days=int(cfg["keep_days"]),
max_delete_rows=int(cfg["max_delete_rows"]),
chunk_size=int(cfg["chunk_size"]),
)
return {
"ok": True,
"dry_run": False,
"cutoff": result.cutoff,
"deleted": int(result.deleted),
"has_more": bool(result.has_more),
"request_id": rid,
}