"""FastAPI 入口""" import os from pathlib import Path import uvicorn from contextlib import asynccontextmanager from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse from .database import init_db from .routers import auth, users, accounts, login, proxy, cookies, logs @asynccontextmanager async def lifespan(app: FastAPI): init_db() yield app = FastAPI( title="斗鱼批量登录后台", version="1.0.0", lifespan=lifespan, ) # CORS(开发期允许前端 localhost:5173) app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:5173", "http://localhost:3000"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 注册路由 app.include_router(auth.router) app.include_router(users.router) app.include_router(accounts.router) app.include_router(login.router) app.include_router(proxy.router) app.include_router(cookies.router) app.include_router(logs.router) @app.get("/api/health") def health(): return {"status": "ok"} # ---- 生产环境:serve 前端静态文件 ---- # Docker 部署时前端构建产物会被复制到 web/frontend/dist _FRONTEND_DIST = Path(__file__).resolve().parents[2] / "web" / "frontend" / "dist" _INDEX_HTML = _FRONTEND_DIST / "index.html" if _INDEX_HTML.exists(): # 挂载静态资源目录(js/css/图片等) _ASSETS_DIR = _FRONTEND_DIST / "assets" if _ASSETS_DIR.exists(): app.mount("/assets", StaticFiles(directory=str(_ASSETS_DIR)), name="assets") @app.get("/{full_path:path}") async def serve_spa(full_path: str, request: Request): """SPA fallback:非 /api 路径返回 index.html""" # 排除 API 路径 if full_path.startswith("api"): return {"detail": "Not Found"} # 尝试返回静态文件 file_path = _FRONTEND_DIST / full_path if file_path.is_file(): return FileResponse(str(file_path)) # SPA fallback 到 index.html return FileResponse(str(_INDEX_HTML)) def run(): uvicorn.run("web.backend.main:app", host="0.0.0.0", port=8000, reload=True) if __name__ == "__main__": run()