"""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)