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:
72
backend/app/main_spa.py
Normal file
72
backend/app/main_spa.py
Normal 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")
|
||||||
@@ -14,7 +14,7 @@ COPY pixi.toml .
|
|||||||
COPY pyproject.toml .
|
COPY pyproject.toml .
|
||||||
COPY scripts/ scripts/
|
COPY scripts/ scripts/
|
||||||
COPY bttoxin/ bttoxin/
|
COPY bttoxin/ bttoxin/
|
||||||
COPY web/ web/
|
COPY backend/ backend/
|
||||||
COPY Data/ Data/
|
COPY Data/ Data/
|
||||||
|
|
||||||
# Install all pixi environments
|
# Install all pixi environments
|
||||||
@@ -31,8 +31,7 @@ FROM node:20 AS frontend-builder
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy frontend source from builder
|
# Copy frontend source
|
||||||
COPY --from=builder /app/web ../web/
|
|
||||||
COPY frontend/ .
|
COPY frontend/ .
|
||||||
|
|
||||||
RUN npm install -g pnpm && \
|
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 --from=builder /shell-hook.sh /shell-hook.sh
|
||||||
|
|
||||||
# Copy backend code
|
# 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/Data /app/Data
|
||||||
COPY --from=builder /app/bttoxin /app/bttoxin
|
COPY --from=builder /app/bttoxin /app/bttoxin
|
||||||
COPY --from=builder /app/scripts /app/scripts
|
COPY --from=builder /app/scripts /app/scripts
|
||||||
@@ -77,11 +76,12 @@ EXPOSE 8000
|
|||||||
|
|
||||||
# Health check
|
# Health check
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
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
|
||||||
ENTRYPOINT ["/bin/bash", "/shell-hook.sh"]
|
ENTRYPOINT ["/bin/bash", "/shell-hook.sh"]
|
||||||
|
|
||||||
# Command: Start FastAPI backend
|
# Command: Start FastAPI backend
|
||||||
# FastAPI will serve both API and frontend static files
|
# 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"]
|
||||||
|
|||||||
Reference in New Issue
Block a user