# syntax=docker/dockerfile:1 # --- Build stage ------------------------------------------------------- FROM node:24-alpine AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN --mount=type=cache,target=/root/.npm \ npm ci COPY . . RUN npm run build # --- Runtime stage ------------------------------------------------------- # Static export served by nginx. /api and /uploads are NOT proxied here -- # in-cluster routing sends those paths straight to the backend Service (see # charts/*/templates/ingress.yaml), the same way vite.config.ts's dev # proxy makes /api and /uploads look same-origin to the browser. That keeps # the refresh-token cookie same-site without any CORS config in prod. # # nginx-unprivileged listens on 8080 and runs as a non-root user out of the # box (no chown/setuid dance needed to satisfy the chart's # runAsNonRoot securityContext). FROM nginxinc/nginx-unprivileged:1.27-alpine COPY nginx.conf /etc/nginx/conf.d/default.conf COPY --from=builder /app/dist /usr/share/nginx/html EXPOSE 8080