diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3be2309 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +node_modules +dist +.git +*.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..77b685a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# ---- Build stage ---- +FROM node:20-alpine AS build +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci + +COPY . . +# VITE_API_BASE_URL is intentionally left unset: nginx.conf proxies /api/* +# to the backend container, so the built app can keep using relative +# same-origin requests (see src/api/client.js). +RUN npm run build + +# ---- Serve stage ---- +FROM nginx:alpine +WORKDIR /usr/share/nginx/html + +RUN rm -rf ./* +COPY --from=build /app/dist/ . +COPY nginx.conf /etc/nginx/nginx.conf + +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..d4aa62c --- /dev/null +++ b/nginx.conf @@ -0,0 +1,40 @@ +events {} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Excel/CSV catalog uploads (see /api/upload/*) can exceed nginx's 1m default. + client_max_body_size 20m; + + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml; + + server { + listen 80; + server_name localhost; + + # Backend API, proxied to the FastAPI container on the same Docker + # network. proxy_pass uses a variable + Docker's embedded DNS + # (127.0.0.11) so the backend's address is re-resolved per request + # instead of cached once at nginx startup - otherwise nginx would + # keep hitting a stale IP after the backend container restarts. + resolver 127.0.0.11 valid=30s; + location /api/ { + set $backend_upstream "http://backend:8000"; + proxy_pass $backend_upstream; + proxy_http_version 1.1; + 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_set_header X-Forwarded-Proto $scheme; + } + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + # This line forces Nginx to pass routing back to React Router + try_files $uri $uri/ /index.html; + } + } +}