# Image für den Dienst `web`. Standalone-Build, siehe web/next.config.ts. # # Die Migration läuft im Startkommando des Containers, nicht hier und nicht in # einem Init-Container: Dockge zeigt beendete Container nicht dauerhaft an. # Begründung in docs/11-betrieb.md. # # Dafür liegt eine eigenständige Prisma-CLI unter /app/migrator im Image. Sie # beim Start aus dem Netz zu ziehen wäre der falsche Weg: der Container startet # dann nur, wenn die Registry gerade erreichbar ist. ARG NODE_VERSION=22-alpine ARG PNPM_VERSION=9.15.4 ARG PRISMA_VERSION=7.10.0 # --- Abhängigkeiten ----------------------------------------------------------- FROM node:${NODE_VERSION} AS deps ARG PNPM_VERSION RUN npm install -g pnpm@${PNPM_VERSION} WORKDIR /repo COPY pnpm-workspace.yaml package.json pnpm-lock.yaml .npmrc ./ COPY packages/db/package.json packages/db/ COPY web/package.json web/ COPY worker/package.json worker/ COPY pdf/package.json pdf/ RUN pnpm install --frozen-lockfile --filter @afis/web... --filter @afis/db... # --- Bauen -------------------------------------------------------------------- FROM node:${NODE_VERSION} AS build ARG PRISMA_VERSION ARG PNPM_VERSION RUN npm install -g pnpm@${PNPM_VERSION} WORKDIR /repo COPY --from=deps /repo/node_modules ./node_modules COPY --from=deps /repo/packages/db/node_modules ./packages/db/node_modules COPY --from=deps /repo/web/node_modules ./web/node_modules COPY . . # Prisma-Client erzeugen und @afis/db nach dist übersetzen. RUN pnpm --filter @afis/db build ENV NEXT_TELEMETRY_DISABLED=1 RUN pnpm --filter @afis/web build # Eigenständige Migrations-CLI mit heruntergeladener Schema-Engine. # Dazu der Seed: er legt den Grundzustand an - Freigabestufen, Status-Etiketten, # Fallarten, Nummernkreise. Ohne ihn startet die Anwendung zwar, aber es laesst # sich keine Akte anlegen, weil es keine Vorlage gibt. Er besteht ausschliesslich # aus upserts und darf deshalb bei jedem Start mitlaufen. RUN mkdir -p /migrator && cd /migrator && \ npm install --no-package-lock --no-audit --no-fund \ "prisma@${PRISMA_VERSION}" \ "@prisma/client@${PRISMA_VERSION}" \ "@prisma/adapter-pg@${PRISMA_VERSION}" \ pg tsx && \ cp -r /repo/packages/db/prisma /migrator/prisma && \ cp -r /repo/packages/db/src /migrator/src && \ cp /repo/packages/db/prisma.config.ts /migrator/prisma.config.ts # --- Laufzeit ----------------------------------------------------------------- FROM node:${NODE_VERSION} AS runtime WORKDIR /app ENV NODE_ENV=production \ NEXT_TELEMETRY_DISABLED=1 \ PORT=3000 \ HOSTNAME=0.0.0.0 # qpdf säubert hochgeladene PDF-Dateien, siehe docs/07-dateien-und-sicherheit.md. # Fehlt es, bleiben PDF-Uploads in der Quarantäne statt ungeprüft durchzugehen. RUN apk add --no-cache wget openssl qpdf && \ addgroup -g 1001 -S afis && \ adduser -u 1001 -S afis -G afis # Standalone-Ausgabe: server.js plus die tatsächlich benutzten node_modules. COPY --from=build --chown=afis:afis /repo/web/.next/standalone/ ./ COPY --from=build --chown=afis:afis /repo/web/.next/static ./web/.next/static COPY --from=build --chown=afis:afis /repo/web/public ./web/public # Migrations-CLI samt Schema, Migrationsordner und Engine. COPY --from=build --chown=afis:afis /migrator ./migrator # Die Dateiablage **muss im Image liegen**, sonst kann niemand hochladen. # # `files` ist ein benanntes Volume und wird auf /data/files gelegt. Existiert # dieser Pfad im Image nicht, legt Docker ihn beim Einhaengen selbst an - als # root mit 755. Der Dienst laeuft aber als afis (1001) und bekommt beim ersten # Schreibversuch EACCES: jeder Upload scheiterte mit einer nichtssagenden # Meldung, vom Lichtbild bis zum Anhang. # # Liegt das Verzeichnis dagegen im Image und ist leer, uebernimmt Docker es # samt Eigentuemer in ein leeres Volume. Nachgemessen: ein bereits befuelltes # Volume behaelt seine alten Rechte, ein leeres nicht. # # quarantine/ und store/ gleich mit anlegen - dieselben zwei Verzeichnisse, die # lib/files/paths.ts erwartet. RUN mkdir -p /data/files/quarantine /data/files/store && \ chown -R afis:afis /data USER afis EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \ CMD wget -qO- http://localhost:3000/api/health || exit 1 # Das Startkommando setzt der Stack (Migration + Server), siehe compose.yaml. CMD ["node", "web/server.js"]