cms.c2sgmbh/CLAUDE.md
Martin Porwoll c33372cc70 fix(community): use UnifiedSyncService for manual sync + document Meta integration
The manual sync button in Community Inbox was only syncing YouTube comments
via the legacy syncAllComments service. Now uses UnifiedSyncService to sync
all platforms (YouTube, Facebook, Instagram). Also adds comprehensive
Community Management documentation to CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 23:40:26 +00:00

53 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.76.1
  • Framework: Next.js 16.2.0-canary.41
  • 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 ~/.pgpass und 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

# Meta (Facebook + Instagram) OAuth
META_APP_ID=your-app-id                   # Facebook App ID
META_APP_SECRET=your-app-secret           # Facebook App Secret
META_REDIRECT_URI=https://your-domain/api/auth/meta/callback

# YouTube OAuth (existing)
GOOGLE_CLIENT_ID=your-client-id           # Google Cloud Console
GOOGLE_CLIENT_SECRET=your-client-secret
YOUTUBE_REDIRECT_URI=https://your-domain/api/youtube/callback

# Cron Jobs
CRON_SECRET=your-64-char-hex              # Auth für Cron-Endpoints

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

# 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 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

# Backup
/home/payload/backups/postgres/backup-db.sh --verbose  # Manuelles Backup
scripts/backup/setup-backup.sh                          # Backup-System einrichten

Git Branching Workflow

Wichtig: Immer zuerst auf develop entwickeln, nach Freigabe mit main mergen.

develop  ──────●────●────●────────●──────  (Entwicklung)
                \              /
main     ────────●────────────●──────────  (Production)

Branches

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

Entwicklungs-Workflow

  1. Auf develop arbeiten:

    git checkout develop
    git pull origin develop
    
  2. Änderungen committen:

    git add .
    git commit -m "feat/fix/docs: beschreibung"
    git push origin develop
    
  3. Nach Freigabe: develop → main mergen:

    git checkout main
    git pull origin main
    git merge develop
    git push origin main
    
  4. develop aktuell halten (falls main Hotfixes hat):

    git checkout develop
    git merge main
    git push origin develop
    

Commit-Konventionen

Prefix Verwendung
feat: Neue Features
fix: Bug-Fixes
docs: Dokumentation
refactor: Code-Refactoring
test: Tests
chore: Maintenance

Workflow nach Code-Änderungen

  1. Code ändern (auf develop Branch)
  2. pnpm build
  3. pm2 restart payload
  4. Testen unter https://pl.porwoll.tech/admin

Bei Collection/Global-Änderungen (zusätzlich)

  1. pnpm payload migrate:create - Migration erstellen
  2. git add src/migrations/ - Migration committen
  3. Auf PROD nach Deployment: ./scripts/sync-schema.sh wird automatisch ausgeführt

⚠️ KRITISCH: Neue Collections hinzufügen

Beim Hinzufügen einer neuen Collection müssen System-Tabellen manuell aktualisiert werden!

Problem: Payload CMS verwendet payload_locked_documents_rels um Document-Locks über alle Collections zu tracken. Diese Tabelle benötigt eine {collection}_id Spalte für JEDE Collection. Ohne diese Spalte tritt nach dem Login ein RSC-Fehler auf:

Error: An error occurred in the Server Components render
caused by: column payload_locked_documents_rels.{collection}_id does not exist

Lösung: Die Migration für eine neue Collection MUSS folgendes enthalten:

-- 1. Collection-Tabellen erstellen (normal)
CREATE TABLE IF NOT EXISTS "{collection}" (...);
CREATE TABLE IF NOT EXISTS "{collection}_rels" (...);  -- falls benötigt
CREATE TABLE IF NOT EXISTS "{collection}_locales" (...);  -- falls lokalisiert

-- 2. KRITISCH: System-Tabelle aktualisieren!
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-Migration: src/migrations/20260109_020000_add_blogwoman_collections.ts

Array-Felder: Collections mit Array-Feldern (z.B. hours_structured in Locations) benötigen zusätzliche Tabellen:

CREATE TABLE IF NOT EXISTS "{collection}_{array_field}" (
  "id" serial PRIMARY KEY NOT NULL,
  "_order" integer NOT NULL,
  "_parent_id" integer NOT NULL REFERENCES {collection}(id) ON DELETE CASCADE,
  -- Array-Feld-Spalten hier
);

Bekannte Besonderheiten

  • ES Modules: package.json hat "type": "module", daher PM2 Config als .cjs
  • Plugin ImportMap: Nach Plugin-Änderungen pnpm payload generate:importmap ausführen
  • User-Tenant-Zuweisung: Neue User müssen manuell Tenants zugewiesen bekommen
  • Admin Login: Custom Route mit Audit-Logging, unterstützt JSON und _payload FormData
  • Queue Worker: Benötigt tsx als explizite devDependency (für TypeScript-Ausführung via PM2)

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/translations für DE/EN
  • Content: Localization mit Fallback auf Deutsch
  • Datenbank: _locales Tabellen für lokalisierte Felder
  • API: ?locale=de oder ?locale=en Parameter
  • Frontend: Routing über /[locale]/...

URLs

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-For und X-Real-IP Header 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/Minute
  • auth: 5 Requests/15 Minuten (Login)
  • email: 10 Requests/Minute
  • search: 30 Requests/Minute
  • form: 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:

  1. User meldet sich an → Status: pending, Token wird generiert
  2. Double Opt-In E-Mail wird automatisch gesendet
  3. User klickt Bestätigungs-Link → Status: confirmed
  4. Willkommens-E-Mail wird gesendet
  5. 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 Konfiguration
  • src/lib/retention/cleanup-service.ts - Lösch-Logik
  • src/lib/queue/jobs/retention-job.ts - Job-Definition
  • src/lib/queue/workers/retention-worker.ts - Worker
  • src/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 (43 Blocks)

Core Blocks

Block Slug Beschreibung
HeroBlock hero-block Einzelner Hero mit Bild, Headline, CTA
HeroSliderBlock hero-slider-block Hero-Slider mit mehreren Slides
ImageSliderBlock image-slider-block Bildergalerie/Karussell
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

Content Blocks

Block Slug Beschreibung
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

Blogging Blocks

Block Slug Beschreibung
AuthorBioBlock author-bio-block Autoren-Biografie
RelatedPostsBlock related-posts-block Verwandte Beiträge
ShareButtonsBlock share-buttons-block Social Share Buttons
TableOfContentsBlock table-of-contents-block Inhaltsverzeichnis

Team Blocks

Block Slug Beschreibung
TeamFilterBlock team-filter-block Team mit Filter-Funktion
OrgChartBlock org-chart-block Organigramm

Feature Blocks

Block Slug Beschreibung
LocationsBlock locations-block Standorte/Filialen
LogoGridBlock logo-grid-block Partner/Kunden-Logos
StatsBlock stats-block Statistiken/Zahlen
JobsBlock jobs-block Stellenangebote
DownloadsBlock downloads-block Download-Bereich
MapBlock map-block Karten-Einbindung

Interactive Blocks

Block Slug Beschreibung
EventsBlock events-block Veranstaltungen
PricingBlock pricing-block Preistabellen
TabsBlock tabs-block Tab-Navigation
AccordionBlock accordion-block Akkordeon/Aufklappbar
ComparisonBlock comparison-block Vergleichstabelle

Tenant-specific Blocks

Block Slug Beschreibung
BeforeAfterBlock before-after-block Vorher/Nachher Bildvergleich (porwoll.de)

BlogWoman Blocks

Block Slug Beschreibung
FavoritesBlock favorites-block Affiliate-Produkte Grid/Liste/Karussell
SeriesBlock series-block YouTube-Serien Übersicht
SeriesDetailBlock series-detail-block Serien-Einzelseite mit Hero
VideoEmbedBlock video-embed-block YouTube/Vimeo Embed mit Privacy Mode
FeaturedContentBlock featured-content-block Kuratierte Mixed-Content Sammlung
ScriptSectionBlock script-section-block YouTube-Script Abschnitte

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 (55+ Collections)

Core Collections

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

Content Collections

Collection Slug Beschreibung
Posts posts Blog/News/Presse mit Kategorien
Categories categories Kategorien für Posts
Tags tags Tags für Posts (Blogging)
Authors authors Autoren für Posts
Testimonials testimonials Kundenbewertungen
FAQs faqs Häufig gestellte Fragen (FAQ)
SocialLinks social-links Social Media Links

Team & Services

Collection Slug Beschreibung
Team team Team-Mitglieder und Mitarbeiter
ServiceCategories service-categories Kategorien für Leistungen
Services services Leistungen und Dienstleistungen
Jobs jobs Stellenangebote

Portfolio & Media

Collection Slug Beschreibung
Portfolios portfolios Portfolio-Galerien (Fotografie)
PortfolioCategories portfolio-categories Kategorien für Portfolios
Videos videos Video-Bibliothek mit YouTube/Vimeo/Uploads
VideoCategories video-categories Kategorien für Videos

Products (E-Commerce)

Collection Slug Beschreibung
Products products Produkte
ProductCategories product-categories Produkt-Kategorien

Feature Collections

Collection Slug Beschreibung
Locations locations Standorte/Filialen
Partners partners Partner/Kunden
Downloads downloads Download-Dateien
Events events Veranstaltungen
Timelines timelines Chronologische Events (Geschichte, Meilensteine)
Workflows workflows Komplexe Prozesse mit Phasen und Schritten

Formulare & Newsletter

Collection Slug Beschreibung
Forms forms Formular-Builder (Plugin)
FormSubmissions form-submissions Formular-Einsendungen mit CRM-Workflow
NewsletterSubscribers newsletter-subscribers Newsletter mit Double Opt-In
Collection Slug Beschreibung
CookieConfigurations cookie-configurations Cookie-Banner Konfiguration
CookieInventory cookie-inventory Cookie-Inventar
ConsentLogs consent-logs Consent-Protokollierung (WORM)
PrivacyPolicySettings privacy-policy-settings Datenschutz-Einstellungen

Tenant-specific Collections

Collection Slug Beschreibung
Bookings bookings Fotografie-Buchungen (porwoll.de)
Certifications certifications Zertifizierungen (C2S)
Projects projects Game-Development-Projekte (gunshin.de)

BlogWoman Collections

Collection Slug Beschreibung
Favorites favorites Affiliate-Produkte mit Kategorien und Badges
Series series YouTube-Serien mit Branding (Logo, Farben)

YouTube & Community Management

Collection Slug Beschreibung
YouTubeChannels youtube-channels Multi-Kanal-Verwaltung mit OAuth
YouTubeContent youtube-content Videos + Shorts mit Kommentaren
YtSeries yt-series Serien mit Branding (Logo, Farben, Playlist)
YtNotifications yt-notifications Handlungsbedarf-System
YtScriptTemplates yt-script-templates Skript-Vorlagen für Videos
YtBatches yt-batches Video-Batches für Produktions-Planung
YtChecklistTemplates yt-checklist-templates Checklisten-Vorlagen für Video-Produktion
YtMonthlyGoals yt-monthly-goals Monatliche YouTube-Ziele
YtTasks yt-tasks YouTube-Aufgaben-Verwaltung
SocialAccounts social-accounts Meta/Social OAuth-Verbindungen
SocialPlatforms social-platforms Plattform-Konfigurationen
CommunityInteractions community-interactions Kommentare & Interaktionen
CommunityRules community-rules Automatische Antwort-Regeln
CommunityTemplates community-templates Antwort-Vorlagen
ReportSchedules report-schedules Geplante Community-Reports

System Collections

Collection Slug Beschreibung
EmailLogs email-logs E-Mail-Protokollierung
AuditLogs audit-logs Security Audit Trail
SiteSettings site-settings Website-Einstellungen (pro Tenant)
Navigations navigations Navigationsmenüs (pro Tenant)
Redirects redirects URL-Weiterleitungen (Plugin)

Timeline Collection

Dedizierte Collection für komplexe chronologische Darstellungen:

Timeline-Typen:

  • history - Unternehmensgeschichte
  • milestones - Projektmeilensteine
  • releases - Produkt-Releases
  • career - Karriere/Lebenslauf
  • events - Ereignisse
  • process - 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-Nummerierung
  • duration - Dauer (z.B. "2-3 Tage")
  • responsible - Verantwortliche Person/Rolle
  • actionRequired - 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äufe
  • business - Geschäftsprozesse
  • approval - Genehmigungs-Workflows
  • onboarding - Mitarbeiter-/Kundeneinführung
  • support - Support/Service-Prozesse
  • development - Entwicklungsprozesse
  • marketing - Marketing-Workflows
  • other - Sonstige

Eigenschaften:

  • estimatedDuration - Geschätzte Gesamtdauer
  • complexity - simple, medium, complex, very_complex
  • isIterative - Kann wiederholt werden
  • allowParallelPhases - 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-Definitionen
  • src/hooks/formSubmissionHooks.ts - Automatisierungen

Community Management System

Plattformübergreifendes Community Management für YouTube, Facebook und Instagram mit einer einheitlichen Inbox, AI-Analyse und automatischer Moderation.

Architektur

                  ┌─────────────────────────────────────────┐
                  │           Admin UI                       │
                  │  ┌─────────────┐  ┌──────────────────┐  │
                  │  │ Community   │  │ Community        │  │
                  │  │ Inbox       │  │ Analytics        │  │
                  │  └──────┬──────┘  └────────┬─────────┘  │
                  └─────────┼──────────────────┼────────────┘
                            │                  │
                  ┌─────────▼──────────────────▼────────────┐
                  │        /api/community/*                  │
                  │  sync, sync-status, stats, reply,        │
                  │  generate-reply, export, stream           │
                  └─────────────────┬───────────────────────┘
                                    │
                  ┌─────────────────▼───────────────────────┐
                  │         UnifiedSyncService               │
                  │  ┌──────────┬───────────┬────────────┐  │
                  │  │ YouTube  │ Facebook  │ Instagram  │  │
                  │  │ Sync     │ Sync      │ Sync       │  │
                  │  └────┬─────┴─────┬─────┴──────┬─────┘  │
                  └───────┼───────────┼────────────┼────────┘
                          │           │            │
                  ┌───────▼───────────▼────────────▼────────┐
                  │       CommunityInteractions              │
                  │  (einheitliche Collection für alle        │
                  │   Plattform-Kommentare)                  │
                  └─────────────────────────────────────────┘

Admin UI

Community Inbox (/admin/community/inbox):

  • Einheitliche Ansicht aller Kommentare (YouTube + Facebook + Instagram)
  • Filter nach Plattform, Status, Priorität, Sentiment, Flags
  • AI-generierte Antwort-Vorschläge (Claude)
  • Direkte Antwort-Funktion
  • Echtzeit-Updates via SSE (/api/community/stream)
  • Export als CSV/JSON
  • Sync-Button (triggert alle Plattformen)

Community Analytics (/admin/community/analytics):

  • KPI-Cards (Gesamt, Heute, Unbeantwortet)
  • Sentiment-Trend-Chart
  • Topic-Cloud
  • Response-Metriken
  • Kanal-Vergleich
  • Top-Content nach Engagement

Meta (Facebook + Instagram) Integration

OAuth-Flow:

1. Admin öffnet /api/auth/meta?socialAccountId=X&accountType=both
2. Redirect zu Facebook OAuth (12 Scopes)
3. Callback: Short-Lived → Long-Lived Token (60 Tage)
4. Facebook Pages + Instagram Business Accounts werden geladen
5. Token + Account-Daten in SocialAccounts gespeichert

Unterstützte Scopes:

  • Facebook: pages_show_list, pages_read_engagement, pages_manage_posts, pages_read_user_content, pages_manage_engagement, pages_messaging
  • Instagram: instagram_basic, instagram_manage_comments, instagram_manage_messages, instagram_content_publish

API-Clients:

Client Datei Funktionen
MetaBaseClient src/lib/integrations/meta/MetaBaseClient.ts HTTP-Basis, Pagination, Retry, Token-Management
FacebookClient src/lib/integrations/meta/FacebookClient.ts Posts, Kommentare, Messenger, Insights
InstagramClient src/lib/integrations/meta/InstagramClient.ts Media, Kommentare, Mentions, DMs, Insights

Sync-Services:

Service Datei Beschreibung
FacebookSyncService src/lib/integrations/meta/FacebookSyncService.ts Synct Page-Kommentare + Replies
InstagramSyncService src/lib/integrations/meta/InstagramSyncService.ts Synct Media-Kommentare + Mentions
UnifiedSyncService src/lib/jobs/UnifiedSyncService.ts Orchestriert YouTube + Facebook + Instagram

Sync-Ablauf (pro Account):

  1. SocialAccount laden + Platform verifizieren
  2. Token validieren
  3. Posts/Media abrufen (mit Datum-Filter)
  4. Kommentare pro Post synchronisieren
  5. AI-Analyse via Claude (Sentiment, Topics, Flags)
  6. In CommunityInteractions speichern (dedupliziert via externalId)
  7. Account-Stats aktualisieren

Sync-Trigger

Trigger Endpoint Plattformen
Cron (alle 15 Min) /api/cron/community-sync Alle aktiven Accounts
Manuell (Inbox-Button) /api/community/sync (POST) Alle (optional filterbar)
YouTube-spezifisch /api/cron/youtube-sync Nur YouTube

Community API-Endpoints

Endpoint Methode Beschreibung
/api/community/sync POST Manueller Sync (alle Plattformen)
/api/community/sync GET Sync-Status
/api/community/sync-status GET Detaillierter Status mit Account-Stats
/api/community/stats GET Interaktions-Statistiken
/api/community/reply POST Antwort senden
/api/community/generate-reply POST AI-Antwort generieren (Claude)
/api/community/export GET Daten-Export (CSV/JSON)
/api/community/stream GET SSE-Stream für Echtzeit-Updates
/api/community/sync-comments POST Kommentar-Sync (Legacy)
/api/community/analytics/* GET Analytics-Daten (6 Endpoints)
/api/auth/meta GET Meta OAuth starten
/api/auth/meta/callback GET Meta OAuth Callback

CommunityRules (Automatisierung)

Regel-basierte Automatisierung für eingehende Kommentare:

Trigger-Typen: keyword, sentiment, question_detected, medical_detected, influencer (>10k Follower), all_new, contains_link, contains_email

Aktionen: set_priority, assign_to, set_flag, suggest_template, send_notification, flag_medical, escalate, mark_spam, set_deadline

Wichtige Dateien

src/lib/integrations/meta/
├── MetaBaseClient.ts          # HTTP-Basis mit Retry + Pagination
├── FacebookClient.ts          # Facebook Graph API Client
├── InstagramClient.ts         # Instagram Graph API Client
├── FacebookSyncService.ts     # Facebook Kommentar-Sync
├── InstagramSyncService.ts    # Instagram Kommentar-Sync
├── oauth.ts                   # Meta OAuth 2.0 Flow
└── index.ts                   # Re-Exports

src/lib/jobs/
├── UnifiedSyncService.ts      # Orchestriert alle Plattform-Syncs
├── syncAllComments.ts         # YouTube-spezifischer Sync (Legacy)
└── JobLogger.ts               # Strukturiertes Logging

src/app/(payload)/api/
├── auth/meta/                 # OAuth Start + Callback
├── community/                 # 14 Community-Endpoints
└── cron/community-sync/       # Cron-Trigger für Unified Sync

Globals

Hinweis: SiteSettings, Navigations und PrivacyPolicySettings wurden zu tenant-spezifischen Collections umgewandelt (siehe Collections Übersicht).

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

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%

Scheduled Cron Jobs (Vercel)

Automatische Hintergrund-Jobs via Vercel Cron (vercel.json):

Endpoint Schedule Beschreibung
/api/cron/community-sync */15 * * * * (alle 15 Min) Synchronisiert Kommentare von YouTube, Facebook, Instagram
/api/cron/token-refresh 0 6,18 * * * (6:00 + 18:00 UTC) Erneuert ablaufende OAuth-Tokens automatisch
/api/cron/send-reports 0 * * * * (stündlich) Versendet fällige Community-Reports per E-Mail

Authentifizierung: Alle Cron-Endpoints erfordern Authorization: Bearer $CRON_SECRET Header.

Manueller Trigger:

# Community Sync manuell auslösen
curl -X POST "https://your-domain/api/cron/community-sync" \
  -H "Authorization: Bearer $CRON_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"platforms": ["youtube", "facebook"]}'

# Token Refresh manuell auslösen (Dry-Run)
curl "https://your-domain/api/cron/token-refresh?dryRun=true" \
  -H "Authorization: Bearer $CRON_SECRET"

# Token Refresh Status prüfen
curl -I "https://your-domain/api/cron/token-refresh" \
  -H "Authorization: Bearer $CRON_SECRET"

Monitoring:

  • HEAD-Requests geben Status-Header zurück (X-Sync-Running, X-Last-Run, etc.)
  • Bei laufendem Job: HTTP 423 (Locked)
  • Fehlerhafte Tokens werden als Benachrichtigungen in yt-notifications gespeichert

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 (flat config, 0 errors)
typecheck TypeScript Compiler (tsc --noEmit, 4GB heap)
test Unit & Integration Tests (Vitest)
build Next.js Production Build
e2e Playwright E2E Tests

Lokale Ausführung:

pnpm lint              # ESLint (0 errors, warnings only)
pnpm typecheck         # TypeScript Check (4GB heap for 55+ collections)
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:

Ablauf:

  1. Pre-deployment Checks (Lint, Tests)
  2. SSH-Verbindung zum Staging-Server
  3. Git Pull + Dependencies installieren
  4. Migrations ausführen
  5. Build + PM2 Restart
  6. 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ür payload@37.24.237.181

deploy-production.yml (Production Deployment)

Manuelles Deployment auf Production-Server (Hetzner 3) bei workflow_dispatch:

Feature Beschreibung
Pre-flight Checks Überprüft Branch-Status und Migrationen
Pre-deployment Tests Optional: ESLint, Unit Tests, Build
Database Backup Automatisches Backup vor Deployment
Health Check Admin Panel (200) + API (< 500)
Post-deployment Verification Admin + API Health Check
Auto-Rollback Bei fehlgeschlagenem Health Check

Manuelles Production-Deployment:

# Via GitHub Actions (empfohlen)
gh workflow run deploy-production.yml

# Auf dem Production-Server (Hetzner 3)
ssh payload@162.55.85.18
./scripts/deploy-production.sh

# Mit Optionen
./scripts/deploy-production.sh -y              # Ohne Bestätigung
./scripts/deploy-production.sh --skip-backup   # Ohne Backup
./scripts/deploy-production.sh --rollback      # Rollback zur vorherigen Version
./scripts/deploy-production.sh --dry-run       # Zeigt was passieren würde

GitHub Secrets erforderlich:

  • STAGING_SSH_KEY - SSH Private Key für sv-payload
  • PRODUCTION_SSH_KEY - SSH Private Key für Hetzner 3

Dokumentation

Hauptdokumentation

  • CLAUDE.md - Diese Datei (Projekt-Übersicht für AI-Assistenten)
  • docs/INFRASTRUCTURE.md - Infrastruktur-Übersicht (Dev + Prod)
  • docs/DEPLOYMENT.md - Deployment-Prozess & Checklisten
  • docs/DEPLOYMENT_STRATEGY.md - Vollständige Deployment-Strategie (Dev → Prod)
  • docs/PROJECT_STATUS.md - Aktueller Projektstatus & Roadmap
  • docs/STAGING-DEPLOYMENT.md - Staging Deployment Workflow

Anleitungen

  • docs/anleitungen/TODO.md - Task-Liste & Changelog
  • docs/anleitungen/SECURITY.md - Sicherheitsrichtlinien
  • docs/anleitungen/FRONTEND.md - Frontend-Entwicklung (sv-frontend)
  • docs/anleitungen/API_ANLEITUNG.md - API-Dokumentation
  • docs/anleitungen/framework-monitoring.md - Framework-Updates beobachten

Scripts & Backup

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

Letzte Aktualisierung: 13.02.2026 (Community Management Doku, Sync-Endpoint Fix: UnifiedSyncService statt YouTube-only)