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>
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
~/.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
# 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
-
Auf develop arbeiten:
git checkout develop git pull origin develop -
Änderungen committen:
git add . git commit -m "feat/fix/docs: beschreibung" git push origin develop -
Nach Freigabe: develop → main mergen:
git checkout main git pull origin main git merge develop git push origin main -
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
- Code ändern (auf
developBranch) pnpm buildpm2 restart payload- Testen unter https://pl.porwoll.tech/admin
Bei Collection/Global-Änderungen (zusätzlich)
pnpm payload migrate:create- Migration erstellengit add src/migrations/- Migration committen- Auf PROD nach Deployment:
./scripts/sync-schema.shwird 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: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 - Queue Worker: Benötigt
tsxals 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/translationsfür DE/EN - Content: Localization mit Fallback auf Deutsch
- Datenbank:
_localesTabellen für lokalisierte Felder - API:
?locale=deoder?locale=enParameter - Frontend: Routing über
/[locale]/...
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)
- YouTube Analytics: https://pl.porwoll.tech/admin/youtube-analytics (Admin View, Auth erforderlich)
- Community Inbox: https://pl.porwoll.tech/admin/community/inbox (Admin View, Auth erforderlich)
- Community Analytics: https://pl.porwoll.tech/admin/community/analytics (Admin View, Auth erforderlich)
- Community Sync: https://pl.porwoll.tech/api/community/sync (POST: Trigger, GET: Status)
- Community Stats: https://pl.porwoll.tech/api/community/stats (GET, Auth erforderlich)
- Meta OAuth: https://pl.porwoll.tech/api/auth/meta (GET, startet OAuth-Flow)
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 (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 |
Consent & Privacy (DSGVO)
| 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- 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
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):
- SocialAccount laden + Platform verifizieren
- Token validieren
- Posts/Media abrufen (mit Datum-Filter)
- Kommentare pro Post synchronisieren
- AI-Analyse via Claude (Sentiment, Topics, Flags)
- In CommunityInteractions speichern (dedupliziert via
externalId) - 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-notificationsgespeichert
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:
- 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
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-payloadPRODUCTION_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 & Checklistendocs/DEPLOYMENT_STRATEGY.md- Vollständige Deployment-Strategie (Dev → Prod)docs/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: 13.02.2026 (Community Management Doku, Sync-Endpoint Fix: UnifiedSyncService statt YouTube-only)