diff --git a/CLAUDE.md b/CLAUDE.md index 05a55a0..1dfd553 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,236 +17,74 @@ Multi-Tenant CMS für 3 aktive Websites unter einer Payload CMS 3.x Instanz: - **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) +- **Reverse Proxy:** Caddy 2.9.x (Dev) / Nginx (Prod) - **Process Manager:** PM2 - **Package Manager:** pnpm -- **Cache:** Redis 7.x (optional, mit In-Memory-Fallback) +- **Cache:** Redis 7.x (optional, 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) +**Development:** `Internet → Cloudflare → Caddy (sv-caddy) → sv-payload:3000 → PgBouncer:6432 → PostgreSQL (sv-postgres:5432)` -``` -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) -``` +| Hostname | IP | Service | +|----------|-----|---------| +| sv-caddy | 10.10.181.99 | Caddy Reverse Proxy | +| sv-payload | 10.10.181.100 | Payload CMS + Redis | +| sv-postgres | 10.10.181.101 | PostgreSQL 17 | +| sv-frontend | 10.10.181.104 | Multi-Frontend Dev (9 Projekte) | -| 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):** `cms.c2sgmbh.de:3001` (Payload), `analytics.c2sgmbh.de:3000` (Umami) -### Production (Hetzner 3) +→ Details: `docs/INFRASTRUCTURE.md` -| 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 | +## Multi-Tenant -## Wichtige Pfade +Verwendet `@payloadcms/plugin-multi-tenant`. -``` -/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` +User-Tenant-Zuweisung: Tabelle `users_tenants` + +## Wichtige Pfade + +``` +/home/payload/payload-cms/ +├── src/ +│ ├── payload.config.ts # Haupt-Konfiguration +│ ├── collections/ # Alle Collections (55+) +│ ├── app/(payload)/api/ # Custom API Routes +│ ├── lib/ +│ │ ├── email/ # E-Mail-System (tenant-spezifisch) +│ │ ├── security/ # Rate-Limiter, CSRF, IP-Allowlist +│ │ ├── queue/ # BullMQ Jobs & Workers +│ │ ├── pdf/ # PDF-Generierung +│ │ ├── retention/ # Data Retention (DSGVO) +│ │ ├── integrations/meta/ # Facebook + Instagram API +│ │ ├── jobs/ # UnifiedSyncService, JobLogger +│ │ ├── search.ts # Volltextsuche +│ │ └── redis.ts # Redis Cache Client +│ └── hooks/ # Collection Hooks +├── tests/ # Unit & Integration Tests +├── scripts/ +│ ├── db-direct.sh # Direkte DB-Verbindung (umgeht PgBouncer) +│ ├── sync-schema.sh # Schema-Sync nach Collection-Änderungen +│ └── backup/ # Backup-System (→ scripts/backup/README.md) +├── .env # Umgebungsvariablen +├── ecosystem.config.cjs # PM2 Config +└── .github/workflows/ # CI/CD Pipelines +``` ## Wichtige Befehle ```bash # Entwicklung pnpm dev - -# Production Build pnpm build # Migrationen @@ -261,116 +99,56 @@ pnpm payload migrate pnpm payload generate:importmap # PM2 -pm2 status -pm2 logs payload -pm2 logs queue-worker -pm2 restart payload -pm2 restart queue-worker +pm2 status | pm2 logs payload | pm2 restart payload +pm2 logs queue-worker | pm2 restart queue-worker # Tests -pnpm test # Alle Tests -pnpm test:security # Security Tests -pnpm test:access-control # Access Control Tests -pnpm test:coverage # Mit Coverage-Report +pnpm test # Alle Tests +pnpm test:security # Security Tests +pnpm test:access-control # Access Control Tests +pnpm test:coverage # Mit Coverage-Report + +# Lint & Format +pnpm lint # ESLint +pnpm typecheck # TypeScript Check (4GB heap) +pnpm format:check # Prettier Check +pnpm format # Prettier Auto-Fix + +# Direkte DB-Verbindung (umgeht PgBouncer für Migrationen) +./scripts/db-direct.sh migrate +./scripts/db-direct.sh psql # Backup -/home/payload/backups/postgres/backup-db.sh --verbose # Manuelles Backup -scripts/backup/setup-backup.sh # Backup-System einrichten +/home/payload/backups/postgres/backup-db.sh --verbose ``` -## Git Branching Workflow +## Git Workflow -**Wichtig:** Immer zuerst auf `develop` entwickeln, nach Freigabe mit `main` mergen. - -``` -develop ──────●────●────●────────●────── (Entwicklung) - \ / -main ────────●────────────●────────── (Production) -``` - -### Branches +**Regel:** Immer auf `develop` entwickeln, nach Freigabe nach `main` mergen. | Branch | Zweck | Deployment | |--------|-------|------------| | `develop` | Aktive Entwicklung | Staging (pl.porwoll.tech) | | `main` | Stabile Version | Production (cms.c2sgmbh.de) | -| `feature/*` | Feature-Branches | Lokal / PR nach develop | +| `feature/*` | Feature-Branches | PR nach develop | -### Entwicklungs-Workflow +**Commit-Konventionen:** `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:` -1. **Auf develop arbeiten:** - ```bash - git checkout develop - git pull origin develop - ``` +**Workflow nach Code-Änderungen:** +1. Code ändern (auf `develop`) +2. `pnpm build` → `pm2 restart payload` +3. Bei Collection-Änderungen zusätzlich: `pnpm payload migrate:create` -2. **Änderungen committen:** - ```bash - git add . - git commit -m "feat/fix/docs: beschreibung" - git push origin develop - ``` +## KRITISCH: Neue Collections hinzufügen -3. **Nach Freigabe: develop → main mergen:** - ```bash - git checkout main - git pull origin main - git merge develop - git push origin main - ``` +Payload CMS verwendet `payload_locked_documents_rels` für Document-Locks. Diese Tabelle benötigt eine `{collection}_id` Spalte für JEDE Collection. Ohne → RSC-Fehler nach Login: -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 +column payload_locked_documents_rels.{collection}_id does not exist ``` -**Lösung:** Die Migration für eine neue Collection MUSS folgendes enthalten: - +**Migration MUSS 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; @@ -379,1115 +157,245 @@ 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` +Beispiel: `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 -); -``` +**Array-Felder:** Collections mit Arrays benötigen zusätzliche `{collection}_{array_field}` Tabellen. -## Bekannte Besonderheiten +## Bekannte Besonderheiten & Gotchas -- **ES Modules:** package.json hat `"type": "module"`, daher PM2 Config als `.cjs` -- **Plugin ImportMap:** Nach Plugin-Änderungen `pnpm payload generate:importmap` ausführen +- **ES Modules:** `"type": "module"` in package.json → PM2 Config als `.cjs` +- **Plugin ImportMap:** Nach Plugin-Änderungen `pnpm payload generate:importmap` - **User-Tenant-Zuweisung:** Neue User müssen manuell Tenants zugewiesen bekommen -- **Admin Login:** Custom Route mit Audit-Logging, unterstützt JSON und `_payload` FormData -- **Queue Worker:** Benötigt `tsx` als explizite devDependency (für TypeScript-Ausführung via PM2) +- **Admin Login:** Custom Route mit Audit-Logging (`src/app/(payload)/api/users/login/route.ts`) +- **Queue Worker:** Benötigt `tsx` als devDependency (TypeScript via PM2) +- **PgBouncer:** Transaction-Mode kann Migrationen stören → `./scripts/db-direct.sh` +- **TRUST_PROXY=true:** PFLICHT hinter Reverse-Proxy, sonst funktionieren Rate-Limiting und IP-Allowlists nicht +- **CSRF_SECRET:** PFLICHT in Production (oder PAYLOAD_SECRET) - Server startet nicht ohne ## 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: - +**Server hat 8GB RAM ohne Swap.** Bei laufendem VS Code kann Build OOM werden: ```bash -pm2 stop payload # Speicher freigeben +pm2 stop payload 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 -## 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]/...` +Deutsch (default) + Englisch. API: `?locale=de|en`. DB: `_locales` Tabellen. ## 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) -- **YouTube Thumbnail Bulk:** https://pl.porwoll.tech/api/youtube/thumbnails/bulk (POST, Super-Admin) - -## 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": "

Inhalt

", - "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= - -# Abmeldung (via Link aus E-Mail) -GET https://pl.porwoll.tech/api/newsletter/unsubscribe?token= -``` - -**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: '

Inhalt

', - 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": "

Test

Inhalt

", - "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('

Test

', { 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 -``` +| Bereich | URL | +|---------|-----| +| Admin Panel | https://pl.porwoll.tech/admin | +| API | https://pl.porwoll.tech/api | +| Swagger UI | https://pl.porwoll.tech/api/docs | +| Production | https://cms.c2sgmbh.de | + +→ Vollständige Endpoint-Liste: `docs/anleitungen/API_ANLEITUNG.md` + +## Security Kurzübersicht + +- **Rate-Limiting:** publicApi(60/min), auth(5/15min), email(10/min), search(30/min), form(5/10min) +- **CSRF:** Double Submit Cookie + Origin-Validierung, Token: `GET /api/csrf-token` +- **IP-Allowlist:** `SEND_EMAIL_ALLOWED_IPS`, `ADMIN_ALLOWED_IPS`, `BLOCKED_IPS` (IPs, CIDRs, Wildcards) +- **Data-Masking:** Automatisch in Logs (Passwörter, Tokens, API-Keys) + +→ Details: `docs/anleitungen/SECURITY.md` + +## Subsysteme (Kurzübersicht) + +| System | Schlüsseldateien | Details | +|--------|-----------------|---------| +| E-Mail (Multi-Tenant SMTP) | `src/lib/email/` | `docs/CLAUDE_REFERENCE.md` | +| Newsletter (Double Opt-In) | `src/lib/email/newsletter-service.ts` | `docs/CLAUDE_REFERENCE.md` | +| BullMQ Queue | `src/lib/queue/`, `ecosystem.config.cjs` | `docs/CLAUDE_REFERENCE.md` | +| Data Retention (DSGVO) | `src/lib/retention/` | `docs/CLAUDE_REFERENCE.md` | +| PDF-Generierung | `src/lib/pdf/`, API: `/api/generate-pdf` | `docs/CLAUDE_REFERENCE.md` | +| Redis Caching | `src/lib/redis.ts` | `docs/CLAUDE_REFERENCE.md` | +| Backup | `scripts/backup/` | `scripts/backup/README.md` | +| Community Management | `src/lib/integrations/meta/`, `src/lib/jobs/` | `docs/CLAUDE_REFERENCE.md` | +| FormSubmissions CRM | `src/collections/FormSubmissionsOverrides.ts` | `docs/CLAUDE_REFERENCE.md` | + +## Collections (55+) + +| Slug | Beschreibung | Gruppe | +|------|--------------|--------| +| users | Benutzer (isSuperAdmin) | Core | +| tenants | Mandanten + E-Mail-Config | Core | +| media | Medien (11 responsive Sizes) | Core | +| pages | Seiten mit Blocks | Core | +| posts | Blog/News/Presse | Content | +| categories | Post-Kategorien | Content | +| tags | Post-Tags | Content | +| authors | Post-Autoren | Content | +| testimonials | Kundenbewertungen | Content | +| faqs | FAQ | Content | +| social-links | Social Media Links | Content | +| team | Team-Mitglieder | Team | +| service-categories | Leistungs-Kategorien | Team | +| services | Leistungen | Team | +| jobs | Stellenangebote | Team | +| portfolios | Portfolio-Galerien | Portfolio | +| portfolio-categories | Portfolio-Kategorien | Portfolio | +| videos | Video-Bibliothek | Media | +| video-categories | Video-Kategorien | Media | +| products | Produkte | E-Commerce | +| product-categories | Produkt-Kategorien | E-Commerce | +| locations | Standorte/Filialen | Feature | +| partners | Partner/Kunden | Feature | +| downloads | Download-Dateien | Feature | +| events | Veranstaltungen | Feature | +| timelines | Chronologische Events | Feature | +| workflows | Prozesse mit Phasen | Feature | +| forms | Formular-Builder (Plugin) | Formulare | +| form-submissions | CRM-Workflow | Formulare | +| newsletter-subscribers | Double Opt-In | Newsletter | +| cookie-configurations | Cookie-Banner | DSGVO | +| cookie-inventory | Cookie-Inventar | DSGVO | +| consent-logs | Consent-Protokoll (WORM) | DSGVO | +| privacy-policy-settings | Datenschutz | DSGVO | +| bookings | Fotografie-Buchungen (porwoll) | Tenant | +| certifications | Zertifizierungen (c2s) | Tenant | +| projects | Game-Dev-Projekte (gunshin) | Tenant | +| favorites | Affiliate-Produkte | BlogWoman | +| series | YouTube-Serien (Branding) | BlogWoman | +| youtube-channels | Multi-Kanal + OAuth | YouTube | +| youtube-content | Videos + Shorts | YouTube | +| yt-series | Serien + Playlist | YouTube | +| yt-notifications | Handlungsbedarf | YouTube | +| yt-script-templates | Skript-Vorlagen | YouTube | +| yt-batches | Produktions-Batches | YouTube | +| yt-checklist-templates | Checklisten | YouTube | +| yt-monthly-goals | Monatsziele | YouTube | +| yt-tasks | Aufgaben | YouTube | +| social-accounts | Meta OAuth | Community | +| social-platforms | Plattform-Config | Community | +| community-interactions | Kommentare (alle Plattformen) | Community | +| community-rules | Auto-Antwort-Regeln | Community | +| community-templates | Antwort-Vorlagen | Community | +| report-schedules | Geplante Reports | Community | +| email-logs | E-Mail-Protokoll | System | +| audit-logs | Security Audit Trail | System | +| site-settings | Website-Einstellungen (pro Tenant) | System | +| navigations | Navigationsmenüs (pro Tenant) | System | +| redirects | URL-Weiterleitungen (Plugin) | System | + +## Blocks (43) + +| Slug | Beschreibung | Gruppe | +|------|--------------|--------| +| hero-block | Hero mit Bild, Headline, CTA | Core | +| hero-slider-block | Hero-Slider (Slides, Animationen, Autoplay) | Core | +| image-slider-block | Bildergalerie/Karussell | Core | +| text-block | Rich-Text Inhalt | Core | +| image-text-block | Bild + Text nebeneinander | Core | +| card-grid-block | Karten-Raster | Core | +| quote-block | Zitat | Core | +| cta-block | Call-to-Action | Core | +| contact-form-block | Kontaktformular | Core | +| timeline-block | Timeline-Darstellung | Core | +| divider-block | Trennlinie | Core | +| video-block | Video einbetten | Core | +| posts-list-block | Beitrags-Liste | Content | +| testimonials-block | Kundenbewertungen | Content | +| newsletter-block | Newsletter-Anmeldung | Content | +| process-steps-block | Prozess-Schritte | Content | +| faq-block | FAQ-Akkordeon | Content | +| team-block | Team-Mitglieder | Content | +| services-block | Leistungen | Content | +| author-bio-block | Autoren-Biografie | Blogging | +| related-posts-block | Verwandte Beiträge | Blogging | +| share-buttons-block | Social Share Buttons | Blogging | +| table-of-contents-block | Inhaltsverzeichnis | Blogging | +| team-filter-block | Team mit Filter | Team | +| org-chart-block | Organigramm | Team | +| locations-block | Standorte/Filialen | Feature | +| logo-grid-block | Partner-Logos | Feature | +| stats-block | Statistiken/Zahlen | Feature | +| jobs-block | Stellenangebote | Feature | +| downloads-block | Download-Bereich | Feature | +| map-block | Karten-Einbindung | Feature | +| events-block | Veranstaltungen | Interactive | +| pricing-block | Preistabellen | Interactive | +| tabs-block | Tab-Navigation | Interactive | +| accordion-block | Akkordeon | Interactive | +| comparison-block | Vergleichstabelle | Interactive | +| before-after-block | Vorher/Nachher (porwoll) | Tenant | +| favorites-block | Affiliate-Produkte | BlogWoman | +| series-block | YouTube-Serien | BlogWoman | +| series-detail-block | Serien-Einzelseite | BlogWoman | +| video-embed-block | YouTube/Vimeo Embed | BlogWoman | +| featured-content-block | Kuratierte Inhalte | BlogWoman | +| script-section-block | Script-Abschnitte | BlogWoman | ## Globals -> **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 +> SiteSettings, Navigations, PrivacyPolicySettings sind tenant-spezifische Collections. -```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`): +## Cron Jobs | 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 | +| `/api/cron/community-sync` | alle 15 Min | Kommentar-Sync (YouTube, Facebook, Instagram) | +| `/api/cron/token-refresh` | 6:00 + 18:00 UTC | OAuth-Token-Erneuerung | +| `/api/cron/send-reports` | stündlich | Community-Reports per E-Mail | -**Authentifizierung:** -Alle Cron-Endpoints erfordern `Authorization: Bearer $CRON_SECRET` Header. +Auth: `Authorization: Bearer $CRON_SECRET` -**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"]}' +## CI/CD -# Token Refresh manuell auslösen (Dry-Run) -curl "https://your-domain/api/cron/token-refresh?dryRun=true" \ - -H "Authorization: Bearer $CRON_SECRET" +| Workflow | Trigger | Beschreibung | +|----------|---------|--------------| +| `ci.yml` | Push/PR auf main/develop | Lint, Typecheck, Test, Build, E2E | +| `security.yml` | Push/PR | Gitleaks, pnpm audit, CodeQL | +| `deploy-staging.yml` | Push auf develop | Auto-Deploy auf pl.porwoll.tech | +| `deploy-production.yml` | Manual dispatch | Deploy auf cms.c2sgmbh.de (mit Backup + Rollback) | -# Token Refresh Status prüfen -curl -I "https://your-domain/api/cron/token-refresh" \ - -H "Authorization: Bearer $CRON_SECRET" -``` +→ Details: `docs/DEPLOYMENT.md`, `docs/DEPLOYMENT_STRATEGY.md` -**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 +## Umgebungsvariablen -## CI/CD Pipeline +Wichtigste Variablen (vollständige Liste in `.env`): -GitHub Actions Workflows in `.github/workflows/`: +| Variable | Beschreibung | Pflicht | +|----------|--------------|---------| +| `DATABASE_URI` | PostgreSQL via PgBouncer (127.0.0.1:6432) | Ja | +| `PAYLOAD_SECRET` | Payload Encryption Secret | Ja | +| `TRUST_PROXY` | `true` hinter Reverse-Proxy | Ja | +| `CSRF_SECRET` | CSRF-Token Secret (oder PAYLOAD_SECRET) | Prod | +| `CRON_SECRET` | Auth für Cron-Endpoints | Ja | +| `REDIS_URL` | Redis Cache (localhost:6379) | Optional | +| `META_APP_ID/SECRET` | Facebook/Instagram OAuth | Für Community | +| `GOOGLE_CLIENT_ID/SECRET` | YouTube OAuth | Für YouTube | -### 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 +Credentials in `~/.pgpass` (chmod 600), nie Klartext in `.env`. ## Dokumentation -### Hauptdokumentation -- `CLAUDE.md` - Diese Datei (Projekt-Übersicht für AI-Assistenten) -- `docs/INFRASTRUCTURE.md` - Infrastruktur-Übersicht (Dev + Prod) +### Projekt-Docs +- `docs/INFRASTRUCTURE.md` - Infrastruktur (Dev + Prod, PgBouncer, Server) - `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 +- `docs/DEPLOYMENT_STRATEGY.md` - Vollständige Deployment-Strategie +- `docs/PROJECT_STATUS.md` - Projektstatus & Roadmap +- `docs/CLAUDE_REFERENCE.md` - **Detaillierte Subsystem-Referenz** (E-Mail, Queue, Retention, Community, etc.) ### Anleitungen -- `docs/anleitungen/TODO.md` - Task-Liste & Changelog +- `docs/anleitungen/API_ANLEITUNG.md` - API-Dokumentation (alle Endpoints + curl-Beispiele) - `docs/anleitungen/SECURITY.md` - Sicherheitsrichtlinien - `docs/anleitungen/FRONTEND.md` - Frontend-Entwicklung (sv-frontend) -- `docs/anleitungen/API_ANLEITUNG.md` - API-Dokumentation -- `docs/anleitungen/framework-monitoring.md` - Framework-Updates beobachten +- `docs/anleitungen/TODO.md` - Task-Liste & Changelog ### Scripts & Backup - `scripts/backup/README.md` - Backup-System Dokumentation -*Letzte Aktualisierung: 14.02.2026 (YouTube Thumbnail-Download, Bulk-Endpoint, Channel-Image-Hook)* +*Letzte Aktualisierung: 14.02.2026* diff --git a/docs/CLAUDE_REFERENCE.md b/docs/CLAUDE_REFERENCE.md new file mode 100644 index 0000000..64c2b3c --- /dev/null +++ b/docs/CLAUDE_REFERENCE.md @@ -0,0 +1,428 @@ +# Payload CMS - Detaillierte Subsystem-Referenz + +> Diese Datei enthält detaillierte Dokumentation zu den Subsystemen des Projekts. +> Kurzübersicht und Schlüsseldateien: siehe `CLAUDE.md`. + +## 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": "

Inhalt

", + "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 + +**Dateien:** +- `src/lib/email/tenant-email-service.ts` - Haupt-Service +- `src/lib/email/payload-email-adapter.ts` - Payload-Integration +- `src/hooks/invalidateEmailCache.ts` - Cache-Invalidierung + +## 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 +# Anmeldung +POST /api/newsletter/subscribe +{"email": "...", "firstName": "...", "tenantId": 1, "source": "footer"} + +# Bestätigung (via Link aus E-Mail) +GET /api/newsletter/confirm?token= + +# Abmeldung (via Link aus E-Mail) +GET /api/newsletter/unsubscribe?token= +``` + +**Features:** +- Token-Ablauf nach 48 Stunden +- Rate-Limiting: 5 Anmeldungen/10 Minuten pro IP +- Erneute Anmeldung nach Abmeldung möglich + +**Dateien:** +- `src/lib/email/newsletter-service.ts` - Service-Logik +- `src/lib/email/newsletter-templates.ts` - E-Mail-Templates +- `src/hooks/sendNewsletterConfirmation.ts` - Hook + +## BullMQ Job Queue + +Asynchrone Job-Verarbeitung mit Redis als Backend. + +**Queue-Worker:** Läuft als separater PM2-Prozess (`pm2 logs queue-worker`). + +### Email Queue + +```typescript +import { queueEmail } from '@/lib/queue' + +await queueEmail({ + tenantId: 1, + to: 'empfaenger@example.com', + subject: 'Betreff', + html: '

Inhalt

', + source: 'form-submission' +}) +``` + +### PDF Queue + +```bash +# PDF aus HTML generieren +POST /api/generate-pdf +{"source": "html", "html": "

Test

", "filename": "test.pdf"} + +# Job-Status abfragen +GET /api/generate-pdf?jobId=abc123 +``` + +```typescript +import { queuePdfFromHtml, queuePdfFromUrl, getPdfJobStatus } from '@/lib/queue' + +const job = await queuePdfFromHtml('

Test

', { filename: 'test.pdf' }) +const job2 = await queuePdfFromUrl('https://example.com', { format: 'A4' }) +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) + +**Dateien:** +- `src/lib/queue/queue-service.ts` - Zentrale Queue-Verwaltung +- `src/lib/queue/jobs/email-job.ts` - E-Mail-Job +- `src/lib/queue/jobs/pdf-job.ts` - PDF-Job +- `src/lib/queue/workers/email-worker.ts` - E-Mail-Worker +- `src/lib/queue/workers/pdf-worker.ts` - PDF-Worker +- `scripts/run-queue-worker.ts` - Worker-Starter + +## Data Retention + +Automatische Datenbereinigung für DSGVO-Compliance und Speicheroptimierung. + +### Retention Policies + +| Collection | Retention | Umgebungsvariable | +|------------|-----------|-------------------| +| email-logs | 90 Tage | `RETENTION_EMAIL_LOGS_DAYS` | +| audit-logs | 90 Tage | `RETENTION_AUDIT_LOGS_DAYS` | +| consent-logs | 3 Jahre | `RETENTION_CONSENT_LOGS_DAYS` | +| media (orphans) | 30 Tage | `RETENTION_MEDIA_ORPHAN_MIN_AGE_DAYS` | + +Scheduler: Täglich 03:00 Uhr (`RETENTION_CRON_SCHEDULE`). + +### API-Endpoint `/api/retention` + +```bash +# Konfiguration abrufen +GET /api/retention + +# Job-Status +GET /api/retention?jobId=abc123 + +# Manueller Job +POST /api/retention +{"type": "full"} # Alle Policies +{"type": "collection", "collection": "email-logs"} # Einzelne Collection +{"type": "media-orphans"} # Nur Media-Orphans +``` + +**Architektur:** +``` +Scheduler (Cron) → Retention Queue (BullMQ) → Retention Worker + → Email-Logs (createdAt) + Audit-Logs (createdAt) + Consent-Logs (expiresAt) + → Media-Orphan-Cleanup +``` + +**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 + +```typescript +import { redis } from '@/lib/redis' + +await redis.set('key', JSON.stringify(data), 'EX', 60) // TTL in Sekunden +const cached = await redis.get('key') +await redis.keys('posts:*').then(keys => keys.length && redis.del(...keys)) // Pattern-Invalidierung +``` + +## FormSubmissions CRM + +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. + +### Architektur + +``` +Admin UI (Inbox + Analytics) + → /api/community/* (sync, stats, reply, generate-reply, export, stream) + → UnifiedSyncService + → YouTube Sync + Facebook Sync + Instagram Sync + → CommunityInteractions (einheitliche Collection) +``` + +### 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) +- Echtzeit-Updates via SSE (`/api/community/stream`) +- Export als CSV/JSON + +**Community Analytics** (`/admin/community/analytics`): +- KPI-Cards, Sentiment-Trend, Topic-Cloud, Response-Metriken, Kanal-Vergleich + +### 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 + +**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-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/analytics/*` | GET | Analytics-Daten (6 Endpoints) | +| `/api/auth/meta` | GET | Meta OAuth starten | +| `/api/auth/meta/callback` | GET | Meta OAuth Callback | + +### 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 | + +### CommunityRules (Automatisierung) + +**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/ # Community-Endpoints +└── cron/community-sync/ # Cron-Trigger +``` + +## Timeline Collection + +Dedizierte Collection für chronologische Darstellungen. + +**Typen:** `history`, `milestones`, `releases`, `career`, `events`, `process` + +**Event-Features:** Flexible Datumsformate, Kategorien, Wichtigkeitsstufen, Bilder, Links, Rich-Text + +**Display-Optionen:** Layouts (vertikal, alternierend, horizontal, kompakt), Sortierung, Gruppierung nach Jahr + +**Prozess-spezifische Felder (type=process):** +- `stepNumber`, `duration`, `responsible`, `actionRequired`, `deliverables` + +**API:** `GET /api/timelines?tenant=1[&type=history][&slug=...][&locale=en]` + +## Workflows Collection + +Komplexe Prozess-Darstellungen mit Phasen, Abhängigkeiten und Status-Tracking. + +**Typen:** `project`, `business`, `approval`, `onboarding`, `support`, `development`, `marketing`, `other` + +**Phasen-Struktur:** +``` +Workflow → Phasen (Array) + → name, description, icon, color, estimatedDuration, responsible, deliverables + → Schritte (Array) + → name, description, stepType, priority, dependencies, conditions, checklist, resources, outputs +``` + +**Display:** Layouts (vertical, horizontal, flowchart, kanban, gantt), Farbschema (phase, status, priority, brand) + +**API:** `GET /api/workflows?tenant=1[&type=project][&complexity=medium][&slug=...][&locale=en]` + +## HeroSliderBlock Features + +**Slides (1-10):** Hintergrundbild (Desktop + Mobil), Headline + Subline (lokalisiert), Text-Ausrichtung, Overlay (Farbe, Deckkraft, Gradient), 2 CTA-Buttons + +**Animationen:** fade, slide, zoom, flip, none (300-1200ms) + +**Autoplay:** Intervall 3-10s, Pause bei Hover/Interaktion + +**Navigation:** Pfeile (verschiedene Stile), Dots (Punkte, Striche, Nummern, Thumbnails, Fortschritt), Touch-Swipe, Tastatur + +**Layout:** Höhe (Vollbild bis kompakt), Mobile-Höhe, Content-Breite + +## CI/CD Pipeline Details + +### ci.yml (Main CI Pipeline) +Läuft bei Push/PR auf `main` und `develop`: +- **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 + +### security.yml +Gitleaks (Secret Scanning), pnpm audit, CodeQL (SAST), Security Tests + +### deploy-staging.yml +- Trigger: Push auf `develop` oder manual dispatch +- Ablauf: Pre-checks → SSH → Git Pull → Dependencies → Migrations → Build → PM2 Restart → Health Check +- Secret: `STAGING_SSH_KEY` + +```bash +# Manuelles Staging-Deployment +./scripts/deploy-staging.sh +./scripts/deploy-staging.sh --skip-build +./scripts/deploy-staging.sh --skip-migrations +``` + +### deploy-production.yml +- Trigger: Manual dispatch +- Features: Pre-flight Checks, Database Backup, Health Check, Auto-Rollback +- Secrets: `STAGING_SSH_KEY`, `PRODUCTION_SSH_KEY` + +```bash +gh workflow run deploy-production.yml # Via GitHub Actions +./scripts/deploy-production.sh # Auf Server +./scripts/deploy-production.sh --rollback # Rollback +./scripts/deploy-production.sh --dry-run # Dry-Run +``` + +## PgBouncer Connection Pooling + +Läuft auf dem App-Server (127.0.0.1:6432), pooled Verbindungen zu PostgreSQL (10.10.181.101:5432). + +**Konfiguration:** `/etc/pgbouncer/pgbouncer.ini` + +| Parameter | Wert | +|-----------|------| +| pool_mode | transaction | +| default_pool_size | 20 | +| min_pool_size | 5 | +| reserve_pool_size | 5 | +| max_db_connections | 50 | +| max_client_conn | 200 | + +**TLS:** `server_tls_sslmode = require` (TLS 1.3) + +```bash +# Statistiken +PGPASSWORD="$DB_PASSWORD" psql -h 127.0.0.1 -p 6432 -U payload -d pgbouncer -c "SHOW POOLS;" + +# Direkte Verbindung für Migrationen (umgeht PgBouncer) +./scripts/db-direct.sh migrate +./scripts/db-direct.sh psql +``` + +## Cron Jobs (Details) + +Alle erfordern `Authorization: Bearer $CRON_SECRET`. + +```bash +# Community Sync manuell +curl -X POST "https://your-domain/api/cron/community-sync" \ + -H "Authorization: Bearer $CRON_SECRET" \ + -d '{"platforms": ["youtube", "facebook"]}' + +# Token Refresh (Dry-Run) +curl "https://your-domain/api/cron/token-refresh?dryRun=true" \ + -H "Authorization: Bearer $CRON_SECRET" +``` + +**Monitoring:** +- HEAD-Requests: Status-Header (`X-Sync-Running`, `X-Last-Run`) +- Bei laufendem Job: HTTP 423 (Locked) +- Fehlerhafte Tokens → `yt-notifications`