58 lines
3.0 KiB
Docker
58 lines
3.0 KiB
Docker
# Build the operator console, then serve the built assets. The previous image ran `vite dev` as
|
|
# root with the whole source tree inside it, which is a development server rather than a release
|
|
# artefact: it rebuilds on request, exposes the module graph and needs write access to its own
|
|
# source. M16 replaces it with a static build served by an unprivileged nginx.
|
|
FROM node:22-alpine@sha256:c610fcdfb1d5b4740dd70c284ed3cb16bb857e0f7166196e36a5501df7a3aa32 AS build
|
|
WORKDIR /app
|
|
RUN apk upgrade --no-cache
|
|
COPY package.json package-lock.json ./
|
|
RUN npm ci
|
|
COPY . .
|
|
|
|
# Vite inlines its VITE_* variables at build time, so the API base URL is a build argument rather
|
|
# than a runtime one. The default matches the standard local deployment.
|
|
ARG VITE_API_BASE_URL=http://localhost:8000
|
|
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
|
|
RUN npm run build
|
|
|
|
FROM nginxinc/nginx-unprivileged:1.29-alpine@sha256:0c79d56aee561a1d81c63f00eee5fb5fe29279560cdc55e91425133104c7fbe6
|
|
USER root
|
|
RUN apk upgrade --no-cache
|
|
ARG VITE_API_BASE_URL=http://localhost:8000
|
|
ARG MODELFORGE_VERSION=0.0.0
|
|
ARG MODELFORGE_COMMIT=""
|
|
ARG MODELFORGE_BUILT_AT=""
|
|
ENV MODELFORGE_BUILD_COMMIT=${MODELFORGE_COMMIT}
|
|
ENV MODELFORGE_BUILD_TIMESTAMP=${MODELFORGE_BUILT_AT}
|
|
LABEL org.opencontainers.image.title="ITWorx ModelForge operator console"
|
|
LABEL org.opencontainers.image.description="Operator console for the ITWorx ModelForge control plane"
|
|
LABEL org.opencontainers.image.version="${MODELFORGE_VERSION}"
|
|
LABEL org.opencontainers.image.revision="${MODELFORGE_COMMIT}"
|
|
LABEL org.opencontainers.image.created="${MODELFORGE_BUILT_AT}"
|
|
LABEL org.opencontainers.image.source="https://git.example.com/example/modelforge.git"
|
|
LABEL org.opencontainers.image.vendor="ITWorx"
|
|
LABEL org.opencontainers.image.licenses="AGPL-3.0-or-later"
|
|
|
|
# The runtime listens above 1024 and returns to the image's unprivileged nginx user after the
|
|
# signed package upgrade and immutable file assembly. It works with a read-only root filesystem
|
|
# plus a tmpfs for its caches.
|
|
COPY --from=build /app/dist /usr/share/nginx/html
|
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
COPY security-headers.inc.template /etc/nginx/security-headers.inc.template
|
|
|
|
# The bundle's API origin is compiled in at build time, so the connect-src that protects it is
|
|
# derived from the same argument rather than maintained separately and allowed to drift. Only the
|
|
# origin is used; a path in connect-src is ignored by the browser anyway. The final grep makes a
|
|
# failed substitution break the build instead of shipping a policy with a placeholder in it.
|
|
RUN set -eu; \
|
|
API_ORIGIN=$(printf '%s' "${VITE_API_BASE_URL}" | cut -d/ -f1-3); \
|
|
sed "s|__API_ORIGIN__|${API_ORIGIN}|" /etc/nginx/security-headers.inc.template \
|
|
> /etc/nginx/conf.d/security-headers.inc; \
|
|
rm /etc/nginx/security-headers.inc.template; \
|
|
grep -q "connect-src 'self' ${API_ORIGIN};" /etc/nginx/conf.d/security-headers.inc
|
|
USER nginx
|
|
|
|
EXPOSE 3000
|
|
HEALTHCHECK --interval=15s --timeout=3s --retries=5 \
|
|
CMD wget -q -O /dev/null http://127.0.0.1:3000/ || exit 1
|