# 多阶段构建 - 构建阶段 FROM node:20-alpine AS builder # 接受构建参数 ARG BUILD_MODE=production ARG RAGFLOW_BASE=/ragflow/ ENV RAGFLOW_BASE=${RAGFLOW_BASE} WORKDIR /app # 复制包管理文件 COPY package.json pnpm-lock.yaml ./ # 安装 pnpm 和依赖(工作空间) RUN npm install -g pnpm && pnpm install # 复制源代码 COPY . . # 设置环境文件(用于根应用的构建) RUN if [ "$BUILD_MODE" = "flask" ]; then \ cp .env.flask .env; \ else \ cp .env.production .env; \ fi # 同时构建两个前端(根 Vite 应用 + ragflow_web Umi 应用) RUN pnpm -r --filter ./ --filter ragflow_web run build # 生产阶段 - nginx FROM nginx:alpine AS production # 接受端口与子路径参数 ARG PORT=5173 ARG RAGFLOW_BASE=/ragflow/ # 复制自定义 nginx 配置,分别部署两个前端 RUN cat > /etc/nginx/conf.d/default.conf << EOF server { listen ${PORT}; server_name localhost; root /usr/share/nginx/html; index index.html; # 根应用(Vite)SPA 路由 location / { try_files \$uri \$uri/ /index.html; } # ragflow_web(Umi)部署在子路径,支持 SPA 路由 location ${RAGFLOW_BASE} { alias /usr/share/nginx/html/ragflow/; # 注意:使用别名目录时,SPA 回退必须指向子路径下的 index.html # 否则会错误地回退到根应用的 index.html,导致页面刷新后空白或错路由 try_files \$uri \$uri/ ${RAGFLOW_BASE}index.html; } # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control "public, immutable"; } # 安全头 add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; # Gzip 压缩 gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json; } EOF # 从构建阶段复制构建产物:根应用与 ragflow_web COPY --from=builder /app/dist /usr/share/nginx/html COPY --from=builder /app/ragflow_web/dist /usr/share/nginx/html/ragflow # 暴露端口 EXPOSE ${PORT} # 启动 nginx CMD ["nginx", "-g", "daemon off;"]