- CLAUDE.md: Update Payload version 3.68.4 → 3.69.0, update date - PROJECT_STATUS.md: Add changelog entry for 27.12.2025, update version - INFRASTRUCTURE.md: Update Payload version - TODO.md: Add detailed changelog entry for bug fixes and updates - BUG_REPORT_CUSTOM_VIEWS.md: Update versions, add note that bug persists 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
33 KiB
Payload CMS Multi-Tenant Project
Projektübersicht
Multi-Tenant CMS für 4 Websites unter einer Payload CMS 3.x Instanz:
- porwoll.de
- complexcaresolutions.de
- gunshin.de
- zweitmein.ng
Tech Stack
- CMS: Payload CMS 3.69.0
- Framework: Next.js 15.5.9
- React: 19.2.3
- 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 mit Let's Encrypt (Dev) / Nginx (Prod)
- Process Manager: PM2
- Package Manager: pnpm
- Cache: Redis 7.x (optional, mit In-Memory-Fallback)
- Job Queue: BullMQ (Redis-basiert)
- Analytics: Umami 3.x
- AI Tools: Claude Code, Codex CLI, Gemini CLI
Architektur
Development (VLAN 181 - porwoll.tech)
Internet → Cloudflare → 37.24.237.181 → Caddy (sv-caddy) → Services
↓
┌───────────────────────────┴───────────────────────────┐
│ │
sv-payload:3000 sv-frontend:3000-3008
(Payload CMS) (9 Frontend-Projekte)
│
PgBouncer (6432)
│
PostgreSQL (sv-postgres:5432)
| LXC | Hostname | IP | Service | Status |
|---|---|---|---|---|
| 699 | sv-caddy | 10.10.181.99 | Caddy Reverse Proxy | ✅ Running |
| 700 | sv-payload | 10.10.181.100 | Payload CMS + Redis | ✅ Running |
| 701 | sv-postgres | 10.10.181.101 | PostgreSQL 17 + Redis Commander | ✅ Running |
| 702 | sv-dev-payload | 10.10.181.102 | Payload Experimental | ⏸️ Stopped |
| 703 | sv-analytics | 10.10.181.103 | Umami Analytics | ✅ Running |
| 704 | sv-frontend | 10.10.181.104 | Multi-Frontend Dev (9 Projekte) | ✅ Running |
Production (Hetzner 3)
| Service | URL | Port |
|---|---|---|
| Payload CMS | https://cms.c2sgmbh.de | 3001 |
| Umami Analytics | https://analytics.c2sgmbh.de | 3000 |
| PostgreSQL 17 | localhost | 5432 |
| Redis Cache | localhost | 6379 |
Wichtige Pfade
/home/payload/payload-cms/ # Projektroot
├── src/
│ ├── payload.config.ts # Haupt-Konfiguration
│ ├── collections/ # Alle Collections
│ │ ├── Users.ts
│ │ ├── Media.ts
│ │ ├── Tenants.ts
│ │ ├── Posts.ts
│ │ ├── Categories.ts
│ │ ├── Portfolios.ts
│ │ ├── PortfolioCategories.ts
│ │ ├── FAQs.ts
│ │ ├── Team.ts
│ │ ├── ServiceCategories.ts
│ │ ├── Services.ts
│ │ ├── EmailLogs.ts
│ │ ├── AuditLogs.ts
│ │ └── ...
│ ├── app/(payload)/api/ # Custom API Routes
│ │ ├── users/login/route.ts # Custom Login mit Audit
│ │ ├── send-email/route.ts
│ │ ├── email-logs/
│ │ │ ├── export/route.ts
│ │ │ └── stats/route.ts
│ │ └── test-email/route.ts
│ ├── lib/
│ │ ├── email/ # E-Mail-System
│ │ │ ├── tenant-email-service.ts
│ │ │ ├── payload-email-adapter.ts
│ │ │ ├── newsletter-service.ts # Newsletter Double Opt-In
│ │ │ └── newsletter-templates.ts # E-Mail-Templates
│ │ ├── security/ # Security-Module
│ │ │ ├── rate-limiter.ts
│ │ │ ├── csrf.ts
│ │ │ ├── ip-allowlist.ts
│ │ │ └── data-masking.ts
│ │ ├── queue/ # BullMQ Job Queue
│ │ │ ├── queue-service.ts
│ │ │ ├── jobs/email-job.ts
│ │ │ ├── jobs/pdf-job.ts
│ │ │ ├── workers/email-worker.ts
│ │ │ └── workers/pdf-worker.ts
│ │ ├── pdf/ # PDF-Generierung
│ │ │ └── pdf-service.ts
│ │ ├── search.ts # Volltextsuche
│ │ └── redis.ts # Redis Cache Client
│ └── hooks/ # Collection Hooks
│ ├── sendFormNotification.ts
│ ├── sendNewsletterConfirmation.ts # Newsletter Double Opt-In
│ ├── invalidateEmailCache.ts
│ └── auditLog.ts
├── tests/ # Test Suite
│ ├── unit/security/ # Security Unit Tests
│ └── int/ # Integration Tests
├── scripts/
│ ├── run-queue-worker.ts # Queue Worker Starter
│ └── backup/ # Backup-System
│ ├── backup-db.sh # PostgreSQL Backup-Skript
│ ├── restore-db.sh # PostgreSQL Restore-Skript
│ ├── setup-backup.sh # Interaktives Setup
│ └── README.md # Backup-Dokumentation
├── .env # Umgebungsvariablen
├── ecosystem.config.cjs # PM2 Config
└── .next/ # Build Output
Umgebungsvariablen (.env)
Hinweis: Sensible Credentials (DB_PASSWORD, SMTP_PASS, etc.) werden aus
~/.pgpassund Umgebungsvariablen gelesen. Niemals Klartext-Passwörter in diese Datei schreiben!
# Datenbank (Passwort in ~/.pgpass oder als Umgebungsvariable)
DATABASE_URI=postgresql://payload:${DB_PASSWORD}@127.0.0.1:6432/payload_db
PAYLOAD_SECRET=a53b254070d3fffd2b5cfcc3
PAYLOAD_PUBLIC_SERVER_URL=https://pl.porwoll.tech
NEXT_PUBLIC_SERVER_URL=https://pl.porwoll.tech
NODE_ENV=production
PORT=3000
# E-Mail (Global Fallback)
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=user@example.com
SMTP_PASS=secret
SMTP_FROM_ADDRESS=noreply@c2sgmbh.de
SMTP_FROM_NAME=Payload CMS
# Redis Cache
REDIS_URL=redis://localhost:6379
# Security
CSRF_SECRET=your-csrf-secret # PFLICHT in Production (oder PAYLOAD_SECRET)
TRUST_PROXY=true # PFLICHT hinter Reverse-Proxy (Caddy/Nginx)
SEND_EMAIL_ALLOWED_IPS= # Optional: Komma-separierte IPs/CIDRs
ADMIN_ALLOWED_IPS= # Optional: IP-Beschränkung für Admin-Panel
BLOCKED_IPS= # Optional: Global geblockte IPs
Wichtig:
TRUST_PROXY=truemuss gesetzt sein wenn die App hinter einem Reverse-Proxy (wie Caddy) läuft. Ohne diese Einstellung funktionieren IP-basierte Sicherheitsfunktionen (Rate-Limiting, IP-Allowlists, Blocklists) nicht korrekt.
PgBouncer Connection Pooling
PgBouncer läuft auf dem App-Server und pooled Datenbankverbindungen:
# Konfiguration
/etc/pgbouncer/pgbouncer.ini
/etc/pgbouncer/userlist.txt # chmod 600
# Service
sudo systemctl status pgbouncer
sudo systemctl restart pgbouncer
# Statistiken abfragen
# Pool-Statistiken (Passwort aus ~/.pgpass)
PGPASSWORD="$DB_PASSWORD" psql -h 127.0.0.1 -p 6432 -U payload -d pgbouncer -c "SHOW POOLS;"
PGPASSWORD="$DB_PASSWORD" psql -h 127.0.0.1 -p 6432 -U payload -d pgbouncer -c "SHOW STATS;"
Konfiguration:
| Parameter | Wert | Beschreibung |
|---|---|---|
| pool_mode | transaction | Verbindung wird nach Transaktion freigegeben |
| default_pool_size | 20 | Standard-Pool-Größe pro DB/User |
| min_pool_size | 5 | Mindestens gehaltene Verbindungen |
| reserve_pool_size | 5 | Reserve für Lastspitzen |
| max_db_connections | 50 | Max. Verbindungen zu PostgreSQL |
| max_client_conn | 200 | Max. Clients zu PgBouncer |
Verbindungen:
- App → PgBouncer:
127.0.0.1:6432(DATABASE_URI) - PgBouncer → PostgreSQL:
10.10.181.101:5432(TLS 1.3,server_tls_sslmode = require)
Direkte Verbindung (für Migrationen/CLI):
PgBouncer im Transaction-Mode kann Probleme mit lang laufenden Migrationen verursachen.
Für solche Fälle das db-direct.sh Skript verwenden:
# Migrationen direkt an PostgreSQL (umgeht PgBouncer)
./scripts/db-direct.sh migrate
# Interaktive psql-Session
./scripts/db-direct.sh psql
# Schema-Änderungen
./scripts/db-direct.sh migrate:create
Das Skript liest Credentials aus ~/.pgpass (chmod 600).
Multi-Tenant Plugin
Verwendet @payloadcms/plugin-multi-tenant für Mandantenfähigkeit.
Aktuelle Tenants:
| ID | Name | Slug |
|---|---|---|
| 1 | porwoll.de | porwoll |
| 4 | Complex Care Solutions GmbH | c2s |
| 5 | Gunshin | gunshin |
User-Tenant-Zuweisung: Tabelle users_tenants
Wichtige Befehle
# Entwicklung
pnpm dev
# Production Build
pnpm build
# Migrationen
pnpm payload migrate:create
pnpm payload migrate
# ImportMap nach Plugin-Änderungen
pnpm payload generate:importmap
# PM2
pm2 status
pm2 logs payload
pm2 logs queue-worker
pm2 restart payload
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
# Datenbank prüfen
# Verwende ./scripts/db-direct.sh psql oder:
PGPASSWORD="$DB_PASSWORD" psql -h 10.10.181.101 -U payload -d payload_db
# Backup
/home/payload/backups/postgres/backup-db.sh --verbose # Manuelles Backup
scripts/backup/setup-backup.sh # Backup-System einrichten
Workflow nach Code-Änderungen
- Code ändern
pnpm buildpm2 restart payload- Testen unter https://pl.porwoll.tech/admin
Bekannte Besonderheiten
- ES Modules: package.json hat
"type": "module", daher PM2 Config als.cjs - Plugin ImportMap: Nach Plugin-Änderungen
pnpm payload generate:importmapausführen - User-Tenant-Zuweisung: Neue User müssen manuell Tenants zugewiesen bekommen
- Admin Login: Custom Route mit Audit-Logging, unterstützt JSON und
_payloadFormData
Build-Konfiguration
Der Build ist für speichereffizientes Kompilieren optimiert:
package.json:--max-old-space-size=2048(2GB Heap-Limit)next.config.mjs:experimental.cpus: 1,workerThreads: false
WICHTIG: Der Server hat 8GB RAM ohne Swap. Bei laufendem VS Code Server kann der Build mit reduziertem Memory ausgeführt werden:
pm2 stop payload # Speicher freigeben
NODE_OPTIONS="--no-deprecation --max-old-space-size=1024" ./node_modules/.bin/next build
pm2 start payload
Ohne PM2-Stop und mit VS Code wird der Build vom OOM Killer beendet (Exit code 137).
Mehrsprachigkeit (i18n)
Das System unterstützt Deutsch (default) und Englisch:
- Admin UI:
@payloadcms/translationsfür DE/EN - Content: Localization mit Fallback auf Deutsch
- Datenbank: 36
_localesTabellen für lokalisierte Felder - API:
?locale=deoder?locale=enParameter - Frontend: Routing über
/[locale]/...
# Locales in der Datenbank prüfen
# Verwende ./scripts/db-direct.sh psql oder:
PGPASSWORD="$DB_PASSWORD" psql -h 10.10.181.101 -U payload -d payload_db -c "\dt *_locales"
URLs
- Admin Panel: https://pl.porwoll.tech/admin
- API: https://pl.porwoll.tech/api
- API-Dokumentation (Swagger UI): https://pl.porwoll.tech/api/docs
- OpenAPI JSON: https://pl.porwoll.tech/api/openapi.json
- E-Mail API: https://pl.porwoll.tech/api/send-email (POST, Auth erforderlich)
- Test-E-Mail: https://pl.porwoll.tech/api/test-email (POST, Admin erforderlich)
- E-Mail Stats: https://pl.porwoll.tech/api/email-logs/stats (GET, Auth erforderlich)
- PDF-Generierung: https://pl.porwoll.tech/api/generate-pdf (POST/GET, Auth erforderlich)
- Newsletter Anmeldung: https://pl.porwoll.tech/api/newsletter/subscribe (POST, öffentlich)
- Newsletter Bestätigung: https://pl.porwoll.tech/api/newsletter/confirm (GET/POST)
- Newsletter Abmeldung: https://pl.porwoll.tech/api/newsletter/unsubscribe (GET/POST)
- Timeline API: https://pl.porwoll.tech/api/timelines (GET, öffentlich, tenant required)
- Workflows API: https://pl.porwoll.tech/api/workflows (GET, öffentlich, tenant required)
- Data Retention API: https://pl.porwoll.tech/api/retention (GET/POST, Super-Admin erforderlich)
Security-Features
Proxy-Vertrauen (TRUST_PROXY)
WICHTIG: Wenn die App hinter einem Reverse-Proxy läuft (Caddy, Nginx, etc.),
muss TRUST_PROXY=true gesetzt sein:
TRUST_PROXY=true
Ohne diese Einstellung:
- Werden
X-Forwarded-ForundX-Real-IPHeader ignoriert - Können Angreifer IP-basierte Sicherheitsmaßnahmen nicht umgehen
- Aber: Rate-Limiting und IP-Allowlists funktionieren nicht korrekt
Rate-Limiting
Zentraler Rate-Limiter mit vordefinierten Limits:
publicApi: 60 Requests/Minuteauth: 5 Requests/15 Minuten (Login)email: 10 Requests/Minutesearch: 30 Requests/Minuteform: 5 Requests/10 Minuten
Hinweis: Rate-Limiting basiert auf Client-IP. Bei TRUST_PROXY=false wird
direct-connection als IP verwendet, was alle Clients zusammenfasst.
CSRF-Schutz
- Double Submit Cookie Pattern
- Origin-Header-Validierung
- Token-Endpoint:
GET /api/csrf-token - Admin-Panel hat eigenen CSRF-Schutz
- CSRF_SECRET oder PAYLOAD_SECRET ist in Production PFLICHT
- Server startet nicht ohne Secret in Production
IP-Allowlist
- Konfigurierbar via
SEND_EMAIL_ALLOWED_IPS,ADMIN_ALLOWED_IPS - Unterstützt IPs, CIDRs (
192.168.1.0/24) und Wildcards (10.10.*.*) - Globale Blocklist via
BLOCKED_IPS - Warnung: Leere Allowlists erlauben standardmäßig alle IPs
Data-Masking
- Automatische Maskierung sensibler Daten in Logs
- Erkennt Passwörter, Tokens, API-Keys, SMTP-Credentials
- Safe-Logger-Factory für konsistentes Logging
E-Mail-System
Multi-Tenant E-Mail-System mit tenant-spezifischer SMTP-Konfiguration:
Architektur:
- Globaler SMTP als Fallback (via .env)
- Tenant-spezifische SMTP in Tenants Collection
- Transporter-Caching mit automatischer Invalidierung
- EmailLogs Collection für Audit-Trail
Tenant E-Mail-Konfiguration:
Tenants → email → fromAddress, fromName, replyTo
→ email → useCustomSmtp (Checkbox)
→ email → smtp → host, port, secure, user, pass
API-Endpoint /api/send-email:
curl -X POST https://pl.porwoll.tech/api/send-email \
-H "Content-Type: application/json" \
-H "Cookie: payload-token=..." \
-d '{
"to": "empfaenger@example.com",
"subject": "Betreff",
"html": "<p>Inhalt</p>",
"tenantId": 1
}'
Sicherheit:
- Authentifizierung erforderlich
- Tenant-Zugriffskontrolle (User muss Tenant-Mitglied sein)
- Rate-Limiting: 10 E-Mails/Minute pro User
- SMTP-Passwort nie in API-Responses
Newsletter Double Opt-In
DSGVO-konformes Newsletter-System mit Double Opt-In:
Flow:
- User meldet sich an → Status:
pending, Token wird generiert - Double Opt-In E-Mail wird automatisch gesendet
- User klickt Bestätigungs-Link → Status:
confirmed - Willkommens-E-Mail wird gesendet
- Abmeldung jederzeit über Link in E-Mails möglich
API-Endpoints:
# Newsletter-Anmeldung
curl -X POST https://pl.porwoll.tech/api/newsletter/subscribe \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"firstName": "Max",
"tenantId": 1,
"source": "footer"
}'
# Bestätigung (via Link aus E-Mail)
GET https://pl.porwoll.tech/api/newsletter/confirm?token=<uuid>
# Abmeldung (via Link aus E-Mail)
GET https://pl.porwoll.tech/api/newsletter/unsubscribe?token=<uuid>
Features:
- Automatischer E-Mail-Versand bei Anmeldung
- Token-Ablauf nach 48 Stunden
- Willkommens-E-Mail nach Bestätigung
- Abmelde-Bestätigung per E-Mail
- Rate-Limiting: 5 Anmeldungen/10 Minuten pro IP
- Erneute Anmeldung nach Abmeldung möglich
BullMQ Job Queue
Das System verwendet BullMQ für asynchrone Job-Verarbeitung mit Redis als Backend.
PM2 Worker
Der Queue-Worker läuft als separater PM2-Prozess:
# Worker-Status prüfen
pm2 status
pm2 logs queue-worker
# Worker neustarten
pm2 restart queue-worker
Email Queue
E-Mails können asynchron über die Queue versendet werden:
import { queueEmail } from '@/lib/queue'
await queueEmail({
tenantId: 1,
to: 'empfaenger@example.com',
subject: 'Betreff',
html: '<p>Inhalt</p>',
source: 'form-submission'
})
PDF Queue
PDF-Generierung erfolgt über Playwright (HTML/URL zu PDF):
API-Endpoint /api/generate-pdf:
# PDF aus HTML generieren
curl -X POST https://pl.porwoll.tech/api/generate-pdf \
-H "Content-Type: application/json" \
-H "Cookie: payload-token=..." \
-d '{
"source": "html",
"html": "<h1>Test</h1><p>Inhalt</p>",
"filename": "test.pdf"
}'
# Job-Status abfragen
curl "https://pl.porwoll.tech/api/generate-pdf?jobId=abc123" \
-H "Cookie: payload-token=..."
Programmatisch:
import { queuePdfFromHtml, queuePdfFromUrl, getPdfJobStatus } from '@/lib/queue'
// HTML zu PDF
const job = await queuePdfFromHtml('<h1>Test</h1>', { filename: 'test.pdf' })
// URL zu PDF
const job2 = await queuePdfFromUrl('https://example.com', { format: 'A4' })
// Status abfragen
const status = await getPdfJobStatus(job.id)
Worker-Konfiguration
Über ecosystem.config.cjs:
QUEUE_EMAIL_CONCURRENCY: Parallele E-Mail-Jobs (default: 3)QUEUE_PDF_CONCURRENCY: Parallele PDF-Jobs (default: 2)QUEUE_RETENTION_CONCURRENCY: Parallele Retention-Jobs (default: 1)QUEUE_DEFAULT_RETRY: Retry-Versuche (default: 3)QUEUE_REDIS_DB: Redis-Datenbank für Queue (default: 1)
Data Retention
Automatische Datenbereinigung für DSGVO-Compliance und Speicheroptimierung.
Retention Policies
| Collection | Retention | Umgebungsvariable | Beschreibung |
|---|---|---|---|
| email-logs | 90 Tage | RETENTION_EMAIL_LOGS_DAYS |
E-Mail-Protokolle |
| audit-logs | 90 Tage | RETENTION_AUDIT_LOGS_DAYS |
Audit-Trail |
| consent-logs | 3 Jahre | RETENTION_CONSENT_LOGS_DAYS |
DSGVO: expiresAt-basiert |
| media (orphans) | 30 Tage | RETENTION_MEDIA_ORPHAN_MIN_AGE_DAYS |
Unreferenzierte Medien |
Automatischer Scheduler
Retention-Jobs laufen täglich um 03:00 Uhr (konfigurierbar via RETENTION_CRON_SCHEDULE).
# Umgebungsvariablen in .env
RETENTION_EMAIL_LOGS_DAYS=90
RETENTION_AUDIT_LOGS_DAYS=90
RETENTION_CONSENT_LOGS_DAYS=1095
RETENTION_MEDIA_ORPHAN_MIN_AGE_DAYS=30
RETENTION_CRON_SCHEDULE="0 3 * * *"
# Worker aktivieren/deaktivieren
QUEUE_ENABLE_RETENTION=true
QUEUE_ENABLE_RETENTION_SCHEDULER=true
API-Endpoint /api/retention
GET - Konfiguration abrufen:
curl https://pl.porwoll.tech/api/retention \
-H "Cookie: payload-token=..."
GET - Job-Status abfragen:
curl "https://pl.porwoll.tech/api/retention?jobId=abc123" \
-H "Cookie: payload-token=..."
POST - Manuellen Job auslösen:
# Vollständige Retention (alle Policies + Media-Orphans)
curl -X POST https://pl.porwoll.tech/api/retention \
-H "Content-Type: application/json" \
-H "Cookie: payload-token=..." \
-d '{"type": "full"}'
# Einzelne Collection bereinigen
curl -X POST https://pl.porwoll.tech/api/retention \
-H "Content-Type: application/json" \
-H "Cookie: payload-token=..." \
-d '{"type": "collection", "collection": "email-logs"}'
# Nur Media-Orphans bereinigen
curl -X POST https://pl.porwoll.tech/api/retention \
-H "Content-Type: application/json" \
-H "Cookie: payload-token=..." \
-d '{"type": "media-orphans"}'
Architektur
Scheduler (Cron)
↓
Retention Queue (BullMQ)
↓
Retention Worker
↓
┌─────────────────┬─────────────────┬─────────────────┐
│ Email-Logs │ Audit-Logs │ Consent-Logs │
│ (createdAt) │ (createdAt) │ (expiresAt) │
└─────────────────┴─────────────────┴─────────────────┘
↓
Media-Orphan-Cleanup
↓
Cleanup-Ergebnis (Logs)
Dateien
src/lib/retention/retention-config.ts- Zentrale Konfigurationsrc/lib/retention/cleanup-service.ts- Lösch-Logiksrc/lib/queue/jobs/retention-job.ts- Job-Definitionsrc/lib/queue/workers/retention-worker.ts- Workersrc/app/(payload)/api/retention/route.ts- API-Endpoint
Redis Caching
Redis wird für API-Response-Caching und E-Mail-Transporter-Caching verwendet:
import { redis } from '@/lib/redis'
// Cache setzen (TTL in Sekunden)
await redis.set('key', JSON.stringify(data), 'EX', 60)
// Cache lesen
const cached = await redis.get('key')
// Pattern-basierte Invalidierung
await redis.keys('posts:*').then(keys => keys.length && redis.del(...keys))
Backup-System
Automatisches tägliches PostgreSQL-Backup mit lokalem Speicher und S3-Offsite-Backup.
Setup auf neuem Server:
cd /home/payload/payload-cms/scripts/backup
./setup-backup.sh
Konfigurationsdateien (nicht im Repo):
~/.pgpass- PostgreSQL-Credentials (chmod 600)~/.s3cfg- S3-Credentials (chmod 600)
Backup-Speicherorte:
| Ort | Pfad | Retention |
|---|---|---|
| Lokal | /home/payload/backups/postgres/ |
30 Tage |
| S3 | s3://c2s/backups/postgres/ |
30 Tage |
Cron-Job: Täglich um 03:00 Uhr
Manuelles Backup:
/home/payload/backups/postgres/backup-db.sh --verbose
Restore aus Backup:
# Lokal
gunzip -c /home/payload/backups/postgres/payload_db_YYYY-MM-DD_HH-MM-SS.sql.gz | \
psql -h 10.10.181.101 -U payload -d payload_db
# Aus S3
s3cmd get s3://c2s/backups/postgres/payload_db_YYYY-MM-DD_HH-MM-SS.sql.gz
gunzip -c payload_db_*.sql.gz | psql -h 10.10.181.101 -U payload -d payload_db
Dokumentation: scripts/backup/README.md
Datenbank-Direktzugriff
# Verwende ./scripts/db-direct.sh psql oder:
PGPASSWORD="$DB_PASSWORD" psql -h 10.10.181.101 -U payload -d payload_db
# Nützliche Queries
SELECT * FROM tenants;
SELECT * FROM users_tenants;
SELECT * FROM email_logs ORDER BY created_at DESC LIMIT 10;
SELECT * FROM audit_logs ORDER BY created_at DESC LIMIT 10;
\dt -- Alle Tabellen
Blocks Übersicht
| Block | Slug | Beschreibung |
|---|---|---|
| HeroBlock | hero-block | Einzelner Hero mit Bild, Headline, CTA |
| HeroSliderBlock | hero-slider-block | Hero-Slider mit mehreren Slides |
| TextBlock | text-block | Textinhalt mit Rich-Text |
| ImageTextBlock | image-text-block | Bild + Text nebeneinander |
| CardGridBlock | card-grid-block | Karten-Raster |
| QuoteBlock | quote-block | Zitat |
| CTABlock | cta-block | Call-to-Action |
| ContactFormBlock | contact-form-block | Kontaktformular |
| TimelineBlock | timeline-block | Timeline-Darstellung |
| DividerBlock | divider-block | Trennlinie |
| VideoBlock | video-block | Video einbetten |
| PostsListBlock | posts-list-block | Beitrags-Liste |
| TestimonialsBlock | testimonials-block | Kundenbewertungen |
| NewsletterBlock | newsletter-block | Newsletter-Anmeldung |
| ProcessStepsBlock | process-steps-block | Prozess-Schritte |
| FAQBlock | faq-block | FAQ-Akkordeon |
| TeamBlock | team-block | Team-Mitglieder |
| ServicesBlock | services-block | Leistungen |
| BeforeAfterBlock | before-after-block | Vorher/Nachher Bildvergleich (porwoll.de) |
HeroSliderBlock Features
Vollwertiger Hero-Slider mit:
Slides (Array, 1-10):
- Hintergrundbild (Desktop + optional Mobil)
- Headline + Subline (lokalisiert)
- Text-Ausrichtung (links/zentriert/rechts)
- Vertikale Position (oben/mitte/unten)
- Overlay (Farbe, Deckkraft, Gradient)
- Primärer + Sekundärer CTA-Button
- Textfarbe (weiß/dunkel/primär)
Animationen:
- Typen: fade, slide, zoom, flip, none
- Konfigurierbare Dauer (300-1200ms)
Autoplay:
- Intervall (3-10 Sekunden)
- Pause bei Hover
- Pause bei Interaktion
Navigation:
- Pfeile (verschiedene Stile, Positionen)
- Dots (Punkte, Striche, Nummern, Thumbnails, Fortschritt)
- Touch-Swipe
- Tastaturnavigation
Layout:
- Höhe (Vollbild bis kompakt)
- Separate Mobile-Höhe
- Content-Breite
Collections Übersicht
| Collection | Slug | Beschreibung |
|---|---|---|
| Users | users | Benutzer mit isSuperAdmin Flag |
| Tenants | tenants | Mandanten mit E-Mail-Konfiguration |
| Media | media | Medien mit 11 responsive Image Sizes |
| Pages | pages | Seiten mit Blocks |
| Posts | posts | Blog/News/Presse mit Kategorien |
| Categories | categories | Kategorien für Posts |
| Portfolios | portfolios | Portfolio-Galerien (Fotografie) |
| PortfolioCategories | portfolio-categories | Kategorien für Portfolios |
| Testimonials | testimonials | Kundenbewertungen |
| FAQs | faqs | Häufig gestellte Fragen (FAQ) |
| Team | team | Team-Mitglieder und Mitarbeiter |
| ServiceCategories | service-categories | Kategorien für Leistungen |
| Services | services | Leistungen und Dienstleistungen |
| NewsletterSubscribers | newsletter-subscribers | Newsletter mit Double Opt-In |
| SocialLinks | social-links | Social Media Links |
| Forms | forms | Formular-Builder |
| FormSubmissions | form-submissions | Formular-Einsendungen mit Status-Workflow |
| EmailLogs | email-logs | E-Mail-Protokollierung |
| AuditLogs | audit-logs | Security Audit Trail |
| CookieConfigurations | cookie-configurations | Cookie-Banner Konfiguration |
| CookieInventory | cookie-inventory | Cookie-Inventar |
| ConsentLogs | consent-logs | Consent-Protokollierung |
| Timelines | timelines | Chronologische Events (Geschichte, Meilensteine) |
| Workflows | workflows | Komplexe Prozesse mit Phasen und Schritten |
| Bookings | bookings | Fotografie-Buchungen (porwoll.de) |
| Certifications | certifications | Zertifizierungen (C2S) |
| Projects | projects | Game-Development-Projekte (gunshin.de) |
| Videos | videos | Video-Bibliothek mit YouTube/Vimeo/Uploads |
| VideoCategories | video-categories | Kategorien für Videos |
Timeline Collection
Dedizierte Collection für komplexe chronologische Darstellungen:
Timeline-Typen:
history- Unternehmensgeschichtemilestones- Projektmeilensteinereleases- Produkt-Releasescareer- Karriere/Lebenslaufevents- Ereignisseprocess- Prozess/Ablauf
Event-Features:
- Flexible Datumsformate (Jahr, Monat+Jahr, vollständig, Zeitraum, Freitext)
- Kategorien (Meilenstein, Gründung, Produkt, Team, Auszeichnung, etc.)
- Wichtigkeitsstufen (Highlight, Normal, Minor)
- Bilder und Galerien
- Links und Metadaten
- Rich-Text-Beschreibungen
Display-Optionen:
- Layouts: vertikal, alternierend, horizontal, kompakt
- Sortierung: aufsteigend/absteigend
- Gruppierung nach Jahr
- Verschiedene Marker-Stile
Prozess-spezifische Felder (type=process):
stepNumber- Explizite Schritt-Nummerierungduration- Dauer (z.B. "2-3 Tage")responsible- Verantwortliche Person/RolleactionRequired- Wer muss aktiv werden (customer, internal, both, automatic)deliverables- Ergebnisse/Dokumente des Schritts
API-Endpoint:
# Liste aller Timelines eines Tenants
curl "https://pl.porwoll.tech/api/timelines?tenant=1"
# Nach Typ filtern
curl "https://pl.porwoll.tech/api/timelines?tenant=1&type=history"
# Einzelne Timeline
curl "https://pl.porwoll.tech/api/timelines?tenant=1&slug=company-history"
# Mit Sprache
curl "https://pl.porwoll.tech/api/timelines?tenant=1&locale=en"
Workflows Collection
Komplexe Prozess- und Workflow-Darstellungen mit Phasen, Abhängigkeiten und Status-Tracking.
Workflow-Typen:
project- Projektabläufebusiness- Geschäftsprozesseapproval- Genehmigungs-Workflowsonboarding- Mitarbeiter-/Kundeneinführungsupport- Support/Service-Prozessedevelopment- Entwicklungsprozessemarketing- Marketing-Workflowsother- Sonstige
Eigenschaften:
estimatedDuration- Geschätzte Gesamtdauercomplexity- simple, medium, complex, very_complexisIterative- Kann wiederholt werdenallowParallelPhases- Parallele Phasen möglich
Display-Optionen:
- Layouts: vertical, horizontal, flowchart, kanban, gantt
- Farbschema: phase, status, priority, brand
- Optionale Anzeige: Nummern, Zeiten, Verantwortliche, Fortschritt
Phasen-Struktur:
Workflow
└── Phasen (Array)
├── name, description, icon, color
├── estimatedDuration, responsible
├── deliverables (Array)
└── Schritte (Array)
├── name, description, stepType, priority
├── estimatedDuration, responsible
├── dependencies (dependsOnSteps, canRunParallel, isBlocking)
├── conditions (für Entscheidungen)
├── checklist (Array)
├── resources (Dokumente, Links, Tools)
└── outputs (Ergebnisse)
API-Endpoint:
# Liste aller Workflows eines Tenants
curl "https://pl.porwoll.tech/api/workflows?tenant=1"
# Nach Typ filtern
curl "https://pl.porwoll.tech/api/workflows?tenant=1&type=project"
# Nach Komplexität filtern
curl "https://pl.porwoll.tech/api/workflows?tenant=1&complexity=medium"
# Einzelner Workflow
curl "https://pl.porwoll.tech/api/workflows?tenant=1&slug=web-project"
# Mit Sprache
curl "https://pl.porwoll.tech/api/workflows?tenant=1&locale=en"
Validierung:
tenant- Pflichtparameter (Tenant-Isolation)type- Validiert gegen erlaubte Typen (400 bei Fehler)complexity- Validiert gegen erlaubte Werte (400 bei Fehler)locale- de (default) oder en
FormSubmissions CRM-Workflow
Die FormSubmissions Collection wurde zu einem leichtgewichtigen CRM erweitert:
Status-Workflow:
- 🆕 Neu → 👀 Gelesen → 🔄 In Bearbeitung → ⏳ Warten → ✅ Erledigt → 🗑️ Archiviert
Features:
- Priorität (Hoch/Normal/Niedrig)
- Zuständigkeits-Zuweisung an User
- Interne Notizen mit Auto-Autor und Zeitstempel
- Antwort-Tracking (Methode, Zeitstempel, Zusammenfassung)
- Tags zur Kategorisierung
- Auto-Markierung als gelesen beim ersten Öffnen
Dateien:
src/collections/FormSubmissionsOverrides.ts- Feld-Definitionensrc/hooks/formSubmissionHooks.ts- Automatisierungen
Globals
| Global | Slug | Beschreibung |
|---|---|---|
| SiteSettings | site-settings | Allgemeine Website-Einstellungen |
| Navigation | navigation | Navigationsmenü |
| SEOSettings | seo-settings | SEO-Einstellungen |
| PrivacyPolicySettings | privacy-policy-settings | Datenschutz-Einstellungen |
Test Suite
# Alle Tests ausführen
pnpm test
# Security Tests
pnpm test:security
# Coverage Report
pnpm test:coverage
Test Coverage Thresholds:
- Lines: 35%
- Functions: 50%
- Branches: 65%
CI/CD Pipeline
GitHub Actions Workflows in .github/workflows/:
ci.yml (Main CI Pipeline)
Läuft bei Push/PR auf main und develop:
| Job | Beschreibung |
|---|---|
| lint | ESLint + Prettier Check |
| typecheck | TypeScript Compiler (tsc --noEmit) |
| test | Unit & Integration Tests (Vitest) |
| build | Next.js Production Build |
| e2e | Playwright E2E Tests |
Lokale Ausführung:
pnpm lint # ESLint
pnpm typecheck # TypeScript Check
pnpm format:check # Prettier Check
pnpm format # Prettier Auto-Fix
pnpm test # Alle Tests
pnpm build # Production Build
security.yml (Security Scanning)
- Gitleaks: Secret Scanning
- pnpm audit: Dependency Vulnerabilities
- CodeQL: Static Analysis (SAST)
- Security Tests: Unit & Integration Tests für Security-Module
deploy-staging.yml (Staging Deployment)
Automatisches Deployment auf Staging-Server bei Push auf develop:
| Trigger | Aktion |
|---|---|
Push auf develop |
Automatisches Deployment |
workflow_dispatch |
Manuelles Deployment (optional: skip_tests) |
Deployment-Ziel:
- URL: https://pl.porwoll.tech
- Server: 37.24.237.181 (sv-payload)
Ablauf:
- Pre-deployment Checks (Lint, Tests)
- SSH-Verbindung zum Staging-Server
- Git Pull + Dependencies installieren
- Migrations ausführen
- Build + PM2 Restart
- Health Check
Manuelles Staging-Deployment:
# Auf dem Staging-Server (pl.porwoll.tech)
./scripts/deploy-staging.sh
# Mit Optionen
./scripts/deploy-staging.sh --skip-build # Nur Code-Update
./scripts/deploy-staging.sh --skip-migrations # Ohne Migrationen
GitHub Secret erforderlich:
STAGING_SSH_KEY- SSH Private Key fürpayload@37.24.237.181
Dokumentation
Hauptdokumentation
CLAUDE.md- Diese Datei (Projekt-Übersicht für AI-Assistenten)docs/INFRASTRUCTURE.md- Infrastruktur-Übersicht (Dev + Prod)docs/DEPLOYMENT.md- Deployment-Prozess & Checklistendocs/PROJECT_STATUS.md- Aktueller Projektstatus & Roadmapdocs/STAGING-DEPLOYMENT.md- Staging Deployment Workflow
Anleitungen
docs/anleitungen/TODO.md- Task-Liste & Changelogdocs/anleitungen/SECURITY.md- Sicherheitsrichtliniendocs/anleitungen/FRONTEND.md- Frontend-Entwicklung (sv-frontend)docs/anleitungen/API_ANLEITUNG.md- API-Dokumentationdocs/anleitungen/framework-monitoring.md- Framework-Updates beobachten
Scripts & Backup
scripts/backup/README.md- Backup-System Dokumentation
Letzte Aktualisierung: 27.12.2025