1+ # Stage 1 — Build React app
2+ FROM node:22-alpine AS builder
3+ WORKDIR /app
4+
5+ # Copy website frontend package files only
6+ COPY frontend/website/package*.json ./frontend/website/
7+ RUN cd frontend/website && npm install
8+
9+ # Copy website frontend source
10+ COPY frontend/website/ ./frontend/website/
11+
12+ # Build website frontend with environment variables
13+ ARG VITE_SITE_URL=https://leetbuddy.app
14+ ARG VITE_VIDEO_URL
15+ ARG VITE_CHROME_STORE_URL
16+ ENV VITE_SITE_URL=$VITE_SITE_URL
17+ ENV VITE_VIDEO_URL=$VITE_VIDEO_URL
18+ ENV VITE_CHROME_STORE_URL=$VITE_CHROME_STORE_URL
19+ ENV NODE_ENV=production
20+ RUN cd frontend/website && npm run build
21+
22+ # Stage 2 — Backend dependencies
23+ FROM node:22-alpine AS backend-deps
24+ WORKDIR /app
25+
26+ # Copy backend package files
27+ COPY backend/package*.json ./backend/
28+
29+ # Install production dependencies
30+ RUN cd backend && npm install --omit=dev
31+
32+ # Stage 3 — Final image: NGINX + Node
33+ FROM nginx:stable-alpine
34+
35+ # Install Node.js and dumb-init for proper signal handling
36+ RUN apk add --no-cache nodejs npm dumb-init
37+
38+ # Create app directory
39+ WORKDIR /app
40+
41+ # Copy backend source and dependencies
42+ COPY backend/ ./backend/
43+ COPY --from=backend-deps /app/backend/node_modules ./backend/node_modules
44+
45+ # Copy built website frontend to nginx directory
46+ COPY --from=builder /app/frontend/website/dist /usr/share/nginx/html
47+
48+ # Copy NGINX config as template
49+ COPY nginx/default.conf /etc/nginx/templates/default.conf.template
50+
51+ # Build args for internal ports (with defaults)
52+ ARG WEBSITE_PORT=3001
53+ ARG EXTENSION_PORT=3002
54+
55+ # Set environment variables from build args
56+ ENV WEBSITE_PORT=$WEBSITE_PORT
57+ ENV EXTENSION_PORT=$EXTENSION_PORT
58+ ENV NODE_ENV=production
59+
60+ # Create an entrypoint that starts backend then delegates to nginx
61+ RUN echo '#!/bin/sh' > /entrypoint.sh && \
62+ echo 'set -e' >> /entrypoint.sh && \
63+ echo '' >> /entrypoint.sh && \
64+ echo '# Start backend in background' >> /entrypoint.sh && \
65+ echo 'cd /app/backend && node index.js &' >> /entrypoint.sh && \
66+ echo 'BACKEND_PID=$!' >> /entrypoint.sh && \
67+ echo '' >> /entrypoint.sh && \
68+ echo '# Handle shutdown gracefully - forward SIGQUIT to backend' >> /entrypoint.sh && \
69+ echo 'trap "kill -QUIT $BACKEND_PID 2>/dev/null || true; wait $BACKEND_PID 2>/dev/null || true; exit" SIGQUIT SIGTERM SIGINT' >> /entrypoint.sh && \
70+ echo '' >> /entrypoint.sh && \
71+ echo '# Run nginx entrypoint (handles templates + starts nginx)' >> /entrypoint.sh && \
72+ echo 'exec /docker-entrypoint.sh "$@"' >> /entrypoint.sh && \
73+ chmod +x /entrypoint.sh
74+
75+ # Health check using PORT from environment
76+ HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
77+ CMD wget -q --spider http://localhost:${PORT:-10000}/health || exit 1
78+
79+ # Use dumb-init with our wrapper entrypoint
80+ ENTRYPOINT ["dumb-init" , "--" , "/entrypoint.sh" ]
81+ CMD ["nginx" , "-g" , "daemon off;" ]
0 commit comments