feat(docker): add SPA static file serving and fix Dockerfile paths\n\n- Add backend/app/main_spa.py with FastAPI static file serving for SPA\n- Fix Dockerfile.traefik to use correct backend path (backend.app.main_spa)\n- Remove web/ references (project has backend/ at root)\n- Frontend dist files served from /app/frontend/dist\n- Health check endpoint at /health\n- Supports Traefik reverse proxy at bttiaw.hzau.edu.cn\n\nCo-Authored-By: Claude <noreply@anthropic.com>

This commit is contained in:
zly
2026-01-13 23:49:19 +08:00
parent af83b5fba8
commit 1a1ba71777
2 changed files with 78 additions and 6 deletions

72
backend/app/main_spa.py Normal file
View File

@@ -0,0 +1,72 @@
"""FastAPI 主应用 - with SPA static file serving"""
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from pathlib import Path
from contextlib import asynccontextmanager
from .config import settings
from .api.v1 import jobs, upload, results, tasks
@asynccontextmanager
async def lifespan(app: FastAPI):
"""应用生命周期管理"""
# 启动时
print("🚀 Starting BtToxin Pipeline API...")
yield
# 关闭时
print("👋 Shutting down BtToxin Pipeline API...")
app = FastAPI(
title=settings.APP_NAME,
version=settings.APP_VERSION,
description="Automated Bacillus thuringiensis toxin mining pipeline",
lifespan=lifespan
)
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=settings.CORS_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 路由
app.include_router(jobs.router, prefix=f"{settings.API_V1_STR}/jobs", tags=["jobs"])
app.include_router(tasks.router, prefix=f"{settings.API_V1_STR}/tasks", tags=["tasks"])
app.include_router(upload.router, prefix=f"{settings.API_V1_STR}/upload", tags=["upload"])
app.include_router(results.router, prefix=f"{settings.API_V1_STR}/results", tags=["results"])
@app.get("/")
async def root():
return {
"name": settings.APP_NAME,
"version": settings.APP_VERSION,
"status": "healthy"
}
@app.get("/health")
async def health():
return {"status": "ok"}
# Mount static files for frontend (in production)
# The frontend dist files are served from /app/frontend/dist in the container
frontend_dist_path = Path("/app/frontend/dist")
if frontend_dist_path.exists():
app.mount("/assets", StaticFiles(directory=str(frontend_dist_path / "assets")), name="assets")
@app.get("/{full_path:path}", include_in_schema=False)
async def serve_spa(full_path: str):
"""Serve frontend SPA - all routes go to index.html"""
file_path = frontend_dist_path / full_path
if file_path.exists() and file_path.is_file():
return FileResponse(file_path)
return FileResponse(frontend_dist_path / "index.html")

View File

@@ -14,7 +14,7 @@ COPY pixi.toml .
COPY pyproject.toml .
COPY scripts/ scripts/
COPY bttoxin/ bttoxin/
COPY web/ web/
COPY backend/ backend/
COPY Data/ Data/
# Install all pixi environments
@@ -31,8 +31,7 @@ FROM node:20 AS frontend-builder
WORKDIR /app
# Copy frontend source from builder
COPY --from=builder /app/web ../web/
# Copy frontend source
COPY frontend/ .
RUN npm install -g pnpm && \
@@ -60,7 +59,7 @@ COPY --from=builder /app/.pixi /app/.pixi
COPY --from=builder /shell-hook.sh /shell-hook.sh
# Copy backend code
COPY --from=builder /app/web /app/web
COPY --from=builder /app/backend /app/backend
COPY --from=builder /app/Data /app/Data
COPY --from=builder /app/bttoxin /app/bttoxin
COPY --from=builder /app/scripts /app/scripts
@@ -77,11 +76,12 @@ EXPOSE 8000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/api/health || exit 1
CMD curl -f http://localhost:8000/health || exit 1
# Entrypoint
ENTRYPOINT ["/bin/bash", "/shell-hook.sh"]
# Command: Start FastAPI backend
# FastAPI will serve both API and frontend static files
CMD ["uvicorn", "web.backend.main:app", "--host", "0.0.0.0", "--port", "8000"]
# Note: Using backend.app.main_spa for SPA static file support
CMD ["uvicorn", "backend.app.main_spa:app", "--host", "0.0.0.0", "--port", "8000"]