import sys from contextlib import asynccontextmanager from collections.abc import AsyncIterator import uvicorn from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware # Direct IDE runs should not leave Python bytecode caches in the project tree. sys.dont_write_bytecode = True from app.agent.scheduler import build_scheduler # noqa: E402 from app.api.routes import router # noqa: E402 from app.core.config import get_settings # noqa: E402 from app.core.log_setup import configure_logging # noqa: E402 @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncIterator[None]: # 测试环境不启动后台采集,避免接口测试触发真实外部请求。 settings = get_settings() if settings.env == "test": yield return # 调度器随Web进程常驻,负责自动周期性整理情报。 scheduler = build_scheduler(settings) scheduler.start() app.state.scheduler = scheduler try: yield finally: scheduler.shutdown(wait=False) def create_app() -> FastAPI: # App factory keeps tests and production startup on the same initialization path. settings = get_settings() configure_logging(settings.log_file) app = FastAPI(title=settings.app_name, lifespan=lifespan) # The separate Vite frontend runs on 5173 during local development. app.add_middleware( CORSMiddleware, allow_origins=["http://127.0.0.1:5173", "http://localhost:5173"], allow_credentials=False, allow_methods=["*"], allow_headers=["*"], ) app.include_router(router) return app app = create_app() if __name__ == "__main__": # Direct execution is for IDE/local development; deployment can still call uvicorn explicitly. uvicorn.run("app.main:app", host="127.0.0.1", port=8010, reload=False)