cms.c2sgmbh/CLAUDE.md
Martin Porwoll 324bd1eb5f docs: add ModSecurity WAF documentation to CLAUDE.md
Document the OWASP CRS 3.3.7 WAF on production nginx, including
exclusion rule IDs and the diagnostic curl method to distinguish
ModSecurity blocks from Payload 403 responses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:02:12 +00:00

19 KiB

Payload CMS Multi-Tenant Project

Projektübersicht

Multi-Tenant CMS für 3 aktive Websites unter einer Payload CMS 3.x Instanz:

  • porwoll.de
  • complexcaresolutions.de (c2s)
  • gunshin.de

Tech Stack

  • CMS: Payload CMS 3.77.0
  • Framework: Next.js 16.2.0-canary.58
  • React: 19.2.4
  • Sprache: TypeScript
  • Runtime: Node.js 22.x
  • Datenbank: PostgreSQL 17.6 (separater Server)
  • Connection Pool: PgBouncer 1.24.1 (Transaction-Mode)
  • Reverse Proxy: Caddy 2.9.x (Dev) / Nginx (Prod)
  • Process Manager: PM2
  • Package Manager: pnpm
  • Cache: Redis 7.x (optional, In-Memory-Fallback)
  • Job Queue: BullMQ (Redis-basiert)
  • Analytics: Umami 3.x

Architektur

Development: Internet → Cloudflare → Caddy (sv-caddy) → sv-payload:3000 → PgBouncer:6432 → PostgreSQL (sv-postgres:5432)

Hostname IP Service
sv-caddy 10.10.181.99 Caddy Reverse Proxy
sv-payload 10.10.181.100 Payload CMS + Redis
sv-postgres 10.10.181.101 PostgreSQL 17
sv-frontend 10.10.181.104 Multi-Frontend Dev (9 Projekte)

Production (Hetzner 3): Internet → Nginx (ModSecurity WAF) → localhost:3001 (Payload), analytics.c2sgmbh.de:3000 (Umami)

→ Details: docs/INFRASTRUCTURE.md

Multi-Tenant

Verwendet @payloadcms/plugin-multi-tenant.

ID Name Slug
1 porwoll.de porwoll
4 Complex Care Solutions GmbH c2s
5 Gunshin gunshin

User-Tenant-Zuweisung: Tabelle users_tenants

Wichtige Pfade

/home/payload/payload-cms/
├── src/
│   ├── payload.config.ts          # Haupt-Konfiguration
│   ├── collections/               # Alle Collections (55+)
│   ├── app/(payload)/api/         # Custom API Routes
│   ├── lib/
│   │   ├── email/                 # E-Mail-System (tenant-spezifisch)
│   │   ├── security/              # Rate-Limiter, CSRF, IP-Allowlist
│   │   ├── queue/                 # BullMQ Jobs & Workers
│   │   ├── pdf/                   # PDF-Generierung
│   │   ├── retention/             # Data Retention (DSGVO)
│   │   ├── integrations/meta/     # Facebook + Instagram API
│   │   ├── jobs/                  # UnifiedSyncService, JobLogger
│   │   ├── search.ts              # Volltextsuche
│   │   └── redis.ts               # Redis Cache Client
│   └── hooks/                     # Collection Hooks
├── tests/                         # Unit & Integration Tests
├── scripts/
│   ├── db-direct.sh               # Direkte DB-Verbindung (umgeht PgBouncer)
│   ├── sync-schema.sh             # Schema-Sync nach Collection-Änderungen
│   └── backup/                    # Backup-System (→ scripts/backup/README.md)
├── .env                           # Umgebungsvariablen
├── ecosystem.config.cjs           # PM2 Config
└── .github/workflows/             # CI/CD Pipelines

Wichtige Befehle

# Entwicklung
pnpm dev
pnpm build

# Migrationen
pnpm payload migrate:create
pnpm payload migrate

# Schema-Sync (wichtig nach Collection-Änderungen!)
./scripts/sync-schema.sh --dry-run   # Zeigt Änderungen
./scripts/sync-schema.sh             # Führt Sync aus

# ImportMap nach Plugin-Änderungen
pnpm payload generate:importmap

# PM2
pm2 status | pm2 logs payload | pm2 restart payload
pm2 logs queue-worker | pm2 restart queue-worker

# Tests
pnpm test                  # Alle Tests
pnpm test:security         # Security Tests
pnpm test:access-control   # Access Control Tests
pnpm test:coverage         # Mit Coverage-Report

# Lint & Format
pnpm lint                  # ESLint
pnpm typecheck             # TypeScript Check (4GB heap)
pnpm format:check          # Prettier Check
pnpm format                # Prettier Auto-Fix

# Direkte DB-Verbindung (umgeht PgBouncer für Migrationen)
./scripts/db-direct.sh migrate
./scripts/db-direct.sh psql

# Backup
/home/payload/backups/postgres/backup-db.sh --verbose

Git Workflow

Regel: Immer auf develop entwickeln, nach Freigabe nach main mergen.

Branch Zweck Deployment
develop Aktive Entwicklung Staging (pl.porwoll.tech)
main Stabile Version Production (cms.c2sgmbh.de)
feature/* Feature-Branches PR nach develop

Commit-Konventionen: feat:, fix:, docs:, refactor:, test:, chore:

Workflow nach Code-Änderungen:

  1. Code ändern (auf develop)
  2. pnpm buildpm2 restart payload
  3. Bei Collection-Änderungen zusätzlich: pnpm payload migrate:create

KRITISCH: Neue Collections hinzufügen

Payload CMS verwendet payload_locked_documents_rels für Document-Locks. Diese Tabelle benötigt eine {collection}_id Spalte für JEDE Collection. Ohne → RSC-Fehler nach Login:

column payload_locked_documents_rels.{collection}_id does not exist

Migration MUSS enthalten:

ALTER TABLE "payload_locked_documents_rels"
  ADD COLUMN IF NOT EXISTS "{collection}_id" integer
  REFERENCES {collection}(id) ON DELETE CASCADE;

CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_{collection}_idx"
  ON "payload_locked_documents_rels" ("{collection}_id");

Beispiel: src/migrations/20260109_020000_add_blogwoman_collections.ts

Array-Felder: Collections mit Arrays benötigen zusätzliche {collection}_{array_field} Tabellen.

Bekannte Besonderheiten & Gotchas

  • ES Modules: "type": "module" in package.json → PM2 Config als .cjs
  • Plugin ImportMap: Nach Plugin-Änderungen pnpm payload generate:importmap
  • User-Tenant-Zuweisung: Neue User müssen manuell Tenants zugewiesen bekommen
  • Admin Login: Custom Route mit Audit-Logging (src/app/(payload)/api/users/login/route.ts)
  • Queue Worker: PM2 nutzt node_modules/tsx/dist/cli.mjs direkt (nicht npx), exec_mode: 'fork'
  • PgBouncer: Transaction-Mode kann Migrationen stören → ./scripts/db-direct.sh
  • Tenant SMTP Save (Stand 17.02.2026): Bei conditional email.smtp keine Feld-required auf host/user; stattdessen Group-validate verwenden. Tenant-Audit-Hook darf Admin-Save nicht mit await blockieren.
  • TRUST_PROXY=true: PFLICHT hinter Reverse-Proxy, sonst funktionieren Rate-Limiting und IP-Allowlists nicht
  • CSRF_SECRET: PFLICHT in Production (oder PAYLOAD_SECRET) - Server startet nicht ohne
  • ModSecurity WAF (Production): Nginx auf Hetzner 3 hat OWASP CRS 3.3.7 in Blocking-Mode. /admin und /api/ sind per Exclusion-Rules freigeschaltet (IDs 1000160/1000170). Bei neuen API-Pfaden außerhalb von /api/ muss ggf. eine neue Exclusion-Rule in /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf ergänzt werden. Diagnose: curl localhost:3001/... (JSON=Payload) vs curl https://cms.c2sgmbh.de/... (HTML=ModSecurity-Block)

Build-Konfiguration

  • package.json: --max-old-space-size=2048 (2GB Heap-Limit)
  • next.config.mjs: experimental.cpus: 1, workerThreads: false

Server hat 8GB RAM ohne Swap. Bei laufendem VS Code kann Build OOM werden:

pm2 stop payload
NODE_OPTIONS="--no-deprecation --max-old-space-size=1024" ./node_modules/.bin/next build
pm2 start payload

Mehrsprachigkeit

Deutsch (default) + Englisch. API: ?locale=de|en. DB: _locales Tabellen.

URLs

Bereich URL
Admin Panel https://pl.porwoll.tech/admin
API https://pl.porwoll.tech/api
Swagger UI https://pl.porwoll.tech/api/docs
Production https://cms.c2sgmbh.de
Monitoring Dashboard https://pl.porwoll.tech/admin/monitoring

→ Vollständige Endpoint-Liste: docs/anleitungen/API_ANLEITUNG.md

Security Kurzübersicht

  • Rate-Limiting: publicApi(60/min), auth(5/15min), email(10/min), search(30/min), form(5/10min)
  • CSRF: Double Submit Cookie + Origin-Validierung, Token: GET /api/csrf-token
  • IP-Allowlist: SEND_EMAIL_ALLOWED_IPS, ADMIN_ALLOWED_IPS, BLOCKED_IPS (IPs, CIDRs, Wildcards)
  • Data-Masking: Automatisch in Logs (Passwörter, Tokens, API-Keys)

→ Details: docs/anleitungen/SECURITY.md

Subsysteme (Kurzübersicht)

System Schlüsseldateien Details
E-Mail (Multi-Tenant SMTP) src/lib/email/ docs/CLAUDE_REFERENCE.md
Newsletter (Double Opt-In) src/lib/email/newsletter-service.ts docs/CLAUDE_REFERENCE.md
BullMQ Queue src/lib/queue/, ecosystem.config.cjs docs/CLAUDE_REFERENCE.md
Data Retention (DSGVO) src/lib/retention/ docs/CLAUDE_REFERENCE.md
PDF-Generierung src/lib/pdf/, API: /api/generate-pdf docs/CLAUDE_REFERENCE.md
Redis Caching src/lib/redis.ts docs/CLAUDE_REFERENCE.md
Backup scripts/backup/ scripts/backup/README.md
Community Management src/lib/integrations/meta/, src/lib/jobs/ docs/CLAUDE_REFERENCE.md
YouTube Operations Hub src/lib/youtube/, API: /api/youtube/* docs/CLAUDE_REFERENCE.md
FormSubmissions CRM src/collections/FormSubmissionsOverrides.ts docs/CLAUDE_REFERENCE.md
Monitoring & Alerting src/lib/monitoring/, API: /api/monitoring/* docs/CLAUDE_REFERENCE.md

Collections (59+)

Slug Beschreibung Gruppe
users Benutzer (isSuperAdmin) Core
tenants Mandanten + E-Mail-Config Core
media Medien (11 responsive Sizes) Core
pages Seiten mit Blocks Core
posts Blog/News/Presse Content
categories Post-Kategorien Content
tags Post-Tags Content
authors Post-Autoren Content
testimonials Kundenbewertungen Content
faqs FAQ Content
social-links Social Media Links Content
team Team-Mitglieder Team
service-categories Leistungs-Kategorien Team
services Leistungen Team
jobs Stellenangebote Team
portfolios Portfolio-Galerien Portfolio
portfolio-categories Portfolio-Kategorien Portfolio
videos Video-Bibliothek Media
video-categories Video-Kategorien Media
products Produkte E-Commerce
product-categories Produkt-Kategorien E-Commerce
locations Standorte/Filialen Feature
partners Partner/Kunden Feature
downloads Download-Dateien Feature
events Veranstaltungen Feature
timelines Chronologische Events Feature
workflows Prozesse mit Phasen Feature
forms Formular-Builder (Plugin) Formulare
form-submissions CRM-Workflow Formulare
newsletter-subscribers Double Opt-In Newsletter
cookie-configurations Cookie-Banner DSGVO
cookie-inventory Cookie-Inventar DSGVO
consent-logs Consent-Protokoll (WORM) DSGVO
privacy-policy-settings Datenschutz DSGVO
bookings Fotografie-Buchungen (porwoll) Tenant
certifications Zertifizierungen (c2s) Tenant
projects Game-Dev-Projekte (gunshin) Tenant
favorites Affiliate-Produkte BlogWoman
series YouTube-Serien (Branding) BlogWoman
youtube-channels Multi-Kanal + OAuth YouTube
youtube-content Videos + Shorts YouTube
yt-series Serien + Playlist YouTube
yt-notifications Handlungsbedarf YouTube
yt-script-templates Skript-Vorlagen YouTube
yt-batches Produktions-Batches YouTube
yt-checklist-templates Checklisten YouTube
yt-monthly-goals Monatsziele YouTube
yt-tasks Aufgaben YouTube
social-accounts Meta OAuth Community
social-platforms Plattform-Config Community
community-interactions Kommentare (alle Plattformen) Community
community-rules Auto-Antwort-Regeln Community
community-templates Antwort-Vorlagen Community
report-schedules Geplante Reports Community
email-logs E-Mail-Protokoll System
audit-logs Security Audit Trail System
site-settings Website-Einstellungen (pro Tenant) System
navigations Navigationsmenüs (pro Tenant) System
redirects URL-Weiterleitungen (Plugin) System
monitoring-snapshots Historische System-Metriken Monitoring
monitoring-logs Structured Business-Logs Monitoring
monitoring-alert-rules Konfigurierbare Alert-Regeln Monitoring
monitoring-alert-history Alert-Log (WORM) Monitoring

Blocks (43)

Slug Beschreibung Gruppe
hero-block Hero mit Bild, Headline, CTA Core
hero-slider-block Hero-Slider (Slides, Animationen, Autoplay) Core
image-slider-block Bildergalerie/Karussell Core
text-block Rich-Text Inhalt Core
image-text-block Bild + Text nebeneinander Core
card-grid-block Karten-Raster Core
quote-block Zitat Core
cta-block Call-to-Action Core
contact-form-block Kontaktformular Core
timeline-block Timeline-Darstellung Core
divider-block Trennlinie Core
video-block Video einbetten Core
posts-list-block Beitrags-Liste Content
testimonials-block Kundenbewertungen Content
newsletter-block Newsletter-Anmeldung Content
process-steps-block Prozess-Schritte Content
faq-block FAQ-Akkordeon Content
team-block Team-Mitglieder Content
services-block Leistungen Content
author-bio-block Autoren-Biografie Blogging
related-posts-block Verwandte Beiträge Blogging
share-buttons-block Social Share Buttons Blogging
table-of-contents-block Inhaltsverzeichnis Blogging
team-filter-block Team mit Filter Team
org-chart-block Organigramm Team
locations-block Standorte/Filialen Feature
logo-grid-block Partner-Logos Feature
stats-block Statistiken/Zahlen Feature
jobs-block Stellenangebote Feature
downloads-block Download-Bereich Feature
map-block Karten-Einbindung Feature
events-block Veranstaltungen Interactive
pricing-block Preistabellen Interactive
tabs-block Tab-Navigation Interactive
accordion-block Akkordeon Interactive
comparison-block Vergleichstabelle Interactive
before-after-block Vorher/Nachher (porwoll) Tenant
favorites-block Affiliate-Produkte BlogWoman
series-block YouTube-Serien BlogWoman
series-detail-block Serien-Einzelseite BlogWoman
video-embed-block YouTube/Vimeo Embed BlogWoman
featured-content-block Kuratierte Inhalte BlogWoman
script-section-block Script-Abschnitte BlogWoman

Globals

Global Slug Beschreibung
SEOSettings seo-settings Globale SEO-Einstellungen (systemweit)

SiteSettings, Navigations, PrivacyPolicySettings sind tenant-spezifische Collections.

Cron Jobs

Endpoint Schedule Beschreibung
/api/cron/community-sync alle 15 Min Kommentar-Sync (YouTube, Facebook, Instagram)
/api/cron/token-refresh 6:00 + 18:00 UTC OAuth-Token-Erneuerung
/api/cron/send-reports stündlich Community-Reports per E-Mail
/api/cron/youtube-sync alle 15 Min YouTube-Kommentar-Sync
/api/cron/youtube-channel-sync täglich 04:00 UTC Kanal-Statistiken (Abos, Views)
/api/cron/youtube-metrics-sync alle 6 Stunden Video-Performance-Metriken

Auth: Authorization: Bearer $CRON_SECRET

CI/CD

Workflow Trigger Beschreibung
ci.yml Push/PR auf main/develop Lint, Typecheck, Test, Build, E2E
security.yml Push/PR Gitleaks, pnpm audit, CodeQL
deploy-staging.yml Push auf develop Auto-Deploy auf pl.porwoll.tech
deploy-production.yml Manual dispatch Deploy auf cms.c2sgmbh.de (mit Backup + Rollback)

→ Details: docs/DEPLOYMENT.md, docs/DEPLOYMENT_STRATEGY.md

Umgebungsvariablen

Wichtigste Variablen (vollständige Liste in .env):

Variable Beschreibung Pflicht
DATABASE_URI PostgreSQL via PgBouncer (127.0.0.1:6432) Ja
PAYLOAD_SECRET Payload Encryption Secret Ja
TRUST_PROXY true hinter Reverse-Proxy Ja
CSRF_SECRET CSRF-Token Secret (oder PAYLOAD_SECRET) Prod
CRON_SECRET Auth für Cron-Endpoints Ja
REDIS_PASSWORD Redis Authentifizierung Ja
REDIS_URL Redis Cache (localhost:6379) Optional
META_APP_ID/SECRET Facebook/Instagram OAuth Für Community
GOOGLE_CLIENT_ID/SECRET YouTube OAuth Für YouTube

Credentials in ~/.pgpass (chmod 600), nie Klartext in .env.

Dokumentation

Projekt-Docs

  • docs/INFRASTRUCTURE.md - Infrastruktur (Dev + Prod, PgBouncer, Server)
  • docs/DEPLOYMENT.md - Deployment-Prozess & Checklisten
  • docs/DEPLOYMENT_STRATEGY.md - Vollständige Deployment-Strategie
  • docs/PROJECT_STATUS.md - Projektstatus & Roadmap
  • docs/CLAUDE_REFERENCE.md - Detaillierte Subsystem-Referenz (E-Mail, Queue, Retention, Community, etc.)

Anleitungen

  • docs/anleitungen/API_ANLEITUNG.md - API-Dokumentation (alle Endpoints + curl-Beispiele)
  • docs/anleitungen/SECURITY.md - Sicherheitsrichtlinien
  • docs/anleitungen/FRONTEND.md - Frontend-Entwicklung (sv-frontend)
  • docs/anleitungen/TODO.md - Task-Liste & Changelog

Scripts & Backup

  • scripts/backup/README.md - Backup-System Dokumentation

Codex CLI — Remote-Orchestrierung (sv-frontend)

OpenAI Codex CLI (v0.101.0, Model gpt-5.3-codex) ist auf sv-frontend installiert und kann von sv-payload aus non-interaktiv gesteuert werden. Nutze Codex für Frontend-Aufgaben um Arbeit zu parallelisieren.

Wichtig: Mehrere Codex-Sessions können parallel laufen — nutze dies für unabhängige Aufgaben in verschiedenen Projekten.

# Einzelne Aufgabe (JSON-Output für Parsing):
ssh sv-frontend "codex exec -s danger-full-access -C ~/frontend.blogwoman.de 'prompt' --json 2>&1"

# Aufgabe mit Ergebnis-Datei:
ssh sv-frontend "codex exec -s danger-full-access -C ~/projekt -o /tmp/result.txt 'prompt' 2>&1"

# Parallele Aufgaben (in separaten SSH-Sessions):
ssh sv-frontend "codex exec -s danger-full-access -C ~/frontend.blogwoman.de 'task1' --json" &
ssh sv-frontend "codex exec -s danger-full-access -C ~/frontend.porwoll.de 'task2' --json" &
wait

Wann Codex delegieren:

  • Frontend-Komponenten erstellen/ändern
  • Lint/Format-Fehler in Frontend-Repos beheben
  • Code-Reviews in Frontend-Projekten
  • Tests schreiben für Frontend-Code
  • Refactoring in Frontend-Repos

Einschränkungen:

  • MUSS -s danger-full-access verwenden (workspace-write hat Landlock-Fehler)
  • rg (ripgrep) nicht installiert — Codex nutzt find als Fallback
  • Keine ~/.codex/config.toml — Konfiguration nur via CLI-Flags
  • Kein Git-Commit durch Codex — nur Code-Änderungen, Commit/Push über SSH

Letzte Aktualisierung: 22.02.2026