You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

147 lines
4.8 KiB

import time
import uuid
import psutil
import os
from datetime import datetime
from fastapi import APIRouter, Depends, Request
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from database import get_db
from models import User, ChatSession, ChatMessage, Task, FlowDefinition, FlowExecution, SystemMetric
from schemas import SystemHealthOut, UsageStatsOut
from dependencies import get_current_user
from middleware.cache_manager import cache_manager
from middleware.rate_limiter import rate_limiter
router = APIRouter(prefix="/api/system", tags=["system"])
_start_time = time.time()
@router.get("/health", response_model=SystemHealthOut)
async def health_check(request: Request, db: AsyncSession = Depends(get_db)):
db_ok = False
try:
await db.execute(select(func.count()).select_from(User))
db_ok = True
except Exception:
pass
mem = psutil.Process(os.getpid()).memory_info()
cpu = psutil.cpu_percent(interval=0.1)
uptime = time.time() - _start_time
try:
user_count = await db.execute(select(func.count(User.id)))
active_users = user_count.scalar() or 0
except Exception:
active_users = 0
return SystemHealthOut(
status="healthy" if db_ok and cache_manager.available else "degraded",
service="enterprise-ai-platform",
uptime_seconds=round(uptime, 1),
db_connected=db_ok,
redis_connected=cache_manager.available,
active_users=active_users,
memory_mb=round(mem.rss / 1024 / 1024, 1),
cpu_percent=round(cpu, 1),
)
@router.get("/stats", response_model=UsageStatsOut)
async def usage_stats(request: Request, db: AsyncSession = Depends(get_db)):
today = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
total_users = (await db.execute(select(func.count(User.id)))).scalar() or 0
active_today = (await db.execute(
select(func.count(func.distinct(User.id)))
.join(ChatSession, ChatSession.user_id == User.id)
.where(ChatSession.created_at >= today)
)).scalar() or 0
total_sessions = (await db.execute(select(func.count(ChatSession.id)))).scalar() or 0
total_messages = (await db.execute(select(func.count(ChatMessage.id)))).scalar() or 0
total_tasks = (await db.execute(select(func.count(Task.id)))).scalar() or 0
total_flows = (await db.execute(select(func.count(FlowDefinition.id)))).scalar() or 0
published = (await db.execute(
select(func.count(FlowDefinition.id)).where(FlowDefinition.status == "published")
)).scalar() or 0
api_calls = (await db.execute(
select(func.count(FlowExecution.id)).where(FlowExecution.started_at >= today)
)).scalar() or 0
return UsageStatsOut(
total_users=total_users,
active_users_today=active_today,
total_sessions=total_sessions,
total_messages=total_messages,
total_tasks=total_tasks,
total_flows=total_flows,
published_flows=published,
api_calls_today=api_calls,
avg_response_time_ms=0.0,
)
@router.post("/metrics")
async def collect_metrics(payload: dict, request: Request, db: AsyncSession = Depends(get_db)):
metric = SystemMetric(
metric_type=payload.get("metric_type", "custom"),
value={"data": payload.get("value", {}), "source": payload.get("source", "api")},
)
db.add(metric)
await db.flush()
return {"code": 200, "metric_id": str(metric.id)}
@router.get("/metrics")
async def list_metrics(
request: Request,
metric_type: str | None = None,
limit: int = 50,
db: AsyncSession = Depends(get_db),
):
q = select(SystemMetric).order_by(SystemMetric.collected_at.desc())
if metric_type:
q = q.where(SystemMetric.metric_type == metric_type)
q = q.limit(limit)
result = await db.execute(q)
metrics = result.scalars().all()
return {
"code": 200,
"data": [{
"id": str(m.id),
"metric_type": m.metric_type,
"value": m.value,
"collected_at": m.collected_at.isoformat() if m.collected_at else None,
} for m in metrics],
}
@router.get("/cache/stats")
async def cache_stats(request: Request):
return {
"code": 200,
"data": {
"redis_available": cache_manager.available,
},
}
@router.get("/ratelimit/stats")
async def ratelimit_stats(request: Request):
remaining = await rate_limiter.remaining("global")
return {
"code": 200,
"data": {
"limit_per_minute": 60,
"window_seconds": 60,
"remaining": remaining,
},
}
@router.post("/cache/clear")
async def clear_cache(request: Request, pattern: str = "*"):
await cache_manager.delete_pattern(pattern)
return {"code": 200, "message": "缓存已清除"}