""" Sanguo VeighNa Web API FastAPI 应用入口 """ from fastapi import FastAPI, Request, status from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse, FileResponse from fastapi.staticfiles import StaticFiles from fastapi.exceptions import RequestValidationError from starlette.exceptions import HTTPException as StarletteHTTPException from contextlib import asynccontextmanager import logging import os from .routes import auth, gateway, market, trading, strategy, system from ..services.main_service import VeighNaService from ..websocket import router as websocket_router, EventMonitorManager logger = logging.getLogger(__name__) # 获取项目根目录 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 全局 VeighNa 服务实例 vn_service: VeighNaService = None # 全局事件监听器管理器 event_monitor_manager: EventMonitorManager = None @asynccontextmanager async def lifespan(app: FastAPI): """应用生命周期管理""" # 启动时初始化 global vn_service, event_monitor_manager logger.info("Starting Sanguo VeighNa Web API...") try: vn_service = VeighNaService() await vn_service.initialize() logger.info("VeighNa service initialized successfully") # 初始化事件监听器管理器 if vn_service.main_engine and vn_service.main_engine.event_engine: event_monitor_manager = EventMonitorManager(vn_service.main_engine.event_engine) event_monitor_manager.start_all() logger.info("Event monitor manager initialized") except Exception as e: logger.error(f"Failed to initialize VeighNa service: {e}") # 允许应用启动,但标记服务为未就绪 vn_service = None event_monitor_manager = None yield # 关闭时清理 if event_monitor_manager: event_monitor_manager.stop_all() logger.info("Event monitor manager stopped") if vn_service: await vn_service.shutdown() logger.info("VeighNa service shutdown complete") # 创建 FastAPI 应用 app = FastAPI( title="Sanguo VeighNa Web API", description="Sanguo 量化交易平台 Web API", version="1.0.0", docs_url="/docs", redoc_url="/redoc", lifespan=lifespan ) # CORS 中间件配置 app.add_middleware( CORSMiddleware, allow_origins=["*"], # 生产环境应限制具体域名 allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 全局异常处理 @app.exception_handler(StarletteHTTPException) async def http_exception_handler(request: Request, exc: StarletteHTTPException): """处理 HTTP 异常""" return JSONResponse( status_code=exc.status_code, content={"error": exc.detail, "status_code": exc.status_code} ) @app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError): """处理请求验证异常""" return JSONResponse( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content={ "error": "Validation error", "details": exc.errors(), "status_code": 422 } ) @app.exception_handler(Exception) async def general_exception_handler(request: Request, exc: Exception): """处理未捕获的异常""" logger.error(f"Unhandled exception: {exc}", exc_info=True) return JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"error": "Internal server error", "status_code": 500} ) # 注册路由 api_prefix = "/api/v1" app.include_router( system.router, prefix=f"{api_prefix}/system", tags=["system"] ) app.include_router( auth.router, prefix=f"{api_prefix}/auth", tags=["auth"] ) app.include_router( gateway.router, prefix=f"{api_prefix}/gateway", tags=["gateway"] ) app.include_router( market.router, prefix=f"{api_prefix}/market", tags=["market"] ) app.include_router( trading.router, prefix=f"{api_prefix}/trading", tags=["trading"] ) app.include_router( strategy.router, prefix=f"{api_prefix}/strategy", tags=["strategy"] ) # WebSocket 路由(不使用 API 前缀) app.include_router( websocket_router, tags=["websocket"] ) # 挂载静态文件 static_dir = os.path.join(BASE_DIR, "static") if os.path.exists(static_dir): app.mount("/static", StaticFiles(directory=static_dir), name="static") logger.info(f"Static files mounted from: {static_dir}") else: logger.warning(f"Static files directory not found: {static_dir}") # 根路径 - 返回主页 @app.get("/") async def root(): """返回前端主页""" template_path = os.path.join(BASE_DIR, "templates", "index.html") if os.path.exists(template_path): return FileResponse(template_path) return { "name": "Sanguo VeighNa Web API", "version": "1.0.0", "status": "running", "docs": "/docs", "health": "/health" } # API 根路径(保持兼容性) @app.get("/api") async def api_root(): """API 根路径""" return { "name": "Sanguo VeighNa Web API", "version": "1.0.0", "status": "running", "docs": "/docs", "health": "/health" } # 健康检查(不通过 /api/v1 前缀,供容器健康检查使用) @app.get("/health") async def health_check(): """健康检查端点""" if vn_service is None: return { "status": "degraded", "message": "VeighNa service not initialized" } return { "status": "healthy", "service": "Sanguo VeighNa Web API", "version": "1.0.0" } # 导出应用实例(供 uvicorn 使用) __all__ = ["app", "vn_service", "event_monitor_manager"]