# syntax=docker/dockerfile:1

# The API image — docs/11-deploy.md.
#
# Built from the workspace root (`docker build -f apps/api/Dockerfile .`),
# because the API imports @educore/shared and a build context of apps/api
# cannot see it.

FROM node:24-alpine AS base
RUN corepack enable
WORKDIR /repo

# A registry request from Uzbekistan routinely takes a minute, and pnpm gives
# up at sixty seconds by default — which turns a slow build into a failed one.
# Observed here: individual packages taking 50–54 seconds, then the whole
# install aborting.
ENV npm_config_fetch_timeout=600000     npm_config_fetch_retries=5     npm_config_fetch_retry_maxtimeout=120000

# ── deps ────────────────────────────────────────────────────────────────────
# Manifests first, so a change to source code does not re-resolve the whole
# dependency tree. This layer is the slow one and it changes rarely.
FROM base AS deps
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY apps/api/package.json apps/api/
COPY packages/shared/package.json packages/shared/
# Only the API's own subtree. Without the filter this stage installs the
# whole workspace — which for the API image means downloading Next.js and
# its platform binaries into an image that never runs them. That is where
# the third build attempt stalled: retrying @next/swc-linux-x64-musl.
#
# `--trust-lockfile` skips re-applying the supply-chain policy to all 1062
# lockfile entries. That check belongs where the lockfile is *changed* — a
# developer's machine and CI — not on every image build of a lockfile that is
# already committed and was verified when it landed. Measured here: 4m52s of
# network calls before the install proper even started, and the build failed
# on a timeout rather than finishing slowly.
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \
    pnpm install --frozen-lockfile --trust-lockfile --filter @educore/api...

# ── build ───────────────────────────────────────────────────────────────────
FROM deps AS build
COPY . .
# The Prisma client is generated code; without this the build compiles against
# whatever the last generate produced, which in a fresh image is nothing.
RUN pnpm --filter @educore/shared build  && pnpm exec prisma generate --schema apps/api/prisma/schema.prisma  && pnpm --filter @educore/api build

# Collects the API and its production dependencies into one self-contained
# directory, from the store that is already on disk. Installing a second time
# in the runtime stage downloads the whole tree again — which on a slow
# connection is not a small detail: it is where the first version of this file
# timed out.
RUN pnpm deploy --filter @educore/api --prod --legacy /out  && cp -r node_modules/.prisma /out/node_modules/.prisma

# ── runtime ─────────────────────────────────────────────────────────────────
FROM node:24-alpine AS runtime
ENV NODE_ENV=production
WORKDIR /app

COPY --from=build /out ./

# Migrations travel with the image: the version that runs the schema change is
# the same version that was tested against it.
COPY --from=build /repo/apps/api/prisma ./prisma

# node:alpine ships a `node` user. Running as root inside a container is one
# kernel bug away from being root outside it.
USER node

EXPOSE 3001
CMD ["node", "dist/main.js"]
