"""rssKeeper - FastAPI 入口""" import os from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse from starlette.middleware.cors import CORSMiddleware from database import init_db, SessionLocal from scheduler import init_feed_jobs, stop_scheduler from routers import feeds, articles, dashboard, external_api import config @asynccontextmanager async def lifespan(app: FastAPI): """应用生命周期管理""" # 启动时:初始化数据库 + 注册定时任务 init_db() db = SessionLocal() try: init_feed_jobs(db) finally: db.close() yield # 关闭时:停止调度器 stop_scheduler() app = FastAPI( title="rssKeeper", description="RSS 抓取、管理与检索系统", version="1.0.0", lifespan=lifespan, ) # CORS app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # API 路由 app.include_router(feeds.router, prefix=config.API_PREFIX) app.include_router(articles.router, prefix=config.API_PREFIX) app.include_router(dashboard.router, prefix=config.API_PREFIX) app.include_router(external_api.router, prefix=config.EXTERNAL_API_PREFIX) @app.get("/api/health") def health_check(): """健康检查""" return {"status": "ok", "service": "rssKeeper"} # 静态文件服务(前端构建产物) static_dir = os.path.join(config.BASE_DIR, "static") if os.path.exists(static_dir): app.mount("/static", StaticFiles(directory=static_dir), name="static") @app.get("/{full_path:path}") async def serve_spa(full_path: str): """Vue SPA 路由回退""" # API 路由不走这里 if full_path.startswith("api/") or full_path.startswith("docs") or full_path.startswith("openapi.json"): return {"detail": "Not found"} index_path = os.path.join(static_dir, "index.html") if os.path.exists(index_path): return FileResponse(index_path) return {"detail": "Frontend not built"}