xtc-backend / app /api /request_logs.py
a3216's picture
sync from GitHub 7633a5e: feat: 修复前端会话存储泄漏与后端管理面板多处问题
88deaca verified
Raw
History Blame Contribute Delete
5.27 kB
"""请求日志管理 API(admin):/admin/api/request-log
- GET /admin/api/request-log 列表(摘要,含筛选)
- GET /admin/api/request-log/{id} 详情(含完整 body/headers)
- DELETE /admin/api/request-log/{id} 删除单条
- DELETE /admin/api/request-log 清空全部
- GET /admin/api/request-log/stats 统计
- GET /admin/api/request-log/settings 读取容量配置
- PUT /admin/api/request-log/settings 设置容量配置
- GET /admin/api/request-log/devices 设备列表(聚合统计)
- GET /admin/api/request-log/devices/{device_id} 某设备最近请求(简化视图)
"""
from __future__ import annotations
from typing import Optional
from fastapi import APIRouter, Body, Depends, Query
from ..errors import HttpError
from ..services import request_log_store
from ._common import ok_with_cors
from .admin import _require_admin
router = APIRouter(prefix="/admin/api/request-log", tags=["request-log"])
@router.get("")
async def list_records(
_admin: str = Depends(_require_admin),
limit: int = Query(default=50, ge=1, le=500),
offset: int = Query(default=0, ge=0),
path: Optional[str] = Query(default=None),
method: Optional[str] = Query(default=None),
status: Optional[str] = Query(default=None),
access_key: Optional[str] = Query(default=None),
ok: Optional[int] = Query(default=None, ge=0, le=1),
hours: Optional[int] = Query(default=None, ge=1, le=720),
keyword: Optional[str] = Query(default=None),
category: Optional[str] = Query(default="business"),
device_id: Optional[str] = Query(default=None),
):
result = request_log_store.list_records(
limit=limit, offset=offset,
path=path, method=method, status=status,
access_key=access_key,
ok=bool(ok) if ok is not None else None,
hours=hours, keyword=keyword, category=category,
device_id=device_id,
)
return ok_with_cors(result)
@router.get("/stats")
async def stats(
_admin: str = Depends(_require_admin),
hours: int = Query(default=24, ge=1, le=720),
category: Optional[str] = Query(default="business"),
):
return ok_with_cors(request_log_store.stats(hours=hours, category=category))
@router.get("/settings")
async def get_settings(_admin: str = Depends(_require_admin)):
return ok_with_cors({"limit": request_log_store.get_limit()})
@router.put("/settings")
async def put_settings(
body: dict = Body(default={}),
_admin: str = Depends(_require_admin),
):
limit = body.get("limit")
if limit is None:
raise HttpError("limit is required", status=400, code="bad_request")
new_limit = request_log_store.set_limit(int(limit))
return ok_with_cors({"limit": new_limit})
@router.get("/devices")
async def list_devices(
_admin: str = Depends(_require_admin),
hours: Optional[int] = Query(default=None, ge=1, le=720),
limit: int = Query(default=100, ge=1, le=500),
offset: int = Query(default=0, ge=0),
keyword: Optional[str] = Query(default=None),
):
"""按 device_id 聚合的设备列表(含统计与最近活跃信息)。"""
return ok_with_cors(
request_log_store.list_devices(
hours=hours, limit=limit, offset=offset, keyword=keyword,
)
)
@router.get("/devices/{device_id}")
async def device_records(
device_id: str,
_admin: str = Depends(_require_admin),
limit: int = Query(default=50, ge=1, le=200),
offset: int = Query(default=0, ge=0),
hours: Optional[int] = Query(default=None, ge=1, le=720),
keyword: Optional[str] = Query(default=None),
scope: Optional[str] = Query(default=None),
):
"""某设备的最近请求记录(简化视图:含从请求体提取的文本摘要)。
keyword: 关键词,在 request_body / response_body 上 LIKE 搜索。
scope: 'req' 仅请求、'resp' 仅响应、'both' 或空 两者都搜。
"""
return ok_with_cors(
request_log_store.list_device_records(
device_id=device_id, limit=limit, offset=offset, hours=hours,
keyword=keyword, scope=scope,
)
)
@router.delete("/devices/{device_id}")
async def clear_device_records(
device_id: str,
_admin: str = Depends(_require_admin),
):
"""清空某设备的全部请求记录。"""
count = request_log_store.clear_device_records(device_id)
return ok_with_cors({"cleared": count, "device_id": device_id})
@router.get("/{rid}")
async def get_record(
rid: int,
_admin: str = Depends(_require_admin),
):
rec = request_log_store.get_record(rid)
if not rec:
raise HttpError(f"request log not found: {rid}", status=404, code="not_found")
return ok_with_cors({"record": rec})
@router.delete("/{rid}")
async def delete_record(
rid: int,
_admin: str = Depends(_require_admin),
):
ok = request_log_store.delete_record(rid)
if not ok:
raise HttpError(f"request log not found: {rid}", status=404, code="not_found")
return ok_with_cors({"deleted": True, "id": rid})
@router.delete("")
async def clear_all(_admin: str = Depends(_require_admin)):
count = request_log_store.clear_all()
return ok_with_cors({"cleared": count})