- Document queue architecture (email/PDF workers) - Add API examples for PDF generation endpoint - Update PM2 commands for queue-worker process - Add programmatic queue usage examples - Update server RAM info (now 8GB) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
13 KiB
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)
- Job Queue: BullMQ (Redis-basiert)
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
│ │ ├── 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
│ ├── invalidateEmailCache.ts
│ └── auditLog.ts
├── tests/ # Test Suite
│ ├── unit/security/ # Security Unit Tests
│ └── int/ # Integration Tests
├── scripts/
│ └── run-queue-worker.ts # Queue Worker Starter
├── .env # Umgebungsvariablen
├── ecosystem.config.cjs # PM2 Config
└── .next/ # Build Output
Umgebungsvariablen (.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
# 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 logs queue-worker
pm2 restart payload
pm2 restart queue-worker
# 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
- Code ändern
pnpm buildpm2 restart payload- 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:importmapausführen - User-Tenant-Zuweisung: Neue User müssen manuell Tenants zugewiesen bekommen
- Admin Login: Custom Route mit Audit-Logging, unterstützt JSON und
_payloadFormData
Build-Konfiguration
Der Build ist für speichereffizientes Kompilieren optimiert:
package.json:--max-old-space-size=2048(2GB Heap-Limit)next.config.mjs:experimental.cpus: 1,workerThreads: false
WICHTIG: Der Server hat 8GB RAM ohne Swap. Bei laufendem VS Code Server kann der Build mit reduziertem Memory ausgeführt werden:
pm2 stop payload # Speicher freigeben
NODE_OPTIONS="--no-deprecation --max-old-space-size=1024" ./node_modules/.bin/next build
pm2 start payload
Ohne PM2-Stop und mit VS Code wird der Build vom OOM Killer beendet (Exit code 137).
Mehrsprachigkeit (i18n)
Das System unterstützt Deutsch (default) und Englisch:
- Admin UI:
@payloadcms/translationsfür DE/EN - Content: Localization mit Fallback auf Deutsch
- Datenbank: 36
_localesTabellen für lokalisierte Felder - API:
?locale=deoder?locale=enParameter - Frontend: Routing über
/[locale]/...
# 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)
- PDF-Generierung: https://pl.c2sgmbh.de/api/generate-pdf (POST/GET, Auth erforderlich)
Security-Features
Rate-Limiting
Zentraler Rate-Limiter mit vordefinierten Limits:
publicApi: 60 Requests/Minuteauth: 5 Requests/15 Minuten (Login)email: 10 Requests/Minutesearch: 30 Requests/Minuteform: 5 Requests/10 Minuten
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:
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": "<p>Inhalt</p>",
"tenantId": 1
}'
Sicherheit:
- Authentifizierung erforderlich
- Tenant-Zugriffskontrolle (User muss Tenant-Mitglied sein)
- Rate-Limiting: 10 E-Mails/Minute pro User
- SMTP-Passwort nie in API-Responses
BullMQ Job Queue
Das System verwendet BullMQ für asynchrone Job-Verarbeitung mit Redis als Backend.
PM2 Worker
Der Queue-Worker läuft als separater PM2-Prozess:
# Worker-Status prüfen
pm2 status
pm2 logs queue-worker
# Worker neustarten
pm2 restart queue-worker
Email Queue
E-Mails können asynchron über die Queue versendet werden:
import { queueEmail } from '@/lib/queue'
await queueEmail({
tenantId: 1,
to: 'empfaenger@example.com',
subject: 'Betreff',
html: '<p>Inhalt</p>',
source: 'form-submission'
})
PDF Queue
PDF-Generierung erfolgt über Playwright (HTML/URL zu PDF):
API-Endpoint /api/generate-pdf:
# PDF aus HTML generieren
curl -X POST https://pl.c2sgmbh.de/api/generate-pdf \
-H "Content-Type: application/json" \
-H "Cookie: payload-token=..." \
-d '{
"source": "html",
"html": "<h1>Test</h1><p>Inhalt</p>",
"filename": "test.pdf"
}'
# Job-Status abfragen
curl "https://pl.c2sgmbh.de/api/generate-pdf?jobId=abc123" \
-H "Cookie: payload-token=..."
Programmatisch:
import { queuePdfFromHtml, queuePdfFromUrl, getPdfJobStatus } from '@/lib/queue'
// HTML zu PDF
const job = await queuePdfFromHtml('<h1>Test</h1>', { filename: 'test.pdf' })
// URL zu PDF
const job2 = await queuePdfFromUrl('https://example.com', { format: 'A4' })
// Status abfragen
const status = await getPdfJobStatus(job.id)
Worker-Konfiguration
Über ecosystem.config.cjs:
QUEUE_EMAIL_CONCURRENCY: Parallele E-Mail-Jobs (default: 3)QUEUE_PDF_CONCURRENCY: Parallele PDF-Jobs (default: 2)QUEUE_DEFAULT_RETRY: Retry-Versuche (default: 3)QUEUE_REDIS_DB: Redis-Datenbank für Queue (default: 1)
Redis Caching
Redis wird für API-Response-Caching und E-Mail-Transporter-Caching verwendet:
import { redis } from '@/lib/redis'
// Cache setzen (TTL in Sekunden)
await redis.set('key', JSON.stringify(data), 'EX', 60)
// Cache lesen
const cached = await redis.get('key')
// Pattern-basierte Invalidierung
await redis.keys('posts:*').then(keys => keys.length && redis.del(...keys))
Datenbank-Direktzugriff
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
# 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 & Deploymentdocs/anleitungen/TODO.md- Task-Liste & Roadmapdocs/anleitungen/SECURITY.md- Sicherheitsrichtlinien
Letzte Aktualisierung: 09.12.2025