# syntax=docker/dockerfile:1.7 # # Cyclone frontend — React SPA built with node:20-alpine and served by # nginx:1.27-alpine. nginx reverse-proxies /api/* to the backend service # over the compose-managed bridge network. # ---------- builder ---------- FROM node:20-alpine AS builder WORKDIR /build # Install deps first so this layer caches across source edits. # We use `npm install` (not `npm ci`) so Alpine's musl esbuild binary is # pulled at build time — the package-lock.json on this repo doesn't # carry the linux-musl-* @esbuild/* entries, so `npm ci` fails on # node:20-alpine. `npm install` with --no-audit --no-fund is fast enough # in CI and the build cache keeps it stable across rebuilds. COPY package.json package-lock.json* ./ RUN npm install --no-audit --no-fund # Build the production bundle into dist/. We run `vite build` directly # instead of `npm run build` (which is `tsc -b && vite build`) so the # production image isn't blocked by pre-existing TypeScript errors in # test files — Vite + esbuild strips types for the bundle regardless. # Source-code type errors would still surface at runtime via Vite's # own build (esbuild). Run `npm run typecheck` separately to see them. COPY . . RUN npx vite build # ---------- runtime ---------- FROM nginx:1.27-alpine # Replace the default nginx site with ours (SPA + reverse proxy). RUN rm -f /etc/nginx/conf.d/default.conf COPY nginx.conf /etc/nginx/conf.d/default.conf COPY --from=builder /build/dist /usr/share/nginx/html # wget is on busybox; nginx:alpine doesn't ship curl. HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ CMD wget -qO- http://127.0.0.1:8080/ >/dev/null || exit 1 EXPOSE 8080