Files
msolarczekandClaude Opus 5 c8e6f30a27
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s
Basis: Certvia dev@a48c5fb als Fundament für Craftvia
Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation
und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 11:05:39 +02:00

122 lines
7.0 KiB
Docker
Raw Permalink 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.
# ISMS-Tool — 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
# seed/ trägt das Richtlinien-Vorlagenpaket (mapping.json + Quelldateien), das der
# Demo-Seed (prisma/seed.ts) bzw. Bootstrap über importPolicies liest. Ohne diesen COPY
# bricht der migrate-Job mit ENOENT auf seed/isms-vorlagenpaket-v2/mapping.json ab.
# Einige Quelldateien liegen als 0600 vor → a+rX, damit der non-root app-User sie lesen kann.
COPY seed ./seed
RUN chmod -R a+rX ./seed
# docs/wizard-uebergabe/: Fachcontent-Quelldateien, aus denen der Demo-Seed den
# Risikokatalog (C4, import-risks.ts) und die Umsetzungshinweise (C6, import-hints.ts)
# liest. Nur im migrate-Image nötig — die Laufzeit/runner ruft diese Importer nicht auf
# (importManaged in provision.ts liest keine Dateien). Ohne diesen COPY bricht der
# Demo-Seed mit ENOENT auf docs/wizard-uebergabe/.../C6_Umsetzungshinweise.md ab.
COPY docs/wizard-uebergabe ./docs/wizard-uebergabe
RUN chmod -R a+rX ./docs
# 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
# Richtlinien-Vorlagenpaket für den Laufzeit-Import: Modul „policies" aktivieren
# (toggleTenantModule), Mandant mit Seed anlegen (createTenant), manueller Paket-Import
# (importPolicyPackageForTenant) lesen alle aus seed/isms-vorlagenpaket-v2/. Fehlt es,
# wirft die App zur Laufzeit ENOENT. a+rX wegen der 0600-Quelldateien (non-root app-User).
COPY seed ./seed
RUN chmod -R a+rX ./seed
# 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