feat: Consent-Service Module nach Compliance migriert (DSR, E-Mail-Templates, Legal Docs, Banner)
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 36s
CI / test-python-backend-compliance (push) Successful in 31s
CI / test-python-document-crawler (push) Successful in 23s
CI / test-python-dsms-gateway (push) Successful in 18s

5-Phasen-Migration: Go consent-service Proxies durch native Python/FastAPI ersetzt.

Phase 1 — DSR (Betroffenenrechte): 6 Tabellen, 30 Endpoints, Frontend-API umgestellt
Phase 2 — E-Mail-Templates: 5 Tabellen, 20 Endpoints, neues Frontend, SDK_STEPS erweitert
Phase 3 — Legal Documents Extension: User Consents, Audit Log, Cookie-Kategorien
Phase 4 — Banner Consent: Device-Consents, Site-Configs, Kategorien, Vendors
Phase 5 — Cleanup: DSR-Proxy aus main.py entfernt, Frontend-URLs aktualisiert

148 neue Tests (50 + 47 + 26 + 25), alle bestanden.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-05 00:36:24 +01:00
parent 2211cb9349
commit b7c1a5da1a
23 changed files with 7146 additions and 542 deletions

View File

@@ -0,0 +1,177 @@
-- Migration 026: DSR (Data Subject Requests) — Betroffenenanfragen nach DSGVO Art. 15-21
-- Ersetzt Go consent-service Proxy durch native Python/FastAPI Implementierung
-- Sequence für Request-Nummern
CREATE SEQUENCE IF NOT EXISTS compliance_dsr_request_number_seq START WITH 1;
-- Haupttabelle: DSR Requests
CREATE TABLE IF NOT EXISTS compliance_dsr_requests (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL DEFAULT '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
request_number TEXT NOT NULL,
request_type TEXT NOT NULL DEFAULT 'access',
status TEXT NOT NULL DEFAULT 'intake',
priority TEXT NOT NULL DEFAULT 'normal',
-- Antragsteller
requester_name TEXT NOT NULL,
requester_email TEXT NOT NULL,
requester_phone TEXT,
requester_address TEXT,
requester_customer_id TEXT,
-- Anfrage-Details
source TEXT NOT NULL DEFAULT 'email',
source_details TEXT,
request_text TEXT,
notes TEXT,
internal_notes TEXT,
-- Fristen (Art. 12 Abs. 3 DSGVO)
received_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deadline_at TIMESTAMPTZ NOT NULL,
extended_deadline_at TIMESTAMPTZ,
extension_reason TEXT,
extension_approved_by TEXT,
extension_approved_at TIMESTAMPTZ,
-- Identitaetspruefung
identity_verified BOOLEAN NOT NULL DEFAULT FALSE,
verification_method TEXT,
verified_at TIMESTAMPTZ,
verified_by TEXT,
verification_notes TEXT,
verification_document_ref TEXT,
-- Zuweisung
assigned_to TEXT,
assigned_at TIMESTAMPTZ,
assigned_by TEXT,
-- Abschluss
completed_at TIMESTAMPTZ,
completion_notes TEXT,
rejection_reason TEXT,
rejection_legal_basis TEXT,
-- Typ-spezifische Daten (JSONB)
erasure_checklist JSONB DEFAULT '[]'::jsonb,
data_export JSONB DEFAULT '{}'::jsonb,
rectification_details JSONB DEFAULT '{}'::jsonb,
objection_details JSONB DEFAULT '{}'::jsonb,
affected_systems JSONB DEFAULT '[]'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by TEXT DEFAULT 'system',
updated_by TEXT
);
CREATE INDEX IF NOT EXISTS idx_dsr_requests_tenant ON compliance_dsr_requests(tenant_id);
CREATE INDEX IF NOT EXISTS idx_dsr_requests_status ON compliance_dsr_requests(status);
CREATE INDEX IF NOT EXISTS idx_dsr_requests_type ON compliance_dsr_requests(request_type);
CREATE INDEX IF NOT EXISTS idx_dsr_requests_priority ON compliance_dsr_requests(priority);
CREATE INDEX IF NOT EXISTS idx_dsr_requests_assigned ON compliance_dsr_requests(assigned_to);
CREATE INDEX IF NOT EXISTS idx_dsr_requests_deadline ON compliance_dsr_requests(deadline_at);
CREATE INDEX IF NOT EXISTS idx_dsr_requests_received ON compliance_dsr_requests(received_at);
CREATE UNIQUE INDEX IF NOT EXISTS idx_dsr_requests_number ON compliance_dsr_requests(tenant_id, request_number);
-- Status-History (Audit-Trail)
CREATE TABLE IF NOT EXISTS compliance_dsr_status_history (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL DEFAULT '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
dsr_id UUID NOT NULL,
previous_status TEXT,
new_status TEXT NOT NULL,
changed_by TEXT,
comment TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_dsr_history_dsr ON compliance_dsr_status_history(dsr_id);
CREATE INDEX IF NOT EXISTS idx_dsr_history_created ON compliance_dsr_status_history(created_at);
-- Kommunikation (E-Mail, Portal, intern)
CREATE TABLE IF NOT EXISTS compliance_dsr_communications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL DEFAULT '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
dsr_id UUID NOT NULL,
communication_type TEXT NOT NULL DEFAULT 'outgoing',
channel TEXT NOT NULL DEFAULT 'email',
subject TEXT,
content TEXT NOT NULL,
template_used TEXT,
attachments JSONB DEFAULT '[]'::jsonb,
sent_at TIMESTAMPTZ,
sent_by TEXT,
received_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by TEXT DEFAULT 'system'
);
CREATE INDEX IF NOT EXISTS idx_dsr_comms_dsr ON compliance_dsr_communications(dsr_id);
-- Kommunikationsvorlagen
CREATE TABLE IF NOT EXISTS compliance_dsr_templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL DEFAULT '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
name TEXT NOT NULL,
template_type TEXT NOT NULL,
request_type TEXT,
language TEXT NOT NULL DEFAULT 'de',
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_dsr_templates_tenant ON compliance_dsr_templates(tenant_id);
CREATE INDEX IF NOT EXISTS idx_dsr_templates_type ON compliance_dsr_templates(template_type);
-- Versionierte Template-Inhalte
CREATE TABLE IF NOT EXISTS compliance_dsr_template_versions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
template_id UUID NOT NULL,
version TEXT NOT NULL DEFAULT '1.0',
subject TEXT NOT NULL,
body_html TEXT NOT NULL,
body_text TEXT,
status TEXT NOT NULL DEFAULT 'draft',
published_at TIMESTAMPTZ,
published_by TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by TEXT DEFAULT 'system'
);
CREATE INDEX IF NOT EXISTS idx_dsr_tpl_versions_template ON compliance_dsr_template_versions(template_id);
CREATE INDEX IF NOT EXISTS idx_dsr_tpl_versions_status ON compliance_dsr_template_versions(status);
-- Art. 17(3) Ausnahmepruefungen
CREATE TABLE IF NOT EXISTS compliance_dsr_exception_checks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL DEFAULT '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
dsr_id UUID NOT NULL,
check_code TEXT NOT NULL,
article TEXT NOT NULL,
label TEXT NOT NULL,
description TEXT,
applies BOOLEAN,
notes TEXT,
checked_by TEXT,
checked_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_dsr_exception_dsr ON compliance_dsr_exception_checks(dsr_id);
-- Default-Templates einfuegen
INSERT INTO compliance_dsr_templates (id, name, template_type, request_type, language)
VALUES
(gen_random_uuid(), 'Eingangsbestaetigung', 'receipt', NULL, 'de'),
(gen_random_uuid(), 'Identitaetsanfrage', 'clarification', NULL, 'de'),
(gen_random_uuid(), 'Auskunft abgeschlossen', 'completion', 'access', 'de'),
(gen_random_uuid(), 'Loeschung abgeschlossen', 'completion', 'erasure', 'de'),
(gen_random_uuid(), 'Berichtigung abgeschlossen', 'completion', 'rectification', 'de'),
(gen_random_uuid(), 'Ablehnung Auskunft', 'rejection', 'access', 'de'),
(gen_random_uuid(), 'Ablehnung Loeschung', 'rejection', 'erasure', 'de'),
(gen_random_uuid(), 'Fristverlaengerung', 'extension', NULL, 'de')
ON CONFLICT DO NOTHING;

View File

@@ -0,0 +1,118 @@
-- Migration 027: E-Mail-Templates — Benachrichtigungsvorlagen fuer DSGVO-Compliance
-- Zentrale Verwaltung von E-Mail-Templates fuer DSR, Consent, Breach-Notifications etc.
-- Template-Definitionen
CREATE TABLE IF NOT EXISTS compliance_email_templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL DEFAULT '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
template_type TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT,
category TEXT NOT NULL DEFAULT 'general',
is_active BOOLEAN NOT NULL DEFAULT TRUE,
sort_order INTEGER NOT NULL DEFAULT 0,
variables JSONB DEFAULT '[]'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_email_tpl_tenant ON compliance_email_templates(tenant_id);
CREATE INDEX IF NOT EXISTS idx_email_tpl_type ON compliance_email_templates(template_type);
CREATE INDEX IF NOT EXISTS idx_email_tpl_category ON compliance_email_templates(category);
CREATE UNIQUE INDEX IF NOT EXISTS idx_email_tpl_tenant_type ON compliance_email_templates(tenant_id, template_type);
-- Versionierte Template-Inhalte
CREATE TABLE IF NOT EXISTS compliance_email_template_versions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
template_id UUID NOT NULL,
version TEXT NOT NULL DEFAULT '1.0',
language TEXT NOT NULL DEFAULT 'de',
subject TEXT NOT NULL,
body_html TEXT NOT NULL,
body_text TEXT,
status TEXT NOT NULL DEFAULT 'draft',
submitted_at TIMESTAMPTZ,
submitted_by TEXT,
published_at TIMESTAMPTZ,
published_by TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by TEXT DEFAULT 'system'
);
CREATE INDEX IF NOT EXISTS idx_email_tpl_ver_template ON compliance_email_template_versions(template_id);
CREATE INDEX IF NOT EXISTS idx_email_tpl_ver_status ON compliance_email_template_versions(status);
-- Approval-Workflow
CREATE TABLE IF NOT EXISTS compliance_email_template_approvals (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
version_id UUID NOT NULL,
action TEXT NOT NULL DEFAULT 'approve',
comment TEXT,
approved_by TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_email_tpl_appr_version ON compliance_email_template_approvals(version_id);
-- Audit-Trail gesendeter E-Mails
CREATE TABLE IF NOT EXISTS compliance_email_send_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL DEFAULT '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
template_type TEXT NOT NULL,
version_id UUID,
recipient TEXT NOT NULL,
subject TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'sent',
variables JSONB DEFAULT '{}'::jsonb,
error_message TEXT,
sent_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_email_logs_tenant ON compliance_email_send_logs(tenant_id);
CREATE INDEX IF NOT EXISTS idx_email_logs_type ON compliance_email_send_logs(template_type);
CREATE INDEX IF NOT EXISTS idx_email_logs_sent ON compliance_email_send_logs(sent_at);
-- Globale Einstellungen (Branding)
CREATE TABLE IF NOT EXISTS compliance_email_template_settings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL DEFAULT '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
sender_name TEXT DEFAULT 'Datenschutzbeauftragter',
sender_email TEXT DEFAULT 'datenschutz@example.de',
reply_to TEXT,
logo_url TEXT,
primary_color TEXT DEFAULT '#4F46E5',
secondary_color TEXT DEFAULT '#7C3AED',
footer_text TEXT DEFAULT 'Datenschutzhinweis: Diese E-Mail enthaelt vertrauliche Informationen.',
company_name TEXT,
company_address TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_email_settings_tenant ON compliance_email_template_settings(tenant_id);
-- Default-Templates einfuegen
INSERT INTO compliance_email_templates (id, tenant_id, template_type, name, description, category, sort_order, variables)
VALUES
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'welcome', 'Willkommen', 'Willkommens-E-Mail fuer neue Nutzer', 'general', 1, '["user_name", "company_name", "login_url"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'verification', 'E-Mail-Verifizierung', 'Verifizierungs-Link fuer E-Mail-Adressen', 'general', 2, '["user_name", "verification_url", "expiry_hours"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'password_reset', 'Passwort zuruecksetzen', 'Link zum Zuruecksetzen des Passworts', 'general', 3, '["user_name", "reset_url", "expiry_hours"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'dsr_receipt', 'DSR Eingangsbestaetigung', 'Bestaetigung fuer eingehende Betroffenenanfrage', 'dsr', 10, '["requester_name", "reference_number", "request_type", "deadline"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'dsr_identity_request', 'DSR Identitaetsanfrage', 'Anforderung zur Identitaetspruefung', 'dsr', 11, '["requester_name", "reference_number"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'dsr_completion', 'DSR Abschluss', 'Benachrichtigung ueber abgeschlossene Anfrage', 'dsr', 12, '["requester_name", "reference_number", "request_type", "completion_date"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'dsr_rejection', 'DSR Ablehnung', 'Benachrichtigung ueber abgelehnte Anfrage', 'dsr', 13, '["requester_name", "reference_number", "rejection_reason", "legal_basis"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'dsr_extension', 'DSR Fristverlaengerung', 'Benachrichtigung ueber verlaengerte Bearbeitungsfrist', 'dsr', 14, '["requester_name", "reference_number", "new_deadline", "extension_reason"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'consent_request', 'Einwilligungsanfrage', 'Anfrage zur Einwilligung in Datenverarbeitung', 'consent', 20, '["user_name", "purpose", "consent_url"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'consent_confirmation', 'Einwilligungsbestaetigung', 'Bestaetigung der erteilten Einwilligung', 'consent', 21, '["user_name", "purpose", "consent_date"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'consent_withdrawal', 'Widerruf bestaetigt', 'Bestaetigung des Widerrufs einer Einwilligung', 'consent', 22, '["user_name", "purpose", "withdrawal_date"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'consent_reminder', 'Einwilligungs-Erinnerung', 'Erinnerung an auslaufende Einwilligung', 'consent', 23, '["user_name", "purpose", "expiry_date"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'breach_notification_authority', 'Datenpanne Aufsichtsbehoerde', 'Meldung an Datenschutzbehoerde (Art. 33)', 'breach', 30, '["incident_date", "incident_description", "affected_count", "measures_taken", "authority_name"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'breach_notification_affected', 'Datenpanne Betroffene', 'Benachrichtigung betroffener Personen (Art. 34)', 'breach', 31, '["user_name", "incident_date", "incident_description", "measures_taken", "contact_info"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'breach_internal', 'Datenpanne intern', 'Interne Meldung einer Datenschutzverletzung', 'breach', 32, '["reporter_name", "incident_date", "incident_description", "severity"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'vendor_dpa_request', 'AVV-Anfrage', 'Anforderung eines Auftragsverarbeitungsvertrags', 'vendor', 40, '["vendor_name", "contact_name", "deadline", "requirements"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'vendor_review_reminder', 'Vendor-Pruefung Erinnerung', 'Erinnerung an faellige Dienstleisterpruefung', 'vendor', 41, '["vendor_name", "review_due_date", "last_review_date"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'training_invitation', 'Schulungseinladung', 'Einladung zu Datenschutz-Schulung', 'training', 50, '["user_name", "training_title", "training_date", "training_url"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'training_reminder', 'Schulungs-Erinnerung', 'Erinnerung an ausstehende Pflichtschulung', 'training', 51, '["user_name", "training_title", "deadline"]'::jsonb),
(gen_random_uuid(), '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'training_completion', 'Schulung abgeschlossen', 'Bestaetigung und Zertifikat nach Schulungsabschluss', 'training', 52, '["user_name", "training_title", "completion_date", "certificate_url"]'::jsonb)
ON CONFLICT DO NOTHING;

View File

@@ -0,0 +1,69 @@
-- =========================================================
-- Migration 028: Legal Documents Extension
-- User Consents, Consent Audit Log, Cookie Categories
-- =========================================================
-- compliance_user_consents: End-User Consent-Records
CREATE TABLE IF NOT EXISTS compliance_user_consents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
user_id TEXT NOT NULL,
document_id UUID NOT NULL REFERENCES compliance_legal_documents(id) ON DELETE CASCADE,
document_version_id UUID REFERENCES compliance_legal_document_versions(id) ON DELETE SET NULL,
document_type TEXT NOT NULL,
consented BOOLEAN NOT NULL DEFAULT TRUE,
ip_address TEXT,
user_agent TEXT,
consented_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
withdrawn_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_user_consents_tenant ON compliance_user_consents(tenant_id);
CREATE INDEX IF NOT EXISTS idx_user_consents_user ON compliance_user_consents(user_id);
CREATE INDEX IF NOT EXISTS idx_user_consents_doc ON compliance_user_consents(document_id);
CREATE INDEX IF NOT EXISTS idx_user_consents_type ON compliance_user_consents(document_type);
-- compliance_consent_audit_log: Immutable Audit-Trail
CREATE TABLE IF NOT EXISTS compliance_consent_audit_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
action TEXT NOT NULL, -- consent_given|consent_withdrawn|consent_checked|document_published
entity_type TEXT NOT NULL, -- user_consent|legal_document|cookie_category
entity_id UUID,
user_id TEXT,
details JSONB DEFAULT '{}',
ip_address TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_consent_audit_tenant ON compliance_consent_audit_log(tenant_id);
CREATE INDEX IF NOT EXISTS idx_consent_audit_action ON compliance_consent_audit_log(action);
CREATE INDEX IF NOT EXISTS idx_consent_audit_entity ON compliance_consent_audit_log(entity_type, entity_id);
CREATE INDEX IF NOT EXISTS idx_consent_audit_created ON compliance_consent_audit_log(created_at);
-- compliance_cookie_categories: Cookie-Kategorien fuer Banner
CREATE TABLE IF NOT EXISTS compliance_cookie_categories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
name_de TEXT NOT NULL,
name_en TEXT,
description_de TEXT,
description_en TEXT,
is_required BOOLEAN NOT NULL DEFAULT FALSE,
sort_order INTEGER NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_cookie_cats_tenant ON compliance_cookie_categories(tenant_id);
-- Default Cookie-Kategorien
INSERT INTO compliance_cookie_categories (tenant_id, name_de, name_en, description_de, description_en, is_required, sort_order)
VALUES
('9282a473-5c95-4b3a-bf78-0ecc0ec71d3e'::UUID, 'Notwendig', 'Necessary', 'Technisch notwendige Cookies fuer den Betrieb der Website.', 'Technically necessary cookies for website operation.', TRUE, 0),
('9282a473-5c95-4b3a-bf78-0ecc0ec71d3e'::UUID, 'Funktional', 'Functional', 'Cookies fuer erweiterte Funktionalitaet und Personalisierung.', 'Cookies for enhanced functionality and personalization.', FALSE, 10),
('9282a473-5c95-4b3a-bf78-0ecc0ec71d3e'::UUID, 'Analyse', 'Analytics', 'Cookies zur Analyse der Websitenutzung.', 'Cookies for analyzing website usage.', FALSE, 20),
('9282a473-5c95-4b3a-bf78-0ecc0ec71d3e'::UUID, 'Marketing', 'Marketing', 'Cookies fuer personalisierte Werbung.', 'Cookies for personalized advertising.', FALSE, 30)
ON CONFLICT DO NOTHING;

View File

@@ -0,0 +1,98 @@
-- =========================================================
-- Migration 029: Banner Consent — Device-basierte Cookie-Consents
-- Fuer Einbettung in Kunden-Websites (Consent-Banner SDK)
-- =========================================================
-- compliance_banner_consents: Anonyme Geraete-Consents
CREATE TABLE IF NOT EXISTS compliance_banner_consents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
site_id TEXT NOT NULL,
device_fingerprint TEXT NOT NULL,
categories JSONB DEFAULT '[]',
vendors JSONB DEFAULT '[]',
ip_hash TEXT,
user_agent TEXT,
consent_string TEXT,
expires_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_banner_consent_tenant ON compliance_banner_consents(tenant_id);
CREATE INDEX IF NOT EXISTS idx_banner_consent_site ON compliance_banner_consents(site_id);
CREATE INDEX IF NOT EXISTS idx_banner_consent_device ON compliance_banner_consents(device_fingerprint);
CREATE UNIQUE INDEX IF NOT EXISTS idx_banner_consent_site_device ON compliance_banner_consents(site_id, device_fingerprint);
-- compliance_banner_consent_audit_log: Immutable Audit
CREATE TABLE IF NOT EXISTS compliance_banner_consent_audit_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
consent_id UUID,
action TEXT NOT NULL,
site_id TEXT NOT NULL,
device_fingerprint TEXT,
categories JSONB DEFAULT '[]',
ip_hash TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_banner_audit_tenant ON compliance_banner_consent_audit_log(tenant_id);
CREATE INDEX IF NOT EXISTS idx_banner_audit_site ON compliance_banner_consent_audit_log(site_id);
CREATE INDEX IF NOT EXISTS idx_banner_audit_created ON compliance_banner_consent_audit_log(created_at);
-- compliance_banner_site_configs: Site-Konfiguration (UI-Theme, DSB-Info)
CREATE TABLE IF NOT EXISTS compliance_banner_site_configs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
site_id TEXT NOT NULL,
site_name TEXT,
site_url TEXT,
banner_title TEXT DEFAULT 'Cookie-Einstellungen',
banner_description TEXT DEFAULT 'Wir verwenden Cookies, um Ihnen die bestmoegliche Erfahrung zu bieten.',
privacy_url TEXT,
imprint_url TEXT,
dsb_name TEXT,
dsb_email TEXT,
theme JSONB DEFAULT '{}',
tcf_enabled BOOLEAN DEFAULT FALSE,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_banner_site_config ON compliance_banner_site_configs(tenant_id, site_id);
-- compliance_banner_category_configs: Consent-Kategorien pro Site
CREATE TABLE IF NOT EXISTS compliance_banner_category_configs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
site_config_id UUID NOT NULL REFERENCES compliance_banner_site_configs(id) ON DELETE CASCADE,
category_key TEXT NOT NULL,
name_de TEXT NOT NULL,
name_en TEXT,
description_de TEXT,
description_en TEXT,
is_required BOOLEAN NOT NULL DEFAULT FALSE,
sort_order INTEGER NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_banner_cat_config ON compliance_banner_category_configs(site_config_id);
-- compliance_banner_vendor_configs: Third-Party-Vendor-Tracking
CREATE TABLE IF NOT EXISTS compliance_banner_vendor_configs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
site_config_id UUID NOT NULL REFERENCES compliance_banner_site_configs(id) ON DELETE CASCADE,
vendor_name TEXT NOT NULL,
vendor_url TEXT,
category_key TEXT NOT NULL,
description_de TEXT,
description_en TEXT,
cookie_names JSONB DEFAULT '[]',
retention_days INTEGER DEFAULT 365,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_banner_vendor_config ON compliance_banner_vendor_configs(site_config_id);