Files
hive-manage-v3/backend/app/api/gateway.py
T
enzoandClaude 6d4a5c955b feat: Gateway health monitoring (Hermes CLI, API, frontend)
- Hermes CLI wrapper using subprocess.run for gateway status
- Gateway API: status list (with filter), events (placeholder), start/stop/restart with audit logging
- Frontend GatewayPage: timeline, filter bar, status table with 10s polling, operation modals
- usePolling hook for periodic data refresh

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-22 17:05:41 +08:00

121 lines
3.6 KiB
Python

"""Gateway API — 状态、事件、启停操作。"""
import logging
import subprocess
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.config import settings
from app.database import get_db
from app.middleware.auth import require_auth, require_role
from app.models.audit import AuditLog
from app.models.user import User
from app.services import hermes_cli
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/gateway", tags=["gateway"])
# ── 占位事件数据(TODO: 从 hermes 日志或系统日志解析) ──
PLACEHOLDER_EVENTS = [
{"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 重启"},
]
@router.get("/status")
async def get_gateway_status(
status: str | None = Query(None, pattern="^(online|offline|warn)$"),
_user: User = Depends(require_auth),
):
"""返回全部 Profile 的 Gateway 状态列表,支持 ?status= 筛选。"""
gateways = hermes_cli.gateway_status_all()
for gw in gateways:
gw.setdefault("downtime_minutes", 0)
gw.setdefault("restarts_today", 0)
gw.setdefault("last_event", "")
gw.setdefault("profile_type", "worker")
if status:
gateways = [g for g in gateways if g.get("status") == status]
return gateways
@router.get("/events")
async def get_gateway_events(
_user: User = Depends(require_auth),
):
"""返回最近 24h 异常事件。"""
# TODO: 从 hermes CLI 日志或系统日志解析真实事件
return PLACEHOLDER_EVENTS
async def _operate_gateway(
profile: str,
action: str,
user: User,
db: AsyncSession,
) -> dict:
"""执行 hermes gateway {action} --profile {profile} 并写入审计日志。"""
try:
proc = subprocess.run(
[settings.hermes_cli_path, "gateway", action, "--profile", profile],
capture_output=True,
text=True,
timeout=30,
)
success = proc.returncode == 0
detail = (proc.stdout or proc.stderr).strip()
except Exception as e:
success = False
detail = str(e)
audit = AuditLog(
user_id=user.id,
username=user.username,
action=f"gateway:{action}",
target=f"profile:{profile}",
detail=detail,
)
db.add(audit)
await db.commit()
if not success:
raise HTTPException(status_code=500, detail=f"Gateway {action} 失败: {detail}")
return {"success": True, "action": action, "profile": profile, "detail": detail}
@router.post("/{profile}/start")
async def start_gateway(
profile: str,
user: User = Depends(require_role("owner", "operator")),
db: AsyncSession = Depends(get_db),
):
"""启动 Gateway。"""
return await _operate_gateway(profile, "start", user, db)
@router.post("/{profile}/stop")
async def stop_gateway(
profile: str,
user: User = Depends(require_role("owner", "operator")),
db: AsyncSession = Depends(get_db),
):
"""停止 Gateway。"""
return await _operate_gateway(profile, "stop", user, db)
@router.post("/{profile}/restart")
async def restart_gateway(
profile: str,
user: User = Depends(require_role("owner", "operator")),
db: AsyncSession = Depends(get_db),
):
"""重启 Gateway。"""
return await _operate_gateway(profile, "restart", user, db)