# 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.68.4 - **Framework:** Next.js 15.5.9 - **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 mit Let's Encrypt (Dev) / Nginx (Prod) - **Process Manager:** PM2 - **Package Manager:** pnpm - **Cache:** Redis 7.x (optional, mit 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) ``` 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) ``` | 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) | 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 | ## 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 │ │ ├── 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 ``` > **Wichtig:** `TRUST_PROXY=true` muss gesetzt sein wenn die App hinter einem Reverse-Proxy > (wie Caddy) läuft. Ohne diese Einstellung funktionieren IP-basierte Sicherheitsfunktionen > (Rate-Limiting, IP-Allowlists, Blocklists) nicht korrekt. ## 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` ## 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 logs queue-worker pm2 restart payload 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 # Datenbank prüfen # Verwende ./scripts/db-direct.sh psql oder: PGPASSWORD="$DB_PASSWORD" psql -h 10.10.181.101 -U payload -d payload_db # Backup /home/payload/backups/postgres/backup-db.sh --verbose # Manuelles Backup scripts/backup/setup-backup.sh # Backup-System einrichten ``` ## Workflow nach Code-Änderungen 1. Code ändern 2. `pnpm build` 3. `pm2 restart payload` 4. Testen unter https://pl.porwoll.tech/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 8GB RAM ohne Swap. Bei laufendem VS Code Server kann 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 # Verwende ./scripts/db-direct.sh psql oder: PGPASSWORD="$DB_PASSWORD" psql -h 10.10.181.101 -U payload -d payload_db -c "\dt *_locales" ``` ## 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) ## 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 | Block | Slug | Beschreibung | |-------|------|--------------| | HeroBlock | hero-block | Einzelner Hero mit Bild, Headline, CTA | | HeroSliderBlock | hero-slider-block | Hero-Slider mit mehreren Slides | | 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 | | 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 | | BeforeAfterBlock | before-after-block | Vorher/Nachher Bildvergleich (porwoll.de) | ### 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 | 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 | | FAQs | faqs | Häufig gestellte Fragen (FAQ) | | Team | team | Team-Mitglieder und Mitarbeiter | | ServiceCategories | service-categories | Kategorien für Leistungen | | Services | services | Leistungen und Dienstleistungen | | NewsletterSubscribers | newsletter-subscribers | Newsletter mit Double Opt-In | | SocialLinks | social-links | Social Media Links | | Forms | forms | Formular-Builder | | FormSubmissions | form-submissions | Formular-Einsendungen mit Status-Workflow | | 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 | | Timelines | timelines | Chronologische Events (Geschichte, Meilensteine) | | Workflows | workflows | Komplexe Prozesse mit Phasen und Schritten | | Bookings | bookings | Fotografie-Buchungen (porwoll.de) | | Certifications | certifications | Zertifizierungen (C2S) | | Projects | projects | Game-Development-Projekte (gunshin.de) | | Videos | videos | Video-Bibliothek mit YouTube/Vimeo/Uploads | | VideoCategories | video-categories | Kategorien für Videos | ## 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 ## 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% ## CI/CD Pipeline GitHub Actions Workflows in `.github/workflows/`: ### ci.yml (Main CI Pipeline) Läuft bei Push/PR auf `main` und `develop`: | Job | Beschreibung | |-----|--------------| | **lint** | ESLint + Prettier Check | | **typecheck** | TypeScript Compiler (`tsc --noEmit`) | | **test** | Unit & Integration Tests (Vitest) | | **build** | Next.js Production Build | | **e2e** | Playwright E2E Tests | **Lokale Ausführung:** ```bash pnpm lint # ESLint pnpm typecheck # TypeScript Check 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` ## Dokumentation ### Hauptdokumentation - `CLAUDE.md` - Diese Datei (Projekt-Übersicht für AI-Assistenten) - `docs/INFRASTRUCTURE.md` - Infrastruktur-Übersicht (Dev + Prod) - `docs/DEPLOYMENT.md` - Deployment-Prozess & Checklisten - `docs/PROJECT_STATUS.md` - Aktueller Projektstatus & Roadmap - `docs/STAGING-DEPLOYMENT.md` - Staging Deployment Workflow ### Anleitungen - `docs/anleitungen/TODO.md` - Task-Liste & Changelog - `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 ### Scripts & Backup - `scripts/backup/README.md` - Backup-System Dokumentation *Letzte Aktualisierung: 18.12.2025*