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

1492 lines
53 KiB
Markdown

# 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!
```env
# 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:
```bash
# 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:
```bash
# 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
```bash
# 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:**
```bash
git checkout develop
git pull origin develop
```
2. **Änderungen committen:**
```bash
git add .
git commit -m "feat/fix/docs: beschreibung"
git push origin develop
```
3. **Nach Freigabe: develop → main mergen:**
```bash
git checkout main
git pull origin main
git merge develop
git push origin main
```
4. **develop aktuell halten (falls main Hotfixes hat):**
```bash
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:
```sql
-- 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:
```sql
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:
```bash
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
- **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:
```env
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`:**
```bash
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:**
```bash
# 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:
```bash
# 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:
```typescript
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`:**
```bash
# 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:**
```typescript
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`).
```bash
# 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:**
```bash
curl https://pl.porwoll.tech/api/retention \
-H "Cookie: payload-token=..."
```
**GET - Job-Status abfragen:**
```bash
curl "https://pl.porwoll.tech/api/retention?jobId=abc123" \
-H "Cookie: payload-token=..."
```
**POST - Manuellen Job auslösen:**
```bash
# 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:
```typescript
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:**
```bash
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:**
```bash
/home/payload/backups/postgres/backup-db.sh --verbose
```
**Restore aus Backup:**
```bash
# 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
```bash
# 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` - 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:**
```bash
# 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:**
```bash
# 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
```bash
# 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:**
```bash
# 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:**
```bash
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:**
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:**
```bash
# 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:**
```bash
# 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)*