From a655e41822b8367ef076f46fe138a63587a227b4 Mon Sep 17 00:00:00 2001 From: zhi Date: Wed, 11 Mar 2026 09:43:06 +0000 Subject: [PATCH] fix: nginx reverse proxy for API/wizard, fix Object.entries null crash - Replace serve with nginx for proper reverse proxy - /api/* proxied to backend:8000, /wizard/* proxied to wizard:8080 - Eliminates CORS issues (same-origin requests) - Fixes SPA fallback returning 200 for API routes (was hiding backend-down state) - Add null guards on Object.entries for dashboard stats - Remove VITE_API_BASE/VITE_WIZARD_PORT build args (no longer needed) --- Dockerfile | 16 ++++++---------- nginx.conf | 29 +++++++++++++++++++++++++++++ src/App.tsx | 4 +--- src/pages/DashboardPage.tsx | 4 ++-- src/pages/SetupWizardPage.tsx | 2 +- src/services/api.ts | 2 +- 6 files changed, 40 insertions(+), 17 deletions(-) create mode 100644 nginx.conf diff --git a/Dockerfile b/Dockerfile index 55964ba..1e4ef54 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,16 +4,12 @@ WORKDIR /app COPY package.json package-lock.json* ./ RUN npm install COPY . . -ARG VITE_API_BASE=/api -ARG VITE_WIZARD_PORT=18080 -ENV VITE_API_BASE=$VITE_API_BASE -ENV VITE_WIZARD_PORT=$VITE_WIZARD_PORT +# API and wizard are proxied via nginx — no VITE_ env vars needed RUN npm run build -# Production stage — lightweight static server, no nginx -FROM node:20-alpine -RUN npm install -g serve@14 -WORKDIR /app -COPY --from=build /app/dist ./dist +# Production stage — nginx with reverse proxy +FROM nginx:alpine +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 3000 -CMD ["serve", "-s", "dist", "-l", "3000"] +CMD ["nginx", "-g", "daemon off;"] diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..6822e7f --- /dev/null +++ b/nginx.conf @@ -0,0 +1,29 @@ +server { + listen 3000; + root /usr/share/nginx/html; + index index.html; + + # Backend API proxy + location /api/ { + proxy_pass http://backend:8000/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_connect_timeout 5s; + proxy_read_timeout 30s; + } + + # Wizard API proxy (setup phase only) + location /wizard/ { + proxy_pass http://wizard:8080/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_connect_timeout 5s; + proxy_read_timeout 10s; + } + + # SPA fallback — only for non-API, non-asset routes + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/src/App.tsx b/src/App.tsx index f3674ed..f528814 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,8 +15,6 @@ import MilestoneDetailPage from '@/pages/MilestoneDetailPage' import NotificationsPage from '@/pages/NotificationsPage' import api from '@/services/api' -const WIZARD_PORT = Number(import.meta.env.VITE_WIZARD_PORT) || 18080 - type AppState = 'checking' | 'setup' | 'ready' export default function App() { @@ -41,7 +39,7 @@ export default function App() { // Backend not ready — show setup wizard if (appState === 'setup') { - return + return } // Backend ready but auth loading diff --git a/src/pages/DashboardPage.tsx b/src/pages/DashboardPage.tsx index 072ae19..4de833f 100644 --- a/src/pages/DashboardPage.tsx +++ b/src/pages/DashboardPage.tsx @@ -28,7 +28,7 @@ export default function DashboardPage() { {stats.total_issues} 总 Issues - {Object.entries(stats.by_status).map(([k, v]) => ( + {Object.entries(stats.by_status || {}).map(([k, v]) => (
{v} {k} @@ -39,7 +39,7 @@ export default function DashboardPage() {

按优先级

- {Object.entries(stats.by_priority).map(([k, v]) => ( + {Object.entries(stats.by_priority || {}).map(([k, v]) => (
{k}
{