# PROMPT: Datenschutzerklärung Integration - Payload Backend ## Kontext Du arbeitest auf dem Server **sv-payload** (10.10.181.100) im Verzeichnis `/home/payload/payload-cms`. Die Datenschutzerklärungen werden extern vom Datenschutzbeauftragten über **Alfright** gepflegt und sollen per iframe eingebunden werden. Die Konfiguration (Tenant-Key, Styling) wird pro Mandant in Payload CMS verwaltet. ## Aufgabe Erweitere das System um eine **PrivacyPolicySettings Collection** für die Verwaltung der externen Datenschutzerklärung pro Tenant. --- ## Schritt 1: Collection erstellen Erstelle `src/collections/PrivacyPolicySettings.ts`: ```typescript // src/collections/PrivacyPolicySettings.ts import type { CollectionConfig } from 'payload' import { tenantScopedPublicRead, authenticatedOnly } from '../lib/tenantAccess' /** * PrivacyPolicySettings Collection * * Konfiguration für externe Datenschutzerklärung (Alfright) pro Tenant. * Öffentlich lesbar (für Frontend), aber tenant-isoliert. */ export const PrivacyPolicySettings: CollectionConfig = { slug: 'privacy-policy-settings', admin: { useAsTitle: 'title', group: 'Consent Management', description: 'Externe Datenschutzerklärung Konfiguration (Alfright)', }, access: { read: tenantScopedPublicRead, create: authenticatedOnly, update: authenticatedOnly, delete: authenticatedOnly, }, fields: [ { name: 'tenant', type: 'relationship', relationTo: 'tenants', required: true, unique: true, admin: { description: 'Jeder Tenant kann nur eine Konfiguration haben', }, }, { name: 'title', type: 'text', required: true, defaultValue: 'Datenschutzerklärung', admin: { description: 'Interner Titel zur Identifikation', }, }, { name: 'provider', type: 'select', required: true, defaultValue: 'alfright', options: [ { label: 'Alfright (extern via iframe)', value: 'alfright' }, { label: 'Eigener Text (nicht implementiert)', value: 'internal' }, ], admin: { description: 'Quelle der Datenschutzerklärung', }, }, // Alfright Konfiguration { name: 'alfright', type: 'group', label: 'Alfright Konfiguration', admin: { condition: (data) => data?.provider === 'alfright', description: 'Einstellungen für die Alfright Integration', }, fields: [ { name: 'tenantId', type: 'text', required: true, defaultValue: 'alfright_schutzteam', admin: { description: 'Alfright Tenant-ID (aus dem iframe-Code)', }, }, { name: 'apiKey', type: 'text', required: true, admin: { description: 'Alfright API-Key / Dokument-ID (aus dem iframe-Code, z.B. "9f315103c43245bcb0806dd56c2be757")', }, }, { name: 'language', type: 'select', required: true, defaultValue: 'de-de', options: [ { label: 'Deutsch (Deutschland)', value: 'de-de' }, { label: 'Deutsch (Österreich)', value: 'de-at' }, { label: 'Deutsch (Schweiz)', value: 'de-ch' }, { label: 'Englisch (UK)', value: 'en-gb' }, { label: 'Englisch (US)', value: 'en-us' }, ], admin: { description: 'Sprache der Datenschutzerklärung', }, }, { name: 'iframeHeight', type: 'number', required: true, defaultValue: 4000, min: 500, max: 10000, admin: { description: 'Höhe des iframes in Pixeln (empfohlen: 3000-5000)', }, }, ], }, // Styling (passend zum Website-Theme) { name: 'styling', type: 'group', label: 'Styling', admin: { condition: (data) => data?.provider === 'alfright', description: 'Farben und Schriften an das Website-Design anpassen', }, fields: [ { name: 'headerColor', type: 'text', required: true, defaultValue: '#ca8a04', admin: { description: 'Farbe der Überschriften (Hex-Code, z.B. #ca8a04 für Gold)', }, }, { name: 'headerFont', type: 'text', required: true, defaultValue: 'Inter, sans-serif', admin: { description: 'Schriftart der Überschriften', }, }, { name: 'headerSize', type: 'text', required: true, defaultValue: '24px', admin: { description: 'Schriftgröße der Hauptüberschriften', }, }, { name: 'subheaderSize', type: 'text', required: true, defaultValue: '18px', admin: { description: 'Schriftgröße der Unterüberschriften', }, }, { name: 'fontColor', type: 'text', required: true, defaultValue: '#f3f4f6', admin: { description: 'Textfarbe (Hex-Code, z.B. #f3f4f6 für hellen Text)', }, }, { name: 'textFont', type: 'text', required: true, defaultValue: 'Inter, sans-serif', admin: { description: 'Schriftart für Fließtext', }, }, { name: 'textSize', type: 'text', required: true, defaultValue: '16px', admin: { description: 'Schriftgröße für Fließtext', }, }, { name: 'linkColor', type: 'text', required: true, defaultValue: '#ca8a04', admin: { description: 'Linkfarbe (Hex-Code)', }, }, { name: 'backgroundColor', type: 'text', required: true, defaultValue: '#111827', admin: { description: 'Hintergrundfarbe (Hex-Code, z.B. #111827 für Dark Theme)', }, }, ], }, // Cookie-Tabelle Option { name: 'showCookieTable', type: 'checkbox', defaultValue: true, admin: { description: 'Cookie-Tabelle aus CookieInventory unterhalb der Datenschutzerklärung anzeigen', }, }, { name: 'cookieTableTitle', type: 'text', defaultValue: 'Übersicht der verwendeten Cookies', admin: { condition: (data) => data?.showCookieTable, description: 'Überschrift für die Cookie-Tabelle', }, }, { name: 'cookieTableDescription', type: 'textarea', defaultValue: 'Ergänzend zur Datenschutzerklärung finden Sie hier eine detaillierte Übersicht aller auf dieser Website eingesetzten Cookies. Sie können Ihre Cookie-Einstellungen jederzeit über den Link "Cookie-Einstellungen" im Footer anpassen.', admin: { condition: (data) => data?.showCookieTable, description: 'Einleitungstext für die Cookie-Tabelle', }, }, // SEO { name: 'seo', type: 'group', label: 'SEO', fields: [ { name: 'metaTitle', type: 'text', defaultValue: 'Datenschutzerklärung', admin: { description: 'Meta-Titel für die Seite', }, }, { name: 'metaDescription', type: 'textarea', defaultValue: 'Informationen zum Datenschutz und zur Verarbeitung Ihrer personenbezogenen Daten.', admin: { description: 'Meta-Beschreibung für Suchmaschinen', }, }, ], }, ], } ``` --- ## Schritt 2: Collection in Payload Config registrieren Aktualisiere `src/payload.config.ts`: ```typescript // Import hinzufügen (bei den anderen Collection-Imports) import { PrivacyPolicySettings } from './collections/PrivacyPolicySettings' // In collections Array hinzufügen collections: [ Users, Media, Tenants, Pages, Posts, Categories, SocialLinks, CookieConfigurations, CookieInventory, ConsentLogs, PrivacyPolicySettings, // NEU ], // In multiTenantPlugin collections hinzufügen plugins: [ multiTenantPlugin({ tenantsSlug: 'tenants', collections: { // ... bestehende Collections ... 'privacy-policy-settings': {}, // NEU }, }), ], ``` --- ## Schritt 3: Build und Migration ```bash cd /home/payload/payload-cms # TypeScript Types generieren pnpm payload generate:types # Migration erstellen pnpm payload migrate:create # Migration ausführen pnpm payload migrate # Build pnpm build # PM2 neu starten pm2 restart payload ``` --- ## Schritt 4: Initiale Daten anlegen Im Admin Panel unter **Consent Management → Privacy Policy Settings → Create**: ### Für porwoll.de (Tenant 1): | Feld | Wert | |------|------| | Tenant | porwoll.de | | Title | Datenschutzerklärung porwoll.de | | Provider | Alfright (extern via iframe) | **Alfright Konfiguration:** | Feld | Wert | |------|------| | Tenant ID | `alfright_schutzteam` | | API Key | `9f315103c43245bcb0806dd56c2be757` | | Language | Deutsch (Deutschland) | | iframe Height | 4000 | **Styling (Dark Theme):** | Feld | Wert | |------|------| | Header Color | `#ca8a04` | | Header Font | `Inter, sans-serif` | | Header Size | `24px` | | Subheader Size | `18px` | | Font Color | `#f3f4f6` | | Text Font | `Inter, sans-serif` | | Text Size | `16px` | | Link Color | `#ca8a04` | | Background Color | `#111827` | **Cookie-Tabelle:** | Feld | Wert | |------|------| | Show Cookie Table | ✅ Aktiviert | | Cookie Table Title | Übersicht der verwendeten Cookies | **SEO:** | Feld | Wert | |------|------| | Meta Title | Datenschutzerklärung \| porwoll.de | | Meta Description | Informationen zum Datenschutz und zur Verarbeitung Ihrer personenbezogenen Daten auf porwoll.de | --- ## Schritt 5: API-Test ```bash # Privacy Policy Settings abrufen (mit Host-Header) curl -s -H "Host: porwoll.de" "http://localhost:3000/api/privacy-policy-settings" | jq # Ohne Host-Header (sollte verweigert werden) curl -s "http://localhost:3000/api/privacy-policy-settings" | jq ``` --- ## Zusammenfassung | Datei | Aktion | |-------|--------| | `src/collections/PrivacyPolicySettings.ts` | NEU erstellt | | `src/payload.config.ts` | Collection registriert | | `src/payload-types.ts` | Automatisch generiert | ## API-Endpoint | Endpoint | Methode | Auth | Beschreibung | |----------|---------|------|--------------| | `/api/privacy-policy-settings` | GET | Public (tenant-scoped) | Datenschutz-Konfiguration | ## Datenmodell ```typescript interface PrivacyPolicySettings { id: number tenant: Tenant title: string provider: 'alfright' | 'internal' alfright: { tenantId: string apiKey: string language: string iframeHeight: number } styling: { headerColor: string headerFont: string headerSize: string subheaderSize: string fontColor: string textFont: string textSize: string linkColor: string backgroundColor: string } showCookieTable: boolean cookieTableTitle: string cookieTableDescription: string seo: { metaTitle: string metaDescription: string } } ```