Initial commit: breakpilot-core - Shared Infrastructure
Docker Compose with 24+ services: - PostgreSQL (PostGIS), Valkey, MinIO, Qdrant - Vault (PKI/TLS), Nginx (Reverse Proxy) - Backend Core API, Consent Service, Billing Service - RAG Service, Embedding Service - Gitea, Woodpecker CI/CD - Night Scheduler, Health Aggregator - Jitsi (Web/XMPP/JVB/Jicofo), Mailpit Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
223
consent-service/migrations/005_banner_consent_tables.sql
Normal file
223
consent-service/migrations/005_banner_consent_tables.sql
Normal file
@@ -0,0 +1,223 @@
|
||||
-- Migration: Banner Consent Tables
|
||||
-- Für @breakpilot/consent-sdk
|
||||
-- DSGVO/TTDSG-konforme Speicherung von Cookie-Einwilligungen
|
||||
|
||||
-- ========================================
|
||||
-- Banner Consents (anonyme Einwilligungen)
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS banner_consents (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
site_id VARCHAR(50) NOT NULL,
|
||||
device_fingerprint VARCHAR(64) NOT NULL,
|
||||
user_id VARCHAR(100), -- Optional: Für eingeloggte Nutzer
|
||||
|
||||
-- Consent-Daten
|
||||
categories JSONB NOT NULL DEFAULT '{}', -- { "analytics": true, "marketing": false }
|
||||
vendors JSONB DEFAULT '{}', -- { "google-analytics": true }
|
||||
tcf_string TEXT, -- IAB TCF String
|
||||
|
||||
-- Metadaten (anonymisiert)
|
||||
ip_hash VARCHAR(64), -- Anonymisierte IP
|
||||
user_agent TEXT,
|
||||
language VARCHAR(10),
|
||||
platform VARCHAR(20), -- web, ios, android
|
||||
app_version VARCHAR(20),
|
||||
|
||||
-- Versionierung
|
||||
version VARCHAR(20) DEFAULT '1.0.0',
|
||||
|
||||
-- Zeitstempel
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
|
||||
-- Constraints
|
||||
CONSTRAINT unique_site_device UNIQUE (site_id, device_fingerprint)
|
||||
);
|
||||
|
||||
-- Indizes für schnelle Abfragen
|
||||
CREATE INDEX IF NOT EXISTS idx_banner_consents_site ON banner_consents(site_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_banner_consents_user ON banner_consents(user_id) WHERE user_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_banner_consents_device ON banner_consents(device_fingerprint);
|
||||
CREATE INDEX IF NOT EXISTS idx_banner_consents_created ON banner_consents(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_banner_consents_expires ON banner_consents(expires_at) WHERE expires_at IS NOT NULL;
|
||||
|
||||
-- ========================================
|
||||
-- Audit Log (unveränderbar)
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS banner_consent_audit_log (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
consent_id UUID NOT NULL,
|
||||
action VARCHAR(20) NOT NULL, -- created, updated, revoked
|
||||
details JSONB,
|
||||
ip_hash VARCHAR(64),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Kein UPDATE/DELETE auf Audit-Log
|
||||
-- REVOKE UPDATE, DELETE ON banner_consent_audit_log FROM PUBLIC;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_banner_audit_consent ON banner_consent_audit_log(consent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_banner_audit_created ON banner_consent_audit_log(created_at);
|
||||
|
||||
-- ========================================
|
||||
-- Site-Konfigurationen
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS banner_site_configs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
site_id VARCHAR(50) UNIQUE NOT NULL,
|
||||
site_name VARCHAR(100) NOT NULL,
|
||||
|
||||
-- UI-Konfiguration
|
||||
ui_theme VARCHAR(20) DEFAULT 'auto',
|
||||
ui_position VARCHAR(20) DEFAULT 'bottom',
|
||||
ui_layout VARCHAR(20) DEFAULT 'modal',
|
||||
custom_css TEXT,
|
||||
|
||||
-- Rechtliche Links
|
||||
privacy_policy_url VARCHAR(255),
|
||||
imprint_url VARCHAR(255),
|
||||
dpo_name VARCHAR(100),
|
||||
dpo_email VARCHAR(100),
|
||||
|
||||
-- TCF 2.2
|
||||
tcf_enabled BOOLEAN DEFAULT FALSE,
|
||||
tcf_cmp_id INTEGER,
|
||||
tcf_cmp_version INTEGER,
|
||||
|
||||
-- Zeitstempel
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- Kategorie-Konfigurationen pro Site
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS banner_category_configs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
site_id VARCHAR(50) NOT NULL,
|
||||
category_id VARCHAR(50) NOT NULL,
|
||||
|
||||
-- Namen (mehrsprachig)
|
||||
name_de VARCHAR(100) NOT NULL,
|
||||
name_en VARCHAR(100),
|
||||
|
||||
-- Beschreibungen (mehrsprachig)
|
||||
description_de TEXT,
|
||||
description_en TEXT,
|
||||
|
||||
-- Einstellungen
|
||||
is_required BOOLEAN DEFAULT FALSE,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Zeitstempel
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT unique_site_category UNIQUE (site_id, category_id)
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- Vendor-Konfigurationen
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS banner_vendor_configs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
site_id VARCHAR(50) NOT NULL,
|
||||
category_id VARCHAR(50) NOT NULL,
|
||||
vendor_id VARCHAR(100) NOT NULL,
|
||||
|
||||
-- Vendor-Informationen
|
||||
name VARCHAR(100) NOT NULL,
|
||||
privacy_policy_url VARCHAR(255),
|
||||
data_retention VARCHAR(50),
|
||||
data_transfer VARCHAR(100),
|
||||
|
||||
-- TCF
|
||||
tcf_vendor_id INTEGER,
|
||||
tcf_purposes JSONB,
|
||||
tcf_legitimate_interests JSONB,
|
||||
|
||||
-- Cookies
|
||||
cookies JSONB DEFAULT '[]',
|
||||
|
||||
-- Zeitstempel
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT unique_site_vendor UNIQUE (site_id, vendor_id)
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- Helper-Funktionen
|
||||
-- ========================================
|
||||
|
||||
-- Abgelaufene Consents bereinigen
|
||||
CREATE OR REPLACE FUNCTION cleanup_expired_banner_consents()
|
||||
RETURNS INTEGER AS $$
|
||||
DECLARE
|
||||
deleted_count INTEGER;
|
||||
BEGIN
|
||||
-- Soft-Delete nach 30 Tagen nach Ablauf
|
||||
WITH deleted AS (
|
||||
DELETE FROM banner_consents
|
||||
WHERE expires_at < NOW() - INTERVAL '30 days'
|
||||
AND revoked_at IS NULL
|
||||
RETURNING id
|
||||
)
|
||||
SELECT COUNT(*) INTO deleted_count FROM deleted;
|
||||
|
||||
RETURN deleted_count;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Statistik-View
|
||||
CREATE OR REPLACE VIEW banner_consent_stats AS
|
||||
SELECT
|
||||
site_id,
|
||||
DATE(created_at) as consent_date,
|
||||
COUNT(*) as total_consents,
|
||||
COUNT(*) FILTER (WHERE revoked_at IS NOT NULL) as revoked_consents,
|
||||
COUNT(*) FILTER (WHERE (categories->>'analytics')::boolean = true) as analytics_accepted,
|
||||
COUNT(*) FILTER (WHERE (categories->>'marketing')::boolean = true) as marketing_accepted,
|
||||
COUNT(*) FILTER (WHERE (categories->>'functional')::boolean = true) as functional_accepted,
|
||||
COUNT(*) FILTER (WHERE (categories->>'social')::boolean = true) as social_accepted
|
||||
FROM banner_consents
|
||||
GROUP BY site_id, DATE(created_at);
|
||||
|
||||
-- ========================================
|
||||
-- Standard-Kategorien einfügen
|
||||
-- ========================================
|
||||
|
||||
INSERT INTO banner_category_configs (site_id, category_id, name_de, name_en, description_de, description_en, is_required, sort_order)
|
||||
VALUES
|
||||
('default', 'essential', 'Essentiell', 'Essential',
|
||||
'Notwendig für die Grundfunktionen der Website.',
|
||||
'Required for basic website functionality.',
|
||||
TRUE, 1),
|
||||
('default', 'functional', 'Funktional', 'Functional',
|
||||
'Ermöglicht Personalisierung und Komfortfunktionen.',
|
||||
'Enables personalization and comfort features.',
|
||||
FALSE, 2),
|
||||
('default', 'analytics', 'Statistik', 'Analytics',
|
||||
'Hilft uns, die Website zu verbessern.',
|
||||
'Helps us improve the website.',
|
||||
FALSE, 3),
|
||||
('default', 'marketing', 'Marketing', 'Marketing',
|
||||
'Ermöglicht personalisierte Werbung.',
|
||||
'Enables personalized advertising.',
|
||||
FALSE, 4),
|
||||
('default', 'social', 'Soziale Medien', 'Social Media',
|
||||
'Ermöglicht Inhalte von sozialen Netzwerken.',
|
||||
'Enables content from social networks.',
|
||||
FALSE, 5)
|
||||
ON CONFLICT (site_id, category_id) DO NOTHING;
|
||||
|
||||
-- Fertig
|
||||
SELECT 'Banner Consent Tables created successfully' as status;
|
||||
Reference in New Issue
Block a user