Files
craftvia/Dockerfile
T
msolarczekandClaude Opus 5 8a6fdd8f7a L5 Berichte & Unterschrift: Berichtsinhalt, Services, Unterschrift und PDF
ReportContent-Vertrag, Content-Builder mit Tagesfilter, Services für Tages-/Abschlussbericht,
Bearbeiten, Absenden, Freigabe, Zurückweisen, neue Version, Unterschrift und PDF-Erzeugung
(playwright-core, Worker-Processor, Dockerfile-Stage worker). Stubs für L2-Transition/Blocker
und Dokumenten-Store.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 12:22:40 +02:00

130 lines
7.2 KiB
Docker
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Craftvia — Next.js App (Multi-Stage-Build)
#
# Base-Image: node:22-slim (Debian/glibc) statt node:22-alpine (musl).
# Begründung (F-11): Mit einem glibc-Base greifen die im Lockfile hinterlegten
# plattformspezifischen Optional-Deps (lightningcss/@tailwindcss/oxide *-linux-*-gnu,
# ebenso die glibc-Prebuilds von @node-rs/argon2 und sharp) zuverlässig. Dadurch
# funktioniert `npm ci` (Lockfile bindend, reproduzierbar) OHNE Lockfile-Änderung —
# das package-lock.json enthält bereits alle vier Linux-Varianten (x64/arm64 × gnu/musl).
# Der frühere Kommentar ("npm install nötig wegen fehlender musl-Binaries") entfällt damit.
FROM node:22.14.0-slim AS deps
WORKDIR /app
# openssl/ca-certificates werden von Prisma (Engine) und für TLS beim npm-Install benötigt.
RUN apt-get update && apt-get install -y --no-install-recommends openssl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# npm auf 11.x pinnen: Das committete package-lock.json wurde mit npm 11 erzeugt.
# node:22.14.0 bringt npm 10.9.2 mit, das next@16.2.12 -> @swc/helpers ANDERS auflöst
# (erwartet 0.5.23, Lock hält 0.5.15) und `npm ci` daher mit "out of sync" abbricht.
# Der Resolver-Gleichstand (npm 11) macht `npm ci` grün OHNE Lockfile-Änderung.
RUN npm i -g npm@11.19.0
COPY package.json package-lock.json ./
COPY prisma ./prisma
# npm ci: installiert exakt die im Lockfile gepinnten Versionen (Supply-Chain-Integrität,
# reproduzierbare Builds). --include=optional stellt sicher, dass die plattformspezifischen
# Native-Binaries für das Zielimage (linux/glibc) mitkommen.
RUN npm ci --include=optional --no-audit --no-fund
# --- Build-Stage: kompletter Next.js-Build (standalone) ---
FROM node:22.14.0-slim AS builder
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends openssl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Platzhalter nur für die Build-Zeit: prisma.config.ts löst env("DATABASE_URL")
# beim Laden auf, und Coolify reicht die Variable nur als Build-ARG rein (nicht in
# process.env). 'prisma generate' und 'next build' (nur dynamische Routen) bauen KEINE
# echte DB-Verbindung auf. Zur Laufzeit überschreibt der echte DATABASE_URL aus der
# Coolify-Env diesen Wert (im migrate-/app-Container).
ENV DATABASE_URL="postgresql://build:build@localhost:5432/build?schema=public"
RUN npx prisma generate && npm run build
# --- Migrate-Stage: schlanker Job-Container für `prisma migrate deploy` (+ optional Seed/Bootstrap) ---
# Bewusst OHNE `next build`: enthält damit weder den kompilierten Next-Server (.next/standalone)
# noch den Build-Cache — deutlich kleinere Angriffsfläche als die builder-Stage (bisher genutzt).
# Warum nicht "nur Prisma-CLI"? prisma/seed.ts und scripts/bootstrap-admin.ts importieren aus
# ../src bzw. @/server (provisionTenant, rbac, modules) und laufen über tsx. Ein reiner
# Prisma-CLI-Container würde diese Jobs brechen. Da src/** in dieser Lane nicht angefasst werden
# darf, bleiben devDeps (tsx/prisma) + src hier nötig — eine spätere Entkopplung von Seed/Bootstrap
# vom App-Code ist als Folgeänderung empfohlen.
FROM node:22.14.0-slim AS migrate
WORKDIR /app
ENV NODE_ENV=production
RUN apt-get update && apt-get install -y --no-install-recommends openssl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY --from=deps /app/node_modules ./node_modules
COPY package.json package-lock.json prisma.config.ts tsconfig.json ./
COPY prisma ./prisma
COPY scripts ./scripts
COPY src ./src
# Prisma-Client für Seed/Bootstrap generieren (migrate deploy selbst braucht nur schema+migrations).
ENV DATABASE_URL="postgresql://build:build@localhost:5432/build?schema=public"
RUN npx prisma generate
RUN groupadd --system --gid 1001 app \
&& useradd --system --uid 1001 --gid app --home-dir /app app
USER app
# Default-Kommando; im Compose (migrate-Service) überschrieben/erweitert.
CMD ["npx", "prisma", "migrate", "deploy"]
# --- Runner-Stage: schlanker Produktions-Container (nur Next-Standalone) ---
FROM node:22.14.0-slim AS runner
WORKDIR /app
ENV NODE_ENV=production
# Next.js standalone server.js bindet sonst an den von Docker gesetzten HOSTNAME
# (= Container-ID) statt an alle Interfaces -> Healthcheck (127.0.0.1) und der
# Coolify-/Traefik-Proxy erreichen den Container nicht (404). 0.0.0.0 behebt beides.
ENV HOSTNAME=0.0.0.0
ENV PORT=3000
# Non-root-Nutzer (Debian: groupadd/useradd statt Alpine adduser -S).
RUN groupadd --system --gid 1001 app \
&& useradd --system --uid 1001 --gid app --home-dir /app app
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
# i18n-Kataloge (messages/<locale>/<namespace>.json) liegen über outputFileTracingIncludes
# bereits im standalone-Output; der explizite COPY hält sie auch bei Tracing-Änderungen vor.
COPY --from=builder /app/messages ./messages
# Backup-/Restore-/DSGVO-Topologie (src/server/backup/topology.ts) liest zur Laufzeit
# prisma/schema.prisma: Prisma 7 entfernt relationFromFields aus dem Laufzeit-DMMF,
# die FK-Relationen kommen daher aus der Schema-Datei. Der Inline-Export (Direkt-
# Download /api/platform/backup/download) läuft in der APP → ohne diese Datei ENOENT.
COPY prisma/schema.prisma ./prisma/schema.prisma
RUN chmod a+rX ./prisma/schema.prisma
USER app
EXPOSE 3000
CMD ["node", "server.js"]
# --- Garage-Stage: Objektspeicher-Image mit EINGEBACKENER Config ---
# Warum nicht das Upstream-Image + Bind-Mount der deploy/garage.toml? Coolify behandelt
# relative Bind-Mount-Quellen als Storage und legt sie als VERZEICHNIS an, wenn die Datei
# dort (noch) nicht existiert. Garage bekommt dann /etc/garage.toml als Ordner und bricht
# mit „IO error: Is a directory (os error 21)" ab. Das Backen der Config ins Image umgeht
# jeden Host-Mount. Secrets bleiben draußen (rpc_secret/admin_token kommen zur Laufzeit aus
# GARAGE_RPC_SECRET/GARAGE_ADMIN_TOKEN); nur die secret-freie Basiskonfig wird kopiert.
FROM dxflrs/garage:v1.2.0 AS garage
COPY deploy/garage.toml /etc/garage.toml
# --- Worker-Stage (Vorschlag Lane L5 Berichte): Craftvia-Job-Worker inkl. Chromium für PDF ---
# ARCHITEKTUR §1: HTML → PDF läuft über playwright-core + Chromium NUR im Worker, nie im App-Container.
# Debian-Chromium aus dem Paketspiegel statt Playwright-Download (reproduzierbar, Updates über das Base-Image);
# render.ts nutzt PDF_CHROMIUM_PATH. fonts-dejavu/-liberation als Fallback, Inter wird eingebettet (src/app/fonts).
# tsx + src/messages/prisma werden wie in der migrate-Stage zur Laufzeit gebraucht (Worker läuft über tsx).
FROM node:22.14.0-slim AS worker
WORKDIR /app
ENV NODE_ENV=production
ENV PDF_CHROMIUM_PATH=/usr/bin/chromium
RUN apt-get update && apt-get install -y --no-install-recommends openssl ca-certificates chromium fonts-dejavu-core fonts-liberation \
&& rm -rf /var/lib/apt/lists/*
COPY --from=deps /app/node_modules ./node_modules
COPY package.json package-lock.json prisma.config.ts tsconfig.json ./
COPY prisma ./prisma
COPY scripts ./scripts
COPY src ./src
COPY messages ./messages
ENV DATABASE_URL="postgresql://build:build@localhost:5432/build?schema=public"
RUN npx prisma generate
RUN groupadd --system --gid 1001 app \
&& useradd --system --uid 1001 --gid app --home-dir /app app
USER app
CMD ["npx", "tsx", "scripts/craftvia-worker.ts"]