"""Router main module - FastAPI app factory. Creates the FastAPI application with: - Feishu WebSocket client - Node WebSocket endpoint - Health check endpoints """ from __future__ import annotations import logging from typing import Optional from fastapi import FastAPI, WebSocket from fastapi.middleware.cors import CORSMiddleware from bot.handler import start_websocket_client from router.nodes import NodeRegistry, get_node_registry from router.ws import ws_node_endpoint logger = logging.getLogger(__name__) def create_app(router_secret: Optional[str] = None) -> FastAPI: """Create the FastAPI application. Args: router_secret: Secret for authenticating host client connections """ app = FastAPI(title="PhoneWork Router", version="3.0.0") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) registry = get_node_registry() if router_secret: registry._secret = router_secret @app.get("/health") async def health(): from router.rpc import get_pending_count nodes = registry.list_nodes() online_nodes = [n for n in nodes if n["status"] == "online"] return { "status": "ok", "nodes": nodes, "online_nodes": len(online_nodes), "total_nodes": len(nodes), "pending_requests": get_pending_count(), } @app.get("/nodes") async def list_nodes(): return registry.list_nodes() @app.websocket("/ws/node") async def ws_node(websocket: WebSocket): await ws_node_endpoint(websocket) @app.on_event("startup") async def startup(): import asyncio loop = asyncio.get_running_loop() start_websocket_client(loop) logger.info("Router started") @app.on_event("shutdown") async def shutdown(): logger.info("Router shut down") return app # Create top-level app instance for uvicorn from config import ROUTER_SECRET app = create_app(router_secret=ROUTER_SECRET) if __name__ == "__main__": from config import PORT import uvicorn uvicorn.run( "router.main:app", host="0.0.0.0", port=PORT, reload=False, log_level="info", ws_ping_interval=20, ws_ping_timeout=60, )