fix: 路由顺序导致 422 + 彻底消除 Header(...) 隐患
1. devices.py: /tags 和 /export/csv 移到 /{device_id}(int) 之前,
避免 FastAPI 将 'tags'/'export' 当作 device_id 解析失败返回 422
2. audit.py: /logs/export/csv 移到 /logs/{log_id}(int) 之前,同上
3. auth.py: Header(...) → Header(None),统一为手动 401 返回,
消除全局最后一个 Header(...) 导致的 422 隐患
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+39
-39
@@ -49,45 +49,6 @@ def get_audit_logs(
|
||||
}
|
||||
|
||||
|
||||
@router.get("/logs/{log_id}")
|
||||
def get_audit_log_detail(
|
||||
log_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
_: dict = Depends(require_permission('*')),
|
||||
):
|
||||
"""获取单条审计日志详情"""
|
||||
log = db.query(AuditLog).filter(AuditLog.id == log_id).first()
|
||||
if not log:
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(status_code=404, detail="日志不存在")
|
||||
return _fmt(log, detail=True)
|
||||
|
||||
|
||||
@router.get("/stats")
|
||||
def get_audit_stats(
|
||||
days: int = Query(7, ge=1, le=90),
|
||||
db: Session = Depends(get_db),
|
||||
_: dict = Depends(require_permission('*')),
|
||||
):
|
||||
"""审计日志统计(最近N天)"""
|
||||
from datetime import timedelta
|
||||
from sqlalchemy import func
|
||||
since = datetime.utcnow() - timedelta(days=days)
|
||||
rows = (
|
||||
db.query(AuditLog.action_type, AuditLog.status, func.count().label("cnt"))
|
||||
.filter(AuditLog.action_time >= since)
|
||||
.group_by(AuditLog.action_type, AuditLog.status)
|
||||
.all()
|
||||
)
|
||||
total = db.query(func.count(AuditLog.id)).filter(AuditLog.action_time >= since).scalar()
|
||||
by_type = {}
|
||||
for row in rows:
|
||||
if row.action_type not in by_type:
|
||||
by_type[row.action_type] = {"success": 0, "failed": 0, "error": 0}
|
||||
by_type[row.action_type][row.status] = row.cnt
|
||||
return {"total": total, "days": days, "by_type": by_type}
|
||||
|
||||
|
||||
@router.get("/logs/export/csv")
|
||||
def export_audit_logs(
|
||||
start_time: Optional[datetime] = Query(None),
|
||||
@@ -130,6 +91,45 @@ def export_audit_logs(
|
||||
)
|
||||
|
||||
|
||||
@router.get("/logs/{log_id}")
|
||||
def get_audit_log_detail(
|
||||
log_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
_: dict = Depends(require_permission('*')),
|
||||
):
|
||||
"""获取单条审计日志详情"""
|
||||
log = db.query(AuditLog).filter(AuditLog.id == log_id).first()
|
||||
if not log:
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(status_code=404, detail="日志不存在")
|
||||
return _fmt(log, detail=True)
|
||||
|
||||
|
||||
@router.get("/stats")
|
||||
def get_audit_stats(
|
||||
days: int = Query(7, ge=1, le=90),
|
||||
db: Session = Depends(get_db),
|
||||
_: dict = Depends(require_permission('*')),
|
||||
):
|
||||
"""审计日志统计(最近N天)"""
|
||||
from datetime import timedelta
|
||||
from sqlalchemy import func
|
||||
since = datetime.utcnow() - timedelta(days=days)
|
||||
rows = (
|
||||
db.query(AuditLog.action_type, AuditLog.status, func.count().label("cnt"))
|
||||
.filter(AuditLog.action_time >= since)
|
||||
.group_by(AuditLog.action_type, AuditLog.status)
|
||||
.all()
|
||||
)
|
||||
total = db.query(func.count(AuditLog.id)).filter(AuditLog.action_time >= since).scalar()
|
||||
by_type = {}
|
||||
for row in rows:
|
||||
if row.action_type not in by_type:
|
||||
by_type[row.action_type] = {"success": 0, "failed": 0, "error": 0}
|
||||
by_type[row.action_type][row.status] = row.cnt
|
||||
return {"total": total, "days": days, "by_type": by_type}
|
||||
|
||||
|
||||
def _fmt(r: AuditLog, detail: bool = False) -> dict:
|
||||
base = {
|
||||
"id": r.id,
|
||||
|
||||
Reference in New Issue
Block a user