# Two-target Dockerfile: slim `web` image for the Next.js server, larger
# `worker` image for the queue worker. Buildkit shares the heavy `deps` +
# `builder` stages between both targets, so building both is the same cost
# as building one — only the final COPY layer differs.
#
# Used by docker-compose.prod.yml:
#   - service `app`    → target: web    (~150MB,  `node server.js`)
#   - service `worker` → target: worker (~300MB,  `pnpm worker`)
#
# Web image stays slim by relying on Next's standalone output (nft trace).
# Worker image ships the full production node_modules + source files —
# `scripts/worker.ts` runs via tsx and imports the toolkit plugins to
# register job handlers, which means it needs the same code surface as
# the web app (just no HTTP server). See DEPLOY.md for the trade-off.

# --- Base image ---
# Node 22 LTS (Debian slim — glibc required for sharp's libvips binding).
FROM node:22-slim AS base

# --- Stage 1: Install dependencies (shared by web + worker) ---
FROM base AS deps
WORKDIR /app
RUN corepack enable pnpm
# pnpm-workspace.yaml carries the `allowBuilds` allowlist that lets sharp /
# esbuild / @swc/core / @parcel/watcher run their postinstall scripts. pnpm
# 10+ blocks postinstalls by default; without the allowlist `next build`
# can't find sharp's binary and the build dies.
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \
    pnpm install --frozen-lockfile

# --- Stage 2: Build the application (web only — worker has no build step) ---
FROM base AS builder
WORKDIR /app
RUN corepack enable pnpm
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Ensure `migrations/` exists so the web + worker stages' COPY succeeds
# even on a fresh scaffold where the user hasn't run `pnpm db:migrate`
# yet (the toolkit's runtime migrator creates files on first call).
RUN mkdir -p migrations
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# Dummy env vars so toolkit.config.ts and route loaders don't throw at
# build time. No DB connection actually opens — these are just required
# to satisfy validation.
ENV DATABASE_URL=postgresql://build:build@localhost:5432/build
ENV BETTER_AUTH_SECRET=build-secret-not-used-at-runtime
RUN pnpm build

# --- Stage 3a: Web runner (slim, standalone trace) ---
FROM base AS web
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
ENV NEXT_TELEMETRY_DISABLED=1

# Copy standalone output (server.js + traced minimal node_modules).
COPY --from=builder --chown=node:node /app/.next/standalone ./
# Static assets (JS/CSS chunks — not in the trace).
COPY --from=builder --chown=node:node /app/.next/static ./.next/static
# Public assets (favicon, robots.txt, images).
COPY --from=builder --chown=node:node /app/public ./public
# Migrations — applied at app startup by the toolkit migration runner.
COPY --from=builder --chown=node:node /app/migrations ./migrations

USER node
EXPOSE 3000
CMD ["node", "server.js"]

# --- Stage 3b: Worker runner (full deps + source for tsx) ---
# The worker has no build step — `scripts/worker.ts` runs via tsx against
# the full source tree. It needs the same code surface as the web app
# (entities, lumi.config, plugin handlers) so the queue handlers register
# correctly. node_modules comes from `deps` (production deps already
# resolved). Bigger image than web (~300MB) but worker images are infra,
# not edge-deployed, so the size is acceptable.
FROM base AS worker
WORKDIR /app
# /app is created by WORKDIR as root:root — re-own it so pnpm (running
# under USER node below) can write its dep-status temp file at startup.
# Without this, `pnpm worker` exits with EACCES on /app before reaching
# the actual worker script.
RUN chown node:node /app
RUN corepack enable pnpm
ENV NODE_ENV=production

# node_modules with pnpm's allowBuilds postinstalls already applied
# (sharp etc. compiled in stage 1).
COPY --from=deps --chown=node:node /app/node_modules ./node_modules
# Full source tree — `scripts/worker.ts` → `lib/app.ts` → `lumi.config.ts`
# transitively pulls in entities + settings + seeds + any custom plugin /
# transform definitions. Using a hand-maintained allowlist of dirs here
# was a footgun: adding a new top-level dir (e.g. `routes/`, `transforms/`)
# silently broke the worker at runtime. `.dockerignore` filters out
# `node_modules/`, `.next/`, tests, docs, etc.
COPY --chown=node:node . .
# Migrations not needed by worker (applied at web startup) but cheap to
# include for parity / future use cases. The web container is the only
# one that should actually apply them — if you ever start a worker-only
# deployment, gate the migration runner explicitly to avoid the race.

USER node
CMD ["pnpm", "worker"]
