from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from starlette.middleware.base import BaseHTTPMiddleware from app.api.main import api_router from app.api.v1 import websocket from app.core.config import settings from app.core.websocket.broadcaster import broadcaster from app.db.session import init_db from app.services.scheduler import ( cleanup_stale_running_tasks, start_scheduler, stop_scheduler, sync_scheduler_with_datasources, ) class WebSocketCORSMiddleware(BaseHTTPMiddleware): async def dispatch(self, request, call_next): if request.url.path.startswith("/ws") and request.method == "GET": response = await call_next(request) response.headers["Access-Control-Allow-Origin"] = "*" response.headers["Access-Control-Allow-Methods"] = "GET, OPTIONS" response.headers["Access-Control-Allow-Headers"] = "*" return response return await call_next(request) @asynccontextmanager async def lifespan(app: FastAPI): await init_db() await cleanup_stale_running_tasks() start_scheduler() await sync_scheduler_with_datasources() broadcaster.start() yield broadcaster.stop() stop_scheduler() app = FastAPI( title=settings.PROJECT_NAME, version=settings.VERSION, description="智能星球计划 - 态势感知系统\n\n## 功能模块\n\n- **用户认证**: JWT-based authentication\n- **数据源管理**: 多源数据采集器管理\n- **任务调度**: 定时任务调度与监控\n- **实时更新**: WebSocket实时数据推送\n- **告警系统**: 多级告警管理\n\n## 数据层级\n\n- **L1**: 核心数据 (TOP500, Epoch AI GPU)\n- **L2**: 扩展数据 (HuggingFace, PeeringDB, 海缆)\n- **L3**: 分析数据\n- **L4**: 决策支持", lifespan=lifespan, docs_url=None, redoc_url="/docs", openapi_url="/openapi.json", ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.add_middleware(WebSocketCORSMiddleware) app.include_router(api_router, prefix="/api/v1") app.include_router(websocket.router) @app.get("/health") async def health_check(): return {"status": "healthy", "version": settings.VERSION} @app.get("/") async def root(): return { "name": settings.PROJECT_NAME, "version": settings.VERSION, "docs": "/docs", "redoc": "/redoc", } @app.get("/api/v1/scheduler/jobs") async def get_scheduler_jobs(): from app.services.scheduler import get_scheduler_jobs return {"jobs": get_scheduler_jobs()}