70 lines
2.7 KiB
Docker
70 lines
2.7 KiB
Docker
# syntax=docker/dockerfile:1
|
|
###############################################################################
|
|
# SHARP-ER web - React Router 8 (framework mode, SSR) on Node 22
|
|
#
|
|
# Adapted from the React Router template Dockerfile. Two things changed:
|
|
# * npm -> pnpm, because this repository is a pnpm workspace and there is no
|
|
# package-lock.json to `npm ci` from;
|
|
# * the build context is the REPOSITORY ROOT, where pnpm-lock.yaml,
|
|
# pnpm-workspace.yaml and .npmrc live.
|
|
#
|
|
# docker build -f apps/web/Dockerfile -t sharper-web .
|
|
# docker compose build web # does exactly that
|
|
#
|
|
# The server reads STRAPI_URL / STRAPI_TOKEN / SITE_URL at RUN time, never at
|
|
# build time, so one image is valid for every environment.
|
|
###############################################################################
|
|
|
|
# --- base: the toolchain both stages share ----------------------------------
|
|
FROM node:22-bookworm-slim AS base
|
|
ENV PNPM_VERSION=9.12.3
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends ca-certificates \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& npm install --global "pnpm@${PNPM_VERSION}"
|
|
WORKDIR /repo
|
|
|
|
|
|
# --- build: install dependencies and produce build/client + build/server -----
|
|
FROM base AS build
|
|
|
|
# Manifests first so the install layer stays cached. Both workspace manifests
|
|
# are copied: pnpm checks the lockfile against every project in the workspace
|
|
# and treats it as stale if one is missing.
|
|
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
|
|
COPY apps/web/package.json ./apps/web/
|
|
COPY apps/cms/package.json ./apps/cms/
|
|
|
|
# `--filter web...` keeps Strapi out of this image entirely.
|
|
# devDependencies are required: vite, @react-router/dev and tailwind build here.
|
|
RUN pnpm install --frozen-lockfile --filter web...
|
|
|
|
COPY apps/web ./apps/web
|
|
RUN pnpm --filter web build
|
|
|
|
|
|
# --- runtime ----------------------------------------------------------------
|
|
FROM base AS runtime
|
|
|
|
ENV NODE_ENV=production \
|
|
PORT=3000
|
|
|
|
COPY --from=build /repo/node_modules ./node_modules
|
|
COPY --from=build /repo/package.json /repo/pnpm-workspace.yaml /repo/.npmrc ./
|
|
COPY --from=build /repo/apps/web ./apps/web
|
|
|
|
RUN chown -R node:node /repo/apps/web
|
|
USER node
|
|
WORKDIR /repo/apps/web
|
|
EXPOSE 3000
|
|
|
|
# /en is a real page: it proves the SSR handler renders, not just that a socket
|
|
# is open. It stays 200 even when the CMS is unreachable, because every route
|
|
# falls back to its bundled draft copy - so this healthcheck tracks the web
|
|
# service alone.
|
|
HEALTHCHECK --interval=15s --timeout=5s --start-period=30s --retries=5 \
|
|
CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||3000)+'/en').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
|
|
|
# react-router-serve, the production server that ships with the framework.
|
|
CMD ["pnpm", "start"]
|