L10b Betrieb & Aufräumen: Deploy – craftvia-worker, CI-Testjob, DEPLOY.md, Certvia-Doku archiviert
- docker-compose.coolify(.prebuilt).yml: Service craftvia-worker (Target worker, Chromium, shm_size 1gb, gleiche Härtung), Craftvia-Variablen für app und worker. - Dockerfile: worker-Stage mit HOME=/home/app (Chromium-Profil für non-root); lokaler docker build der Targets runner und worker erfolgreich, PDF-Erzeugung im Image geprüft. - .env.example/.env.prod.example/.env.coolify.example: alle Craftvia-Variablen inkl. RLS, KI-Provider, PDF_CHROMIUM_PATH, OFFLINE_MAX_DAYS, API_RATE_LIMIT_*, AI_GENERATION_RETENTION_DAYS, AI_MONTHLY_TOKEN_LIMIT. - CI (.github, .gitea): Job gate mit Postgres (pgvector) und Redis als Service: migrate deploy, seed, Passwort für craftvia_app, tsc, lint, build, npm run test. - docs/craftvia/DEPLOY.md (aus den Certvia-Deploy-Docs abgeleitet): Architektur, Domains, Secrets, Worker, Migrationen, RLS-Aktivierung, Backup/Restore, KI, Rate Limits, Aufbewahrung, Smoke, Update/Rollback. build-and-push-images.sh baut craftvia-worker. - Certvia-/ISMS-Dokumente aus docs/ nach docs/_certvia-archiv/ (mit README); Verweise in README.md und Skript-Kommentaren angepasst. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+95
-38
@@ -1,6 +1,7 @@
|
||||
# Referenz für die PRODUKTIV-Env-Variablen (Contabo-VPS + Coolify).
|
||||
# ECHTE Secrets NUR in Coolify eintragen – diese Datei enthält nur Platzhalter.
|
||||
# Unterschiede zum Testserver: HTTPS-AUTH_URL, KEIN Demo-Seed, stattdessen Bootstrap-Admin.
|
||||
# Referenz für die PRODUKTIV-Env-Variablen (Coolify, docker-compose.coolify[.prebuilt].yml).
|
||||
# ECHTE Secrets NUR in Coolify eintragen – diese Datei enthält nur Platzhalter.
|
||||
# Unterschiede zum Testserver: HTTPS-AUTH_URL, KEIN Demo-Seed, stattdessen Bootstrap-Admin,
|
||||
# RLS scharf. Betriebsdoku: docs/craftvia/DEPLOY.md
|
||||
|
||||
# --- Datenbank (Service "postgres") ---
|
||||
POSTGRES_USER=craftvia
|
||||
@@ -9,68 +10,124 @@ POSTGRES_DB=craftvia
|
||||
DATABASE_URL=postgresql://craftvia:CHANGE_ME_starkes_db_passwort@postgres:5432/craftvia?schema=public
|
||||
|
||||
# --- Row Level Security scharfschalten (F-04) ---
|
||||
# RLS_ENFORCED=true – die App verbindet sich als eingeschränkte Rolle craftvia_app
|
||||
# (NOBYPASSRLS) und setzt app.tenant_id pro Transaktion; FORCE ROW LEVEL SECURITY
|
||||
# macht die Policies dann scharf. Ist der Kontext nicht gesetzt, sieht craftvia_app
|
||||
# NULL Zeilen – daher NUR mit korrekt gesetztem RLS_DATABASE_URL einschalten.
|
||||
# RLS_ENFORCED=true – app und craftvia-worker verbinden sich als eingeschränkte Rolle
|
||||
# craftvia_app (NOBYPASSRLS) und setzen app.tenant_id pro Transaktion; FORCE ROW LEVEL
|
||||
# SECURITY macht die Policies dann scharf. Ist der Kontext nicht gesetzt, sieht craftvia_app
|
||||
# NULL Zeilen – daher NUR mit korrekt gesetztem RLS_DATABASE_URL einschalten (sonst
|
||||
# bricht die App beim Start bewusst ab).
|
||||
# WICHTIG: Die Owner-/Migrate-Rolle in DATABASE_URL MUSS BYPASSRLS/Superuser sein
|
||||
# (Migrationen, Seed und der mandantenübergreifende Login-Lookup laufen darüber),
|
||||
# sonst sähe der Login keine Nutzer. craftvia_app in Prod EINMALIG mit LOGIN + starkem
|
||||
# Passwort versehen: ALTER ROLE craftvia_app WITH LOGIN PASSWORD '<stark>';
|
||||
# (Migrationen, Seed, Mail-/Backup-Worker und der mandantenübergreifende Login-Lookup
|
||||
# laufen darüber). craftvia_app wird von der Baseline-Migration NOLOGIN angelegt und in
|
||||
# Prod EINMALIG mit LOGIN + starkem Passwort versehen:
|
||||
# ALTER ROLE craftvia_app WITH LOGIN PASSWORD '<stark>';
|
||||
RLS_ENFORCED=true
|
||||
RLS_DATABASE_URL=postgresql://craftvia_app:CHANGE_ME_starkes_craftvia_app_passwort@postgres:5432/craftvia?schema=public
|
||||
|
||||
# --- Redis ---
|
||||
# F-18: Redis läuft mit requirepass. REDIS_PASSWORD setzen (stark!) und identisch
|
||||
# in die REDIS_URL einsetzen (redis://:<pw>@redis:6379).
|
||||
# --- Redis (Service "redis") ---
|
||||
# F-18: Redis läuft mit requirepass. NUR REDIS_PASSWORD setzen – REDIS_URL wird in den
|
||||
# Coolify-Compose-Dateien daraus abgeleitet (redis://:${REDIS_PASSWORD}@redis:6379).
|
||||
REDIS_PASSWORD=CHANGE_ME_starkes_redis_passwort
|
||||
REDIS_URL=redis://:CHANGE_ME_starkes_redis_passwort@redis:6379
|
||||
|
||||
# --- Objektspeicher (MinIO) – S3_* muss zu MINIO_ROOT_* passen ---
|
||||
S3_ENDPOINT=http://minio:9000
|
||||
S3_ACCESS_KEY=CHANGE_ME_GK_plus_24_hex
|
||||
S3_SECRET_KEY=CHANGE_ME_starkes_minio_passwort
|
||||
# --- Objektspeicher (Service "garage", S3-kompatibel) ---
|
||||
# Bucket/Key legt der Init-Job "garage-provision" an (Admin-API). Format erzwungen:
|
||||
# S3_ACCESS_KEY = "GK" + 24 Hex -> echo "GK$(openssl rand -hex 12)"
|
||||
# S3_SECRET_KEY = 64 Hex -> openssl rand -hex 32
|
||||
S3_ENDPOINT=http://garage:3900
|
||||
S3_ACCESS_KEY=GK000000000000000000000000
|
||||
S3_SECRET_KEY=CHANGE_ME_openssl_rand_hex_32
|
||||
S3_BUCKET=craftvia-documents
|
||||
MINIO_ROOT_USER=craftvia
|
||||
MINIO_ROOT_PASSWORD=CHANGE_ME_starkes_minio_passwort
|
||||
S3_REGION=us-east-1
|
||||
# Garage-Daemon-Secrets (LITERAL in Coolify setzen, nicht via ${...}).
|
||||
GARAGE_RPC_SECRET=CHANGE_ME_openssl_rand_hex_32
|
||||
GARAGE_ADMIN_TOKEN=CHANGE_ME_openssl_rand_hex_32
|
||||
# GARAGE_ZONE=dc1
|
||||
# GARAGE_CAPACITY_BYTES=100000000000
|
||||
|
||||
# --- Backup-Zielspeicher (optional) ---
|
||||
# Ziel der Backup-/DSGVO-Artefakte ist im Betreiber-Portal (/admin/backup) waehlbar
|
||||
# (Lokal/S3) und wird verschluesselt in der DB gehalten. Praezedenz: DB-Config →
|
||||
# Env (S3_*/BACKUP_LOCAL_DIR) → lokaler Default. Sobald im Portal gespeichert, hat
|
||||
# die DB-Config Vorrang. Fuer „Lokal" auf ein gemountetes, persistentes Volume zeigen.
|
||||
# Env (S3_*/BACKUP_LOCAL_DIR) → lokaler Default. Fuer „Lokal" mountet die Compose-Datei
|
||||
# das persistente Volume `backups` auf /app/.backups (app + backup-worker).
|
||||
BACKUP_LOCAL_DIR=/app/.backups
|
||||
# Optionaler eigener Backup-Bucket (nur wenn der Backup-Store auf S3 laeuft).
|
||||
# BACKUP_S3_BUCKET=
|
||||
# Verschluesselung der Backup-Artefakte (AES-256-GCM); leer = AUTH_SECRET. Je Umgebung
|
||||
# eigener Wert, alte Keys bis Retention-Ende aufbewahren.
|
||||
BACKUP_ENC_KEY=CHANGE_ME_openssl_rand_hex_32
|
||||
|
||||
# --- Auth (NextAuth) – Produktiv über HTTPS ---
|
||||
# --- Auth (Auth.js v5) – Produktiv über HTTPS ---
|
||||
# AUTH_SECRET: openssl rand -base64 32 (frisch, NICHT der Testwert)
|
||||
AUTH_SECRET=CHANGE_ME_openssl_rand_base64_32
|
||||
# PASSWORD_PEPPER (Härtung §1): openssl rand -hex 32 — frisch je Umgebung, NICHT rotierbar, nie ins Artefakt.
|
||||
PASSWORD_PEPPER=CHANGE_ME_openssl_rand_hex_32
|
||||
AUTH_URL=https://app.craftvia.de
|
||||
# AUTH_TRUST_HOST ist im Compose fest auf true (hinter dem Coolify-Proxy) – nicht nötig.
|
||||
# MFA_ENC_KEY: TOTP-Secrets at-rest (leer = aus AUTH_SECRET). ⚠ Nach dem Setzen nicht mehr ändern.
|
||||
MFA_ENC_KEY=CHANGE_ME_openssl_rand_hex_32
|
||||
AUTH_URL=https://app.craftvia.example
|
||||
# AUTH_TRUST_HOST ist im Compose fest auf true (hinter dem Coolify-Proxy) – nicht nötig.
|
||||
# Passkeys/WebAuthn: leer = aus AUTH_URL abgeleitet.
|
||||
# WEBAUTHN_ORIGIN=https://app.craftvia.example
|
||||
# WEBAUTHN_RP_ID=app.craftvia.example
|
||||
|
||||
# --- KI-Provider (optional) ---
|
||||
AI_PROVIDER=anthropic
|
||||
AI_API_KEY=
|
||||
|
||||
# --- E-Mail (produktives SMTP-Relay, sobald Einladungs-/Mailflow aktiv) ---
|
||||
# --- E-Mail (produktives SMTP-Relay; SPF/DKIM/DMARC der Absenderdomain vorher einrichten) ---
|
||||
SMTP_HOST=
|
||||
SMTP_PORT=587
|
||||
# true = implizites TLS (465), false = STARTTLS (587); leer = aus Port abgeleitet.
|
||||
SMTP_SECURE=
|
||||
SMTP_USER=
|
||||
SMTP_PASSWORD=
|
||||
SMTP_FROM=noreply@craftvia.de
|
||||
SMTP_FROM=no-reply@craftvia.example
|
||||
MAIL_FROM_NAME=Craftvia
|
||||
MAIL_REPLY_TO=
|
||||
# Basis für absolute Links in Mails/PDFs; leer = AUTH_URL.
|
||||
APP_BASE_URL=https://app.craftvia.example
|
||||
|
||||
# --- KI: Auftragsimport-Extraktion & Lotse (Anthropic) ---
|
||||
# Ohne ANTHROPIC_API_KEY: graceful degradation (manuelle Erfassung, kein Lotse-Entwurf).
|
||||
AI_EXTRACTION_PROVIDER=anthropic
|
||||
ANTHROPIC_API_KEY=
|
||||
# Leer = Default-Modell aus src/server/ai/client.ts
|
||||
ANTHROPIC_MODEL=
|
||||
|
||||
# --- KI: Transkription von Sprachnotizen (Whisper-kompatible API) ---
|
||||
# Ohne TRANSCRIPTION_API_KEY bleibt die Transkription deaktiviert (Status "disabled").
|
||||
TRANSCRIPTION_PROVIDER=openai-compatible
|
||||
TRANSCRIPTION_API_URL=https://api.openai.com/v1/audio/transcriptions
|
||||
TRANSCRIPTION_API_KEY=
|
||||
TRANSCRIPTION_MODEL=whisper-1
|
||||
|
||||
# --- KI: Kostenbremse & Aufbewahrung ---
|
||||
# Tokens (ein + aus) je Mandant je Kalendermonat; darüber lehnen Lotse und
|
||||
# Import-Extraktion ab. 0 = unbegrenzt.
|
||||
AI_MONTHLY_TOKEN_LIMIT=0
|
||||
# Nach N Tagen leert/pseudonymisiert ein Worker-Job Ein-/Ausgaben im KI-Protokoll
|
||||
# (AiGeneration). Frist mit dem DSB abstimmen.
|
||||
AI_GENERATION_RETENTION_DAYS=180
|
||||
|
||||
# --- Craftvia: API-Rate-Limits (je Nutzer, Anfragen pro Minute) ---
|
||||
API_RATE_LIMIT_PER_MINUTE=300
|
||||
# /api/v1/sync, /api/v1/uploads, /api/v1/field/**
|
||||
API_FIELD_RATE_LIMIT_PER_MINUTE=1200
|
||||
|
||||
# --- Craftvia: Offline/PWA ---
|
||||
OFFLINE_MAX_DAYS=7
|
||||
|
||||
# --- Craftvia: optionaler Malware-Scan (ClamAV clamd) ---
|
||||
CLAMAV_HOST=
|
||||
CLAMAV_PORT=3310
|
||||
|
||||
# PDF_CHROMIUM_PATH ist im Worker-Image/Compose fest /usr/bin/chromium – nicht setzen.
|
||||
|
||||
# --- Demo-Seed: in PROD AUS lassen! ---
|
||||
RUN_DEMO_SEED=false
|
||||
|
||||
# --- Erst-Superadmin-Bootstrap (statt Demo-Seed) ---
|
||||
# Beim ersten Deploy true setzen -> migrate-Job legt Admin + Mandant an (idempotent).
|
||||
# Danach kann true bleiben (tut nichts, wenn der Admin existiert) oder auf false.
|
||||
# --- Erst-Admin-Bootstrap (statt Demo-Seed) ---
|
||||
# Beim ersten Deploy true setzen -> migrate-Job legt Plattform-Admin + ersten Mandanten an
|
||||
# (idempotent). Danach auf false setzen oder stehen lassen (No-op, wenn vorhanden).
|
||||
BOOTSTRAP_ADMIN=true
|
||||
BOOTSTRAP_ADMIN_EMAIL=admin@craftvia.de
|
||||
BOOTSTRAP_ADMIN_EMAIL=admin@craftvia.example
|
||||
BOOTSTRAP_ADMIN_PASSWORD=CHANGE_ME_initiales_admin_passwort
|
||||
BOOTSTRAP_ADMIN_NAME=Craftvia Admin
|
||||
BOOTSTRAP_TENANT_NAME=Craftvia
|
||||
BOOTSTRAP_TENANT_SLUG=craftvia
|
||||
BOOTSTRAP_TENANT_SHORT=Craftvia
|
||||
BOOTSTRAP_TENANT_NAME=Musterbetrieb GmbH
|
||||
BOOTSTRAP_TENANT_SLUG=musterbetrieb
|
||||
BOOTSTRAP_TENANT_SHORT=Musterbetrieb
|
||||
BOOTSTRAP_TENANT_SECTOR=
|
||||
|
||||
Reference in New Issue
Block a user