Infra: Worker/Garage/Test-Runner

- docker-compose.yml: explizite Build-Targets (runner/migrate) — Ursache für
  "npx not found" bei garage-provision war das Default-Target (letzte Stage =
  Garage-Image ohne Node); worker startet npm run worker:mail; Defaults craftvia
- Coolify-Compose: incident-inbound-worker und sync-policy-templates entfernt,
  Rollen-/Bucket-/Image-Namen auf craftvia
- Dockerfile: seed/ und docs/wizard-uebergabe entfernt, messages/ im Runner
- npm: Name craftvia, Scripts test (scripts/run-tests.ts) und gate, ISMS-Pakete
  entfernt (handlebars, marked, sanitize-html, @xyflow/react, @dagrejs/dagre,
  html-to-image, imapflow, mailparser, exceljs, @dnd-kit/core)
- .env-Beispiele, launch.json (craftvia-dev), CI-Kommentare umbenannt

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 11:44:58 +02:00
co-authored by Claude Opus 5
parent 6f3f5a5947
commit ba7d6ac3ed
45 changed files with 167 additions and 7932 deletions
+10 -51
View File
@@ -1,7 +1,7 @@
# Coolify-Deployment (PREBUILT-Variante) — zieht fertige Images aus der Registry
# statt auf dem Host zu bauen (Plan B: langsamer/timeoutender Host-Build umgehen).
# Images vorher bauen+pushen: scripts/build-and-push-images.sh
# Registry/Tag via Coolify-Env: REGISTRY (Default git.certvia.de/msolarczek), IMAGE_TAG (Default main).
# Registry/Tag via Coolify-Env: REGISTRY (Default registry.example.com/craftvia), IMAGE_TAG (Default main).
# Abgeleitet von docker-compose.coolify.yml.
# Unterschiede zum lokalen Dev-Compose:
# - keine host "ports": Coolify-Proxy routet die Domain intern auf app:3000
@@ -24,8 +24,8 @@ services:
# Optionaler Demo-Seed nur, wenn RUN_DEMO_SEED=true (Testserver). Idempotent (upserts).
# In Produktion die Variable NICHT setzen; stattdessen BOOTSTRAP_ADMIN.
migrate:
image: ${REGISTRY:-git.certvia.de/msolarczek}/certvia-migrate:${IMAGE_TAG:-main}
command: sh -c "npx prisma migrate deploy && echo '>> Rollen-Rechte-Sync läuft…' && npx tsx scripts/sync-role-permissions.ts && echo '>> Vorlagen-Sync läuft…' && npx tsx scripts/sync-policy-templates.ts && if [ \"$$RUN_DEMO_SEED\" = \"true\" ]; then echo '>> Demo-Seed läuft…'; npx tsx prisma/seed.ts; fi && if [ \"$$BOOTSTRAP_ADMIN\" = \"true\" ]; then echo '>> Bootstrap-Admin läuft…'; npx tsx scripts/bootstrap-admin.ts; fi"
image: ${REGISTRY:-registry.example.com/craftvia}/craftvia-migrate:${IMAGE_TAG:-main}
command: sh -c "npx prisma migrate deploy && echo '>> Rollen-Rechte-Sync läuft…' && npx tsx scripts/sync-role-permissions.ts && if [ \"$$RUN_DEMO_SEED\" = \"true\" ]; then echo '>> Demo-Seed läuft…'; npx tsx prisma/seed.ts; fi && if [ \"$$BOOTSTRAP_ADMIN\" = \"true\" ]; then echo '>> Bootstrap-Admin läuft…'; npx tsx scripts/bootstrap-admin.ts; fi"
environment:
DATABASE_URL: ${DATABASE_URL}
# Härtung §1: Passwort-Pepper (Argon2 `secret`). Seed/Bootstrap hashen Passwörter
@@ -59,12 +59,12 @@ services:
restart: "no"
app:
image: ${REGISTRY:-git.certvia.de/msolarczek}/certvia-app:${IMAGE_TAG:-main}
image: ${REGISTRY:-registry.example.com/craftvia}/craftvia-app:${IMAGE_TAG:-main}
environment:
DATABASE_URL: ${DATABASE_URL}
# F-04: Row Level Security scharf. Der migrate-Job oben läuft bewusst
# weiter mit der Owner-DATABASE_URL (BYPASSRLS). Nur der App-Prozess
# verbindet als eingeschränkte Rolle isms_app (NOBYPASSRLS) über
# verbindet als eingeschränkte Rolle craftvia_app (NOBYPASSRLS) über
# RLS_DATABASE_URL und setzt app.tenant_id pro Transaktion.
RLS_ENFORCED: ${RLS_ENFORCED:-false}
RLS_DATABASE_URL: ${RLS_DATABASE_URL}
@@ -148,7 +148,7 @@ services:
# Owner-DATABASE_URL (kein RLS): der Worker arbeitet mandantenübergreifend.
# Im default-Netz (Egress), damit der externe SMTP-Server erreichbar ist.
worker:
image: ${REGISTRY:-git.certvia.de/msolarczek}/certvia-migrate:${IMAGE_TAG:-main}
image: ${REGISTRY:-registry.example.com/craftvia}/craftvia-migrate:${IMAGE_TAG:-main}
command: ["npx", "tsx", "scripts/mail-worker.ts"]
environment:
DATABASE_URL: ${DATABASE_URL}
@@ -192,7 +192,7 @@ services:
# Braucht das S3/MinIO-Backup-Bucket (S3_*) und den Backup-Schlüssel (BACKUP_ENC_KEY,
# Fallback AUTH_SECRET). concurrency ist im Worker seriell (destruktiv).
backup-worker:
image: ${REGISTRY:-git.certvia.de/msolarczek}/certvia-migrate:${IMAGE_TAG:-main}
image: ${REGISTRY:-registry.example.com/craftvia}/craftvia-migrate:${IMAGE_TAG:-main}
command: ["npx", "tsx", "scripts/backup-worker.ts"]
environment:
DATABASE_URL: ${DATABASE_URL}
@@ -238,47 +238,6 @@ services:
condition: service_completed_successfully
restart: unless-stopped
# IM-D — Inbound-Mail-Worker (E-Mail-to-Ticket). Holt Mails vom Catch-all-Postfach
# (vorfall-<token>@in.certvia.de) per IMAP ab und legt daraus Vorfälle an bzw. reiht
# unklare Mails in die Betreiber-Review. Kein Redis nötig (IMAP-Poller, keine Queue).
# Ohne INCIDENT_IMAP_* beendet sich der Prozess sauber („nicht konfiguriert").
incident-inbound-worker:
image: ${REGISTRY:-git.certvia.de/msolarczek}/certvia-migrate:${IMAGE_TAG:-main}
command: ["npx", "tsx", "scripts/incident-inbound-worker.ts"]
environment:
DATABASE_URL: ${DATABASE_URL}
AUTH_SECRET: ${AUTH_SECRET}
# Pepper wird von der Fail-Secure-Startprüfung erwartet (assertSecureEnv).
PASSWORD_PEPPER: ${PASSWORD_PEPPER}
# IMAP-Zugang des Catch-all-Postfachs. Fehlt es, beendet sich der Worker sauber.
INCIDENT_IMAP_HOST: ${INCIDENT_IMAP_HOST:-}
INCIDENT_IMAP_PORT: ${INCIDENT_IMAP_PORT:-993}
INCIDENT_IMAP_USER: ${INCIDENT_IMAP_USER:-}
INCIDENT_IMAP_PASSWORD: ${INCIDENT_IMAP_PASSWORD:-}
INCIDENT_IMAP_TLS: ${INCIDENT_IMAP_TLS:-true}
INCIDENT_IMAP_MAILBOX: ${INCIDENT_IMAP_MAILBOX:-INBOX}
INCIDENT_IMAP_POLL_MS: ${INCIDENT_IMAP_POLL_MS:-60000}
# Muss zur Catch-all-Subdomain passen (Ableitung der Intake-Adressen).
INCIDENT_INTAKE_DOMAIN: ${INCIDENT_INTAKE_DOMAIN:-in.certvia.de}
AUTH_URL: ${AUTH_URL}
networks:
- backend
- default
security_opt:
- "no-new-privileges:true"
cap_drop:
- ALL
deploy:
resources:
limits:
cpus: "0.5"
memory: 512M
depends_on:
postgres:
condition: service_healthy
migrate:
condition: service_completed_successfully
restart: unless-stopped
postgres:
image: pgvector/pgvector:0.8.0-pg16
@@ -346,7 +305,7 @@ services:
# Image mit eingebackener deploy/garage.toml (Dockerfile-Stage „garage"). KEIN
# Bind-Mount der Config: Coolify legt relative Bind-Quellen sonst als Verzeichnis an
# → Garage bekäme /etc/garage.toml als Ordner („IO error: Is a directory").
image: ${REGISTRY:-git.certvia.de/msolarczek}/certvia-garage:${IMAGE_TAG:-main}
image: ${REGISTRY:-registry.example.com/craftvia}/craftvia-garage:${IMAGE_TAG:-main}
# Secrets stehen NICHT in der eingebackenen deploy/garage.toml — der Daemon liest
# rpc_secret/admin_token aus der Env (in Coolify literal setzen, nicht via ${…}).
environment:
@@ -383,7 +342,7 @@ services:
# Läuft bei JEDEM Deploy; "already exists" = Erfolg. Ohne GARAGE_ADMIN_TOKEN No-op.
# Nutzt das schlanke "migrate"-Image (Node/tsx) — kein Garage-Binary nötig.
garage-provision:
image: ${REGISTRY:-git.certvia.de/msolarczek}/certvia-migrate:${IMAGE_TAG:-main}
image: ${REGISTRY:-registry.example.com/craftvia}/craftvia-migrate:${IMAGE_TAG:-main}
command: ["npx", "tsx", "scripts/garage-provision.ts"]
environment:
GARAGE_ADMIN_URL: http://garage:3903
@@ -391,7 +350,7 @@ services:
# Der zu importierende Garage-Key = App-Key (App-Env und Garage synchron).
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
S3_SECRET_KEY: ${S3_SECRET_KEY}
S3_BUCKET: ${S3_BUCKET:-isms-documents}
S3_BUCKET: ${S3_BUCKET:-craftvia-documents}
# Optionaler separater Backup-Bucket (nur falls Backup-Store auf S3 statt lokal).
BACKUP_S3_BUCKET: ${BACKUP_S3_BUCKET:-}
# Single-Node-Layout (nominale Kapazität/Zone; für Prod ggf. anheben).