# Payload CMS Multi-Tenant Project ## Projektübersicht Multi-Tenant CMS für 4 Websites unter einer Payload CMS 3.x Instanz: - porwoll.de - complexcaresolutions.de - gunshin.de - zweitmein.ng ## Tech Stack - **CMS:** Payload CMS 3.x - **Framework:** Next.js 15.4.7 - **Sprache:** TypeScript - **Datenbank:** PostgreSQL 17 (separater Server) - **Reverse Proxy:** Caddy 2.10.2 mit Let's Encrypt - **Process Manager:** PM2 - **Package Manager:** pnpm - **Cache:** Redis (optional, mit In-Memory-Fallback) ## Architektur ``` Internet → 37.24.237.181 → Caddy (443) → Payload (3000) ↓ PostgreSQL (10.10.181.101:5432) ``` | Server | IP | Funktion | | --------------------- | ------------- | ---------- | | sv-payload (LXC 700) | 10.10.181.100 | App Server | | sv-postgres (LXC 701) | 10.10.181.101 | Datenbank | ## Wichtige Pfade ``` /home/payload/payload-cms/ # Projektroot ├── src/ │ ├── payload.config.ts # Haupt-Konfiguration │ ├── collections/ # Alle Collections │ │ ├── Users.ts │ │ ├── Media.ts │ │ ├── Tenants.ts │ │ ├── Posts.ts │ │ ├── Categories.ts │ │ ├── Portfolios.ts │ │ ├── PortfolioCategories.ts │ │ ├── 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 │ │ ├── security/ # Security-Module │ │ │ ├── rate-limiter.ts │ │ │ ├── csrf.ts │ │ │ ├── ip-allowlist.ts │ │ │ └── data-masking.ts │ │ ├── search.ts # Volltextsuche │ │ └── redis.ts # Redis Cache Client │ └── hooks/ # Collection Hooks │ ├── sendFormNotification.ts │ ├── invalidateEmailCache.ts │ └── auditLog.ts ├── tests/ # Test Suite │ ├── unit/security/ # Security Unit Tests │ └── int/ # Integration Tests ├── .env # Umgebungsvariablen ├── ecosystem.config.cjs # PM2 Config └── .next/ # Build Output ``` ## Umgebungsvariablen (.env) ```env DATABASE_URI=postgresql://payload:Finden55@10.10.181.101:5432/payload_db PAYLOAD_SECRET=a53b254070d3fffd2b5cfcc3 PAYLOAD_PUBLIC_SERVER_URL=https://pl.c2sgmbh.de NEXT_PUBLIC_SERVER_URL=https://pl.c2sgmbh.de 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 SEND_EMAIL_ALLOWED_IPS= # Optional: Komma-separierte IPs/CIDRs BLOCKED_IPS= # Optional: Global geblockte IPs ``` ## Multi-Tenant Plugin Verwendet `@payloadcms/plugin-multi-tenant` für Mandantenfähigkeit. **Aktuelle Tenants:** | ID | Name | Slug | |----|------|------| | 1 | porwoll.de | porwoll | | 4 | Complex Care Solutions GmbH | c2s | | 5 | Gunshin | gunshin | **User-Tenant-Zuweisung:** Tabelle `users_tenants` ## Wichtige Befehle ```bash # Entwicklung pnpm dev # Production Build pnpm build # Migrationen pnpm payload migrate:create pnpm payload migrate # ImportMap nach Plugin-Änderungen pnpm payload generate:importmap # PM2 pm2 status pm2 logs payload pm2 restart payload # Tests pnpm test # Alle Tests pnpm test:security # Security Tests pnpm test:coverage # Mit Coverage-Report # Datenbank prüfen PGPASSWORD=Finden55 psql -h 10.10.181.101 -U payload -d payload_db ``` ## Workflow nach Code-Änderungen 1. Code ändern 2. `pnpm build` 3. `pm2 restart payload` 4. Testen unter https://pl.c2sgmbh.de/admin ## Bekannte Besonderheiten - **ES Modules:** package.json hat `"type": "module"`, daher PM2 Config als `.cjs` - **Plugin ImportMap:** Nach Plugin-Änderungen `pnpm payload generate:importmap` ausführen - **User-Tenant-Zuweisung:** Neue User müssen manuell Tenants zugewiesen bekommen - **Admin Login:** Custom Route mit Audit-Logging, unterstützt JSON und `_payload` FormData ## 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 nur 4GB RAM ohne Swap. Bei laufendem VS Code Server muss der Build mit reduziertem Memory ausgeführt werden: ```bash pm2 stop payload # Speicher freigeben NODE_OPTIONS="--no-deprecation --max-old-space-size=1024" ./node_modules/.bin/next build pm2 start payload ``` Ohne PM2-Stop und mit VS Code wird der Build vom OOM Killer beendet (Exit code 137). ## Mehrsprachigkeit (i18n) Das System unterstützt Deutsch (default) und Englisch: - **Admin UI:** `@payloadcms/translations` für DE/EN - **Content:** Localization mit Fallback auf Deutsch - **Datenbank:** 36 `_locales` Tabellen für lokalisierte Felder - **API:** `?locale=de` oder `?locale=en` Parameter - **Frontend:** Routing über `/[locale]/...` ```bash # Locales in der Datenbank prüfen PGPASSWORD=Finden55 psql -h 10.10.181.101 -U payload -d payload_db -c "\dt *_locales" ``` ## URLs - **Admin Panel:** https://pl.c2sgmbh.de/admin - **API:** https://pl.c2sgmbh.de/api - **E-Mail API:** https://pl.c2sgmbh.de/api/send-email (POST, Auth erforderlich) - **Test-E-Mail:** https://pl.c2sgmbh.de/api/test-email (POST, Admin erforderlich) - **E-Mail Stats:** https://pl.c2sgmbh.de/api/email-logs/stats (GET, Auth erforderlich) ## Security-Features ### 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 ### CSRF-Schutz - Double Submit Cookie Pattern - Origin-Header-Validierung - Token-Endpoint: `GET /api/csrf-token` - Admin-Panel hat eigenen CSRF-Schutz ### IP-Allowlist - Konfigurierbar via `SEND_EMAIL_ALLOWED_IPS` - Unterstützt IPs, CIDRs (`192.168.1.0/24`) und Wildcards (`10.10.*.*`) - Globale Blocklist via `BLOCKED_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.c2sgmbh.de/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 ## 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)) ``` ## Datenbank-Direktzugriff ```bash PGPASSWORD=Finden55 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 ``` ## Collections Übersicht | 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 | | Posts | posts | Blog/News/Presse mit Kategorien | | Categories | categories | Kategorien für Posts | | Portfolios | portfolios | Portfolio-Galerien (Fotografie) | | PortfolioCategories | portfolio-categories | Kategorien für Portfolios | | Testimonials | testimonials | Kundenbewertungen | | NewsletterSubscribers | newsletter-subscribers | Newsletter mit Double Opt-In | | SocialLinks | social-links | Social Media Links | | Forms | forms | Formular-Builder | | FormSubmissions | form-submissions | Formular-Einsendungen | | EmailLogs | email-logs | E-Mail-Protokollierung | | AuditLogs | audit-logs | Security Audit Trail | | CookieConfigurations | cookie-configurations | Cookie-Banner Konfiguration | | CookieInventory | cookie-inventory | Cookie-Inventar | | ConsentLogs | consent-logs | Consent-Protokollierung | ## Globals | Global | Slug | Beschreibung | |--------|------|--------------| | SiteSettings | site-settings | Allgemeine Website-Einstellungen | | Navigation | navigation | Navigationsmenü | | SEOSettings | seo-settings | SEO-Einstellungen | | PrivacyPolicySettings | privacy-policy-settings | Datenschutz-Einstellungen | ## Test Suite ```bash # Alle Tests ausführen pnpm test # Security Tests pnpm test:security # Coverage Report pnpm test:coverage ``` **Test Coverage Thresholds:** - Lines: 35% - Functions: 50% - Branches: 65% ## Dokumentation - `CLAUDE.md` - Diese Datei (Projekt-Übersicht) - `docs/INFRASTRUCTURE.md` - Server-Architektur & Deployment - `docs/anleitungen/TODO.md` - Task-Liste & Roadmap - `docs/anleitungen/SECURITY.md` - Sicherheitsrichtlinien *Letzte Aktualisierung: 09.12.2025*