cms.c2sgmbh/CLAUDE.md

413 lines
17 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 (Dev) / Nginx (Prod)
- **Process Manager:** PM2
- **Package Manager:** pnpm
- **Cache:** Redis 7.x (optional, In-Memory-Fallback)
- **Job Queue:** BullMQ (Redis-basiert)
- **Analytics:** Umami 3.x
## Architektur
**Development:** `Internet → Cloudflare → Caddy (sv-caddy) → sv-payload:3000 → 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) |
**Production (Hetzner 3):** `cms.c2sgmbh.de:3001` (Payload), `analytics.c2sgmbh.de:3000` (Umami)
→ Details: `docs/INFRASTRUCTURE.md`
## Multi-Tenant
Verwendet `@payloadcms/plugin-multi-tenant`.
| ID | Name | Slug |
|----|------|------|
| 1 | porwoll.de | porwoll |
| 4 | Complex Care Solutions GmbH | c2s |
| 5 | Gunshin | gunshin |
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
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 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
# 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
```
## Git Workflow
**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 | PR nach develop |
**Commit-Konventionen:** `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`
**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`
## KRITISCH: Neue Collections hinzufügen
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:
```
column payload_locked_documents_rels.{collection}_id does not exist
```
**Migration MUSS enthalten:**
```sql
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: `src/migrations/20260109_020000_add_blogwoman_collections.ts`
**Array-Felder:** Collections mit Arrays benötigen zusätzliche `{collection}_{array_field}` Tabellen.
## Bekannte Besonderheiten & Gotchas
- **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 (`src/app/(payload)/api/users/login/route.ts`)
- **Queue Worker:** PM2 nutzt `node_modules/tsx/dist/cli.mjs` direkt (nicht `npx`), `exec_mode: 'fork'`
- **PgBouncer:** Transaction-Mode kann Migrationen stören → `./scripts/db-direct.sh`
- **Tenant SMTP Save (Stand 17.02.2026):** Bei conditional `email.smtp` keine Feld-`required` auf `host/user`; stattdessen Group-`validate` verwenden. Tenant-Audit-Hook darf Admin-Save nicht mit `await` blockieren.
- **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
- `package.json`: `--max-old-space-size=2048` (2GB Heap-Limit)
- `next.config.mjs`: `experimental.cpus: 1`, `workerThreads: false`
**Server hat 8GB RAM ohne Swap.** Bei laufendem VS Code kann Build OOM werden:
```bash
pm2 stop payload
NODE_OPTIONS="--no-deprecation --max-old-space-size=1024" ./node_modules/.bin/next build
pm2 start payload
```
## Mehrsprachigkeit
Deutsch (default) + Englisch. API: `?locale=de|en`. DB: `_locales` Tabellen.
## URLs
| 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 |
| Monitoring Dashboard | https://pl.porwoll.tech/admin/monitoring |
→ 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` |
| YouTube Operations Hub | `src/lib/youtube/`, API: `/api/youtube/*` | `docs/CLAUDE_REFERENCE.md` |
| FormSubmissions CRM | `src/collections/FormSubmissionsOverrides.ts` | `docs/CLAUDE_REFERENCE.md` |
| Monitoring & Alerting | `src/lib/monitoring/`, API: `/api/monitoring/*` | `docs/CLAUDE_REFERENCE.md` |
## Collections (59+)
| 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 |
| monitoring-snapshots | Historische System-Metriken | Monitoring |
| monitoring-logs | Structured Business-Logs | Monitoring |
| monitoring-alert-rules | Konfigurierbare Alert-Regeln | Monitoring |
| monitoring-alert-history | Alert-Log (WORM) | Monitoring |
## 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
| Global | Slug | Beschreibung |
|--------|------|--------------|
| SEOSettings | seo-settings | Globale SEO-Einstellungen (systemweit) |
> SiteSettings, Navigations, PrivacyPolicySettings sind tenant-spezifische Collections.
## Cron Jobs
| Endpoint | Schedule | Beschreibung |
|----------|----------|--------------|
| `/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 |
| `/api/cron/youtube-sync` | alle 15 Min | YouTube-Kommentar-Sync |
| `/api/cron/youtube-channel-sync` | täglich 04:00 UTC | Kanal-Statistiken (Abos, Views) |
| `/api/cron/youtube-metrics-sync` | alle 6 Stunden | Video-Performance-Metriken |
Auth: `Authorization: Bearer $CRON_SECRET`
## CI/CD
| 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) |
→ Details: `docs/DEPLOYMENT.md`, `docs/DEPLOYMENT_STRATEGY.md`
## Umgebungsvariablen
Wichtigste Variablen (vollständige Liste in `.env`):
| 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_PASSWORD` | Redis Authentifizierung | 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 |
Credentials in `~/.pgpass` (chmod 600), nie Klartext in `.env`.
## Dokumentation
### Projekt-Docs
- `docs/INFRASTRUCTURE.md` - Infrastruktur (Dev + Prod, PgBouncer, Server)
- `docs/DEPLOYMENT.md` - Deployment-Prozess & Checklisten
- `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/API_ANLEITUNG.md` - API-Dokumentation (alle Endpoints + curl-Beispiele)
- `docs/anleitungen/SECURITY.md` - Sicherheitsrichtlinien
- `docs/anleitungen/FRONTEND.md` - Frontend-Entwicklung (sv-frontend)
- `docs/anleitungen/TODO.md` - Task-Liste & Changelog
### Scripts & Backup
- `scripts/backup/README.md` - Backup-System Dokumentation
*Letzte Aktualisierung: 14.02.2026*