Files
FastapiAdmin/backend/app/modules/monitor/health/controller.py
T
zhangtao a8d29d4ab4 fix: 修复多项后端缺陷并加固安全与限流
- 修正 AI 会话表初始化及异步数据库 URI 处理
- 新增全局 API 与 WS 聊天限流,健康检查豁免
- 修复忘记密码接口防用户枚举与参数校验
- 修复 SSE 推送数据重复编码问题
- 修复 Cron 表达式 Quartz 问号兼容
- 修复存储模块权限标识前缀
- 新增过期日志清理与任务错误中文提示
2026-09-07 00:08:00 +08:00

49 lines
2.1 KiB
Python

import asyncio
from collections.abc import AsyncGenerator
from typing import Annotated, Any
from fastapi import APIRouter, Depends, Request
from fastapi.responses import JSONResponse
from fastapi.sse import EventSourceResponse, ServerSentEvent
from fastapi_limiter.decorators import skip_limiter
from app.common.response import ResponseSchema, SuccessResponse
from app.config.setting import Settings, get_settings
from app.core.logger import logger
from app.core.router_class import OperationLogRoute
from .schema import ServiceInfoOut
from .service import HealthService
HealthRouter = APIRouter(route_class=OperationLogRoute, prefix="/health", tags=["健康检查"])
@HealthRouter.get("/check", summary="健康检查", response_model=ResponseSchema[ServiceInfoOut])
@skip_limiter # 监控探针高频调用,豁免全局限流
async def health_check(request: Request, settings: Annotated[Settings, Depends(get_settings)]) -> JSONResponse:
"""健康检查:实时探测 DB / Redis 网络连通状态。"""
redis: Any | None = getattr(request.app.state, "redis", None)
info = await HealthService.collect(redis)
ok = info.db_status == 1 and info.redis_status == 1
return SuccessResponse(data=info, msg=f"系统健康-{settings.VERSION}" if ok else f"服务异常-{settings.VERSION}")
@HealthRouter.get("/stream", summary="健康检查实时流(SSE)", response_class=EventSourceResponse)
@skip_limiter
async def health_stream_controller(
request: Request,
) -> AsyncGenerator[ServerSentEvent, None]:
"""健康检查实时流(SSE):每 30s 实时探测 DB / Redis 后推送一帧。"""
redis: Any | None = getattr(request.app.state, "redis", None)
while True:
info = await HealthService.collect(redis)
# raw_data 原样发送;用 data= 会被 fastapi.sse 再 JSON 编码一次,前端需二次解析
logger.debug(
"健康检查 SSE 推送: DB={} Redis={}",
"正常" if info.db_status == 1 else "异常",
"正常" if info.redis_status == 1 else "异常",
)
yield ServerSentEvent(raw_data=info.model_dump_json())
await asyncio.sleep(30) # SSE 推送间隔(秒)