# Build context is frontend/ (Coolify base directory /frontend). # # Cache contract: # 1. npm CLI pin (node:22-alpine ships npm 10.9.9, which crashes with "edgesOut") # 2. package.json + package-lock.json -> npm ci # 3. Source, then Vite. VITE_API_BASE_URL is applied here so it does not # invalidate the install layer. # A React/source edit must leave `npm ci` CACHED. A lockfile edit must not. ARG NODE_IMAGE=node:22-alpine ARG NGINX_IMAGE=nginx:1.27-alpine FROM ${NODE_IMAGE} AS build # Build-only proxy. Not written into the nginx image. ARG HTTP_PROXY ARG HTTPS_PROXY ARG http_proxy ARG https_proxy ARG NO_PROXY ARG no_proxy ARG NPM_VERSION=12.2.0 # Empty keeps https://registry.npmjs.org. Set in Coolify for an Iran mirror. ARG NPM_REGISTRY= WORKDIR /app RUN --mount=type=cache,target=/root/.npm \ if [ -n "${NPM_REGISTRY}" ]; then npm config set registry "${NPM_REGISTRY}"; fi \ && npm install -g "npm@${NPM_VERSION}" COPY package.json package-lock.json ./ RUN --mount=type=cache,target=/root/.npm \ if [ -n "${NPM_REGISTRY}" ]; then npm config set registry "${NPM_REGISTRY}"; fi \ && npm ci --no-audit --no-fund --allow-remote=all COPY . . # Vite inlines this at build time. A runtime env var does not change the bundle. # Declared after npm ci so a URL change rebuilds the bundle only. ARG VITE_API_BASE_URL= ENV VITE_API_BASE_URL=${VITE_API_BASE_URL} RUN npm run build FROM ${NGINX_IMAGE} # Local compose keeps nginx.conf (it proxies /api to the backend service). # Coolify passes nginx.prod.conf: the SPA is kamidso.com and the API is api.kamidso.com. ARG NGINX_CONF=nginx.conf COPY ${NGINX_CONF} /etc/nginx/conf.d/default.conf COPY --from=build /app/dist /usr/share/nginx/html EXPOSE 80 HEALTHCHECK --interval=20s --timeout=5s --start-period=10s --retries=5 \ CMD wget -qO- http://127.0.0.1/ >/dev/null || exit 1