- 多阶段Dockerfile:第一阶段构建前端(npm),第二阶段Python运行时(uv+opencv) - docker-compose.yml:单服务,data/logs目录挂载持久化 - deploy.sh一键部署脚本:自动构建+启动+健康检查 - .dockerignore排除不必要文件减小构建上下文 - main.py添加前端静态文件服务(生产环境单端口部署) - README更新部署文档和代理配置说明
83 lines
2.2 KiB
Python
83 lines
2.2 KiB
Python
"""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()
|