dow-prod-tracker/Dockerfile
DJP 29e76b7e33 Seed bundle: switch CJS→ESM so import.meta.url works
Prisma's generated client calls fileURLToPath(import.meta.url) to locate
its query engine binary. In CJS output that's undefined, and the require
of the seed bundle blew up before running a line of actual seed code.

ESM output (.mjs) runs natively on Node 22, import.meta.url resolves
correctly, everything else about the bundle (external npm packages,
inlined generated client) stays the same.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 08:56:00 -04:00

106 lines
3.6 KiB
Docker

FROM node:22-alpine AS base
# FFmpeg for video transcoding (HLS), thumbnail extraction, and metadata
RUN apk add --no-cache ffmpeg
# Install dependencies only when needed
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci --ignore-scripts
RUN npx prisma generate || true
# Rebuild source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Generate Prisma client
RUN npx prisma generate
# Build the Next.js app
RUN npm run build
# Pre-compile the seed to a self-contained ESM bundle. Runtime then runs
# plain `node prisma/seed-dow.mjs` and avoids the whole tsx+module-
# resolution nightmare: Next.js's standalone node_modules was aggressively
# reshuffling the tree after install, leaving packages like postgres-array
# with a manifest but no index.js. Bundling sidesteps that entirely.
#
# ESM (not CJS) because Prisma's generated client uses `import.meta.url`
# for engine detection — that's `undefined` in CJS and crashes on load.
#
# `--packages=external` keeps NPM packages (bcryptjs, @prisma/*) as
# runtime import() calls so the bundle stays small and native .node files
# in @prisma/client work normally.
RUN npx -y esbuild@0.24 prisma/seed-dow.ts \
--bundle --platform=node --format=esm --target=node22 \
--outfile=prisma/seed-dow.mjs \
--packages=external
# Production image
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Copy Next.js standalone output
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# Copy Prisma schema, config, and migrations for runtime
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/prisma.config.ts ./prisma.config.ts
COPY --from=builder /app/src/generated ./src/generated
# Runtime deps — the Next.js standalone bundle covers the app itself;
# Runtime deps. The Next.js standalone tree ships with PARTIAL copies of
# the pg driver chain — package.json manifests present but index.js
# missing. npm's "version already satisfies, skipping" logic then fires
# on every subsequent install attempt (including --force), because the
# manifest version check passes and npm never looks at file integrity.
#
# Nuke the whole pg chain explicitly before reinstalling. Fail-fast at
# the end so if it ever silently regresses we see FATAL in the build
# log, not four hours later at seed time.
RUN rm -rf \
node_modules/@prisma/adapter-pg \
node_modules/pg \
node_modules/pg-cloudflare \
node_modules/pg-connection-string \
node_modules/pg-int8 \
node_modules/pg-numeric \
node_modules/pg-pool \
node_modules/pg-protocol \
node_modules/pg-types \
node_modules/postgres-array \
node_modules/postgres-bytea \
node_modules/postgres-date \
node_modules/postgres-interval \
&& npm install --no-save \
prisma@7.4.2 \
dotenv@17.3.1 \
@prisma/adapter-pg@7.4.2 \
pg@8 \
bcryptjs@3 \
&& test -f node_modules/postgres-array/index.js \
|| (echo "FATAL: postgres-array/index.js still missing after reinstall" >&2 && exit 1)
# Create uploads directories for media storage
RUN mkdir -p /data/uploads && chown nextjs:nodejs /data/uploads
RUN mkdir -p /app/public/uploads/revisions && chown -R nextjs:nodejs /app/public/uploads
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["sh", "-c", "./node_modules/.bin/prisma migrate deploy && node server.js"]