优化静态路由

This commit is contained in:
Sakurasan
2026-07-07 16:07:10 +08:00
parent 0ba7952082
commit 62961017ea
3 changed files with 34 additions and 21 deletions
+15 -8
View File
@@ -1,4 +1,4 @@
# ---- Frontend build (仅构建机原生架构编译一次) ---- # ---- Frontend build ----
FROM --platform=$BUILDPLATFORM node:22-alpine AS frontend-build FROM --platform=$BUILDPLATFORM node:22-alpine AS frontend-build
WORKDIR /app WORKDIR /app
COPY package.json pnpm-lock.yaml .npmrc ./ COPY package.json pnpm-lock.yaml .npmrc ./
@@ -6,18 +6,25 @@ RUN corepack enable && corepack prepare pnpm@10 --activate && pnpm install
COPY . . COPY . .
RUN pnpm build RUN pnpm build
# ---- Backend runtime ---- # ---- Python deps build(带 gcc 编译,产物仅用于复制)----
FROM python:3.11-slim AS python-deps
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends gcc \
&& rm -rf /var/lib/apt/lists/*
COPY backend/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 删除字节码缓存,减小体积
RUN find /usr/local/lib/python3.11 -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
# ---- Final runtime ----
FROM python:3.11-slim FROM python:3.11-slim
WORKDIR /app WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends gcc \ # 仅复制已编译好的包,无需 gcc
&& rm -rf /var/lib/apt/lists/* COPY --from=python-deps /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY backend/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY backend/ . COPY backend/ .
COPY --from=frontend-build /app/dist ./dist COPY --from=frontend-build /app/dist ./dist
EXPOSE 8000 EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
+7 -11
View File
@@ -1,18 +1,14 @@
FROM python:3.11-slim FROM python:3.11-slim AS python-deps
WORKDIR /app WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends gcc \
# System deps for akshare (pandas/numpy) and network requests
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
COPY requirements.txt . COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir -r requirements.txt
RUN find /usr/local/lib/python3.11 -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
FROM python:3.11-slim
WORKDIR /app
COPY --from=python-deps /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY . . COPY . .
# SQLite db will be created at runtime in /app
EXPOSE 8000 EXPOSE 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
+12 -2
View File
@@ -1,7 +1,7 @@
import os import os
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse, JSONResponse
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from dotenv import load_dotenv from dotenv import load_dotenv
@@ -33,6 +33,16 @@ app.include_router(shares.router, prefix="/api/share")
app.include_router(sectors.router, prefix="/api/sectors") app.include_router(sectors.router, prefix="/api/sectors")
# 生产模式:后端同时托管前端静态文件 # 生产模式:后端同时托管前端静态文件
# catch-all 路由在 API 路由之后注册,所以 API 优先级更高
dist_path = os.path.join(os.path.dirname(__file__), "dist") dist_path = os.path.join(os.path.dirname(__file__), "dist")
if os.path.isdir(dist_path): if os.path.isdir(dist_path):
app.mount("/", StaticFiles(directory=dist_path, html=True), name="static") @app.get("/{full_path:path}")
async def serve_spa(full_path: str):
file_path = os.path.join(dist_path, full_path) if full_path else os.path.join(dist_path, "index.html")
if os.path.isfile(file_path):
return FileResponse(file_path)
# SPA fallback: 非文件路径统一返回 index.html
index_path = os.path.join(dist_path, "index.html")
if os.path.isfile(index_path):
return FileResponse(index_path, media_type="text/html")
return JSONResponse({"detail": "Not Found"}, status_code=404)