FROM node:22-slim AS build WORKDIR /app COPY package.json package-lock.json* ./ RUN npm ci COPY . . RUN npm run build FROM nginx:alpine COPY --from=build /app/dist /usr/share/nginx/html COPY <<'EOF' /etc/nginx/conf.d/default.conf server { listen 80; root /usr/share/nginx/html; index index.html; # SPA fallback location / { try_files $uri $uri/ /index.html; } # Auth routes → alert service (any service works, they all share auth) location /api/v1/auth/ { proxy_pass http://alert:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # Drift API location /api/v1/stacks { proxy_pass http://drift:3000; proxy_set_header Host $host; } location /api/v1/reports { proxy_pass http://drift:3000; proxy_set_header Host $host; } location /api/v1/dashboard { proxy_pass http://drift:3000; proxy_set_header Host $host; } # Alert API location /api/v1/incidents { proxy_pass http://alert:3000; proxy_set_header Host $host; } location /api/v1/notifications { proxy_pass http://alert:3000; proxy_set_header Host $host; } location /api/v1/webhooks { proxy_pass http://alert:3000; proxy_set_header Host $host; } # Portal API location /api/v1/services { proxy_pass http://portal:3000; proxy_set_header Host $host; } # Cost API location /api/v1/anomalies { proxy_pass http://cost:3000; proxy_set_header Host $host; } location /api/v1/baselines { proxy_pass http://cost:3000; proxy_set_header Host $host; } location /api/v1/governance { proxy_pass http://cost:3000; proxy_set_header Host $host; } location /api/v1/ingest { proxy_pass http://cost:3000; proxy_set_header Host $host; } location /api/v1/cost { proxy_pass http://cost:3000; proxy_set_header Host $host; } # Run API location /api/v1/runbooks { proxy_pass http://run:3000; proxy_set_header Host $host; } location /api/v1/approvals { proxy_pass http://run:3000; proxy_set_header Host $host; } } EOF EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]