- 新增完整的 Python 实现,替代 Go 版本 - 添加 Web 登录界面和仪表板 - 实现 JWT 认证和 API 密钥管理 - 添加数据库存储功能 - 保持与 Go 版本一致的目录结构和启动脚本 - 包含完整的文档和测试脚本
106 lines
3.5 KiB
Python
106 lines
3.5 KiB
Python
from datetime import datetime, timedelta
|
|
from typing import List, Optional
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from pydantic import BaseModel
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.database import get_db
|
|
from app.models.trigger_log import TriggerLog
|
|
from app.auth import get_current_user
|
|
|
|
router = APIRouter(prefix="/api/logs", tags=["logs"])
|
|
|
|
|
|
class TriggerLogResponse(BaseModel):
|
|
id: int
|
|
repository_name: str
|
|
branch_name: str
|
|
commit_sha: str
|
|
job_name: str
|
|
status: str
|
|
error_message: Optional[str] = None
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
@router.get("/", response_model=List[TriggerLogResponse])
|
|
async def get_trigger_logs(
|
|
repository: Optional[str] = Query(None, description="Repository name filter"),
|
|
branch: Optional[str] = Query(None, description="Branch name filter"),
|
|
since: Optional[str] = Query(None, description="Since timestamp (RFC3339 format)"),
|
|
limit: int = Query(100, ge=1, le=1000, description="Maximum number of logs to return"),
|
|
db: Session = Depends(get_db),
|
|
current_user: dict = Depends(get_current_user)
|
|
):
|
|
"""
|
|
获取触发日志
|
|
"""
|
|
try:
|
|
# 构建查询
|
|
query = db.query(TriggerLog)
|
|
|
|
# 应用过滤器
|
|
if repository:
|
|
query = query.filter(TriggerLog.repository_name == repository)
|
|
if branch:
|
|
query = query.filter(TriggerLog.branch_name == branch)
|
|
if since:
|
|
try:
|
|
since_time = datetime.fromisoformat(since.replace('Z', '+00:00'))
|
|
query = query.filter(TriggerLog.created_at >= since_time)
|
|
except ValueError:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Invalid since parameter format (use RFC3339)"
|
|
)
|
|
|
|
# 按时间倒序排列并限制数量
|
|
logs = query.order_by(TriggerLog.created_at.desc()).limit(limit).all()
|
|
|
|
return logs
|
|
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Failed to get trigger logs: {str(e)}")
|
|
|
|
|
|
@router.get("/stats")
|
|
async def get_log_stats(
|
|
db: Session = Depends(get_db),
|
|
current_user: dict = Depends(get_current_user)
|
|
):
|
|
"""
|
|
获取日志统计信息
|
|
"""
|
|
try:
|
|
# 总日志数
|
|
total_logs = db.query(TriggerLog).count()
|
|
|
|
# 成功和失败的日志数
|
|
successful_logs = db.query(TriggerLog).filter(TriggerLog.status == "success").count()
|
|
failed_logs = db.query(TriggerLog).filter(TriggerLog.status == "failed").count()
|
|
|
|
# 最近24小时的日志数
|
|
yesterday = datetime.utcnow() - timedelta(days=1)
|
|
recent_logs = db.query(TriggerLog).filter(TriggerLog.created_at >= yesterday).count()
|
|
|
|
# 按仓库分组的统计
|
|
repo_stats = db.query(
|
|
TriggerLog.repository_name,
|
|
db.func.count(TriggerLog.id).label('count')
|
|
).group_by(TriggerLog.repository_name).all()
|
|
|
|
return {
|
|
"total_logs": total_logs,
|
|
"successful_logs": successful_logs,
|
|
"failed_logs": failed_logs,
|
|
"recent_logs_24h": recent_logs,
|
|
"repository_stats": [
|
|
{"repository": repo, "count": count}
|
|
for repo, count in repo_stats
|
|
]
|
|
}
|
|
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Failed to get log stats: {str(e)}") |