ymlin105 commited on
Commit
9998702
·
1 Parent(s): 653865f

feat: upgrade to full-stack deployment (React + FastAPI)

Browse files
Files changed (3) hide show
  1. .dockerignore +28 -0
  2. Dockerfile +15 -2
  3. src/main.py +27 -2
.dockerignore ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Exclude version control
2
+ .git
3
+ .gitignore
4
+
5
+ # Exclude local dependencies
6
+ node_modules/
7
+ web/node_modules/
8
+ __pycache__/
9
+ *.pyc
10
+
11
+ # Exclude data (will be downloaded or mounted)
12
+ data/
13
+ !data/.gitkeep
14
+
15
+ # Exclude local environment
16
+ .env
17
+ .venv
18
+ venv/
19
+
20
+ # Exclude VSCode
21
+ .vscode/
22
+
23
+ # Exclude existing builds
24
+ web/dist/
25
+ build/
26
+ dist/
27
+ eggs/
28
+ .eggs/
Dockerfile CHANGED
@@ -1,3 +1,13 @@
 
 
 
 
 
 
 
 
 
 
1
  FROM python:3.10-slim
2
 
3
  WORKDIR /app
@@ -12,14 +22,17 @@ COPY requirements.txt .
12
  RUN pip install --no-cache-dir -r requirements.txt
13
  RUN pip install "uvicorn[standard]"
14
 
15
- # Copy application code
16
  COPY . .
17
 
 
 
 
 
18
  # Create a non-root user (Standard for HF Spaces)
19
  RUN useradd -m -u 1000 user
20
 
21
  # Create data directory and set permissions
22
- # This ensures local fallback works and persistent storage mountpoint is accessible
23
  RUN mkdir -p /app/data && chown -R user:user /app
24
 
25
  # Switch to non-root user
 
1
+ # --- Stage 1: Build Frontend ---
2
+ FROM node:18-alpine as frontend-builder
3
+
4
+ WORKDIR /app/web
5
+ COPY web/package*.json ./
6
+ RUN npm ci
7
+ COPY web/ .
8
+ RUN npm run build
9
+
10
+ # --- Stage 2: Final Backend Image ---
11
  FROM python:3.10-slim
12
 
13
  WORKDIR /app
 
22
  RUN pip install --no-cache-dir -r requirements.txt
23
  RUN pip install "uvicorn[standard]"
24
 
25
+ # Copy backend code
26
  COPY . .
27
 
28
+ # Copy built frontend assets from Stage 1
29
+ # We allow the copy to fail if web dir isn't perfect, but here we expect success
30
+ COPY --from=frontend-builder /app/web/dist /app/web/dist
31
+
32
  # Create a non-root user (Standard for HF Spaces)
33
  RUN useradd -m -u 1000 user
34
 
35
  # Create data directory and set permissions
 
36
  RUN mkdir -p /app/data && chown -R user:user /app
37
 
38
  # Switch to non-root user
src/main.py CHANGED
@@ -37,8 +37,33 @@ app = FastAPI(
37
  # Include Routers
38
  app.include_router(chat_router)
39
 
40
- # 挂载静态目录,确保前端能访问 /assets/cover-not-found.jpg
41
- app.mount("/assets", StaticFiles(directory="assets"), name="assets")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
 
44
 
 
37
  # Include Routers
38
  app.include_router(chat_router)
39
 
40
+ # --- Frontend Serving (SPA) ---
41
+ import os
42
+ from fastapi.responses import FileResponse
43
+
44
+ # 1. Mount React Assets (JS/CSS)
45
+ if os.path.exists("web/dist/assets"):
46
+ app.mount("/assets", StaticFiles(directory="web/dist/assets"), name="assets")
47
+
48
+ # 2. Mount Local Content Assets (Book Covers)
49
+ app.mount("/content", StaticFiles(directory="assets"), name="content")
50
+
51
+ # 3. Serve React App (Catch-All for Client-Side Routing)
52
+ @app.get("/{full_path:path}")
53
+ async def serve_react_app(full_path: str):
54
+ # API requests pass through (FastAPI matches specific routes first)
55
+ if full_path.startswith("api") or full_path.startswith("docs") or full_path.startswith("openapi"):
56
+ raise HTTPException(status_code=404, detail="Not Found")
57
+
58
+ # Serve index.html for all other routes (SPA)
59
+ if os.path.exists("web/dist/index.html"):
60
+ return FileResponse("web/dist/index.html")
61
+
62
+ # Fallback if frontend isn't built
63
+ return {
64
+ "message": "Backend is running. Frontend not found (did you run npm build?)",
65
+ "docs_url": "/docs"
66
+ }
67
 
68
 
69