from __future__ import annotations from fastapi import APIRouter, Query from fastapi.responses import StreamingResponse from app.db.platform_store import ALL_PERMISSIONS, get_platform_store router = APIRouter(prefix="/system", tags=["system"]) @router.get("/permissions/codes") def permission_codes() -> dict: """返回平台权限码清单(权限码接口)。""" return {"code": 0, "message": "ok", "data": {"codes": ALL_PERMISSIONS}} @router.get("/permissions") def permissions_overview() -> dict: """返回权限码清单与角色定义。""" store = get_platform_store() return { "code": 0, "message": "ok", "data": {"codes": ALL_PERMISSIONS, "roles": store.roles()}, } @router.get("/audit-logs") def audit_logs( tenant_id: str | None = Query(default=None, description="租户 ID"), project_id: str | None = Query(default=None, description="项目 ID"), actor_id: str | None = Query(default=None, description="操作人 ID"), action: str | None = Query(default=None, description="动作类型"), target_type: str | None = Query(default=None, description="目标类型"), start_time: str | None = Query(default=None, description="ISO8601 起始时间"), end_time: str | None = Query(default=None, description="ISO8601 结束时间"), limit: int = Query(default=50, ge=1, le=200), offset: int = Query(default=0, ge=0), ) -> dict: """审计日志查询:按租户/项目/操作人/动作/目标类型/时间范围分页过滤。""" store = get_platform_store() result = store.audit_logs( tenant_id=tenant_id, project_id=project_id, actor_id=actor_id, action=action, target_type=target_type, start_time=start_time, end_time=end_time, limit=limit, offset=offset, ) return {"code": 0, "message": "ok", "data": result} @router.get("/audit-logs/export") def audit_logs_export( tenant_id: str | None = Query(default=None, description="租户 ID"), project_id: str | None = Query(default=None, description="项目 ID"), actor_id: str | None = Query(default=None, description="操作人 ID"), action: str | None = Query(default=None, description="动作类型"), target_type: str | None = Query(default=None, description="目标类型"), start_time: str | None = Query(default=None, description="ISO8601 起始时间"), end_time: str | None = Query(default=None, description="ISO8601 结束时间"), ) -> StreamingResponse: """审计日志导出:返回 CSV 流,与应用查询相同的过滤条件。""" store = get_platform_store() result = store.audit_logs( tenant_id=tenant_id, project_id=project_id, actor_id=actor_id, action=action, target_type=target_type, start_time=start_time, end_time=end_time, limit=10000, offset=0, ) items = result["items"] columns = ["time", "tenant_id", "project_id", "actor_id", "action", "target_type", "target_id", "detail", "client_ip"] header = ",".join(columns) + "\n" def iter_rows(): yield header for row in items: yield ",".join(f'"{str(row.get(c, "") or "")}"' for c in columns) + "\n" return StreamingResponse( iter_rows(), media_type="text/csv", headers={"Content-Disposition": "attachment; filename=audit_logs.csv"}, )