Initial certificate system
This commit is contained in:
115
backend/app/api/routes/admin_logs.py
Normal file
115
backend/app/api/routes/admin_logs.py
Normal file
@@ -0,0 +1,115 @@
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from fastapi.responses import StreamingResponse
|
||||
from openpyxl import Workbook
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import require_roles
|
||||
from app.core.paths import data_path
|
||||
from app.db.session import get_db
|
||||
from app.models import AdminUser, OperationLog
|
||||
from app.schemas.log import OperationLogOut
|
||||
from app.services.logs import HIGH_RISK_LOG_RETENTION_DAYS, LOG_RETENTION_DAYS, cleanup_expired_logs, format_log
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("", response_model=list[OperationLogOut])
|
||||
def list_logs(
|
||||
action: str | None = Query(default=None),
|
||||
object_type: str | None = Query(default=None),
|
||||
risk: str | None = Query(default=None),
|
||||
keyword: str | None = Query(default=None),
|
||||
page: int = Query(default=1, ge=1),
|
||||
page_size: int = Query(default=20, ge=1, le=100),
|
||||
db: Session = Depends(get_db),
|
||||
_: AdminUser = Depends(require_roles("system_admin", "certificate_admin", "readonly")),
|
||||
) -> dict[str, object]:
|
||||
items = _query_logs(db, action, object_type, risk, keyword)
|
||||
total = len(items)
|
||||
total_pages = max(1, (total + page_size - 1) // page_size)
|
||||
page = min(page, total_pages)
|
||||
start = (page - 1) * page_size
|
||||
return {
|
||||
"items": items[start:start + page_size],
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"total_pages": total_pages,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/export")
|
||||
def export_logs(
|
||||
action: str | None = Query(default=None),
|
||||
object_type: str | None = Query(default=None),
|
||||
risk: str | None = Query(default=None),
|
||||
keyword: str | None = Query(default=None),
|
||||
db: Session = Depends(get_db),
|
||||
_: AdminUser = Depends(require_roles("system_admin", "certificate_admin", "readonly")),
|
||||
) -> StreamingResponse:
|
||||
items = _query_logs(db, action, object_type, risk, keyword)
|
||||
workbook = Workbook()
|
||||
sheet = workbook.active
|
||||
sheet.title = "\u64cd\u4f5c\u65e5\u5fd7"
|
||||
sheet.append(["\u65f6\u95f4", "\u64cd\u4f5c\u4ebaID", "\u64cd\u4f5c", "\u5bf9\u8c61", "\u5bf9\u8c61ID", "\u98ce\u9669\u7b49\u7ea7", "\u6458\u8981", "\u8be6\u60c5"])
|
||||
for item in items:
|
||||
sheet.append([
|
||||
item.get("created_at"),
|
||||
item.get("admin_user_id"),
|
||||
item.get("action_label"),
|
||||
item.get("object_label"),
|
||||
item.get("object_id"),
|
||||
item.get("risk_label"),
|
||||
item.get("summary"),
|
||||
item.get("detail_json"),
|
||||
])
|
||||
for column in "ABCDEFGH":
|
||||
sheet.column_dimensions[column].width = 22 if column != "H" else 60
|
||||
export_path = data_path("exports") / "operation-logs.xlsx"
|
||||
workbook.save(export_path)
|
||||
file_handle = export_path.open("rb")
|
||||
return StreamingResponse(
|
||||
file_handle,
|
||||
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
headers={"Content-Disposition": 'attachment; filename="operation-logs.xlsx"'},
|
||||
)
|
||||
|
||||
|
||||
@router.post("/cleanup")
|
||||
def cleanup_logs(
|
||||
db: Session = Depends(get_db),
|
||||
_: AdminUser = Depends(require_roles("system_admin", "certificate_admin")),
|
||||
) -> dict[str, int]:
|
||||
removed = cleanup_expired_logs(db)
|
||||
db.commit()
|
||||
return {
|
||||
"removed": removed,
|
||||
"normal_retention_days": LOG_RETENTION_DAYS,
|
||||
"high_risk_retention_days": HIGH_RISK_LOG_RETENTION_DAYS,
|
||||
}
|
||||
|
||||
|
||||
def _query_logs(
|
||||
db: Session,
|
||||
action: str | None,
|
||||
object_type: str | None,
|
||||
risk: str | None,
|
||||
keyword: str | None,
|
||||
) -> list[dict[str, object]]:
|
||||
query = db.query(OperationLog).order_by(OperationLog.id.desc())
|
||||
cleanup_expired_logs(db)
|
||||
db.commit()
|
||||
if action:
|
||||
query = query.filter(OperationLog.action == action)
|
||||
if object_type:
|
||||
query = query.filter(OperationLog.object_type == object_type)
|
||||
items = [format_log(item) for item in query.limit(500).all()]
|
||||
if risk:
|
||||
items = [item for item in items if item["risk"] == risk]
|
||||
if keyword:
|
||||
word = keyword.strip().lower()
|
||||
items = [
|
||||
item for item in items
|
||||
if word in " ".join(str(item.get(key, "")) for key in ["object_id", "summary", "detail_json", "action_label", "object_label"]).lower()
|
||||
]
|
||||
return items[:200]
|
||||
Reference in New Issue
Block a user