diff --git a/BUG_REPORT_CUSTOM_VIEWS.md b/BUG_REPORT_CUSTOM_VIEWS.md
new file mode 100644
index 0000000..fdd2ad4
--- /dev/null
+++ b/BUG_REPORT_CUSTOM_VIEWS.md
@@ -0,0 +1,126 @@
+# Bug Report: Custom Admin Views cause TypeError with path-to-regexp
+
+## Summary
+Custom admin views registered via `admin.components.views` cause a `TypeError: Missing parameter name at 5` error from path-to-regexp when used together with `@payloadcms/plugin-multi-tenant`.
+
+## Environment
+- **Payload Version:** 3.68.4
+- **Next.js Version:** 15.5.9
+- **React Version:** 19.2.3
+- **Node.js Version:** 22.x
+- **Database:** PostgreSQL 17.6
+- **Plugin:** @payloadcms/plugin-multi-tenant 3.68.4
+
+## Steps to Reproduce
+
+1. Create a Payload 3.x project with the multi-tenant plugin
+2. Add a custom view to the admin config:
+
+```typescript
+// payload.config.ts
+export default buildConfig({
+ admin: {
+ user: Users.slug,
+ components: {
+ views: {
+ MyCustomView: {
+ Component: '@/components/admin/MyCustomView#MyCustomView',
+ path: '/my-custom-view',
+ },
+ },
+ },
+ },
+ plugins: [
+ multiTenantPlugin({
+ tenantsSlug: 'tenants',
+ collections: { /* ... */ },
+ }),
+ // other plugins...
+ ],
+})
+```
+
+3. Create a simple client component:
+
+```tsx
+// src/components/admin/MyCustomView.tsx
+'use client'
+
+import React from 'react'
+
+export const MyCustomView: React.FC = () => {
+ return (
+
+
Custom View
+
+ )
+}
+
+export default MyCustomView
+```
+
+4. Build and run the project
+5. Navigate to `/admin/my-custom-view`
+
+## Expected Behavior
+The custom view should render correctly.
+
+## Actual Behavior
+A server-side error occurs with the following message in production:
+
+```
+Uncaught Error: An error occurred in the Server Components render.
+The specific message is omitted in production builds to avoid leaking sensitive details.
+```
+
+When running in development or checking server logs, the actual error is:
+
+```
+⨯ TypeError: Missing parameter name at 5
+ at (.next/server/chunks/XXXX.js:5:9989)
+ at (.next/server/chunks/XXXX.js:5:10666)
+ at g (.next/server/chunks/XXXX.js:5:11896)
+ at e (.next/server/chunks/824.js:96:517138)
+ at (.next/server/app/(payload)/admin/[[...segments]]/page.js:1:31665)
+ at Array.find ()
+ at (.next/server/app/(payload)/admin/[[...segments]]/page.js:1:31644)
+ at ax (.next/server/app/(payload)/admin/[[...segments]]/page.js:1:34637) {
+ digest: '3964718924'
+}
+```
+
+## Additional Context
+
+### What works:
+- Admin panel without custom views
+- Components in `afterNavLinks` (e.g., TenantBreadcrumb)
+- Components in `beforeNavLinks` (e.g., DashboardNavLink)
+
+### What fails:
+- ANY custom view registered via `admin.components.views`, even the simplest component without any dependencies
+
+### Investigation findings:
+- The error originates from `path-to-regexp` (version 6.3.0)
+- The error occurs during route matching in `handleEndpoints.js`
+- The error message "Missing parameter name at 5" suggests an invalid route pattern like `/:` is being generated somewhere
+- This happens regardless of the view path used (tested with `/dashboard`, `/tenant-dashboard`, `/test-dashboard`)
+- The issue appears to be triggered by the combination of custom views and the multi-tenant plugin
+
+### Workaround
+Disable custom views and use only `afterNavLinks`/`beforeNavLinks` components:
+
+```typescript
+admin: {
+ components: {
+ afterNavLinks: ['@/components/admin/TenantBreadcrumb#TenantBreadcrumb'],
+ // views disabled due to bug
+ },
+},
+```
+
+## Related Plugins
+- `@payloadcms/plugin-multi-tenant` (may be related to the conflict)
+- `@payloadcms/plugin-redirects`
+- `@payloadcms/plugin-seo`
+- `@payloadcms/plugin-form-builder`
+- `@payloadcms/plugin-nested-docs`
diff --git a/CLAUDE.md b/CLAUDE.md
index 538fc7f..c028b2f 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -11,31 +11,55 @@ Multi-Tenant CMS für 4 Websites unter einer Payload CMS 3.x Instanz:
## Tech Stack
-- **CMS:** Payload CMS 3.x
-- **Framework:** Next.js 15.4.7
+- **CMS:** Payload CMS 3.68.4
+- **Framework:** Next.js 15.5.9
+- **React:** 19.2.3
- **Sprache:** TypeScript
-- **Datenbank:** PostgreSQL 17 (separater Server)
+- **Runtime:** Node.js 22.x
+- **Datenbank:** PostgreSQL 17.6 (separater Server)
- **Connection Pool:** PgBouncer 1.24.1 (Transaction-Mode)
-- **Reverse Proxy:** Caddy 2.10.2 mit Let's Encrypt
+- **Reverse Proxy:** Caddy 2.9.x mit Let's Encrypt (Dev) / Nginx (Prod)
- **Process Manager:** PM2
- **Package Manager:** pnpm
-- **Cache:** Redis (optional, mit In-Memory-Fallback)
+- **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 → 37.24.237.181 → Caddy (443) → Payload (3000)
- ↓
- PgBouncer (6432)
- ↓
- PostgreSQL (10.10.181.101:5432)
+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)
```
-| Server | IP | Funktion |
-| --------------------- | ------------- | ---------- |
-| sv-payload (LXC 700) | 10.10.181.100 | App Server |
-| sv-postgres (LXC 701) | 10.10.181.101 | Datenbank |
+| 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
@@ -758,6 +782,8 @@ Vollwertiger Hero-Slider mit:
| 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
@@ -985,11 +1011,21 @@ Automatisches Deployment auf Staging-Server bei Push auf `develop`:
## Dokumentation
-- `CLAUDE.md` - Diese Datei (Projekt-Übersicht)
-- `docs/INFRASTRUCTURE.md` - Server-Architektur & Deployment
+### 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
-- `docs/anleitungen/TODO.md` - Task-Liste & Roadmap
+
+### 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: 14.12.2025*
+*Letzte Aktualisierung: 18.12.2025*
diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md
new file mode 100644
index 0000000..03d8dbd
--- /dev/null
+++ b/docs/DEPLOYMENT.md
@@ -0,0 +1,525 @@
+# Deployment Guide - Payload CMS Multi-Tenant
+
+*Letzte Aktualisierung: 18. Dezember 2025*
+
+## Übersicht
+
+Dieses Dokument beschreibt den Deployment-Prozess für das Payload CMS Multi-Tenant-System.
+
+```
+┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
+│ DEVELOPMENT │ │ STAGING │ │ PRODUCTION │
+│ │ │ │ │ │
+│ sv-frontend │────▶│ sv-payload │────▶│ Hetzner 3 │
+│ Local Dev │ │ pl.porwoll.tech │ │ cms.c2sgmbh.de │
+│ │ │ │ │ │
+│ develop branch │ │ develop branch │ │ main branch │
+└─────────────────┘ └─────────────────┘ └─────────────────┘
+```
+
+---
+
+## Umgebungen
+
+| Umgebung | Server | URL | Branch | Zweck |
+|----------|--------|-----|--------|-------|
+| **Development** | sv-payload (LXC 700) | https://pl.porwoll.tech | `develop` | Entwicklung & Testing |
+| **Production** | Hetzner 3 | https://cms.c2sgmbh.de | `main` | Live-System |
+
+---
+
+## Staging Deployment (Development → pl.porwoll.tech)
+
+### Automatisch via GitHub Actions
+
+Bei jedem Push auf `develop` wird automatisch deployed:
+
+```yaml
+# .github/workflows/deploy-staging.yml
+on:
+ push:
+ branches: [develop]
+```
+
+**Ablauf:**
+1. Pre-deployment Checks (Lint, Tests)
+2. SSH-Verbindung zu sv-payload
+3. `git pull origin develop`
+4. `pnpm install`
+5. `pnpm payload migrate`
+6. `pnpm build`
+7. `pm2 restart payload`
+8. Health Check
+
+### Manuell auf sv-payload
+
+```bash
+# SSH zum Server
+ssh payload@10.10.181.100
+
+# Ins Projektverzeichnis
+cd /home/payload/payload-cms
+
+# Änderungen holen
+git pull origin develop
+
+# Dependencies aktualisieren
+pnpm install
+
+# Migrationen ausführen (falls vorhanden)
+pnpm payload migrate
+
+# Build erstellen
+pnpm build
+
+# PM2 neustarten
+pm2 restart payload
+
+# Logs prüfen
+pm2 logs payload --lines 20
+```
+
+### Mit Deploy-Script
+
+```bash
+# Auf sv-payload
+./scripts/deploy-staging.sh
+
+# Optionen:
+./scripts/deploy-staging.sh --skip-build # Nur Code-Update
+./scripts/deploy-staging.sh --skip-migrations # Ohne Migrationen
+```
+
+---
+
+## Production Deployment (main → cms.c2sgmbh.de)
+
+### Schritt 1: Merge zu main
+
+```bash
+# Auf dem Development-Server oder lokal
+git checkout main
+git pull origin main
+git merge develop
+git push origin main
+```
+
+### Schritt 2: Deploy auf Hetzner 3
+
+```bash
+# SSH zum Production-Server
+ssh payload@162.55.85.18
+
+# Deploy-Script ausführen
+~/deploy.sh
+```
+
+### Deploy-Script (~/deploy.sh)
+
+```bash
+#!/bin/bash
+set -e
+
+cd ~/payload-cms
+
+echo "📥 Pulling latest changes..."
+git pull origin main
+
+echo "📦 Installing dependencies..."
+pnpm install
+
+echo "🔄 Running migrations..."
+pnpm payload migrate
+
+echo "🏗️ Building..."
+pnpm build
+
+echo "🔄 Restarting PM2..."
+pm2 restart payload
+
+echo "✅ Deployment complete!"
+pm2 status
+```
+
+### Manuelles Deployment
+
+```bash
+ssh payload@162.55.85.18
+cd ~/payload-cms
+git pull origin main
+pnpm install
+pnpm payload migrate
+pnpm build
+pm2 restart payload
+```
+
+---
+
+## Rollback
+
+### Auf vorherigen Commit zurücksetzen
+
+```bash
+# Letzten funktionierenden Commit finden
+git log --oneline -10
+
+# Zurücksetzen (VORSICHT: Ändert History nicht)
+git checkout
+pnpm install
+pnpm build
+pm2 restart payload
+
+# Oder: Hard Reset (VORSICHT: Ändert History)
+git reset --hard
+git push --force origin main # Nur im Notfall!
+```
+
+### Datenbank-Rollback
+
+```bash
+# Backup wiederherstellen (nur bei Datenverlust)
+gunzip -c ~/backups/payload_db_YYYY-MM-DD_HH-MM-SS.sql.gz | \
+ psql -h localhost -U payload -d payload_db
+```
+
+---
+
+## Migrationen
+
+### Migration erstellen
+
+```bash
+# Auf Development-Server
+pnpm payload migrate:create
+
+# Generiert: src/migrations/YYYYMMDD_HHMMSS_.ts
+```
+
+### Migration ausführen
+
+```bash
+# Normale Ausführung
+pnpm payload migrate
+
+# Bei PgBouncer-Problemen: Direkte Verbindung
+./scripts/db-direct.sh migrate
+```
+
+### Migration-Status prüfen
+
+```bash
+pnpm payload migrate:status
+```
+
+---
+
+## Build-Konfiguration
+
+### Memory-Limits
+
+Der Server hat 8GB RAM. Build-Einstellungen:
+
+```json
+// package.json
+{
+ "scripts": {
+ "build": "cross-env NODE_OPTIONS='--no-deprecation --max-old-space-size=2048' next build"
+ }
+}
+```
+
+### Bei Memory-Problemen
+
+```bash
+# PM2 stoppen um Speicher freizugeben
+pm2 stop payload
+
+# Build mit reduziertem Memory
+NODE_OPTIONS="--no-deprecation --max-old-space-size=1024" pnpm build
+
+# PM2 wieder starten
+pm2 start payload
+```
+
+---
+
+## PM2 Konfiguration
+
+### ecosystem.config.cjs
+
+```javascript
+module.exports = {
+ apps: [
+ {
+ name: 'payload',
+ script: 'node_modules/.bin/next',
+ args: 'start',
+ cwd: '/home/payload/payload-cms',
+ instances: 1,
+ max_memory_restart: '2G',
+ env: {
+ NODE_ENV: 'production',
+ PORT: 3000
+ }
+ },
+ {
+ name: 'queue-worker',
+ script: './scripts/run-queue-worker.ts',
+ interpreter: 'node',
+ interpreter_args: '--import tsx',
+ cwd: '/home/payload/payload-cms',
+ instances: 1,
+ max_memory_restart: '500M'
+ }
+ ]
+}
+```
+
+### PM2 Befehle
+
+```bash
+pm2 status # Status aller Prozesse
+pm2 logs payload # Logs anzeigen
+pm2 logs queue-worker # Queue-Worker Logs
+pm2 restart payload # Neustart
+pm2 restart all # Alle neustarten
+pm2 save # Autostart-Konfiguration speichern
+pm2 startup # Systemd-Integration
+```
+
+---
+
+## Health Checks
+
+### Nach Deployment prüfen
+
+```bash
+# 1. PM2 Status
+pm2 status
+
+# 2. Logs auf Fehler prüfen
+pm2 logs payload --lines 50
+
+# 3. API erreichbar?
+curl -I https://cms.c2sgmbh.de/api/users
+
+# 4. Admin Panel erreichbar?
+curl -I https://cms.c2sgmbh.de/admin
+
+# 5. Redis verbunden?
+redis-cli ping
+```
+
+### Automatischer Health Check
+
+```bash
+#!/bin/bash
+# health-check.sh
+
+API_URL="https://cms.c2sgmbh.de/api"
+ADMIN_URL="https://cms.c2sgmbh.de/admin"
+
+# API Check
+if curl -sf "$API_URL/users" > /dev/null; then
+ echo "✅ API OK"
+else
+ echo "❌ API FAILED"
+ exit 1
+fi
+
+# Admin Check
+if curl -sf "$ADMIN_URL" > /dev/null; then
+ echo "✅ Admin OK"
+else
+ echo "❌ Admin FAILED"
+ exit 1
+fi
+
+echo "✅ All checks passed"
+```
+
+---
+
+## Umgebungsvariablen
+
+### Production (.env)
+
+```env
+# Datenbank
+DATABASE_URI=postgresql://payload:***@localhost:5432/payload_db
+PAYLOAD_SECRET=***
+
+# URLs
+PAYLOAD_PUBLIC_SERVER_URL=https://cms.c2sgmbh.de
+NEXT_PUBLIC_SERVER_URL=https://cms.c2sgmbh.de
+
+# Environment
+NODE_ENV=production
+PORT=3001
+
+# Redis
+REDIS_URL=redis://localhost:6379
+
+# Security
+CSRF_SECRET=***
+TRUST_PROXY=true
+```
+
+### Staging (.env)
+
+```env
+# Datenbank (via PgBouncer)
+DATABASE_URI=postgresql://payload:***@127.0.0.1:6432/payload_db
+PAYLOAD_SECRET=***
+
+# URLs
+PAYLOAD_PUBLIC_SERVER_URL=https://pl.porwoll.tech
+NEXT_PUBLIC_SERVER_URL=https://pl.porwoll.tech
+
+# Environment
+NODE_ENV=production
+PORT=3000
+
+# Redis
+REDIS_URL=redis://localhost:6379
+
+# Security
+CSRF_SECRET=***
+TRUST_PROXY=true
+```
+
+---
+
+## CI/CD Pipeline
+
+### GitHub Actions Workflows
+
+| Workflow | Trigger | Aktion |
+|----------|---------|--------|
+| `ci.yml` | Push/PR auf main, develop | Lint, Test, Build |
+| `security.yml` | Push/PR, Schedule | Security Scanning |
+| `deploy-staging.yml` | Push auf develop | Auto-Deploy zu Staging |
+
+### Secrets (GitHub)
+
+| Secret | Beschreibung |
+|--------|--------------|
+| `STAGING_SSH_KEY` | SSH Private Key für sv-payload |
+
+### Manuelles Deployment triggern
+
+```bash
+# Via GitHub CLI
+gh workflow run deploy-staging.yml
+
+# Mit skip_tests Option
+gh workflow run deploy-staging.yml -f skip_tests=true
+```
+
+---
+
+## Backup vor Deployment
+
+### Automatisches Backup
+
+Backups laufen täglich um 03:00 Uhr. Vor großen Deployments manuell:
+
+```bash
+# Auf Production
+~/backup.sh
+
+# Auf Staging
+/home/payload/backups/postgres/backup-db.sh --verbose
+```
+
+### Backup-Speicherorte
+
+| Server | Lokal | Offsite |
+|--------|-------|---------|
+| Production | ~/backups/ | - |
+| Staging | /home/payload/backups/postgres/ | s3://c2s/backups/postgres/ |
+
+---
+
+## Troubleshooting
+
+### Build schlägt fehl
+
+```bash
+# Cache löschen
+rm -rf .next
+pnpm build
+
+# Mit mehr Speicher
+NODE_OPTIONS="--max-old-space-size=4096" pnpm build
+```
+
+### PM2 startet nicht
+
+```bash
+# Logs prüfen
+pm2 logs payload --lines 100
+
+# Prozess komplett entfernen und neu starten
+pm2 delete payload
+pm2 start ecosystem.config.cjs
+```
+
+### Datenbank-Verbindung fehlgeschlagen
+
+```bash
+# PgBouncer Status (Staging)
+sudo systemctl status pgbouncer
+
+# PostgreSQL Status (Production)
+sudo systemctl status postgresql
+
+# Verbindung testen
+psql -h localhost -U payload -d payload_db -c "SELECT 1"
+```
+
+### Migration fehlgeschlagen
+
+```bash
+# Status prüfen
+pnpm payload migrate:status
+
+# Bei PgBouncer-Problemen
+./scripts/db-direct.sh migrate
+
+# Einzelne Migration manuell
+pnpm payload migrate --name 20251216_073000_add_video_collections
+```
+
+---
+
+## Checkliste
+
+### Vor dem Deployment
+
+- [ ] Alle Tests grün (`pnpm test`)
+- [ ] Build erfolgreich (`pnpm build`)
+- [ ] Migrationen getestet auf Staging
+- [ ] Backup erstellt (bei Datenbank-Änderungen)
+- [ ] CLAUDE.md/Docs aktualisiert (bei neuen Features)
+
+### Nach dem Deployment
+
+- [ ] `pm2 status` - Prozesse online
+- [ ] Admin Panel erreichbar
+- [ ] API funktioniert
+- [ ] Logs auf Fehler geprüft
+- [ ] Neue Features manuell getestet
+
+---
+
+## Kontakte
+
+| Rolle | Kontakt |
+|-------|---------|
+| Server Admin | Martin Porwoll |
+| Repository | https://github.com/complexcaresolutions/cms.c2sgmbh |
+
+---
+
+*Dokumentation: Complex Care Solutions GmbH | 18.12.2025*
diff --git a/docs/INFRASTRUCTURE.md b/docs/INFRASTRUCTURE.md
index 1db94ec..ddcff69 100644
--- a/docs/INFRASTRUCTURE.md
+++ b/docs/INFRASTRUCTURE.md
@@ -1,85 +1,174 @@
-# Payload CMS Multi-Tenant Infrastructure
+# Infrastruktur Dokumentation
-> Letzte Aktualisierung: 09.12.2025
+*Letzte Aktualisierung: 18. Dezember 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
+## Gesamtübersicht
```
┌─────────────────────────────────────────────────────────────────────────────────────┐
-│ GESAMTARCHITEKTUR │
+│ INFRASTRUKTUR ÜBERSICHT │
│ │
-│ ┌───────────────────────────────────────────────────────────────────────────────┐ │
-│ │ 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 │ │
-│ └─────────────────────┘ │
+│ INTERNET │
+│ │ │
+│ │ Vodafone Business │
+│ │ 5 öffentliche IPs │
+│ │ │
+│ ▼ │
+│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
+│ │ UBIQUITI DREAM MACHINE PRO SE │ │
+│ │ │ │
+│ │ 37.24.237.178 │ Internetzugang │ │
+│ │ 37.24.237.179 │ cloud.complexcaresolutions.de → 10.10.179.100 │ │
+│ │ 37.24.237.180 │ zh3.de (Nginx PM) → 10.10.180.100 │ │
+│ │ 37.24.237.181 │ porwoll.tech (Caddy) → 10.10.181.99 │ │
+│ │ 37.24.237.182 │ FREI (Reserve) │ │
+│ │ │ │
+│ └──────────────────────────────────┬──────────────────────────────────────────┘ │
+│ │
+│ CLOUDFLARE (Proxy) │
+│ ├── zh3.de + Subdomains → 37.24.237.180 │
+│ ├── porwoll.tech + *.porwoll.tech → 37.24.237.181 │
+│ └── porwoll.org (intern DNS only) │
+│ │
+│ HETZNER (Extern) │
+│ ├── 78.46.87.137 (Hetzner 1 - zweitmeinu.ng) │
+│ ├── 94.130.141.114 (Hetzner 2 - Porwoll) │
+│ └── 162.55.85.18 (Hetzner 3 - Payload Prod) │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
```
---
-## Server-Details
+## Öffentliche IP-Adressen
-### HETZNER 3 - Backend & Analytics (Produktion)
+| IP | Verwendung | Ziel (intern) |
+|----|------------|---------------|
+| 37.24.237.178 | Internetzugang (Default) | - |
+| 37.24.237.179 | cloud.complexcaresolutions.de | 10.10.179.100 (Nextcloud) |
+| 37.24.237.180 | zh3.de (via Cloudflare) | 10.10.180.100 (Nginx PM) |
+| 37.24.237.181 | porwoll.tech (Cloudflare) | 10.10.181.99 (Caddy) |
+| 37.24.237.182 | FREI (Reserve) | - |
-| 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
+## VLANs
+
+| VLAN | Name | Subnetz | Zweck |
+|------|------|---------|-------|
+| 40 | c2s-prd | 10.10.40.0/24 | Produktion |
+| 90 | c2s-mgt | 10.10.90.0/24 | Management (Proxmox) |
+| 179 | c2s-179 | 10.10.179.0/24 | Cloud Services |
+| 180 | c2s-180 | 10.10.180.0/24 | Web Services (zh3.de) |
+| 181 | c2s-181 | 10.10.181.0/24 | Development (porwoll.tech) |
+
+---
+
+## VLAN 181 - Development (porwoll.tech)
+
+| ID | Hostname | IP | Service | Status |
+|----|----------|-----|---------|--------|
+| 699 | sv-caddy | 10.10.181.99 | Caddy Reverse Proxy | ✅ Running |
+| 700 | sv-payload | 10.10.181.100 | Payload CMS Dev | ✅ Running |
+| 701 | sv-postgres | 10.10.181.101 | PostgreSQL 17 + Redis Commander | ✅ Running |
+| 702 | sv-dev-payload | 10.10.181.102 | Payload Test | ⏸️ Stopped |
+| 703 | sv-analytics | 10.10.181.103 | Umami Analytics | ✅ Running |
+| 704 | sv-frontend | 10.10.181.104 | Frontend Dev (9 Projekte) | ✅ Running |
+
+---
+
+## sv-frontend (LXC 704) - Frontend Development
+
+**SSH:** `ssh frontend@10.10.181.104`
+
+### Software Stack
+- Node.js 22.x
+- pnpm
+- Next.js 16.0.10
+- Claude Code 2.0.72
+- Codex CLI 0.73.0
+- Gemini CLI 0.21.2
+
+### Projekte & Ports
+
+| Port | Service | Repository | URL |
+|------|---------|------------|-----|
+| 3000 | frontend-porwoll | frontend.porwoll.de | porwoll-dev.porwoll.tech |
+| 3001 | frontend-blogwoman | frontend.blogwoman.de | blogwoman-dev.porwoll.tech |
+| 3002 | frontend-caroline-com | frontend.caroline-porwoll.com | caroline-com-dev.porwoll.tech |
+| 3003 | frontend-caroline-de | frontend.caroline-porwoll.de | caroline-de-dev.porwoll.tech |
+| 3004 | frontend-ccs | frontend.complexcaresolutions.de | ccs-dev.porwoll.tech |
+| 3005 | frontend-gunshin | frontend.gunshin.de | gunshin-dev.porwoll.tech |
+| 3006 | frontend-sensual | frontend.sensualmoment.de | sensual-dev.porwoll.tech |
+| 3007 | frontend-zweitmeinu | frontend.zweitmeinu.ng | zweitmeinu-dev.porwoll.tech |
+| 3008 | frontend-zytoskandal | frontend.zytoskandal.de | zytoskandal-dev.porwoll.tech |
+
+### Service-Verwaltung
+
+```bash
+# Service starten
+systemctl start frontend-porwoll
+
+# Service stoppen
+systemctl stop frontend-porwoll
+
+# Alle Status
+systemctl status frontend-*
+```
+
+---
+
+## sv-caddy (LXC 699) - Reverse Proxy
+
+- **IP:** 10.10.181.99
+- **Software:** Caddy 2.9.x + Cloudflare DNS Plugin
+- **SSL:** Wildcard *.porwoll.tech via Let's Encrypt DNS-Challenge
+- **Config:** `/etc/caddy/Caddyfile`
+
+### Routing
+
+| URL | Backend |
+|-----|---------|
+| pl.porwoll.tech | 10.10.181.100:3000 |
+| redis.porwoll.tech | 10.10.181.101:8081 |
+| umami.porwoll.tech | 10.10.181.103:3000 |
+| *-dev.porwoll.tech | 10.10.181.104:300x |
+
+---
+
+## GitHub Organisation: complexcaresolutions
+
+| Repository | Beschreibung | Visibility |
+|------------|--------------|------------|
+| cms.c2sgmbh | Payload CMS Backend | Internal |
+| frontend.porwoll.de | porwoll.de Frontend | Internal |
+| frontend.blogwoman.de | blogwoman.de Frontend | Internal |
+| frontend.caroline-porwoll.com | caroline-porwoll.com Frontend | Internal |
+| frontend.caroline-porwoll.de | caroline-porwoll.de Frontend | Internal |
+| frontend.complexcaresolutions.de | CCS Website Frontend | Internal |
+| frontend.gunshin.de | gunshin.de Frontend | Internal |
+| frontend.sensualmoment.de | sensualmoment.de Frontend | Internal |
+| frontend.zweitmeinu.ng | zweitmeinu.ng Frontend | Internal |
+| frontend.zytoskandal.de | zytoskandal.de Frontend | Internal |
+
+---
+
+## Hetzner 3 - Payload Production
+
+- **IP:** 162.55.85.18
+- **Domain:** cms.c2sgmbh.de
+- **User:** payload
+- **SSH:** `ssh payload@162.55.85.18`
+
+### Software
+- Payload CMS 3.68.4
+- Next.js 15.5.9
+- React 19.2.3
+- PostgreSQL 17
+- Redis
+- Umami Analytics
+
+### Services
| Service | User | Port | URL | Status |
|---------|------|------|-----|--------|
@@ -88,501 +177,66 @@ Diese Dokumentation beschreibt die Infrastruktur eines Payload CMS 3.x Multi-Ten
| 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
+### Deploy
```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
+~/deploy.sh
```
---
-## 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
+## Development Workflow
```
-tenants - Mandanten-Stammdaten
-tenants_domains - Domain-Zuordnungen
-users_tenants - User-Mandanten-Beziehung (N:M)
+DEVELOPMENT STAGING PRODUCTION
+sv-frontend → sv-payload → Hetzner 03
+porwoll.tech pl.porwoll.tech cms.c2sgmbh.de
+ develop branch main branch
```
----
-
-## 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
-```
+**AI Tools:** Claude Code, Codex CLI, Gemini CLI, VS Code Remote-SSH
---
## 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 |
+### Development (porwoll.tech)
+
+| Service | URL |
+|---------|-----|
+| Portal | https://porwoll.tech |
+| Payload CMS | https://pl.porwoll.tech |
+| Redis Commander | https://redis.porwoll.tech |
+| Umami Analytics | https://umami.porwoll.tech |
+| Frontend porwoll.de | https://porwoll-dev.porwoll.tech |
+| (8 weitere) | https://*-dev.porwoll.tech |
+
+### Production (Hetzner)
+
+| Service | URL |
+|---------|-----|
+| Payload Admin | https://cms.c2sgmbh.de/admin |
+| Payload API | https://cms.c2sgmbh.de/api |
+| Umami Analytics | https://analytics.c2sgmbh.de |
---
-## SSH Schnellzugriff
+## Quick Reference
```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
+# Frontend-Server
+ssh frontend@10.10.181.104
-# Hetzner Server
-ssh root@78.46.87.137 # Hetzner 1 (CCS)
-ssh root@94.130.141.114 # Hetzner 2 (Porwoll)
+# Hetzner 3 Production
+ssh payload@162.55.85.18
-# Entwicklung (Proxmox)
-ssh payload@10.10.181.100 # sv-payload
-ssh root@10.10.181.101 # sv-postgres
+# Caddy neu laden
+ssh root@10.10.181.99 "systemctl reload caddy"
+
+# Frontend Service starten
+systemctl start frontend-porwoll
```
---
-## 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*
+*Dokumentation: Martin Porwoll | Complex Care Solutions GmbH | 18.12.2025*
diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md
new file mode 100644
index 0000000..bc0a1d2
--- /dev/null
+++ b/docs/PROJECT_STATUS.md
@@ -0,0 +1,206 @@
+# Projekt Status - Dezember 2025
+
+**Stand:** 18. Dezember 2025
+
+## Zusammenfassung
+
+Die komplette Entwicklungsinfrastruktur ist eingerichtet und funktionsfähig:
+- Payload CMS Multi-Tenant (Dev + Prod)
+- Multi-Frontend Development Environment
+- AI-gestützte Entwicklungstools
+- Reverse Proxy Stack (Caddy + Nginx)
+- Analytics (Umami)
+
+---
+
+## ✅ Abgeschlossen
+
+### Infrastruktur VLAN 181 (Development)
+
+| 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 Cmd | ✅ 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-Project Next.js | ✅ Running |
+
+### Hetzner 3 (Production)
+
+- [x] Debian 13 Installation
+- [x] PostgreSQL 17 mit payload_db und umami_db
+- [x] Redis Cache
+- [x] Payload CMS Production (cms.c2sgmbh.de)
+- [x] Umami Analytics Production (analytics.c2sgmbh.de)
+- [x] Nginx Reverse Proxy mit Let's Encrypt
+- [x] PM2 Process Management
+- [x] Claude Code CLI
+- [x] Backup-Scripts (täglich)
+- [x] CVE-2025-55182 Hotfix
+
+### Caddy Reverse Proxy (sv-caddy)
+
+- [x] Caddy 2.9.x mit Cloudflare DNS Plugin
+- [x] Wildcard SSL für *.porwoll.tech
+- [x] Cloudflare DNS-Challenge
+- [x] Routing für alle Services
+- [x] Security Headers
+
+### Payload CMS
+
+- [x] Multi-Tenant Plugin
+- [x] Redis Caching
+- [x] Package-Versionen synchronisiert:
+ - Next.js 15.5.9
+ - React 19.2.3
+ - Payload 3.68.4
+- [x] GitHub Repository (complexcaresolutions/cms.c2sgmbh)
+
+### sv-frontend (Multi-Project)
+
+- [x] Node.js 22.x + pnpm
+- [x] AI-Tools installiert:
+ - Claude Code 2.0.72
+ - Codex CLI 0.73.0
+ - Gemini CLI 0.21.2
+- [x] 9 Frontend-Projekte initialisiert
+- [x] Systemd Services (Ports 3000-3008)
+- [x] SSH-Zugriff mit Key
+- [x] VS Code Remote-SSH kompatibel
+
+### GitHub Repositories
+
+Organisation: **complexcaresolutions** (Internal)
+
+- [x] cms.c2sgmbh (Payload CMS)
+- [x] frontend.porwoll.de
+- [x] frontend.blogwoman.de
+- [x] frontend.caroline-porwoll.com
+- [x] frontend.caroline-porwoll.de
+- [x] frontend.complexcaresolutions.de
+- [x] frontend.gunshin.de
+- [x] frontend.sensualmoment.de
+- [x] frontend.zweitmeinu.ng
+- [x] frontend.zytoskandal.de
+
+---
+
+## ⚠️ Kritische Aufgaben
+
+| Priorität | Aufgabe | Status |
+|-----------|---------|--------|
+| 🔴 | Umami Dev Admin-Passwort ändern | Offen |
+| 🔴 | auth.zh3.de - 502 Bad Gateway beheben | Offen |
+| 🟡 | pve04/pve05/Backup: Enterprise Repo | Offen |
+
+---
+
+## 🔜 Nächste Schritte
+
+### Kurzfristig (diese Woche)
+
+1. [ ] Porwoll.de Frontend-Entwicklung starten
+2. [ ] VS Code Remote-SSH testen
+3. [ ] Claude Code auf sv-frontend authentifizieren
+
+### Mittelfristig (Januar 2025)
+
+4. [ ] Frontend-Staging auf Hetzner 3 einrichten
+5. [ ] GitHub Actions für Deployment
+6. [ ] Content Collections in Payload erweitern
+7. [ ] Design-System (Tailwind + Shadcn/UI)
+
+### Langfristig (Q1 2025)
+
+8. [ ] Alle 9 Frontends entwickeln
+9. [ ] Migration von Plesk-Domains zu neuer Infra
+10. [ ] CI/CD Pipeline komplett
+11. [ ] Monitoring & Alerting
+
+---
+
+## 📊 Service-URLs
+
+### Development (porwoll.tech)
+
+| Service | URL | Status |
+|---------|-----|--------|
+| Portal | https://porwoll.tech | ✅ |
+| Payload CMS | https://pl.porwoll.tech | ✅ |
+| Redis Commander | https://redis.porwoll.tech | ✅ |
+| Umami Analytics | https://umami.porwoll.tech | ✅ |
+| Frontend porwoll.de | https://porwoll-dev.porwoll.tech | ✅ |
+| (8 weitere) | https://*-dev.porwoll.tech | ⏸️ On-Demand |
+
+### Production (Hetzner)
+
+| Service | URL | Status |
+|---------|-----|--------|
+| Payload CMS | https://cms.c2sgmbh.de | ✅ |
+| Umami Analytics | https://analytics.c2sgmbh.de | ✅ |
+
+---
+
+## 🔧 Quick Commands
+
+### sv-frontend
+
+```bash
+# SSH Zugang
+ssh frontend@10.10.181.104
+
+# Service starten/stoppen
+systemctl start frontend-porwoll
+systemctl stop frontend-porwoll
+
+# AI-Tools
+claude # Claude Code
+codex # Codex CLI
+gemini # Gemini CLI
+```
+
+### Hetzner 3
+
+```bash
+# SSH Zugang
+ssh payload@162.55.85.18
+
+# Deploy
+~/deploy.sh
+
+# Logs
+pm2 logs payload
+```
+
+---
+
+## 📝 Änderungsprotokoll
+
+### 18.12.2025
+- sv-frontend (LXC 704) komplett eingerichtet
+- 9 GitHub Repositories erstellt
+- Alle Next.js Projekte initialisiert
+- Systemd Services für alle Frontends
+- Caddy Routing für *-dev.porwoll.tech
+- AI-Tools (Claude Code, Codex, Gemini) installiert
+- Infrastruktur-Dokumentation aktualisiert
+
+### 12.12.2025
+- sv-caddy mit Cloudflare DNS-Challenge
+- Wildcard SSL für porwoll.tech
+- Redis Commander auf sv-postgres
+- pgAdmin4 entfernt (MIME-Type Probleme)
+
+### 11.12.2025
+- Hetzner 3 Production Setup
+- Payload CMS Migration
+- Umami Analytics Production
+- Redis Caching (Dev + Prod)
+- Package-Versionen synchronisiert
+
+### 26.11.2025
+- Initial Payload CMS Setup
+- Multi-Tenant Plugin
+- PostgreSQL auf sv-postgres
+- Erste Tenants angelegt
diff --git a/docs/anleitungen/Analytics.md b/docs/anleitungen/Analytics.md
new file mode 100644
index 0000000..fc6b9aa
--- /dev/null
+++ b/docs/anleitungen/Analytics.md
@@ -0,0 +1,981 @@
+# ANALYTICS-LÖSUNG: Implementierungsübersicht für Payload CMS
+
+*Letzte Aktualisierung: 18. Dezember 2025*
+
+## Kontext
+
+Du entwickelst das Multi-Tenant Payload CMS Backend und Next.js Frontend für 4 Websites. Diese Dokumentation beschreibt die Analytics-Lösung, die in das Frontend integriert werden muss.
+
+**Wichtig:** Frontends verwenden die **Production-API** (cms.c2sgmbh.de) und **Production-Analytics** (analytics.c2sgmbh.de).
+
+---
+
+## Architektur-Übersicht
+
+```
+┌─────────────────────────────────────────────────────────────────────────────────────┐
+│ ANALYTICS ARCHITEKTUR │
+│ │
+│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
+│ │ OHNE CONSENT (immer aktiv) │ │
+│ │ │ │
+│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
+│ │ │ UMAMI ANALYTICS │ │ │
+│ │ │ │ │ │
+│ │ │ Server: sv-analytics (10.10.181.103:3000) │ │ │
+│ │ │ Dashboard: http://10.10.181.103:3000 │ │ │
+│ │ │ Script: /custom.js (Anti-Adblock) │ │ │
+│ │ │ Endpoint: /api/send │ │ │
+│ │ │ │ │ │
+│ │ │ Features: │ │ │
+│ │ │ • Cookieless Tracking (DSGVO-konform ohne Einwilligung) │ │ │
+│ │ │ • Pageviews, Sessions, Referrer, UTM-Parameter │ │ │
+│ │ │ • Custom Events (Newsletter, Formulare, CTAs, Downloads) │ │ │
+│ │ │ • 100% Erfassung aller Besucher │ │ │
+│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
+│ └─────────────────────────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
+│ │ MIT CONSENT (Kategorie: "marketing") │ │
+│ │ │ │
+│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
+│ │ │ GOOGLE ADS CONVERSION │ │ │
+│ │ │ │ │ │
+│ │ │ Client-Side (bei Consent): │ │ │
+│ │ │ • Google Ads Tag (gtag.js) │ │ │
+│ │ │ • Conversion Tracking │ │ │
+│ │ │ • Remarketing Audiences │ │ │
+│ │ │ │ │ │
+│ │ │ Server-Side (immer, anonymisiert): │ │ │
+│ │ │ • Google Ads Conversion API │ │ │
+│ │ │ • Enhanced Conversions (gehashte E-Mail) │ │ │
+│ │ │ • GCLID-basierte Attribution │ │ │
+│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
+│ │ │ │
+│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
+│ │ │ GOOGLE CONSENT MODE v2 │ │ │
+│ │ │ │ │ │
+│ │ │ Integration mit bestehendem Orestbida Consent-Banner │ │ │
+│ │ │ Kategorie "marketing" steuert: │ │ │
+│ │ │ • ad_storage │ │ │
+│ │ │ • ad_user_data │ │ │
+│ │ │ • ad_personalization │ │ │
+│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
+│ └─────────────────────────────────────────────────────────────────────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Infrastruktur
+
+### Umami Server
+
+| Umgebung | Server | URL | Dashboard |
+|----------|--------|-----|-----------|
+| **Production** | Hetzner 3 | https://analytics.c2sgmbh.de | https://analytics.c2sgmbh.de |
+| **Development** | sv-analytics (LXC 703) | https://umami.porwoll.tech | https://umami.porwoll.tech |
+
+| Eigenschaft | Production | Development |
+|-------------|------------|-------------|
+| **Tracking Script** | https://analytics.c2sgmbh.de/script.js | https://umami.porwoll.tech/script.js |
+| **API Endpoint** | https://analytics.c2sgmbh.de/api/send | https://umami.porwoll.tech/api/send |
+| **Datenbank** | umami_db auf Hetzner 3 | umami_db auf sv-postgres |
+
+### Website-IDs (Multi-Tenant)
+
+Die Website-IDs werden in Umami für jeden Tenant erstellt:
+
+```typescript
+// src/config/analytics.ts
+
+export const UMAMI_WEBSITE_IDS: Record = {
+ 'porwoll.de': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
+ 'complexcaresolutions.de': 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy',
+ 'gunshin.de': 'zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz',
+ 'zweitmeinu.ng': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
+}
+
+// Production URL für Frontends
+export const UMAMI_HOST = process.env.NEXT_PUBLIC_UMAMI_HOST || 'https://analytics.c2sgmbh.de'
+```
+
+---
+
+## Frontend-Integration
+
+### 1. Umami Script Komponente
+
+```typescript
+// src/components/analytics/UmamiScript.tsx
+
+'use client'
+
+import Script from 'next/script'
+
+interface UmamiScriptProps {
+ websiteId: string
+ host?: string
+}
+
+export function UmamiScript({
+ websiteId,
+ host = 'https://analytics.c2sgmbh.de' // Production default
+}: UmamiScriptProps) {
+ if (!websiteId) return null
+
+ return (
+
+ )
+}
+```
+
+### 2. Layout Integration (Multi-Tenant)
+
+```typescript
+// src/app/layout.tsx
+
+import { UmamiScript } from '@/components/analytics/UmamiScript'
+import { UMAMI_WEBSITE_IDS, UMAMI_HOST } from '@/config/analytics'
+import { getCurrentTenant } from '@/lib/tenant'
+
+export default async function RootLayout({
+ children
+}: {
+ children: React.ReactNode
+}) {
+ const tenant = await getCurrentTenant()
+ const umamiWebsiteId = UMAMI_WEBSITE_IDS[tenant.domain]
+
+ return (
+
+
+ {children}
+
+ {/* Umami Analytics - Läuft OHNE Consent (cookieless) */}
+ {umamiWebsiteId && (
+
+ )}
+
+
+ )
+}
+```
+
+### 3. Analytics Hook für Custom Events
+
+```typescript
+// src/hooks/useAnalytics.ts
+
+'use client'
+
+import { useCallback } from 'react'
+
+declare global {
+ interface Window {
+ umami?: {
+ track: (eventName: string, eventData?: Record) => void
+ }
+ gtag?: (...args: unknown[]) => void
+ CookieConsent?: {
+ acceptedCategory: (category: string) => boolean
+ }
+ }
+}
+
+export function useAnalytics() {
+ /**
+ * Generisches Event-Tracking
+ */
+ const trackEvent = useCallback((
+ eventName: string,
+ eventData?: Record
+ ) => {
+ if (typeof window !== 'undefined' && window.umami) {
+ window.umami.track(eventName, eventData)
+ }
+ }, [])
+
+ /**
+ * Newsletter Anmeldung
+ */
+ const trackNewsletterSubscribe = useCallback((source: string = 'unknown') => {
+ trackEvent('newsletter_subscribe', { source })
+ }, [trackEvent])
+
+ /**
+ * Newsletter Bestätigung (Double Opt-In)
+ */
+ const trackNewsletterConfirm = useCallback(() => {
+ trackEvent('newsletter_confirm')
+ }, [trackEvent])
+
+ /**
+ * Kontaktformular abgesendet
+ */
+ const trackContactFormSubmit = useCallback((formType: string = 'contact') => {
+ trackEvent('contact_form_submit', { form_type: formType })
+ }, [trackEvent])
+
+ /**
+ * CTA Klick
+ */
+ const trackCtaClick = useCallback((
+ ctaName: string,
+ ctaLocation: string = 'unknown'
+ ) => {
+ trackEvent('cta_click', { cta_name: ctaName, location: ctaLocation })
+ }, [trackEvent])
+
+ /**
+ * Download
+ */
+ const trackDownload = useCallback((
+ fileName: string,
+ fileType: string = 'unknown'
+ ) => {
+ trackEvent('download', { file_name: fileName, file_type: fileType })
+ }, [trackEvent])
+
+ /**
+ * Funnel-Step (für Conversion-Funnels)
+ */
+ const trackFunnelStep = useCallback((
+ funnelName: string,
+ stepNumber: number,
+ stepName: string
+ ) => {
+ trackEvent('funnel_step', {
+ funnel: funnelName,
+ step: stepNumber,
+ step_name: stepName
+ })
+ }, [trackEvent])
+
+ /**
+ * Scroll-Tiefe
+ */
+ const trackScrollDepth = useCallback((depth: number) => {
+ trackEvent('scroll_depth', { depth_percent: depth })
+ }, [trackEvent])
+
+ /**
+ * Externer Link Klick
+ */
+ const trackExternalLink = useCallback((url: string) => {
+ trackEvent('external_link', { url })
+ }, [trackEvent])
+
+ return {
+ trackEvent,
+ trackNewsletterSubscribe,
+ trackNewsletterConfirm,
+ trackContactFormSubmit,
+ trackCtaClick,
+ trackDownload,
+ trackFunnelStep,
+ trackScrollDepth,
+ trackExternalLink,
+ }
+}
+```
+
+### 4. Server-Side Event Tracking
+
+```typescript
+// src/lib/analytics.server.ts
+
+const UMAMI_HOST = process.env.UMAMI_HOST || 'https://analytics.c2sgmbh.de'
+const UMAMI_WEBSITE_ID = process.env.UMAMI_WEBSITE_ID
+
+interface ServerEventParams {
+ event: string
+ url: string
+ websiteId?: string
+ referrer?: string
+ data?: Record
+}
+
+/**
+ * Server-Side Event an Umami senden
+ * Für Events die im Backend passieren (z.B. Newsletter-Bestätigung)
+ */
+export async function trackServerEvent(params: ServerEventParams) {
+ const websiteId = params.websiteId || UMAMI_WEBSITE_ID
+
+ if (!websiteId) {
+ console.warn('[Analytics] No websiteId configured for server-side tracking')
+ return
+ }
+
+ try {
+ const response = await fetch(`${UMAMI_HOST}/api/send`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'User-Agent': 'Payload-CMS-Server/1.0',
+ },
+ body: JSON.stringify({
+ type: 'event',
+ payload: {
+ website: websiteId,
+ url: params.url,
+ referrer: params.referrer || '',
+ name: params.event,
+ data: params.data,
+ },
+ }),
+ })
+
+ if (!response.ok) {
+ console.error('[Analytics] Server event failed:', response.status)
+ }
+ } catch (error) {
+ console.error('[Analytics] Server event error:', error)
+ }
+}
+```
+
+---
+
+## Komponenten-Beispiele
+
+### Newsletter-Formular mit Tracking
+
+```typescript
+// src/components/forms/NewsletterForm.tsx
+
+'use client'
+
+import { useState } from 'react'
+import { useAnalytics } from '@/hooks/useAnalytics'
+
+interface NewsletterFormProps {
+ source?: string // z.B. 'footer', 'hero', 'popup'
+}
+
+export function NewsletterForm({ source = 'unknown' }: NewsletterFormProps) {
+ const [email, setEmail] = useState('')
+ const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle')
+ const { trackNewsletterSubscribe, trackFunnelStep } = useAnalytics()
+
+ async function handleSubmit(e: React.FormEvent) {
+ e.preventDefault()
+ setStatus('loading')
+
+ // Funnel-Step tracken
+ trackFunnelStep('newsletter', 1, 'form_submitted')
+
+ try {
+ const response = await fetch('/api/newsletter/subscribe', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ email, source }),
+ })
+
+ if (response.ok) {
+ setStatus('success')
+ // Erfolg tracken
+ trackNewsletterSubscribe(source)
+ trackFunnelStep('newsletter', 2, 'subscription_pending')
+ } else {
+ setStatus('error')
+ }
+ } catch {
+ setStatus('error')
+ }
+ }
+
+ if (status === 'success') {
+ return (
+
+ Bitte bestätige deine E-Mail-Adresse.
+
+ )
+ }
+
+ return (
+
+ )
+}
+```
+
+### CTA Button mit Tracking
+
+```typescript
+// src/components/ui/TrackedButton.tsx
+
+'use client'
+
+import { useAnalytics } from '@/hooks/useAnalytics'
+import Link from 'next/link'
+
+interface TrackedButtonProps {
+ href: string
+ ctaName: string
+ location?: string
+ children: React.ReactNode
+ className?: string
+ external?: boolean
+}
+
+export function TrackedButton({
+ href,
+ ctaName,
+ location = 'unknown',
+ children,
+ className,
+ external = false,
+}: TrackedButtonProps) {
+ const { trackCtaClick, trackExternalLink } = useAnalytics()
+
+ function handleClick() {
+ trackCtaClick(ctaName, location)
+ if (external) {
+ trackExternalLink(href)
+ }
+ }
+
+ if (external) {
+ return (
+
+ {children}
+
+ )
+ }
+
+ return (
+
+ {children}
+
+ )
+}
+```
+
+### Download-Link mit Tracking
+
+```typescript
+// src/components/ui/TrackedDownload.tsx
+
+'use client'
+
+import { useAnalytics } from '@/hooks/useAnalytics'
+
+interface TrackedDownloadProps {
+ href: string
+ fileName: string
+ fileType?: string
+ children: React.ReactNode
+ className?: string
+}
+
+export function TrackedDownload({
+ href,
+ fileName,
+ fileType = 'document',
+ children,
+ className,
+}: TrackedDownloadProps) {
+ const { trackDownload } = useAnalytics()
+
+ function handleClick() {
+ trackDownload(fileName, fileType)
+ }
+
+ return (
+
+ {children}
+
+ )
+}
+```
+
+---
+
+## Google Ads Integration
+
+### Consent Mode v2 Komponente
+
+```typescript
+// src/components/analytics/GoogleConsentMode.tsx
+
+'use client'
+
+import Script from 'next/script'
+import { useEffect } from 'react'
+
+interface GoogleConsentModeProps {
+ googleAdsId: string
+}
+
+export function GoogleConsentMode({ googleAdsId }: GoogleConsentModeProps) {
+ useEffect(() => {
+ // Consent-Änderungen von Orestbida abonnieren
+ window.addEventListener('cc:onConsent', handleConsentChange)
+ window.addEventListener('cc:onChange', handleConsentChange)
+
+ // Initial setzen
+ updateGoogleConsent()
+
+ return () => {
+ window.removeEventListener('cc:onConsent', handleConsentChange)
+ window.removeEventListener('cc:onChange', handleConsentChange)
+ }
+ }, [])
+
+ function handleConsentChange() {
+ updateGoogleConsent()
+ }
+
+ function updateGoogleConsent() {
+ if (typeof window.gtag !== 'function') return
+
+ const cc = window.CookieConsent
+ if (!cc) return
+
+ const hasMarketing = cc.acceptedCategory('marketing')
+
+ window.gtag('consent', 'update', {
+ 'ad_storage': hasMarketing ? 'granted' : 'denied',
+ 'ad_user_data': hasMarketing ? 'granted' : 'denied',
+ 'ad_personalization': hasMarketing ? 'granted' : 'denied',
+ 'analytics_storage': 'denied', // Wir nutzen Umami
+ })
+ }
+
+ return (
+ <>
+ {/* Consent Default (vor gtag.js) */}
+
+
+ {/* Google Ads Tag */}
+
+
+
+ >
+ )
+}
+```
+
+### GCLID Hook (für Conversion Attribution)
+
+```typescript
+// src/hooks/useGclid.ts
+
+'use client'
+
+import { useEffect } from 'react'
+import { useSearchParams } from 'next/navigation'
+
+const GCLID_STORAGE_KEY = 'gclid'
+const GCLID_EXPIRY_DAYS = 90
+
+/**
+ * GCLID aus URL erfassen und speichern
+ */
+export function useGclid() {
+ const searchParams = useSearchParams()
+
+ useEffect(() => {
+ const gclid = searchParams.get('gclid')
+
+ if (gclid) {
+ const data = {
+ value: gclid,
+ expires: Date.now() + (GCLID_EXPIRY_DAYS * 24 * 60 * 60 * 1000)
+ }
+ localStorage.setItem(GCLID_STORAGE_KEY, JSON.stringify(data))
+ }
+ }, [searchParams])
+}
+
+/**
+ * Gespeicherte GCLID abrufen
+ */
+export function getStoredGclid(): string | null {
+ if (typeof window === 'undefined') return null
+
+ try {
+ const stored = localStorage.getItem(GCLID_STORAGE_KEY)
+ if (!stored) return null
+
+ const data = JSON.parse(stored)
+
+ if (Date.now() > data.expires) {
+ localStorage.removeItem(GCLID_STORAGE_KEY)
+ return null
+ }
+
+ return data.value
+ } catch {
+ return null
+ }
+}
+```
+
+### Client-Side Conversion Tracking
+
+```typescript
+// src/lib/google-ads.ts
+
+declare global {
+ interface Window {
+ gtag: (...args: unknown[]) => void
+ CookieConsent: {
+ acceptedCategory: (category: string) => boolean
+ }
+ }
+}
+
+interface ConversionParams {
+ conversionId: string
+ value?: number
+ currency?: string
+ transactionId?: string
+}
+
+/**
+ * Client-Side Conversion (nur bei Marketing-Consent)
+ */
+export function trackConversion(params: ConversionParams) {
+ if (typeof window === 'undefined') return
+ if (typeof window.gtag !== 'function') return
+
+ const hasConsent = window.CookieConsent?.acceptedCategory('marketing')
+ if (!hasConsent) return
+
+ window.gtag('event', 'conversion', {
+ 'send_to': params.conversionId,
+ 'value': params.value || 1.0,
+ 'currency': params.currency || 'EUR',
+ 'transaction_id': params.transactionId,
+ })
+}
+
+/**
+ * Enhanced Conversion Data setzen
+ */
+export function setEnhancedConversionData(data: {
+ email?: string
+ phone?: string
+ firstName?: string
+ lastName?: string
+}) {
+ if (typeof window === 'undefined') return
+ if (typeof window.gtag !== 'function') return
+
+ const hasConsent = window.CookieConsent?.acceptedCategory('marketing')
+ if (!hasConsent) return
+
+ window.gtag('set', 'user_data', {
+ 'email': data.email,
+ 'phone_number': data.phone,
+ 'address': {
+ 'first_name': data.firstName,
+ 'last_name': data.lastName,
+ }
+ })
+}
+```
+
+### Server-Side Conversion API
+
+```typescript
+// src/lib/google-ads.server.ts
+
+import crypto from 'crypto'
+
+const GOOGLE_ADS_CUSTOMER_ID = process.env.GOOGLE_ADS_CUSTOMER_ID
+const GOOGLE_ADS_CONVERSION_ACTION_ID = process.env.GOOGLE_ADS_CONVERSION_ACTION_ID
+const GOOGLE_ADS_API_TOKEN = process.env.GOOGLE_ADS_API_TOKEN
+const GOOGLE_ADS_DEVELOPER_TOKEN = process.env.GOOGLE_ADS_DEVELOPER_TOKEN
+
+interface ServerConversionParams {
+ conversionAction: string
+ email?: string
+ phone?: string
+ value?: number
+ currency?: string
+ gclid?: string
+}
+
+/**
+ * Server-Side Conversion Upload
+ * DSGVO-konform: Nur gehashte Daten
+ */
+export async function trackServerConversion(params: ServerConversionParams) {
+ if (!GOOGLE_ADS_CUSTOMER_ID || !GOOGLE_ADS_API_TOKEN) {
+ console.log('[Google Ads] Server-Side nicht konfiguriert')
+ return
+ }
+
+ const conversion: Record = {
+ conversionAction: `customers/${GOOGLE_ADS_CUSTOMER_ID}/conversionActions/${GOOGLE_ADS_CONVERSION_ACTION_ID}`,
+ conversionDateTime: new Date().toISOString().replace('Z', '+00:00'),
+ conversionValue: params.value || 1.0,
+ currencyCode: params.currency || 'EUR',
+ }
+
+ if (params.gclid) {
+ conversion.gclid = params.gclid
+ }
+
+ if (params.email || params.phone) {
+ conversion.userIdentifiers = []
+
+ if (params.email) {
+ (conversion.userIdentifiers as Array).push({
+ hashedEmail: hashForGoogle(params.email.toLowerCase().trim())
+ })
+ }
+
+ if (params.phone) {
+ (conversion.userIdentifiers as Array).push({
+ hashedPhoneNumber: hashForGoogle(params.phone.replace(/[^0-9+]/g, ''))
+ })
+ }
+ }
+
+ try {
+ const response = await fetch(
+ `https://googleads.googleapis.com/v15/customers/${GOOGLE_ADS_CUSTOMER_ID}:uploadConversionAdjustments`,
+ {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${GOOGLE_ADS_API_TOKEN}`,
+ 'Content-Type': 'application/json',
+ 'developer-token': GOOGLE_ADS_DEVELOPER_TOKEN!,
+ },
+ body: JSON.stringify({
+ conversions: [conversion],
+ partialFailure: true,
+ }),
+ }
+ )
+
+ if (!response.ok) {
+ console.error('[Google Ads] Upload failed:', await response.text())
+ }
+ } catch (error) {
+ console.error('[Google Ads] Error:', error)
+ }
+}
+
+function hashForGoogle(value: string): string {
+ return crypto.createHash('sha256').update(value).digest('hex')
+}
+```
+
+---
+
+## CookieInventory Erweiterung
+
+Füge diese Cookies zur `cookie-inventory` Collection hinzu:
+
+```typescript
+// Über Payload Admin oder Seed-Script
+
+const googleAdsCookies = [
+ {
+ name: '_gcl_au',
+ provider: 'Google Ads',
+ category: 'marketing',
+ purpose: 'Conversion-Tracking und Anzeigenmessung',
+ duration: '90 Tage',
+ type: 'first-party',
+ },
+ {
+ name: '_gcl_aw',
+ provider: 'Google Ads',
+ category: 'marketing',
+ purpose: 'Speichert GCLID für Conversion-Attribution',
+ duration: '90 Tage',
+ type: 'first-party',
+ },
+ {
+ name: 'IDE',
+ provider: 'Google (doubleclick.net)',
+ category: 'marketing',
+ purpose: 'Remarketing und Anzeigenpersonalisierung',
+ duration: '1 Jahr',
+ type: 'third-party',
+ },
+]
+```
+
+---
+
+## Environment Variables
+
+### Frontend (.env.local)
+
+```env
+# Payload CMS API (Production)
+NEXT_PUBLIC_PAYLOAD_URL=https://cms.c2sgmbh.de
+NEXT_PUBLIC_API_URL=https://cms.c2sgmbh.de/api
+
+# Umami Analytics (Production)
+NEXT_PUBLIC_UMAMI_HOST=https://analytics.c2sgmbh.de
+
+# Tenant-Konfiguration (je nach Projekt)
+NEXT_PUBLIC_TENANT_ID=4
+NEXT_PUBLIC_TENANT_SLUG=c2s
+
+# Google Ads (pro Tenant unterschiedlich)
+NEXT_PUBLIC_GOOGLE_ADS_ID=AW-XXXXXXXXX
+```
+
+### Backend (.env)
+
+```env
+# Umami Server-Side Tracking
+UMAMI_HOST=https://analytics.c2sgmbh.de
+UMAMI_WEBSITE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+
+# Google Ads Server-Side API
+GOOGLE_ADS_CUSTOMER_ID=1234567890
+GOOGLE_ADS_CONVERSION_ACTION_ID=987654321
+GOOGLE_ADS_API_TOKEN=ya29.xxx
+GOOGLE_ADS_DEVELOPER_TOKEN=xxx
+```
+
+---
+
+## Event-Naming-Konvention
+
+| Event | Name | Data |
+|-------|------|------|
+| Newsletter Anmeldung | `newsletter_subscribe` | `{ source: string }` |
+| Newsletter Bestätigung | `newsletter_confirm` | - |
+| Kontaktformular | `contact_form_submit` | `{ form_type: string }` |
+| CTA Klick | `cta_click` | `{ cta_name: string, location: string }` |
+| Download | `download` | `{ file_name: string, file_type: string }` |
+| Funnel-Step | `funnel_step` | `{ funnel: string, step: number, step_name: string }` |
+| Scroll-Tiefe | `scroll_depth` | `{ depth_percent: number }` |
+| Externer Link | `external_link` | `{ url: string }` |
+
+---
+
+## Zusammenfassung
+
+```
+┌─────────────────────────────────────────────────────────────────────────────────────┐
+│ WAS IMPLEMENTIERT WERDEN MUSS │
+│ │
+│ 1. UMAMI (ohne Consent) │
+│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
+│ • UmamiScript Komponente in Layout einbinden │
+│ • useAnalytics Hook in Formulare/CTAs integrieren │
+│ • Server-Side Events für Backend-Actions │
+│ │
+│ 2. GOOGLE ADS (mit Consent) │
+│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
+│ • GoogleConsentMode Komponente (integriert mit Orestbida) │
+│ • useGclid Hook für Attribution │
+│ • Client-Side Conversions bei Consent │
+│ • Server-Side Conversions immer (gehashte Daten) │
+│ │
+│ 3. COOKIE INVENTORY │
+│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
+│ • Google Ads Cookies dokumentieren │
+│ │
+└─────────────────────────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Dateien zu erstellen
+
+```
+src/
+├── components/
+│ ├── analytics/
+│ │ ├── UmamiScript.tsx # Umami Tracking Script
+│ │ └── GoogleConsentMode.tsx # Google Consent Mode v2
+│ ├── forms/
+│ │ └── NewsletterForm.tsx # Mit Analytics-Integration
+│ └── ui/
+│ ├── TrackedButton.tsx # CTA mit Tracking
+│ └── TrackedDownload.tsx # Download mit Tracking
+├── config/
+│ └── analytics.ts # Website-IDs, Config
+├── hooks/
+│ ├── useAnalytics.ts # Client-Side Event Tracking
+│ └── useGclid.ts # GCLID Erfassung
+└── lib/
+ ├── analytics.server.ts # Umami Server-Side
+ ├── google-ads.ts # Google Ads Client
+ └── google-ads.server.ts # Google Ads Server API
+```
+
+---
+
+*Letzte Aktualisierung: 18. Dezember 2025*
diff --git a/docs/anleitungen/FRONTEND.md b/docs/anleitungen/FRONTEND.md
index 45657aa..367b3ea 100644
--- a/docs/anleitungen/FRONTEND.md
+++ b/docs/anleitungen/FRONTEND.md
@@ -1,21 +1,53 @@
# Frontend-Entwicklung - Payload CMS Multi-Tenant
> **Server:** sv-frontend (LXC 704) - 10.10.181.104
-> **Backend API:** https://pl.c2sgmbh.de/api
+> **Backend API:** https://cms.c2sgmbh.de/api (Production)
## Übersicht
Das Frontend wird als separates Next.js-Projekt entwickelt und nutzt Payload CMS als Headless CMS über die REST-API.
+**Wichtig:** Die Frontend-Entwicklung verwendet die **Produktions-API und -Datenbank**, um mit echten Inhalten zu arbeiten. SEO-Einstellungen und Cookie-Consent-Konfigurationen werden ebenfalls aus der Produktionsumgebung geladen.
+
+---
+
+## Umgebungskonfiguration
+
+### Environment Variables (.env.local)
+
+```env
+# API-Endpunkte (PRODUKTION)
+NEXT_PUBLIC_PAYLOAD_URL=https://cms.c2sgmbh.de
+NEXT_PUBLIC_API_URL=https://cms.c2sgmbh.de/api
+
+# Analytics (optional)
+NEXT_PUBLIC_UMAMI_HOST=https://analytics.c2sgmbh.de
+NEXT_PUBLIC_UMAMI_WEBSITE_ID=
+
+# Tenant-Konfiguration (je nach Projekt)
+NEXT_PUBLIC_TENANT_ID=4
+NEXT_PUBLIC_TENANT_SLUG=c2s
+```
+
+### Warum Production-Daten?
+
+| Aspekt | Grund |
+|--------|-------|
+| **Content** | Echte Inhalte für realistische Entwicklung |
+| **SEO** | Produktions-Meta-Tags und Structured Data |
+| **Cookie-Consent** | Live Cookie-Konfigurationen (DSGVO-relevant) |
+| **Media** | Produktions-Bilder mit allen Größen |
+| **Consistency** | Keine Sync-Probleme zwischen Dev/Prod |
+
---
## API-Dokumentation
| Ressource | URL |
|-----------|-----|
-| **Swagger UI** | https://pl.c2sgmbh.de/api/docs |
-| **OpenAPI JSON** | https://pl.c2sgmbh.de/api/openapi.json |
-| **REST API Base** | https://pl.c2sgmbh.de/api |
+| **Swagger UI** | https://cms.c2sgmbh.de/api/docs |
+| **OpenAPI JSON** | https://cms.c2sgmbh.de/api/openapi.json |
+| **REST API Base** | https://cms.c2sgmbh.de/api |
---
@@ -25,6 +57,7 @@ Das Frontend wird als separates Next.js-Projekt entwickelt und nutzt Payload CMS
- [ ] **Block-Komponenten entwickeln**
- [ ] Hero Block
+ - [ ] Hero Slider Block
- [ ] Text Block
- [ ] Image Text Block
- [ ] Card Grid Block
@@ -38,6 +71,9 @@ Das Frontend wird als separates Next.js-Projekt entwickelt und nutzt Payload CMS
- [ ] Testimonials Block
- [ ] Newsletter Block
- [ ] Process Steps Block
+ - [ ] FAQ Block
+ - [ ] Team Block
+ - [ ] Services Block
- [ ] **Newsletter-Anmelde-Formular**
- API: `POST /api/newsletter/subscribe`
@@ -45,7 +81,7 @@ Das Frontend wird als separates Next.js-Projekt entwickelt und nutzt Payload CMS
- Felder: email, firstName (optional), tenantId, source
- [ ] **Cookie-Banner implementieren**
- - Cookie Configurations aus API laden
+ - Cookie Configurations aus Production-API laden
- Consent-Logs an Backend senden
- DSGVO-konform mit Opt-In
@@ -57,9 +93,9 @@ Das Frontend wird als separates Next.js-Projekt entwickelt und nutzt Payload CMS
- Unterstützte Locales: `de` (default), `en`
- [ ] **SEO-Integration**
- - Meta-Tags aus Pages/Posts
+ - Meta-Tags aus Pages/Posts (Production)
- Structured Data (JSON-LD)
- - Sitemap: https://pl.c2sgmbh.de/sitemap.xml
+ - Sitemap: https://cms.c2sgmbh.de/sitemap.xml
- [ ] **Suche implementieren**
- API: `GET /api/search?q=...&locale=de`
@@ -69,26 +105,26 @@ Das Frontend wird als separates Next.js-Projekt entwickelt und nutzt Payload CMS
### Tenant-spezifische Features
#### porwoll.de
-- [ ] Immobilien-Listing
-- [ ] Objektsuche mit Filtern
-- [ ] Kontaktformular mit Objekt-Referenz
+- [ ] Portfolio-Galerie (Fotografie)
+- [ ] Buchungsformular
+- [ ] Before/After Bildvergleich
#### complexcaresolutions.de (C2S)
- [ ] Team-Übersicht
- [ ] Leistungs-Seiten
+- [ ] Zertifizierungen
- [ ] Karriere-Seite mit Stellenangeboten
-#### gunshin.de (Fotografin-Portfolio)
-- [ ] Portfolio-Galerie
- - API: `GET /api/portfolios?where[tenant][equals]=5`
- - Kategorien: `GET /api/portfolio-categories`
-- [ ] Projekt-Detailseiten mit Lightbox
+#### gunshin.de (Game Development)
+- [ ] Projekt-Galerie
+ - API: `GET /api/projects?where[tenant][equals]=5`
+- [ ] Portfolio-Seiten
- [ ] Referenzen-Slider
#### zweitmein.ng
-- [ ] Produkt-Übersicht (falls E-Commerce)
- [ ] FAQ-Sektion
- [ ] Preistabellen
+- [ ] Kontaktformular
---
@@ -107,14 +143,18 @@ Das Frontend wird als separates Next.js-Projekt entwickelt und nutzt Payload CMS
| FAQs | `GET /api/faqs` | FAQ-Einträge |
| Portfolios | `GET /api/portfolios` | Portfolio-Projekte |
| Media | `GET /api/media` | Medien/Bilder |
+| Videos | `GET /api/videos` | Video-Bibliothek |
+| Timelines | `GET /api/timelines` | Chronologische Events |
+| Workflows | `GET /api/workflows` | Prozess-Darstellungen |
-### Globals
+### Globals (SEO & Settings aus Production)
| Global | Endpoint | Beschreibung |
|--------|----------|--------------|
-| Site Settings | `GET /api/globals/site-settings` | Logo, Name, SEO |
+| Site Settings | `GET /api/globals/site-settings` | Logo, Name, Kontakt |
| Navigation | `GET /api/globals/navigation` | Menü-Struktur |
-| SEO Settings | `GET /api/globals/seo-settings` | Default SEO |
+| SEO Settings | `GET /api/globals/seo-settings` | Default SEO (Production) |
+| Privacy Policy | `GET /api/globals/privacy-policy-settings` | Datenschutz |
### Spezielle Endpoints
@@ -123,6 +163,8 @@ Das Frontend wird als separates Next.js-Projekt entwickelt und nutzt Payload CMS
| `/api/search` | GET | Volltextsuche |
| `/api/search/suggestions` | GET | Auto-Complete |
| `/api/newsletter/subscribe` | POST | Newsletter-Anmeldung |
+| `/api/timelines` | GET | Timeline-Daten |
+| `/api/workflows` | GET | Workflow-Daten |
---
@@ -132,10 +174,13 @@ Alle Collection-Anfragen sollten nach Tenant gefiltert werden:
```typescript
// Beispiel: Posts für Tenant "c2s" (ID: 4)
-fetch('https://pl.c2sgmbh.de/api/posts?where[tenant][equals]=4&locale=de')
+fetch('https://cms.c2sgmbh.de/api/posts?where[tenant][equals]=4&locale=de')
// Beispiel: Pages für Tenant "gunshin" (ID: 5)
-fetch('https://pl.c2sgmbh.de/api/pages?where[tenant][equals]=5&locale=de')
+fetch('https://cms.c2sgmbh.de/api/pages?where[tenant][equals]=5&locale=de')
+
+// Beispiel: SEO-Settings (Global, kein Tenant-Filter)
+fetch('https://cms.c2sgmbh.de/api/globals/seo-settings')
```
### Tenant-IDs
@@ -181,10 +226,10 @@ Unterstützte Locales: `de` (default), `en`
```typescript
// Deutsch (default)
-fetch('https://pl.c2sgmbh.de/api/posts?locale=de')
+fetch('https://cms.c2sgmbh.de/api/posts?locale=de')
// Englisch
-fetch('https://pl.c2sgmbh.de/api/posts?locale=en')
+fetch('https://cms.c2sgmbh.de/api/posts?locale=en')
// Fallback: Wenn EN nicht vorhanden, wird DE zurückgegeben
```
@@ -196,7 +241,7 @@ fetch('https://pl.c2sgmbh.de/api/posts?locale=en')
### Anmeldung
```typescript
-const response = await fetch('https://pl.c2sgmbh.de/api/newsletter/subscribe', {
+const response = await fetch('https://cms.c2sgmbh.de/api/newsletter/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -219,18 +264,25 @@ const response = await fetch('https://pl.c2sgmbh.de/api/newsletter/subscribe', {
---
-## Cookie-Consent
+## Cookie-Consent (Production-Daten)
### Konfiguration laden
```typescript
-const config = await fetch('https://pl.c2sgmbh.de/api/cookie-configurations?where[tenant][equals]=4')
+// Cookie-Konfiguration aus Production laden
+const config = await fetch('https://cms.c2sgmbh.de/api/cookie-configurations?where[tenant][equals]=4')
+ .then(r => r.json())
+
+// config.docs enthält:
+// - Kategorien (necessary, analytics, marketing, etc.)
+// - Cookie-Details pro Kategorie
+// - Texte für Banner (lokalisiert)
```
### Consent loggen
```typescript
-await fetch('https://pl.c2sgmbh.de/api/consent-logs', {
+await fetch('https://cms.c2sgmbh.de/api/consent-logs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -245,12 +297,49 @@ await fetch('https://pl.c2sgmbh.de/api/consent-logs', {
---
+## SEO-Integration (Production-Daten)
+
+### Global SEO-Settings
+
+```typescript
+// SEO-Defaults aus Production laden
+const seoSettings = await fetch('https://cms.c2sgmbh.de/api/globals/seo-settings')
+ .then(r => r.json())
+
+// Enthält:
+// - defaultTitle, titleTemplate
+// - defaultDescription
+// - defaultImage (OG-Image)
+// - robotsDefault
+```
+
+### Page-spezifische SEO
+
+```typescript
+// SEO-Daten aus Page laden
+const page = await fetch('https://cms.c2sgmbh.de/api/pages?where[slug][equals]=about&where[tenant][equals]=4')
+ .then(r => r.json())
+
+// page.docs[0].meta enthält:
+// - title, description
+// - image (OG-Image Override)
+// - noIndex, noFollow
+```
+
+### Sitemap
+
+Die Sitemap wird automatisch von Payload generiert:
+- **URL:** https://cms.c2sgmbh.de/sitemap.xml
+- Enthält alle publizierten Pages und Posts
+
+---
+
## Kontaktformular
Formular-Submissions werden über die Forms-Collection verarbeitet:
```typescript
-await fetch('https://pl.c2sgmbh.de/api/form-submissions', {
+await fetch('https://cms.c2sgmbh.de/api/form-submissions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -272,14 +361,14 @@ Falls User-Authentifizierung benötigt wird:
```typescript
// Login
-const { token, user } = await fetch('https://pl.c2sgmbh.de/api/users/login', {
+const { token, user } = await fetch('https://cms.c2sgmbh.de/api/users/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
}).then(r => r.json())
// Authentifizierte Requests
-fetch('https://pl.c2sgmbh.de/api/...', {
+fetch('https://cms.c2sgmbh.de/api/...', {
headers: { 'Authorization': `JWT ${token}` }
})
```
@@ -293,7 +382,9 @@ fetch('https://pl.c2sgmbh.de/api/...', {
Die Payload-Typen können aus dem Backend exportiert werden:
```bash
-# Auf dem Payload-Server
+# Auf dem Payload-Server (Production)
+ssh payload@162.55.85.18
+cd ~/payload-cms
pnpm payload generate:types
# Datei: src/payload-types.ts
@@ -316,14 +407,60 @@ Diese Datei kann ins Frontend-Projekt kopiert werden für typsichere API-Calls.
- TTL: 60 Sekunden für Suche
- Cache wird bei Content-Änderungen invalidiert
+### CORS
+
+Die Production-API erlaubt Requests von:
+- `*.porwoll.tech` (Development)
+- `porwoll.de`, `complexcaresolutions.de`, `gunshin.de` (Production)
+
+---
+
+## Development Server (sv-frontend)
+
+### SSH-Zugang
+
+```bash
+ssh frontend@10.10.181.104
+```
+
+### Projekt starten
+
+```bash
+cd ~/frontend.porwoll.de
+pnpm dev
+# Läuft auf Port 3000 → https://porwoll-dev.porwoll.tech
+```
+
+### AI-Tools
+
+```bash
+claude # Claude Code CLI
+codex # Codex CLI
+gemini # Gemini CLI
+```
+
+### Service-Management
+
+```bash
+# Systemd Service starten
+systemctl start frontend-porwoll
+
+# Service stoppen
+systemctl stop frontend-porwoll
+
+# Logs anzeigen
+journalctl -u frontend-porwoll -f
+```
+
---
## Ressourcen
- **Payload CMS Docs:** https://payloadcms.com/docs
-- **API-Dokumentation:** https://pl.c2sgmbh.de/api/docs
-- **Backend-Repository:** https://github.com/c2s-admin/cms.c2sgmbh.git
+- **API-Dokumentation:** https://cms.c2sgmbh.de/api/docs
+- **Backend-Repository:** https://github.com/complexcaresolutions/cms.c2sgmbh.git
+- **Analytics:** https://analytics.c2sgmbh.de
---
-*Erstellt: 11.12.2025*
+*Letzte Aktualisierung: 18.12.2025*
diff --git a/docs/anleitungen/SECURITY.md b/docs/anleitungen/SECURITY.md
index 2d7881a..61b8cd2 100644
--- a/docs/anleitungen/SECURITY.md
+++ b/docs/anleitungen/SECURITY.md
@@ -1,11 +1,18 @@
# Security-Richtlinien - Payload CMS Multi-Tenant
-> Letzte Aktualisierung: 17.12.2025
+> Letzte Aktualisierung: 18.12.2025
## Übersicht
Dieses Dokument beschreibt die implementierten Sicherheitsmaßnahmen für das Payload CMS Multi-Tenant-Projekt.
+**Umgebungen:**
+
+| Umgebung | URL | TRUST_PROXY |
+|----------|-----|-------------|
+| Production | https://cms.c2sgmbh.de | `true` (Nginx) |
+| Staging | https://pl.porwoll.tech | `true` (Caddy) |
+
---
## Security-Module
@@ -289,16 +296,48 @@ Das Admin Panel verwendet eine Custom Login Route (`src/app/(payload)/api/users/
- **Audit-Logging:** Jeder Login-Versuch wird in AuditLogs protokolliert
- **Rate-Limiting:** 5 Versuche pro 15 Minuten (authLimiter)
+- **Browser-Redirect:** Sichere Weiterleitung nach erfolgreichem Login
- **Content-Type Support:**
- JSON (`application/json`)
- FormData mit `_payload` JSON-Feld (Payload Admin Panel Format)
- Standard FormData (`multipart/form-data`)
- URL-encoded (`application/x-www-form-urlencoded`)
+**Browser Form Redirect:**
+```
+POST /api/users/login?redirect=/admin/collections/posts
+Content-Type: application/x-www-form-urlencoded
+
+email=admin@example.com&password=secret
+```
+
+**Redirect-Validierung:**
+- Nur relative Pfade erlaubt (`/admin/...`)
+- Externe URLs werden blockiert
+- Protocol-Handler (`javascript:`, `data:`) abgelehnt
+- Default: `/admin` bei fehlendem/ungültigem Redirect
+
**Sicherheitsaspekte:**
- Passwort wird nie in Logs/Responses exponiert
- Fehlgeschlagene Login-Versuche werden mit IP und User-Agent geloggt
- Rate-Limiting verhindert Brute-Force-Angriffe
+- Open Redirect Prevention durch URL-Validierung
+
+### Custom Admin Login Page
+
+Eine optionale Custom Login-Seite ist verfügbar unter `src/app/(payload)/admin/login/`:
+
+```
+src/app/(payload)/admin/login/
+├── page.tsx # Login-Formular mit Styling
+└── page.module.scss # Custom Styles
+```
+
+**Features:**
+- Styled Login-Form passend zum Admin-Theme
+- Redirect-Parameter Support (`?redirect=/admin/...`)
+- Fehlerbehandlung mit User-Feedback
+- Kompatibel mit Payload's Session-Management
---
@@ -306,8 +345,26 @@ Das Admin Panel verwendet eine Custom Login Route (`src/app/(payload)/api/users/
| Datum | Änderung |
|-------|----------|
+| 18.12.2025 | **Custom Admin Login Page:** Styled Login-Formular, Browser-Redirect mit Safe-URL-Validierung, Open Redirect Prevention |
| 17.12.2025 | **Security-Audit Fixes:** TRUST_PROXY für IP-Header-Spoofing, CSRF_SECRET Pflicht in Production, IP-Allowlist Startup-Warnungen, Tests auf 177 erweitert |
| 09.12.2025 | Custom Login Route Dokumentation, multipart/form-data _payload Support |
| 08.12.2025 | Security Test Suite (143 Tests) |
| 07.12.2025 | Rate Limiter, CSRF, IP Allowlist, Data Masking |
| 07.12.2025 | Pre-Commit Hook, GitHub Actions Workflow |
+
+---
+
+## Dateien
+
+| Pfad | Beschreibung |
+|------|--------------|
+| `src/lib/security/rate-limiter.ts` | Rate Limiting mit Redis/Memory |
+| `src/lib/security/ip-allowlist.ts` | IP-basierte Zugriffskontrolle |
+| `src/lib/security/csrf.ts` | CSRF Token Generation & Validation |
+| `src/lib/security/data-masking.ts` | Sensitive Data Masking |
+| `src/app/(payload)/api/users/login/route.ts` | Custom Login API |
+| `src/app/(payload)/admin/login/page.tsx` | Custom Login Page |
+| `scripts/detect-secrets.sh` | Pre-Commit Secret Detection |
+| `.github/workflows/security.yml` | CI Security Scanning |
+| `tests/unit/security/` | Security Unit Tests |
+| `tests/int/security-api.int.spec.ts` | Security Integration Tests |
diff --git a/docs/anleitungen/SEO_ERWEITERUNG.md b/docs/anleitungen/SEO_ERWEITERUNG.md
index 595a391..03f5d28 100644
--- a/docs/anleitungen/SEO_ERWEITERUNG.md
+++ b/docs/anleitungen/SEO_ERWEITERUNG.md
@@ -1,9 +1,60 @@
# SEO-Erweiterung
+*Letzte Aktualisierung: 18. Dezember 2025*
+
## Übersicht
Diese Dokumentation beschreibt die implementierten SEO-Features für das Payload CMS Multi-Tenant System.
+**Wichtig:** Frontends verwenden die **Production-API** (cms.c2sgmbh.de) für SEO-Daten, um konsistente Meta-Tags und Structured Data zu gewährleisten.
+
+---
+
+## API-Endpoints für Frontend
+
+### SEO-Daten abrufen
+
+| Endpoint | Beschreibung |
+|----------|--------------|
+| `GET /api/globals/seo-settings` | Globale SEO-Konfiguration |
+| `GET /api/pages?where[slug][equals]=...` | Page-spezifische SEO (meta-Feld) |
+| `GET /api/posts?where[slug][equals]=...` | Post-spezifische SEO |
+
+### Beispiel: SEO-Settings laden
+
+```typescript
+// Frontend: SEO-Defaults aus Production laden
+const seoSettings = await fetch('https://cms.c2sgmbh.de/api/globals/seo-settings')
+ .then(r => r.json())
+
+// Enthält:
+// - metaDefaults (titleSuffix, defaultDescription, defaultImage)
+// - organization (name, legalName, logo, foundingDate)
+// - contact (email, phone, fax)
+// - address (street, city, country, etc.)
+// - socialProfiles (Array)
+// - localBusiness (type, priceRange, openingHours)
+// - robots (indexing, additionalDisallow)
+// - verification (google, bing, yandex)
+```
+
+### Beispiel: Page-SEO laden
+
+```typescript
+// Page mit SEO-Daten laden
+const page = await fetch(
+ 'https://cms.c2sgmbh.de/api/pages?where[slug][equals]=about&where[tenant][equals]=4&locale=de'
+).then(r => r.json())
+
+// page.docs[0].meta enthält:
+// - title (Page-spezifischer Titel)
+// - description (Meta-Description)
+// - image (OG-Image Override)
+// - noIndex, noFollow (Indexierungssteuerung)
+```
+
+---
+
## Implementierte Features
### 1. Dynamische Sitemap (`/sitemap.xml`)
@@ -16,6 +67,7 @@ Die Sitemap wird dynamisch aus der Datenbank generiert und enthält:
- Alle veröffentlichten Posts mit typ-basierter URL (Priorität: 0.6, Änderungshäufigkeit: monatlich)
**URL-Schema für Posts:**
+
| Post-Typ | URL-Prefix |
|----------|------------|
| blog | `/blog/{slug}` |
@@ -42,8 +94,8 @@ Allow: /
Disallow: /admin
Disallow: /api
-Host: https://pl.c2sgmbh.de
-Sitemap: https://pl.c2sgmbh.de/sitemap.xml
+Host: https://cms.c2sgmbh.de
+Sitemap: https://cms.c2sgmbh.de/sitemap.xml
```
### 3. Structured Data (JSON-LD)
@@ -69,12 +121,26 @@ Bietet Helper-Funktionen für Schema.org-konforme JSON-LD Daten:
| `combineSchemas()` | Kombiniert mehrere Schemas |
| `renderJsonLd()` | Sicheres Rendering von JSON-LD |
-#### Verwendungsbeispiel
+#### Verwendungsbeispiel (Frontend)
```tsx
+// src/components/seo/JsonLd.tsx
import { generateArticleSchema, renderJsonLd } from '@/lib/structuredData'
-export default function BlogPost({ post }) {
+interface BlogPostProps {
+ post: {
+ title: string
+ excerpt: string
+ slug: string
+ publishedAt: string
+ updatedAt: string
+ author?: { name: string }
+ featuredImage?: { url: string }
+ categories?: Array<{ title: string }>
+ }
+}
+
+export default function BlogPost({ post }: BlogPostProps) {
const schema = generateArticleSchema({
title: post.title,
description: post.excerpt,
@@ -84,7 +150,7 @@ export default function BlogPost({ post }) {
author: post.author,
featuredImage: post.featuredImage,
categories: post.categories,
- }, 'https://example.com')
+ }, 'https://complexcaresolutions.de') // Tenant-Domain
return (
<>
@@ -149,6 +215,117 @@ Globale SEO-Konfiguration im Admin-Panel unter "Einstellungen > SEO Einstellunge
- Bing Webmaster Tools
- Yandex Webmaster
+---
+
+## Frontend-Integration
+
+### Next.js Metadata API
+
+```typescript
+// src/app/[locale]/page.tsx
+import type { Metadata } from 'next'
+
+async function getSeoSettings() {
+ const res = await fetch('https://cms.c2sgmbh.de/api/globals/seo-settings', {
+ next: { revalidate: 3600 } // 1 Stunde Cache
+ })
+ return res.json()
+}
+
+async function getPage(slug: string, tenantId: number, locale: string) {
+ const res = await fetch(
+ `https://cms.c2sgmbh.de/api/pages?where[slug][equals]=${slug}&where[tenant][equals]=${tenantId}&locale=${locale}`,
+ { next: { revalidate: 60 } }
+ )
+ return res.json()
+}
+
+export async function generateMetadata({ params }): Promise {
+ const seoSettings = await getSeoSettings()
+ const pageData = await getPage(params.slug || 'home', 4, params.locale)
+ const page = pageData.docs[0]
+
+ const title = page?.meta?.title
+ ? `${page.meta.title} ${seoSettings.metaDefaults?.titleSuffix || ''}`
+ : seoSettings.metaDefaults?.titleSuffix
+
+ const description = page?.meta?.description
+ || seoSettings.metaDefaults?.defaultDescription
+
+ const image = page?.meta?.image?.url
+ || seoSettings.metaDefaults?.defaultImage?.url
+
+ return {
+ title,
+ description,
+ openGraph: {
+ title,
+ description,
+ images: image ? [{ url: image }] : [],
+ type: 'website',
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title,
+ description,
+ images: image ? [image] : [],
+ },
+ robots: {
+ index: !page?.meta?.noIndex,
+ follow: !page?.meta?.noFollow,
+ },
+ }
+}
+```
+
+### Verification Meta Tags
+
+```typescript
+// src/app/layout.tsx
+export async function generateMetadata(): Promise {
+ const seoSettings = await getSeoSettings()
+
+ return {
+ verification: {
+ google: seoSettings.verification?.google,
+ // Bing und Yandex als other
+ other: {
+ 'msvalidate.01': seoSettings.verification?.bing,
+ 'yandex-verification': seoSettings.verification?.yandex,
+ },
+ },
+ }
+}
+```
+
+---
+
+## Multi-Tenant SEO
+
+Jeder Tenant hat eigene SEO-Konfigurationen. Die SEO Settings Global gilt pro Installation, aber Page/Post-SEO ist tenant-spezifisch.
+
+### Tenant-spezifische Domains
+
+| Tenant ID | Domain | Sitemap |
+|-----------|--------|---------|
+| 1 | porwoll.de | https://porwoll.de/sitemap.xml |
+| 4 | complexcaresolutions.de | https://complexcaresolutions.de/sitemap.xml |
+| 5 | gunshin.de | https://gunshin.de/sitemap.xml |
+
+### Lokalisierung
+
+SEO-Felder sind lokalisiert (de/en):
+
+```typescript
+// Deutscher Content
+fetch('https://cms.c2sgmbh.de/api/pages?slug=about&tenant=4&locale=de')
+
+// Englischer Content
+fetch('https://cms.c2sgmbh.de/api/pages?slug=about&tenant=4&locale=en')
+```
+
+---
+
## Datenbank-Tabellen
Die Migration `20251130_150000_blocks_tables.ts` erstellt:
@@ -159,15 +336,43 @@ Die Migration `20251130_150000_blocks_tables.ts` erstellt:
- `seo_settings_local_business_opening_hours` - Öffnungszeiten
- `seo_settings_robots_additional_disallow` - Ausgeschlossene Pfade
+---
+
## URLs
-- **Sitemap:** https://pl.c2sgmbh.de/sitemap.xml
-- **Robots:** https://pl.c2sgmbh.de/robots.txt
-- **SEO Settings:** https://pl.c2sgmbh.de/admin/globals/seo-settings
+### Production (für Frontends)
-## Nächste Schritte
+| Resource | URL |
+|----------|-----|
+| **API Base** | https://cms.c2sgmbh.de/api |
+| **SEO Settings** | https://cms.c2sgmbh.de/api/globals/seo-settings |
+| **Sitemap** | https://cms.c2sgmbh.de/sitemap.xml |
+| **Robots** | https://cms.c2sgmbh.de/robots.txt |
+| **Admin Panel** | https://cms.c2sgmbh.de/admin/globals/seo-settings |
-1. SEO Settings im Admin-Panel konfigurieren
-2. JSON-LD in Frontend-Templates einbinden
-3. Meta-Tags in Layout integrieren
-4. Google Search Console einrichten
+### Development
+
+| Resource | URL |
+|----------|-----|
+| **API Base** | https://pl.porwoll.tech/api |
+| **SEO Settings** | https://pl.porwoll.tech/api/globals/seo-settings |
+| **Admin Panel** | https://pl.porwoll.tech/admin/globals/seo-settings |
+
+---
+
+## Checkliste: SEO-Setup pro Tenant
+
+- [ ] SEO Settings im Admin-Panel konfigurieren
+- [ ] Organisation (Name, Logo, Beschreibung)
+- [ ] Kontaktdaten und Adresse
+- [ ] Social Media Profile hinzufügen
+- [ ] Local Business aktivieren (falls relevant)
+- [ ] Google Search Console Code eintragen
+- [ ] JSON-LD in Frontend-Templates einbinden
+- [ ] Meta-Tags in Layout integrieren
+- [ ] Sitemap bei Google Search Console einreichen
+- [ ] robots.txt prüfen
+
+---
+
+*Letzte Aktualisierung: 18. Dezember 2025*
diff --git a/docs/anleitungen/TODO.md b/docs/anleitungen/TODO.md
index a52be40..d065247 100644
--- a/docs/anleitungen/TODO.md
+++ b/docs/anleitungen/TODO.md
@@ -39,7 +39,7 @@
### Dokumentation
| Status | Task |
|--------|------|
-| [ ] | DEPLOYMENT.md erstellen |
+| [x] | DEPLOYMENT.md erstellen |
---
@@ -173,7 +173,7 @@
## Dokumentation
-- [ ] DEPLOYMENT.md (Deployment-Prozess)
+- [x] DEPLOYMENT.md (Deployment-Prozess) *(erledigt: 18.12.2025)*
---
@@ -222,12 +222,21 @@
---
-*Letzte Aktualisierung: 17.12.2025*
+*Letzte Aktualisierung: 18.12.2025*
---
## Changelog
+### 18.12.2025
+- **Dokumentation konsolidiert:**
+ - CLAUDE.md: Tech-Stack auf aktuelle Versionen aktualisiert (Payload 3.68.4, Next.js 15.5.9, React 19.2.3)
+ - CLAUDE.md: Architektur-Diagramm erweitert (sv-caddy, sv-frontend hinzugefügt)
+ - CLAUDE.md: Videos/VideoCategories Collections hinzugefügt
+ - docs/INFRASTRUCTURE.md: Komplett aktualisiert mit neuer Infrastruktur
+ - docs/PROJECT_STATUS.md: Neues Statusdokument behalten
+ - Obsolete Dateien entfernt: INFRASTRUCTURE_COMPLETE_DECEMBER_2025.md, TECHSTACK_COMPLETE_DECEMBER_2025.md
+
### 17.12.2025
- **Security Code-Review abgeschlossen:**
- **IP Header Spoofing behoben:** `X-Forwarded-For`/`X-Real-IP` werden nur bei `TRUST_PROXY=true` vertraut
diff --git a/docs/anleitungen/UNIVERSAL_FEATURES.md b/docs/anleitungen/UNIVERSAL_FEATURES.md
index 2d00da4..90e8b18 100644
--- a/docs/anleitungen/UNIVERSAL_FEATURES.md
+++ b/docs/anleitungen/UNIVERSAL_FEATURES.md
@@ -1,8 +1,59 @@
# Universal Features - Dokumentation
+*Letzte Aktualisierung: 18. Dezember 2025*
+
## Übersicht
-Die Universal Features erweitern das Payload CMS um wiederverwendbare Collections und Blocks für Blog, News, Testimonials, Newsletter und Prozess-Darstellungen. Alle Features sind Multi-Tenant-fähig.
+Die Universal Features erweitern das Payload CMS um wiederverwendbare Collections und Blocks für Blog, News, Testimonials, Newsletter, FAQ, Team und Prozess-Darstellungen. Alle Features sind Multi-Tenant-fähig.
+
+**Wichtig:** Frontends verwenden die **Production-API** (cms.c2sgmbh.de).
+
+---
+
+## API-Endpoints für Frontend
+
+### Collections
+
+| Collection | Endpoint | Beschreibung |
+|------------|----------|--------------|
+| Posts | `GET /api/posts` | Blog, News, Presse |
+| Testimonials | `GET /api/testimonials` | Kundenbewertungen |
+| FAQs | `GET /api/faqs` | FAQ-Einträge |
+| Team | `GET /api/team` | Team-Mitglieder |
+| Services | `GET /api/services` | Leistungen |
+| Timelines | `GET /api/timelines` | Chronologische Events |
+| Workflows | `GET /api/workflows` | Prozess-Darstellungen |
+
+### Beispiel: Posts laden
+
+```typescript
+// Featured Blog-Posts für Tenant C2S laden
+const posts = await fetch(
+ 'https://cms.c2sgmbh.de/api/posts?where[tenant][equals]=4&where[type][equals]=blog&where[isFeatured][equals]=true&locale=de&limit=6'
+).then(r => r.json())
+```
+
+### Newsletter API
+
+| Endpoint | Methode | Beschreibung |
+|----------|---------|--------------|
+| `/api/newsletter/subscribe` | POST | Newsletter-Anmeldung |
+| `/api/newsletter/confirm` | GET/POST | Double Opt-In Bestätigung |
+| `/api/newsletter/unsubscribe` | GET/POST | Abmeldung |
+
+```typescript
+// Newsletter-Anmeldung
+const response = await fetch('https://cms.c2sgmbh.de/api/newsletter/subscribe', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ email: 'user@example.com',
+ firstName: 'Max',
+ tenantId: 4,
+ source: 'footer'
+ })
+})
+```
---
@@ -31,7 +82,7 @@ Die Posts Collection dient zur Verwaltung von Blog-Artikeln, News, Pressemitteil
| `author` | Text | Autorname | Nein |
| `status` | Select | draft/published/archived | Nein |
| `publishedAt` | Date | Veröffentlichungsdatum | Nein |
-| `seo` | Group | SEO-Einstellungen | Nein |
+| `meta` | Group | SEO-Einstellungen | Nein |
#### Post-Typen
@@ -40,11 +91,6 @@ Die Posts Collection dient zur Verwaltung von Blog-Artikeln, News, Pressemitteil
- `press` - Pressemitteilung
- `announcement` - Ankündigung
-#### Access Control
-
-- **Read:** Öffentlich für veröffentlichte Beiträge des eigenen Tenants
-- **Create/Update/Delete:** Nur authentifizierte Benutzer
-
---
### 2. Testimonials Collection
@@ -71,11 +117,6 @@ Kundenstimmen und Bewertungen für Referenz-Seiten.
| `isActive` | Checkbox | Sichtbarkeit | Nein |
| `order` | Number | Sortierung | Nein |
-#### Access Control
-
-- **Read:** Öffentlich für aktive Testimonials des eigenen Tenants
-- **Create/Update/Delete:** Nur authentifizierte Benutzer
-
---
### 3. Newsletter Subscribers Collection
@@ -84,7 +125,7 @@ Kundenstimmen und Bewertungen für Referenz-Seiten.
**Slug:** `newsletter-subscribers`
**Admin-Gruppe:** Marketing
-DSGVO-konforme Speicherung von Newsletter-Anmeldungen mit Double Opt-In Support.
+DSGVO-konforme Speicherung von Newsletter-Anmeldungen mit Double Opt-In.
#### Felder
@@ -103,6 +144,14 @@ DSGVO-konforme Speicherung von Newsletter-Anmeldungen mit Double Opt-In Support.
| `ipAddress` | Text | IP-Adresse (DSGVO) | Nein |
| `userAgent` | Text | Browser-Info | Nein |
+#### Double Opt-In Flow
+
+1. User meldet sich an → Status: `pending`, Token generiert
+2. E-Mail mit Bestätigungs-Link wird automatisch gesendet
+3. User klickt Link → Status: `confirmed`
+4. Willkommens-E-Mail wird gesendet
+5. Abmeldung jederzeit über Link möglich
+
#### Status-Werte
- `pending` - Ausstehend (Double Opt-In)
@@ -110,29 +159,124 @@ DSGVO-konforme Speicherung von Newsletter-Anmeldungen mit Double Opt-In Support.
- `unsubscribed` - Abgemeldet
- `bounced` - E-Mail nicht zustellbar
-#### Interessen-Optionen
+---
-- `general` - Allgemeine Updates
-- `blog` - Blog-Artikel
-- `products` - Produkt-News
-- `offers` - Angebote & Aktionen
-- `events` - Events
+### 4. FAQs Collection
-#### Access Control
+**Pfad:** `src/collections/FAQs.ts`
+**Slug:** `faqs`
+**Admin-Gruppe:** Content
-- **Read:** Nur authentifizierte Benutzer (Datenschutz!)
-- **Create:** Öffentlich (für Anmeldungen)
-- **Update/Delete:** Nur authentifizierte Benutzer
+Häufig gestellte Fragen mit Kategorisierung.
-#### Automatische Hooks
+#### Felder
-Bei der Erstellung wird automatisch:
-- `subscribedAt` auf aktuelles Datum gesetzt
-- `confirmationToken` generiert (UUID)
+| Feld | Typ | Beschreibung | Pflicht |
+|------|-----|--------------|---------|
+| `question` | Text | Frage | Ja |
+| `answer` | RichText | Antwort | Ja |
+| `category` | Text | Kategorie | Nein |
+| `order` | Number | Sortierung | Nein |
+| `isActive` | Checkbox | Sichtbarkeit | Nein |
-Bei Status-Änderungen:
-- `confirmedAt` wird gesetzt bei Wechsel zu "confirmed"
-- `unsubscribedAt` wird gesetzt bei Wechsel zu "unsubscribed"
+---
+
+### 5. Team Collection
+
+**Pfad:** `src/collections/Team.ts`
+**Slug:** `team`
+**Admin-Gruppe:** Content
+
+Team-Mitglieder und Mitarbeiter.
+
+#### Felder
+
+| Feld | Typ | Beschreibung | Pflicht |
+|------|-----|--------------|---------|
+| `name` | Text | Name | Ja |
+| `role` | Text | Position/Rolle | Nein |
+| `image` | Upload | Portrait-Foto | Nein |
+| `bio` | Textarea | Kurzbiografie | Nein |
+| `email` | Email | E-Mail-Adresse | Nein |
+| `phone` | Text | Telefon | Nein |
+| `socialLinks` | Array | Social Media Links | Nein |
+| `order` | Number | Sortierung | Nein |
+| `isActive` | Checkbox | Sichtbarkeit | Nein |
+
+---
+
+### 6. Services Collection
+
+**Pfad:** `src/collections/Services.ts`
+**Slug:** `services`
+**Admin-Gruppe:** Content
+
+Leistungen und Dienstleistungen.
+
+#### Felder
+
+| Feld | Typ | Beschreibung | Pflicht |
+|------|-----|--------------|---------|
+| `title` | Text | Leistungsname | Ja |
+| `slug` | Text | URL-Pfad | Ja |
+| `excerpt` | Textarea | Kurzbeschreibung | Nein |
+| `content` | RichText | Detailbeschreibung | Nein |
+| `icon` | Text | Icon-Name | Nein |
+| `image` | Upload | Bild | Nein |
+| `category` | Relationship | Service-Kategorie | Nein |
+| `order` | Number | Sortierung | Nein |
+
+---
+
+### 7. Timelines Collection
+
+**Pfad:** `src/collections/Timelines.ts`
+**Slug:** `timelines`
+**Admin-Gruppe:** Content
+
+Chronologische Events für Geschichte, Meilensteine, Releases.
+
+#### API
+
+```typescript
+// Timeline laden
+fetch('https://cms.c2sgmbh.de/api/timelines?tenant=4&slug=company-history&locale=de')
+```
+
+#### Timeline-Typen
+
+- `history` - Unternehmensgeschichte
+- `milestones` - Projektmeilensteine
+- `releases` - Produkt-Releases
+- `career` - Karriere/Lebenslauf
+- `events` - Ereignisse
+- `process` - Prozess/Ablauf
+
+---
+
+### 8. Workflows Collection
+
+**Pfad:** `src/collections/Workflows.ts`
+**Slug:** `workflows`
+**Admin-Gruppe:** Content
+
+Komplexe Prozesse mit Phasen, Abhängigkeiten und Status-Tracking.
+
+#### API
+
+```typescript
+// Workflow laden
+fetch('https://cms.c2sgmbh.de/api/workflows?tenant=4&type=project&locale=de')
+```
+
+#### Workflow-Typen
+
+- `project` - Projektabläufe
+- `business` - Geschäftsprozesse
+- `approval` - Genehmigungs-Workflows
+- `onboarding` - Mitarbeiter-/Kundeneinführung
+- `support` - Support/Service-Prozesse
+- `development` - Entwicklungsprozesse
---
@@ -140,6 +284,19 @@ Bei Status-Änderungen:
Alle Blocks können in der Pages Collection verwendet werden.
+### Block-Übersicht
+
+| Block | Slug | Beschreibung |
+|-------|------|--------------|
+| Posts List | `posts-list-block` | Blog/News-Liste |
+| Testimonials | `testimonials-block` | Kundenstimmen |
+| Newsletter | `newsletter-block` | Anmeldeformular |
+| Process Steps | `process-steps-block` | Prozess-Schritte |
+| Timeline | `timeline-block` | Chronologie |
+| FAQ | `faq-block` | FAQ-Akkordeon |
+| Team | `team-block` | Team-Mitglieder |
+| Services | `services-block` | Leistungen |
+
### 1. Posts List Block
**Slug:** `posts-list-block`
@@ -164,8 +321,6 @@ Zeigt eine Liste von Blog-Artikeln, News oder anderen Post-Typen an.
| `showCategory` | Checkbox | true | Kategorie anzeigen |
| `showPagination` | Checkbox | false | Pagination |
| `showReadMore` | Checkbox | true | "Alle anzeigen" Link |
-| `readMoreLabel` | Text | "Alle Beiträge anzeigen" | Link-Text |
-| `readMoreLink` | Text | /blog | Ziel-URL |
| `backgroundColor` | Select | white | Hintergrundfarbe |
#### Layout-Optionen
@@ -184,25 +339,6 @@ Zeigt eine Liste von Blog-Artikeln, News oder anderen Post-Typen an.
Zeigt Kundenstimmen aus der Testimonials Collection.
-#### Konfigurationsoptionen
-
-| Option | Typ | Standard | Beschreibung |
-|--------|-----|----------|--------------|
-| `title` | Text | "Das sagen unsere Kunden" | Überschrift |
-| `subtitle` | Text | - | Untertitel |
-| `layout` | Select | slider | Darstellung |
-| `columns` | Select | 3 | Spalten (bei Grid) |
-| `displayMode` | Select | all | Auswahl-Modus |
-| `selectedTestimonials` | Relationship | - | Handverlesene Auswahl |
-| `limit` | Number | 6 | Max. Anzahl |
-| `showRating` | Checkbox | true | Sterne anzeigen |
-| `showImage` | Checkbox | true | Foto anzeigen |
-| `showCompany` | Checkbox | true | Unternehmen anzeigen |
-| `showSource` | Checkbox | false | Quelle anzeigen |
-| `autoplay` | Checkbox | true | Auto-Wechsel (Slider) |
-| `autoplaySpeed` | Number | 5000 | Wechselintervall (ms) |
-| `backgroundColor` | Select | light | Hintergrundfarbe |
-
#### Layout-Optionen
- `slider` - Karussell
@@ -219,27 +355,6 @@ Zeigt Kundenstimmen aus der Testimonials Collection.
Anmeldeformular für Newsletter mit DSGVO-Hinweis.
-#### Konfigurationsoptionen
-
-| Option | Typ | Standard | Beschreibung |
-|--------|-----|----------|--------------|
-| `title` | Text | "Newsletter abonnieren" | Überschrift |
-| `subtitle` | Textarea | Standard-Text | Beschreibung |
-| `layout` | Select | inline | Formular-Layout |
-| `image` | Upload | - | Bild (bei with-image) |
-| `imagePosition` | Select | left | Bildposition |
-| `collectName` | Checkbox | false | Name abfragen |
-| `showInterests` | Checkbox | false | Interessen anzeigen |
-| `availableInterests` | Select (hasMany) | - | Verfügbare Interessen |
-| `buttonText` | Text | "Anmelden" | Button-Text |
-| `placeholderEmail` | Text | "Ihre E-Mail-Adresse" | Placeholder |
-| `successMessage` | Textarea | Standard-Text | Erfolgsmeldung |
-| `errorMessage` | Text | Standard-Text | Fehlermeldung |
-| `privacyText` | Textarea | Standard-Text | Datenschutz-Hinweis |
-| `privacyLink` | Text | /datenschutz | Link zur DSE |
-| `source` | Text | website | Tracking-Quelle |
-| `backgroundColor` | Select | accent | Hintergrundfarbe |
-
#### Layout-Optionen
- `inline` - Eingabe + Button nebeneinander
@@ -250,85 +365,54 @@ Anmeldeformular für Newsletter mit DSGVO-Hinweis.
---
-### 4. Process Steps Block
+### 4. FAQ Block
-**Slug:** `process-steps-block`
+**Slug:** `faq-block`
-Zeigt Prozess-Schritte / "So funktioniert es" Darstellungen.
+FAQ-Akkordeon mit Schema.org Markup.
#### Konfigurationsoptionen
-| Option | Typ | Standard | Beschreibung |
-|--------|-----|----------|--------------|
-| `title` | Text | "So funktioniert es" | Überschrift |
-| `subtitle` | Text | - | Untertitel |
-| `layout` | Select | horizontal | Darstellung |
-| `showNumbers` | Checkbox | true | Schritt-Nummern |
-| `showIcons` | Checkbox | true | Icons anzeigen |
-| `steps` | Array | - | Schritte (2-10) |
-| `cta.show` | Checkbox | false | CTA anzeigen |
-| `cta.label` | Text | "Jetzt starten" | Button-Text |
-| `cta.href` | Text | - | Button-Link |
-| `cta.variant` | Select | default | Button-Stil |
-| `backgroundColor` | Select | white | Hintergrundfarbe |
-
-#### Schritte-Felder
-
-- `title` - Schritt-Titel (Pflicht)
-- `description` - Beschreibung
-- `icon` - Emoji oder Icon-Name
-- `image` - Optionales Bild
-
-#### Layout-Optionen
-
-- `horizontal` - Nebeneinander
-- `vertical` - Untereinander
-- `alternating` - Zickzack
-- `connected` - Mit Verbindungslinien
-- `timeline` - Timeline-Stil
+| Option | Typ | Beschreibung |
+|--------|-----|--------------|
+| `title` | Text | Überschrift |
+| `subtitle` | Text | Untertitel |
+| `displayMode` | Select | all / selected / byCategory |
+| `selectedFaqs` | Relationship | Handverlesene FAQs |
+| `filterCategory` | Text | Kategorie-Filter |
+| `layout` | Select | accordion / list / grid |
+| `expandFirst` | Checkbox | Erste FAQ offen |
+| `showSchema` | Checkbox | JSON-LD generieren |
---
-### 5. Timeline Block
+### 5. Team Block
-**Slug:** `timeline-block`
+**Slug:** `team-block`
-Chronologische Darstellung von Ereignissen (z.B. Firmengeschichte).
-
-#### Konfigurationsoptionen
-
-| Option | Typ | Standard | Beschreibung |
-|--------|-----|----------|--------------|
-| `title` | Text | - | Überschrift |
-| `subtitle` | Text | - | Untertitel |
-| `layout` | Select | vertical | Darstellung |
-| `showConnector` | Checkbox | true | Verbindungslinie |
-| `markerStyle` | Select | dot | Marker-Stil |
-| `items` | Array | - | Einträge |
-| `backgroundColor` | Select | white | Hintergrundfarbe |
-
-#### Einträge-Felder
-
-- `year` - Jahr/Datum
-- `title` - Titel (Pflicht)
-- `description` - Beschreibung
-- `icon` - Emoji oder Icon
-- `image` - Optionales Bild
-- `link.label` - Link-Text
-- `link.href` - Link-URL
+Team-Mitglieder aus der Team Collection.
#### Layout-Optionen
-- `vertical` - Standard vertikal
-- `alternating` - Links/Rechts wechselnd
-- `horizontal` - Horizontale Zeitleiste
+- `grid` - Karten im Grid
+- `list` - Listenansicht
+- `carousel` - Karussell
+- `compact` - Kompakt
-#### Marker-Stile
+---
-- `dot` - Punkt
-- `number` - Nummer
-- `icon` - Icon
-- `date` - Jahr/Datum
+### 6. Services Block
+
+**Slug:** `services-block`
+
+Leistungen aus der Services Collection.
+
+#### Layout-Optionen
+
+- `grid` - Karten im Grid
+- `list` - Listenansicht mit Details
+- `icons` - Icon-Grid
+- `tabs` - Tab-Navigation
---
@@ -344,6 +428,11 @@ multiTenantPlugin({
posts: {},
testimonials: {},
'newsletter-subscribers': {},
+ faqs: {},
+ team: {},
+ services: {},
+ timelines: {},
+ workflows: {},
// ... weitere Collections
},
})
@@ -351,10 +440,18 @@ multiTenantPlugin({
### Tenant-Zuordnung
-Jedes Dokument enthält ein `tenant`-Feld, das auf die Tenants-Collection verweist. Die Access-Control-Funktionen in `src/lib/tenantAccess.ts` sorgen für die Isolation:
+Jedes Dokument enthält ein `tenant`-Feld. Die Access-Control sorgt für Isolation:
- **Authentifizierte Admins:** Sehen alle Dokumente
-- **Anonyme Requests:** Nur Dokumente des Tenants, der zur Domain passt
+- **Anonyme Requests:** Nur Dokumente des passenden Tenants
+
+### Tenant-IDs
+
+| ID | Name | Slug |
+|----|------|------|
+| 1 | porwoll.de | porwoll |
+| 4 | Complex Care Solutions GmbH | c2s |
+| 5 | Gunshin | gunshin |
---
@@ -363,38 +460,83 @@ Jedes Dokument enthält ein `tenant`-Feld, das auf die Tenants-Collection verwei
```
src/
├── collections/
-│ ├── Posts.ts # Blog/News Collection
-│ ├── Testimonials.ts # Kundenstimmen
-│ └── NewsletterSubscribers.ts # Newsletter-Anmeldungen
+│ ├── Posts.ts
+│ ├── Categories.ts
+│ ├── Testimonials.ts
+│ ├── NewsletterSubscribers.ts
+│ ├── FAQs.ts
+│ ├── Team.ts
+│ ├── Services.ts
+│ ├── ServiceCategories.ts
+│ ├── Timelines.ts
+│ └── Workflows.ts
├── blocks/
-│ ├── PostsListBlock.ts # Blog/News-Liste
-│ ├── TestimonialsBlock.ts # Testimonials-Anzeige
-│ ├── NewsletterBlock.ts # Newsletter-Formular
-│ ├── ProcessStepsBlock.ts # Prozess-Schritte
-│ ├── TimelineBlock.ts # Timeline
-│ └── index.ts # Block-Exports
+│ ├── PostsListBlock.ts
+│ ├── TestimonialsBlock.ts
+│ ├── NewsletterBlock.ts
+│ ├── ProcessStepsBlock.ts
+│ ├── TimelineBlock.ts
+│ ├── FAQBlock.ts
+│ ├── TeamBlock.ts
+│ ├── ServicesBlock.ts
+│ └── index.ts
├── lib/
-│ └── tenantAccess.ts # Access-Control-Funktionen
-└── payload.config.ts # Haupt-Konfiguration
+│ ├── tenantAccess.ts
+│ └── email/
+│ ├── newsletter-service.ts
+│ └── newsletter-templates.ts
+├── hooks/
+│ └── sendNewsletterConfirmation.ts
+└── app/(payload)/api/newsletter/
+ ├── subscribe/route.ts
+ ├── confirm/route.ts
+ └── unsubscribe/route.ts
```
---
-## Datenbank-Tabellen
+## URLs
-| Tabelle | Beschreibung |
-|---------|--------------|
-| `posts` | Blog/News-Beiträge |
-| `posts_rels` | Kategorien-Beziehungen |
-| `testimonials` | Kundenstimmen |
-| `newsletter_subscribers` | Newsletter-Anmeldungen |
-| `newsletter_subscribers_interests` | Interessen (hasMany) |
-| `pages_rels` | Block-Relationships |
+### Production (für Frontends)
+
+| Resource | URL |
+|----------|-----|
+| Posts API | https://cms.c2sgmbh.de/api/posts |
+| Testimonials API | https://cms.c2sgmbh.de/api/testimonials |
+| FAQs API | https://cms.c2sgmbh.de/api/faqs |
+| Team API | https://cms.c2sgmbh.de/api/team |
+| Services API | https://cms.c2sgmbh.de/api/services |
+| Timelines API | https://cms.c2sgmbh.de/api/timelines |
+| Workflows API | https://cms.c2sgmbh.de/api/workflows |
+| Newsletter Subscribe | https://cms.c2sgmbh.de/api/newsletter/subscribe |
+
+### Development
+
+| Resource | URL |
+|----------|-----|
+| API Base | https://pl.porwoll.tech/api |
+| Admin Panel | https://pl.porwoll.tech/admin |
---
## Changelog
+### Version 1.2 (18.12.2025)
+
+- Dokumentation auf Production-URLs aktualisiert
+- API-Endpoints für Frontend hinzugefügt
+- Newsletter Double Opt-In Flow dokumentiert
+- Timelines und Workflows Collections hinzugefügt
+- FAQ, Team, Services Blocks dokumentiert
+
+### Version 1.1 (14.12.2025)
+
+- Timelines Collection für chronologische Darstellungen
+- Workflows Collection für Prozesse
+- FAQs Collection mit Kategorisierung
+- Team Collection mit Social Links
+- Services Collection mit Kategorien
+
### Version 1.0 (30.11.2025)
- Posts Collection um `type`, `isFeatured`, `excerpt` erweitert
@@ -402,4 +544,7 @@ src/
- NewsletterSubscribers Collection erstellt (DSGVO-konform)
- 5 neue Blocks für Pages implementiert
- Multi-Tenant Integration für alle Collections
-- Migration `20251130_135459` erstellt und angewendet
+
+---
+
+*Letzte Aktualisierung: 18. Dezember 2025*
diff --git a/src/payload.config.ts b/src/payload.config.ts
index 2fa98f1..fdb6028 100644
--- a/src/payload.config.ts
+++ b/src/payload.config.ts
@@ -100,20 +100,15 @@ export default buildConfig({
components: {
// Tenant-Kontext in der Admin-Header-Leiste anzeigen
afterNavLinks: ['@/components/admin/TenantBreadcrumb#TenantBreadcrumb'],
- // Custom Views
- views: {
- // Tenant Self-Service Dashboard
- tenantDashboard: {
- Component: '@/components/admin/TenantDashboardView#TenantDashboardView',
- path: '/tenant-dashboard',
- meta: {
- title: 'Tenant Dashboard',
- description: 'E-Mail-Statistiken und Übersicht für Ihren Tenant',
- },
- },
- },
- // Navigation um Dashboard-Link zu ergänzen
- beforeNavLinks: ['@/components/admin/DashboardNavLink#DashboardNavLink'],
+ // Custom Views disabled due to bug - see https://github.com/payloadcms/payload/issues/XXXX
+ // TypeError: Missing parameter name at 5 (path-to-regexp error)
+ // views: {
+ // TenantDashboard: {
+ // Component: '@/components/admin/TenantDashboardView#TenantDashboardView',
+ // path: '/tenant-dashboard',
+ // },
+ // },
+ // beforeNavLinks: ['@/components/admin/DashboardNavLink#DashboardNavLink'],
},
},
// Multi-Tenant Email Adapter