# syntax=docker/dockerfile:1
#
# The frontend deployment artifact. The repository had none — `dist/server.mjs`
# is a preview server that applies neither the security headers nor the cache
# policy `config/hosting/` declares — so a deployment had nothing to run.
#
# Two stages: the build produces `dist/` and, from the serving contract, the
# nginx configuration that matches it; the runtime is nginx with both.

# ---------------------------------------------------------------------------
# build
# ---------------------------------------------------------------------------
# Pinned by digest: the release-provenance gate requires an immutable runner
# identity, and a floating tag cannot give one.
FROM node@sha256:3638d9a6fe4030bd716be989438248074489337ba3275657f93595428be4fc03 AS build
WORKDIR /src

# The profile is baked at build time (scripts/generate-runtime-config.ts), so it
# has to be chosen here rather than at `docker run`. `dist/config.json` stays a
# separate file in the image, which is what makes build-once/promote possible:
# a deployment can replace just that file without rebuilding the bundle.
ARG APP_PROFILE=production
ENV APP_PROFILE=${APP_PROFILE}

# The bundle and the nginx locations must agree on the prefix the deployment
# serves this under: "/" at a domain root, "/dev/" behind a path prefix.
ARG VITE_ROUTER_BASE_PATH=/
ENV VITE_ROUTER_BASE_PATH=${VITE_ROUTER_BASE_PATH}

# `CI=true` turns on the release-provenance gate (scripts/lib/build-environment.ts),
# which refuses to build without an identity for the artifact. That is the point:
# a deployed bundle that cannot say which commit it came from is not traceable,
# and the checklist asks exactly that. Supplied as build args so the caller —
# a pipeline or the deploy script — owns the values.
ENV CI=true
ARG VITE_BUILD_ID
ARG VITE_COMMIT_SHA
ARG RELEASE_ID
ARG CI_RUNNER_IMAGE
ARG SOURCE_DATE_EPOCH
ENV VITE_BUILD_ID=${VITE_BUILD_ID}
ENV VITE_COMMIT_SHA=${VITE_COMMIT_SHA}
ENV RELEASE_ID=${RELEASE_ID}
ENV CI_RUNNER_IMAGE=${CI_RUNNER_IMAGE}
ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}

# The two values a deployment is allowed to supply (scripts/generate-runtime-
# config.ts OVERRIDES); everything else is fixed by the profile. API_BASE_URL
# has to be absolute — the runtime canonicalises it with `new URL(value)` — so
# even a same-origin deployment names its own origin here. The committed
# production profile ships a placeholder (https://api.example.com/), which is
# what a deployment that forgets this would silently serve.
ARG RUNTIME_API_BASE_URL
ARG RUNTIME_TELEMETRY_ENDPOINT
ENV RUNTIME_API_BASE_URL=${RUNTIME_API_BASE_URL}
ENV RUNTIME_TELEMETRY_ENDPOINT=${RUNTIME_TELEMETRY_ENDPOINT}

RUN corepack enable

# Dependencies first so a source-only change does not re-resolve them.
COPY package.json pnpm-lock.yaml ./
RUN corepack pnpm install --frozen-lockfile --ignore-scripts

COPY . .
RUN corepack pnpm build \
 && node scripts/generate-nginx-config.ts

# ---------------------------------------------------------------------------
# runtime
# ---------------------------------------------------------------------------
FROM nginx@sha256:65645c7bb6a0661892a8b03b89d0743208a18dd2f3f17a54ef4b76fb8e2f2a10 AS runtime

# Replaces the packaged default server block; the generated file is the whole
# server definition, including the BFF proxy locations.
RUN rm /etc/nginx/conf.d/default.conf
COPY --from=build /src/dist/nginx.conf /etc/nginx/conf.d/tech-log.conf
COPY --from=build /src/dist/ /usr/share/nginx/html/

# The generated config is served from /usr/share/nginx/html as root, so the two
# copies above would also publish nginx.conf itself. It is not secret, but it is
# not a page either.
RUN rm -f /usr/share/nginx/html/nginx.conf /usr/share/nginx/html/server.mjs \
 && rm -rf /usr/share/nginx/html/.vite \
 # The build writes config.json 0600, which nginx (running as `nginx`) cannot
 # read — the container came up healthy and answered 403 for the one file the
 # SPA needs before it can boot. Normalise what is served to world-readable.
 && chmod -R a+rX /usr/share/nginx/html

EXPOSE 80

# No `nginx -t` here: proxy_pass names are resolved when the config loads, and
# `backend`/`keycloak` only exist on the compose network. The container's own
# startup is the check, and it fails loudly.

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://127.0.0.1${VITE_ROUTER_BASE_PATH:-/}config.json || exit 1
