3.1 KiB
3.1 KiB
实现 Gateway 健康监控(P0-1)。后端 Hermes CLI 封装 + API + 前端页面。
第一部分: Hermes CLI 服务(真实数据源)
创建 backend/app/services/hermes_cli.py:
- 封装
hermes gateway statusCLI 调用 - 用 subprocess.run 执行,解析 JSON 输出
- 出错时返回空数据 + 日志告警,不抛异常
- 函数: gateway_status_all() -> list[GatewayInfo]
- 数据类型: id, name, status(online/offline/warn), downtime_minutes, restarts_today, last_event
第二部分: Gateway API
创建 backend/app/api/gateway.py,全部 require_auth
GET /api/v1/gateway/status
返回全部 Profile 的 Gateway 状态列表,支持:
- query param
?status=online|offline|warn筛选 - 每行: name, status(🟢/🔴/🟡), downtime, restarts_today, last_event, profile_type
- 数据来源: hermes_cli.gateway_status_all()
GET /api/v1/gateway/events
返回最近 24h 异常事件:
[
{"time": "2026-07-22T18:45", "profile": "monitor", "type": "heartbeat_timeout", "detail": "连续 3 次心跳无响应"},
{"time": "2026-07-22T14:20", "profile": "feishu-gw", "type": "feishu_disconnect", "detail": "WebSocket 断开 8 分钟"},
{"time": "2026-07-22T10:30", "profile": "gateway-monitor", "type": "restart", "detail": "Gateway 重启"}
]
数据来源: hermes CLI 日志或系统日志解析。暂用占位数据(TODO 标注)。
POST /api/v1/gateway/{profile}/start, /stop, /restart
Gateway 启停操作:
- 执行
hermes gateway start/stop/restart --profile {profile} - 需要 operator 或 owner 角色(require_role("owner","operator"))
- 返回操作结果 + 写入审计日志
- 操作前二次确认(前端处理)
第三部分: 前端 Gateway 页面
修改 frontend/src/pages/GatewayPage.tsx:
顶部: 异常事件时间线
- Timeline 组件,24h 横向展示
- 事件类型: 🔴心跳超时 🟠飞书断连 🔵Gateway 重启
- Hover 显示详情 tooltip
筛选栏
- 状态筛选: 全部/在线/离线/异常
- 蜂型筛选: 全部/工蜂/蜂王
- 筛选器即时生效(前端过滤,不重新请求)
状态表
- Table 组件,10s 轮询 (usePolling hook)
- 列: Profile 名, 状态 Badge, 掉线时长, 今日重启, 最近异常, 操作
- 异常行标红: Gateway 连续重启≥3次 或 掉线≥30分钟
- 操作按钮: 启动/停止/重启 (二次确认 Modal)
四态覆盖
- Loading: Skeleton (table 形状)
- Empty: "全部 Gateway 正常运行 🎉"
- Error: 错误 + 重试
- Normal: 数据展示
usePolling hook
创建 frontend/src/hooks/usePolling.ts:
import { useEffect, useRef } from 'react'
export function usePolling(callback: () => void, intervalMs: number, enabled: boolean = true) {
const savedCallback = useRef(callback)
useEffect(() => { savedCallback.current = callback }, [callback])
useEffect(() => {
if (!enabled) return
savedCallback.current() // 立即执行一次
const id = setInterval(() => savedCallback.current(), intervalMs)
return () => clearInterval(id)
}, [intervalMs, enabled])
}
注册路由: main.py 添加 from app.api import gateway; app.include_router(gateway.router, prefix="/api/v1")
完成后提交。