# ───────────────────────────────────────────────────────────────────────────── # Stage 1 — Build the Vite frontend # ───────────────────────────────────────────────────────────────────────────── FROM node:20-alpine AS frontend-builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # → dist/index.html (single-file bundle) # ───────────────────────────────────────────────────────────────────────────── # Stage 2 — Build the Express API proxy # ───────────────────────────────────────────────────────────────────────────── FROM node:20-alpine AS api-builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npx tsc -p tsconfig.server.json # → dist-server/ # The root package.json is type=module, but the server build is CommonJS. # Add a scoped package.json so Node treats dist-server/*.js as CommonJS. RUN printf '{"type":"commonjs"}\n' > dist-server/package.json # ───────────────────────────────────────────────────────────────────────────── # Stage 3 — Frontend served by Nginx # ───────────────────────────────────────────────────────────────────────────── FROM nginx:alpine AS frontend RUN rm -rf /usr/share/nginx/html/* COPY nginx.conf /etc/nginx/conf.d/default.conf COPY --from=frontend-builder /app/dist /usr/share/nginx/html EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] # ───────────────────────────────────────────────────────────────────────────── # Stage 4 — API proxy running on Node # ───────────────────────────────────────────────────────────────────────────── FROM node:20-alpine AS api WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY --from=api-builder /app/dist-server ./dist-server COPY --from=api-builder /app/src/types ./src/types EXPOSE 3001 CMD ["node", "dist-server/server/index.js"]