This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/consent-service/migrations/005_banner_consent_tables.sql
BreakPilot Dev 19855efacc
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
2026-02-11 13:25:58 +01:00

224 lines
7.3 KiB
PL/PgSQL

-- 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;