cms.c2sgmbh/docs/INFRASTRUCTURE.md
Martin Porwoll ce4962e74b feat: BullMQ queue system for email and PDF processing
- Add BullMQ-based job queue with Redis backend
- Implement email worker with tenant-specific SMTP support
- Add PDF worker with Playwright for HTML/URL-to-PDF generation
- Create /api/generate-pdf endpoint with job status polling
- Fix TypeScript errors in Tenants, TenantBreadcrumb, TenantDashboard
- Fix type casts in auditAuthEvents and audit-service
- Remove credentials from ecosystem.config.cjs (now loaded via dotenv)
- Fix ESM __dirname issue with fileURLToPath for PM2 compatibility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 22:59:17 +00:00

588 lines
25 KiB
Markdown

# Payload CMS Multi-Tenant Infrastructure
> Letzte Aktualisierung: 09.12.2025
## Übersicht
Diese Dokumentation beschreibt die Infrastruktur eines Payload CMS 3.x Multi-Tenant-Systems für den Betrieb mehrerer Websites unter einer zentralen CMS-Instanz.
## Gesamtarchitektur
```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ GESAMTARCHITEKTUR │
│ │
│ ┌───────────────────────────────────────────────────────────────────────────────┐ │
│ │ LOKALE ENTWICKLUNGSUMGEBUNG │ │
│ │ (Proxmox VE Cluster) │ │
│ │ LAN: 10.10.181.0/24 │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ sv-payload │ │ sv-postgres │ │sv-dev-payload│ │sv-analytics │ │ │
│ │ │ LXC 700 │ │ LXC 701 │ │ LXC 702 │ │ LXC 703 │ │ │
│ │ │ Payload CMS │ │ PostgreSQL │ │ Next.js │ │ Umami │ │ │
│ │ │10.10.181.100│ │10.10.181.101│ │10.10.181.102│ │10.10.181.103│ │ │
│ │ │ + Redis │ │ │ │ │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────┴───────────────────┐ │
│ │ LOKALER INTERNETZUGANG │ │
│ │ 850 Mbps ↓ / 50 Mbps ↑ │ │
│ │ │ │
│ │ Feste IP-Adressen: │ │
│ │ 37.24.237.178 - Router │ │
│ │ 37.24.237.179 - complexcaresolutions │ │
│ │ 37.24.237.180 - Nginx Proxy Manager │ │
│ │ 37.24.237.181 - pl.c2sgmbh.de │ │
│ │ 37.24.237.182 - frei │ │
│ └───────────────────┬───────────────────┘ │
│ │ │
│ INTERNET │
│ │ │
│ ┌──────────────────────────────────┼──────────────────────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ HETZNER 1 │ │ HETZNER 2 │ │ HETZNER 3 │ │
│ │ CCS GmbH │ │ Martin Porwoll │ │ Backend/Analytics │ │
│ │ │ │ │ │ │ │
│ │ 78.46.87.137 │ │ 94.130.141.114 │ │ 162.55.85.18 │ │
│ │ Debian 12.12 │ │ Ubuntu 24.04 │ │ Debian 13 │ │
│ │ Plesk │ │ Plesk │ │ Native │ │
│ │ │ │ │ │ │ │
│ │ Next.js Frontends │ │ Next.js Frontends │ │ ✅ Payload CMS │ │
│ │ • complexcare... │ │ • porwoll.de │ │ ✅ Umami │ │
│ │ • gunshin.de │ │ • caroline-... │ │ ✅ PostgreSQL 17 │ │
│ └─────────────────────┘ └─────────────────────┘ │ ✅ Redis Cache │ │
│ │ ✅ Claude Code │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
```
---
## Server-Details
### HETZNER 3 - Backend & Analytics (Produktion)
| Eigenschaft | Wert |
|-------------|------|
| **Hostname** | sv-hz03-backend |
| **IP-Adresse** | 162.55.85.18 |
| **Betriebssystem** | Debian 13 "Trixie" |
| **CPU** | AMD Ryzen 5 3600 (6 Cores / 12 Threads) |
| **RAM** | 64 GB DDR4 ECC |
| **Storage** | 2x 512 GB NVMe SSD (Software RAID 1) |
| **Netzwerk** | 1 Gbit/s (garantiert) |
| **Traffic** | Unbegrenzt |
| **Kosten** | ~€52/Monat |
#### Services auf Hetzner 3
| Service | User | Port | URL | Status |
|---------|------|------|-----|--------|
| PostgreSQL 17 | postgres | 5432 | localhost | ✅ Läuft |
| Payload CMS | payload | 3001 | https://cms.c2sgmbh.de | ✅ Läuft |
| Umami Analytics | umami | 3000 | https://analytics.c2sgmbh.de | ✅ Läuft |
| Redis Cache | redis | 6379 | localhost | ✅ Läuft |
| Nginx | root | 80/443 | Reverse Proxy | ✅ Läuft |
| Claude Code | claude | - | CLI Tool | ✅ Installiert |
#### System-User
| User | Zweck | Home-Verzeichnis |
|------|-------|------------------|
| root | System-Administration | /root |
| payload | Payload CMS | /home/payload |
| umami | Umami Analytics | /home/umami |
| claude | Claude Code / Server-Admin | /home/claude |
---
### HETZNER 1 - Complex Care Solutions GmbH
| Eigenschaft | Wert |
|-------------|------|
| **Eigentümer** | Complex Care Solutions GmbH |
| **IP-Adresse** | 78.46.87.137 |
| **Betriebssystem** | Debian 12.12 |
| **Control Panel** | Plesk Web Pro Edition 18.0.73 |
| **CPU** | AMD Ryzen 7 Pro 8700GE |
| **RAM** | 64 GB |
| **Storage** | 2x 512 GB NVMe SSD (Software RAID 1) |
#### Domains auf Hetzner 1
| Domain | Zweck |
|--------|-------|
| **complexcaresolutions.de** | Hauptdomain |
| **gunshin.de** | Portfolio/Holding |
| c2sgmbh.de | Kurzform → Redirect |
| zweitmeinung-*.de | Fachgebiete → Redirect |
---
### HETZNER 2 - Martin Porwoll (privat)
| Eigenschaft | Wert |
|-------------|------|
| **Eigentümer** | Martin Porwoll (privat) |
| **IP-Adresse** | 94.130.141.114 |
| **Betriebssystem** | Ubuntu 24.04 LTS |
| **Control Panel** | Plesk Web Pro Edition 18.0.73 |
| **CPU** | Intel Xeon E3-1275v6 |
| **RAM** | 64 GB |
| **Storage** | 2x 512 GB NVMe SSD (Software RAID 1) |
#### Domains auf Hetzner 2
| Domain | Zweck |
|--------|-------|
| **porwoll.de** | Hauptdomain |
| **caroline-porwoll.de** | Dr. Caroline Porwoll |
---
### Lokale Infrastruktur (Proxmox) - Entwicklung
| Server | IP | Port | Funktion | OS |
|--------|-----|------|----------|-----|
| sv-payload | 10.10.181.100 | 3000 | Payload CMS (Dev) + Redis | Debian 13 |
| sv-postgres | 10.10.181.101 | 5432 | PostgreSQL (Dev) | Debian 13 |
| sv-dev-payload | 10.10.181.102 | 3001 | Next.js Frontend | Debian 13 |
| sv-analytics | 10.10.181.103 | 3000 | Umami (Dev) | Debian 13 |
#### Feste IP-Adressen (Lokal → Internet)
| IP | Verwendung |
|----|------------|
| 37.24.237.178 | Router / Gateway |
| 37.24.237.179 | complexcaresolutions.cloud |
| 37.24.237.180 | Nginx Proxy Manager |
| 37.24.237.181 | pl.c2sgmbh.de (Payload Dev) |
| 37.24.237.182 | **Frei** |
---
## Credentials
### Produktion (sv-hz03-backend)
#### PostgreSQL
| Datenbank | User | Passwort |
|-----------|------|----------|
| payload_db | payload | Suchen55 |
| umami_db | umami | Suchen55 |
#### Redis
```bash
redis-cli -h localhost -p 6379
# Kein Passwort (nur localhost)
```
#### Environment Variables - Payload (.env)
```env
DATABASE_URI=postgresql://payload:Suchen55@localhost:5432/payload_db
PAYLOAD_SECRET=hxPARlMkmv+ZdCOAMw+N4o2x4mNbERB237iDQTYXALY=
PAYLOAD_PUBLIC_SERVER_URL=https://cms.c2sgmbh.de
NEXT_PUBLIC_SERVER_URL=https://cms.c2sgmbh.de
NODE_ENV=production
PORT=3001
REDIS_HOST=localhost
REDIS_PORT=6379
```
### Entwicklung (pl.c2sgmbh.de)
#### PostgreSQL (sv-postgres)
| Datenbank | User | Passwort |
|-----------|------|----------|
| payload_db | payload | Finden55 |
#### Environment Variables (.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
REDIS_HOST=localhost
REDIS_PORT=6379
```
---
## Multi-Tenant Konzept
Das System verwendet `@payloadcms/plugin-multi-tenant` für die Mandantenfähigkeit.
### Tenants (Mandanten)
Jeder Tenant repräsentiert eine separate Website:
| Tenant | Slug | Domains |
|--------|------|---------|
| porwoll.de | porwoll | porwoll.de, www.porwoll.de |
| Complex Care Solutions GmbH | c2s | complexcaresolutions.de |
| Gunshin | gunshin | gunshin.de |
### Datenisolation
- Jeder Content (Media, Pages, Posts etc.) gehört zu genau einem Tenant
- User werden Tenants zugewiesen und sehen nur deren Inhalte
- Die Domain-Erkennung erfolgt automatisch durch das Plugin
### Datenbank-Tabellen
```
tenants - Mandanten-Stammdaten
tenants_domains - Domain-Zuordnungen
users_tenants - User-Mandanten-Beziehung (N:M)
```
---
## Redis Caching
### Architektur
```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ REDIS CACHING STRATEGIE │
│ │
│ Request → Payload CMS → Redis Cache? │
│ │ │
│ ┌────┴────┐ │
│ HIT MISS │
│ │ │ │
│ ▼ ▼ │
│ Return PostgreSQL → Cache in Redis → Return │
│ │
│ Cache-Typen: │
│ • API Response Cache (GET /api/pages, /api/posts) │
│ • Automatische Invalidierung bei Content-Änderungen │
│ │
│ Konfiguration: │
│ • Max Memory: 2GB (Prod) / 512MB (Dev) │
│ • Eviction: allkeys-lru │
│ • TTL: 5 Minuten (Standard) │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
```
### Redis Befehle
```bash
# Status prüfen
redis-cli ping
# Statistiken
redis-cli info stats
# Cache-Keys anzeigen
redis-cli keys "*"
# Cache leeren
redis-cli flushdb
# Live-Monitoring
redis-cli monitor
```
### Background Jobs (geplant)
**Evaluation (09.12.2025): BullMQ vs Agenda.js**
| Kriterium | BullMQ | Agenda.js |
|-----------|--------|-----------|
| Database | Redis ✅ | MongoDB ❌ |
| TypeScript | Native ✅ | Begrenzt ⚠️ |
| Priority Jobs | Ja ✅ | Nein ❌ |
| Rate Limiting | Ja ✅ | Nein ❌ |
| UI Dashboard | @bull-board ✅ | Keine ❌ |
**Entscheidung: BullMQ** (Redis bereits vorhanden, TypeScript-native, @bull-board UI)
**Geplante Konfiguration:**
```bash
# Environment Variables (wenn implementiert)
QUEUE_REDIS_URL=redis://localhost:6379/1 # Separate DB für Jobs
QUEUE_CONCURRENCY=5 # Parallele Worker
QUEUE_DEFAULT_RETRY=3 # Wiederholungsversuche
```
**PM2 Worker-Konfiguration (geplant):**
```javascript
// ecosystem.config.cjs - Erweiterung
{
name: 'queue-worker',
script: './scripts/run-queue-worker.ts',
instances: 1,
max_memory_restart: '500M'
}
```
---
## Deployment Workflow
```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ DEPLOYMENT WORKFLOW │
│ │
│ ┌──────────────────────────────────┐ ┌──────────────────────────────────┐ │
│ │ ENTWICKLUNG (DEV) │ │ PRODUKTION (PROD) │ │
│ │ pl.c2sgmbh.de │ │ cms.c2sgmbh.de │ │
│ │ 37.24.237.181 │ │ 162.55.85.18 │ │
│ └──────────────────────────────────┘ └──────────────────────────────────┘ │
│ │
│ Step 1: CODE ENTWICKELN │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ cd /home/payload/payload-cms │ │
│ │ pnpm dev # Lokal testen │ │
│ │ pnpm build # Build-Test │ │
│ │ pm2 restart payload # Auf Dev-Server deployen │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ │
│ Step 2: ZU GITHUB PUSHEN │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ git add . # Alle Änderungen stagen │ │
│ │ git commit -m "feat: XYZ" # Commit erstellen │ │
│ │ git push origin main # Zu GitHub pushen │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ GITHUB REPOSITORY (PRIVAT) │ │
│ │ https://github.com/c2s-admin/cms.c2sgmbh │ │
│ └────────────────────────────────────────────────────┘ │
│ ↓ │
│ │
│ Step 3: AUF PRODUKTION DEPLOYEN │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ ssh payload@162.55.85.18 │ │
│ │ ~/deploy.sh # Automatisches Deployment │ │
│ │ │ │
│ │ Das deploy.sh Script macht: │ │
│ │ ├─ git pull origin main # Code von GitHub holen │ │
│ │ ├─ pnpm install # Dependencies aktualisieren │ │
│ │ ├─ pnpm build # Produktions-Build │ │
│ │ └─ pm2 restart payload # Service neustarten │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
```
### Git-Setup auf Servern
| Server | User | Remote | Auth-Methode | Status |
|--------|------|--------|--------------|--------|
| pl.c2sgmbh.de (Dev) | payload | HTTPS | GitHub CLI (`gh auth`) | ✅ Konfiguriert |
| cms.c2sgmbh.de (Prod) | payload | SSH | SSH-Key | ✅ Eingerichtet |
### Deployment-Befehle
**Entwicklungsserver → GitHub:**
```bash
cd /home/payload/payload-cms
git status
pnpm build
pm2 restart payload
git add .
git commit -m "feat: Beschreibung der Änderung"
git push origin main
```
**GitHub → Produktionsserver:**
```bash
# Option A: SSH + Deploy-Script (empfohlen)
ssh payload@162.55.85.18 '~/deploy.sh'
# Option B: Manuelles SSH-Login
ssh payload@162.55.85.18
cd ~/payload-cms
git pull origin main
pnpm install
pnpm build
pm2 restart payload
```
---
## Backup
### Backup-Script (~/backup.sh)
```bash
#!/bin/bash
set -e
BACKUP_DIR=~/backups
DATE=$(date +%Y-%m-%d_%H-%M-%S)
RETENTION_DAYS=7
mkdir -p $BACKUP_DIR
# PostgreSQL Backup
PGPASSWORD=Suchen55 pg_dump -h localhost -U payload payload_db > $BACKUP_DIR/payload_db_$DATE.sql
gzip $BACKUP_DIR/payload_db_$DATE.sql
# Alte Backups löschen
find $BACKUP_DIR -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
```
### Cronjob (täglich 3:00 Uhr)
```
0 3 * * * /home/payload/backup.sh >> /home/payload/backups/backup.log 2>&1
```
---
## Service-Management
### PM2 Befehle
```bash
pm2 status # Status
pm2 logs payload # Logs
pm2 restart payload # Neustart
pm2 save # Autostart speichern
```
### Systemd Services
```bash
# PostgreSQL
systemctl status postgresql
systemctl restart postgresql
# Nginx
systemctl status nginx
systemctl restart nginx
nginx -t # Config testen
# Redis
systemctl status redis-server
systemctl restart redis-server
```
---
## URLs Übersicht
| Service | Entwicklung | Produktion |
|---------|-------------|------------|
| Payload Admin | https://pl.c2sgmbh.de/admin | https://cms.c2sgmbh.de/admin |
| Payload API | https://pl.c2sgmbh.de/api | https://cms.c2sgmbh.de/api |
| Umami | - | https://analytics.c2sgmbh.de |
---
## SSH Schnellzugriff
```bash
# Produktion (Hetzner 3)
ssh root@162.55.85.18 # Root
ssh payload@162.55.85.18 # Payload User
ssh umami@162.55.85.18 # Umami User
ssh claude@162.55.85.18 # Claude Code
# Hetzner Server
ssh root@78.46.87.137 # Hetzner 1 (CCS)
ssh root@94.130.141.114 # Hetzner 2 (Porwoll)
# Entwicklung (Proxmox)
ssh payload@10.10.181.100 # sv-payload
ssh root@10.10.181.101 # sv-postgres
```
---
## Netzwerk & Firewall
### UFW Regeln auf sv-payload (Dev)
```bash
22/tcp ALLOW 10.10.181.0/24 # SSH aus VLAN
80/tcp ALLOW Anywhere # HTTP (ACME)
443/tcp ALLOW Anywhere # HTTPS
```
### UFW Regeln auf sv-postgres (Dev)
```bash
22/tcp ALLOW 10.10.181.0/24 # SSH aus VLAN
5432/tcp ALLOW 10.10.181.100 # PostgreSQL nur von Payload
```
### UFW Regeln auf sv-hz03-backend (Prod)
```bash
22/tcp ALLOW Anywhere # SSH
80/tcp ALLOW Anywhere # HTTP
443/tcp ALLOW Anywhere # HTTPS
```
---
## SSL Zertifikate
| Domain | Anbieter | Status |
|--------|----------|--------|
| pl.c2sgmbh.de | Let's Encrypt (Caddy) | Auto-Renewal |
| cms.c2sgmbh.de | Let's Encrypt (Certbot) | Auto-Renewal |
| analytics.c2sgmbh.de | Let's Encrypt (Certbot) | Auto-Renewal |
---
## Tech Stack
| Komponente | Technologie | Version |
|------------|-------------|---------|
| CMS | Payload CMS | 3.x |
| Framework | Next.js | 15.4.7 |
| Runtime | Node.js | 22.x |
| Datenbank | PostgreSQL | 17 |
| Cache | Redis | 7.x |
| Analytics | Umami | 3.x |
| Process Manager | PM2 | Latest |
| Package Manager | pnpm | Latest |
| Reverse Proxy (Dev) | Caddy | 2.10.2 |
| Reverse Proxy (Prod) | Nginx | Latest |
| SSL | Let's Encrypt | - |
---
## DSGVO-Konformität
Die Architektur wurde bewusst ohne Cloudflare designed:
- Keine US-Dienste im Datenpfad für Admin-Zugriffe
- Direkte öffentliche IP statt Proxy
- Keine Auftragsverarbeiter-Verträge für CDN nötig
- Redakteur-IPs und Sessions bleiben in DE
---
## Checkliste nach Deployment
- [ ] `pm2 status` - Alle Prozesse online?
- [ ] `redis-cli ping` - Redis antwortet?
- [ ] Admin Panel erreichbar?
- [ ] `pm2 logs payload --lines 10` - Keine Fehler?
---
*Stand: 09. Dezember 2025*