feat: Package 4 Nachbesserungen — History-Tracking, Pagination, Frontend-Fixes
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
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
Backend:
- Migration 009: compliance_einwilligungen_consent_history Tabelle
- EinwilligungenConsentHistoryDB Modell (consent_id, action, version, ip, ua, source)
- _record_history() Helper: automatisch bei POST /consents (granted) + PUT /revoke (revoked)
- GET /consents/{id}/history Endpoint (vor revoke platziert für korrektes Routing)
- GET /consents: history-Array pro Eintrag (inline Sub-Query)
- 5 neue Tests (TestConsentHistoryTracking) — 32/32 bestanden
Frontend:
- consent/route.ts: limit+offset aus Frontend-Request weitergeleitet, total-Feld ergänzt
- Neuer Proxy consent/[id]/history/route.ts für GET /consents/{id}/history
- page.tsx: globalStats state + loadStats() (Backend /consents/stats für globale Zahlen)
- page.tsx: Stats-Kacheln auf globalStats umgestellt (nicht mehr page-relativ)
- page.tsx: history-Mapper: created_at→timestamp, consent_version→version
- page.tsx: loadStats() bei Mount + nach Revoke
Dokumentation:
- Developer Portal: neue API-Docs-Seite /api/einwilligungen (Consent + Legal Docs + Cookie Banner)
- developer-portal/app/api/page.tsx: Consent Management Abschnitt
- MkDocs: History-Endpoint, Pagination-Abschnitt, History-Tracking Abschnitt
- Deploy-Skript: scripts/apply_consent_history_migration.sh
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
371
developer-portal/app/api/einwilligungen/page.tsx
Normal file
371
developer-portal/app/api/einwilligungen/page.tsx
Normal file
@@ -0,0 +1,371 @@
|
||||
import { DevPortalLayout, ApiEndpoint, CodeBlock, ParameterTable, InfoBox } from '@/components/DevPortalLayout'
|
||||
|
||||
export default function EinwilligungenApiPage() {
|
||||
return (
|
||||
<DevPortalLayout
|
||||
title="Consent Management API"
|
||||
description="Einwilligungen, rechtliche Dokumente und Cookie-Banner verwalten"
|
||||
>
|
||||
<h2>Übersicht</h2>
|
||||
<p>
|
||||
Die Consent Management API ermöglicht die vollständige Verwaltung von Nutzereinwilligungen
|
||||
(Art. 6 Abs. 1a, Art. 7 DSGVO), rechtlichen Dokumenten (Art. 13/14 DSGVO) und
|
||||
Cookie-Banner-Konfigurationen (TTDSG § 25).
|
||||
</p>
|
||||
|
||||
<InfoBox type="info" title="Tenant-ID erforderlich">
|
||||
Alle Endpoints erfordern den Header <code>X-Tenant-ID</code> mit Ihrer Tenant-ID.
|
||||
Ohne diesen Header erhalten Sie einen 400-Fehler.
|
||||
</InfoBox>
|
||||
|
||||
{/* ===================================================================== */}
|
||||
{/* Consent Management */}
|
||||
{/* ===================================================================== */}
|
||||
|
||||
<h2>Consent Management</h2>
|
||||
<p>
|
||||
Verwalten Sie Einwilligungsnachweise granular nach Nutzer und Datenpunkt.
|
||||
Jede Einwilligung wird mit vollständiger Änderungshistorie protokolliert.
|
||||
</p>
|
||||
|
||||
<h3>GET /einwilligungen/consents</h3>
|
||||
<p>Gibt eine paginierte Liste aller Einwilligungen zurück.</p>
|
||||
|
||||
<h4>Query-Parameter</h4>
|
||||
<ParameterTable
|
||||
parameters={[
|
||||
{ name: 'limit', type: 'integer', required: false, description: 'Einträge pro Seite (Default: 50, Max: 500)' },
|
||||
{ name: 'offset', type: 'integer', required: false, description: 'Startposition für Pagination (Default: 0)' },
|
||||
{ name: 'user_id', type: 'string', required: false, description: 'Filtert nach Nutzer-ID' },
|
||||
{ name: 'data_point_id', type: 'string', required: false, description: 'Filtert nach Datenpunkt-ID' },
|
||||
{ name: 'granted', type: 'boolean', required: false, description: 'Filtert nach Einwilligungsstatus (true/false)' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<CodeBlock language="bash" filename="cURL">
|
||||
{`curl -X GET "https://api.breakpilot.io/sdk/v1/einwilligungen/consents?limit=50&offset=0" \\
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \\
|
||||
-H "X-Tenant-ID: your-tenant-id"`}
|
||||
</CodeBlock>
|
||||
|
||||
<CodeBlock language="json" filename="Response (200)">
|
||||
{`{
|
||||
"total": 1234,
|
||||
"offset": 0,
|
||||
"limit": 50,
|
||||
"consents": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"tenant_id": "your-tenant-id",
|
||||
"user_id": "nutzer@beispiel.de",
|
||||
"data_point_id": "dp_analytics",
|
||||
"granted": true,
|
||||
"granted_at": "2024-01-15T10:30:00Z",
|
||||
"revoked_at": null,
|
||||
"consent_version": "v1.2",
|
||||
"source": "web_banner",
|
||||
"ip_address": "192.168.1.1",
|
||||
"user_agent": "Mozilla/5.0...",
|
||||
"created_at": "2024-01-15T10:30:00Z",
|
||||
"history": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"action": "granted",
|
||||
"consent_version": "v1.2",
|
||||
"ip_address": "192.168.1.1",
|
||||
"user_agent": "Mozilla/5.0...",
|
||||
"source": "web_banner",
|
||||
"created_at": "2024-01-15T10:30:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<ApiEndpoint method="GET" path="/einwilligungen/consents" description="Consent-Liste mit Pagination und Filtern" />
|
||||
|
||||
<h3>POST /einwilligungen/consents</h3>
|
||||
<p>Erfasst eine neue Einwilligung. Erstellt automatisch einen History-Eintrag mit action="granted".</p>
|
||||
|
||||
<ParameterTable
|
||||
parameters={[
|
||||
{ name: 'user_id', type: 'string', required: true, description: 'Nutzer-ID oder E-Mail' },
|
||||
{ name: 'data_point_id', type: 'string', required: true, description: 'ID des Datenpunkts (z.B. dp_analytics)' },
|
||||
{ name: 'granted', type: 'boolean', required: true, description: 'true = Einwilligung erteilt' },
|
||||
{ name: 'consent_version', type: 'string', required: false, description: 'Version der Datenschutzerklärung (Default: 1.0)' },
|
||||
{ name: 'source', type: 'string', required: false, description: 'Quelle der Einwilligung (z.B. web_banner, api)' },
|
||||
{ name: 'ip_address', type: 'string', required: false, description: 'IP-Adresse des Nutzers (IPv4/IPv6)' },
|
||||
{ name: 'user_agent', type: 'string', required: false, description: 'Browser/Client User-Agent' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<CodeBlock language="bash" filename="cURL">
|
||||
{`curl -X POST "https://api.breakpilot.io/sdk/v1/einwilligungen/consents" \\
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \\
|
||||
-H "X-Tenant-ID: your-tenant-id" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"user_id": "nutzer@beispiel.de",
|
||||
"data_point_id": "dp_analytics",
|
||||
"granted": true,
|
||||
"consent_version": "v1.2",
|
||||
"source": "web_banner",
|
||||
"ip_address": "192.168.1.1"
|
||||
}'`}
|
||||
</CodeBlock>
|
||||
|
||||
<CodeBlock language="json" filename="Response (201)">
|
||||
{`{
|
||||
"success": true,
|
||||
"id": "uuid",
|
||||
"user_id": "nutzer@beispiel.de",
|
||||
"data_point_id": "dp_analytics",
|
||||
"granted": true,
|
||||
"granted_at": "2024-01-15T10:30:00Z"
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<ApiEndpoint method="POST" path="/einwilligungen/consents" description="Neue Einwilligung erfassen (erstellt automatisch History-Eintrag)" />
|
||||
|
||||
<h3>PUT /einwilligungen/consents/{'{id}'}/revoke</h3>
|
||||
<p>
|
||||
Widerruft eine aktive Einwilligung. Setzt <code>revoked_at</code> auf den aktuellen Zeitstempel
|
||||
und erstellt einen History-Eintrag mit action="revoked".
|
||||
</p>
|
||||
|
||||
<InfoBox type="warning" title="Nicht rückgängig machbar">
|
||||
Ein Widerruf kann nicht rückgängig gemacht werden. Für eine neue Einwilligung muss
|
||||
ein neuer POST-Request gesendet werden.
|
||||
</InfoBox>
|
||||
|
||||
<CodeBlock language="bash" filename="cURL">
|
||||
{`curl -X PUT "https://api.breakpilot.io/sdk/v1/einwilligungen/consents/CONSENT_ID/revoke" \\
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \\
|
||||
-H "X-Tenant-ID: your-tenant-id"`}
|
||||
</CodeBlock>
|
||||
|
||||
<CodeBlock language="json" filename="Response (200)">
|
||||
{`{
|
||||
"success": true,
|
||||
"id": "uuid",
|
||||
"revoked_at": "2024-02-01T14:00:00Z"
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<ApiEndpoint method="PUT" path="/einwilligungen/consents/{id}/revoke" description="Einwilligung widerrufen (setzt revoked_at, erstellt History-Eintrag)" />
|
||||
|
||||
<h3>GET /einwilligungen/consents/{'{id}'}/history</h3>
|
||||
<p>
|
||||
Gibt die vollständige Änderungshistorie einer Einwilligung zurück.
|
||||
Alle Aktionen (granted, revoked, version_update, renewed) werden chronologisch aufgelistet.
|
||||
</p>
|
||||
|
||||
<CodeBlock language="bash" filename="cURL">
|
||||
{`curl -X GET "https://api.breakpilot.io/sdk/v1/einwilligungen/consents/CONSENT_ID/history" \\
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \\
|
||||
-H "X-Tenant-ID: your-tenant-id"`}
|
||||
</CodeBlock>
|
||||
|
||||
<CodeBlock language="json" filename="Response (200)">
|
||||
{`[
|
||||
{
|
||||
"id": "uuid",
|
||||
"consent_id": "uuid",
|
||||
"action": "granted",
|
||||
"consent_version": "v1.0",
|
||||
"ip_address": "192.168.1.1",
|
||||
"user_agent": "Mozilla/5.0...",
|
||||
"source": "web_banner",
|
||||
"created_at": "2024-01-15T10:30:00Z"
|
||||
},
|
||||
{
|
||||
"id": "uuid",
|
||||
"consent_id": "uuid",
|
||||
"action": "revoked",
|
||||
"consent_version": "v1.0",
|
||||
"ip_address": null,
|
||||
"user_agent": null,
|
||||
"source": null,
|
||||
"created_at": "2024-02-01T14:00:00Z"
|
||||
}
|
||||
]`}
|
||||
</CodeBlock>
|
||||
|
||||
<ApiEndpoint method="GET" path="/einwilligungen/consents/{id}/history" description="Änderungshistorie einer Einwilligung (chronologisch, älteste zuerst)" />
|
||||
|
||||
<h3>GET /einwilligungen/consents/stats</h3>
|
||||
<p>Gibt Statistiken über alle Einwilligungen des Tenants zurück.</p>
|
||||
|
||||
<CodeBlock language="json" filename="Response (200)">
|
||||
{`{
|
||||
"total_consents": 1234,
|
||||
"active_consents": 1100,
|
||||
"revoked_consents": 134,
|
||||
"unique_users": 800,
|
||||
"conversion_rate": 89.2,
|
||||
"by_data_point": {
|
||||
"dp_analytics": { "total": 600, "active": 550, "revoked": 50 },
|
||||
"dp_marketing": { "total": 634, "active": 550, "revoked": 84 }
|
||||
}
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<ApiEndpoint method="GET" path="/einwilligungen/consents/stats" description="Einwilligungs-Statistiken nach Datenpunkt und Nutzer" />
|
||||
|
||||
{/* ===================================================================== */}
|
||||
{/* Legal Documents */}
|
||||
{/* ===================================================================== */}
|
||||
|
||||
<h2>Legal Documents API</h2>
|
||||
<p>
|
||||
Verwalten Sie rechtliche Dokumente (Datenschutzerklärung, AGB, Cookie-Richtlinie,
|
||||
Impressum, AVV) mit vollständigem Versionierungs- und Freigabe-Workflow.
|
||||
</p>
|
||||
|
||||
<InfoBox type="info" title="Proxy-Route">
|
||||
Frontend-Proxy: <code>/api/admin/consent/*</code> → <code>backend:8002/api/compliance/legal-documents/*</code>
|
||||
</InfoBox>
|
||||
|
||||
<h3>GET /legal-documents/documents</h3>
|
||||
<p>Gibt alle rechtlichen Dokumente des Tenants zurück.</p>
|
||||
|
||||
<CodeBlock language="bash" filename="cURL">
|
||||
{`curl -X GET "https://api.breakpilot.io/sdk/v1/legal-documents/documents" \\
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \\
|
||||
-H "X-Tenant-ID: your-tenant-id"`}
|
||||
</CodeBlock>
|
||||
|
||||
<ApiEndpoint method="GET" path="/legal-documents/documents" description="Alle rechtlichen Dokumente (Filter: tenant_id, document_type, status)" />
|
||||
|
||||
<h3>POST /legal-documents/documents</h3>
|
||||
<p>Legt ein neues rechtliches Dokument an (initial als Entwurf).</p>
|
||||
|
||||
<ParameterTable
|
||||
parameters={[
|
||||
{ name: 'title', type: 'string', required: true, description: 'Titel des Dokuments' },
|
||||
{ name: 'document_type', type: 'string', required: true, description: 'privacy_policy | terms | cookie_policy | imprint | dpa' },
|
||||
{ name: 'language', type: 'string', required: false, description: 'Sprache (Default: de)' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<ApiEndpoint method="POST" path="/legal-documents/documents" description="Neues rechtliches Dokument anlegen (Status: draft)" />
|
||||
|
||||
<h3>GET /legal-documents/documents/{'{id}'}/versions</h3>
|
||||
<p>
|
||||
Gibt alle Versionen eines Dokuments zurück.
|
||||
</p>
|
||||
|
||||
<InfoBox type="warning" title="Array-Response">
|
||||
Dieser Endpoint gibt ein <strong>direktes JSON-Array</strong> zurück, nicht
|
||||
ein Objekt mit <code>versions</code>-Key. Frontend-Code muss
|
||||
<code>Array.isArray(data) ? data : (data.versions || [])</code> prüfen.
|
||||
</InfoBox>
|
||||
|
||||
<CodeBlock language="json" filename="Response (200) — direktes Array">
|
||||
{`[
|
||||
{
|
||||
"id": "uuid",
|
||||
"document_id": "uuid",
|
||||
"version_number": 3,
|
||||
"title": "Datenschutzerklärung v3",
|
||||
"status": "published",
|
||||
"created_at": "2024-01-20T14:00:00Z",
|
||||
"published_at": "2024-01-21T09:00:00Z"
|
||||
}
|
||||
]`}
|
||||
</CodeBlock>
|
||||
|
||||
<ApiEndpoint method="GET" path="/legal-documents/documents/{id}/versions" description="Alle Versionen eines Dokuments (gibt direktes Array zurück)" />
|
||||
|
||||
<h3>POST /legal-documents/versions/{'{id}'}/publish</h3>
|
||||
<p>Veröffentlicht eine freigegebene Version. Status muss "approved" sein.</p>
|
||||
|
||||
<ApiEndpoint method="POST" path="/legal-documents/versions/{id}/publish" description="Freigegebene Version veröffentlichen (approved → published)" />
|
||||
|
||||
{/* ===================================================================== */}
|
||||
{/* Cookie Banner */}
|
||||
{/* ===================================================================== */}
|
||||
|
||||
<h2>Cookie Banner API</h2>
|
||||
<p>
|
||||
Konfigurieren Sie den Cookie-Banner für Ihre Anwendung. Die Konfiguration wird
|
||||
in der Datenbank persistiert und überlebt Container-Neustarts.
|
||||
</p>
|
||||
|
||||
<h3>GET /einwilligungen/cookies</h3>
|
||||
<p>Lädt die Cookie-Banner-Konfiguration des Tenants.</p>
|
||||
|
||||
<CodeBlock language="bash" filename="cURL">
|
||||
{`curl -X GET "https://api.breakpilot.io/sdk/v1/einwilligungen/cookies" \\
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \\
|
||||
-H "X-Tenant-ID: your-tenant-id"`}
|
||||
</CodeBlock>
|
||||
|
||||
<CodeBlock language="json" filename="Response (200)">
|
||||
{`{
|
||||
"tenant_id": "your-tenant-id",
|
||||
"categories": [
|
||||
{
|
||||
"id": "necessary",
|
||||
"name": "Notwendig",
|
||||
"isRequired": true,
|
||||
"defaultEnabled": true
|
||||
},
|
||||
{
|
||||
"id": "analytics",
|
||||
"name": "Analyse",
|
||||
"isRequired": false,
|
||||
"defaultEnabled": false
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"position": "bottom",
|
||||
"style": "bar",
|
||||
"primaryColor": "#6366f1",
|
||||
"showDeclineAll": true,
|
||||
"showSettings": true,
|
||||
"banner_texts": {
|
||||
"title": "Wir verwenden Cookies",
|
||||
"description": "Wir nutzen Cookies, um unsere Website zu verbessern.",
|
||||
"privacyLink": "/datenschutz"
|
||||
}
|
||||
},
|
||||
"updated_at": "2024-01-15T10:30:00Z"
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<ApiEndpoint method="GET" path="/einwilligungen/cookies" description="Cookie-Banner-Konfiguration laden (Kategorien + Config + Banner-Texte)" />
|
||||
|
||||
<h3>PUT /einwilligungen/cookies</h3>
|
||||
<p>
|
||||
Speichert die Cookie-Banner-Konfiguration (Upsert). Alle Felder werden vollständig
|
||||
überschrieben.
|
||||
</p>
|
||||
|
||||
<CodeBlock language="bash" filename="cURL">
|
||||
{`curl -X PUT "https://api.breakpilot.io/sdk/v1/einwilligungen/cookies" \\
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \\
|
||||
-H "X-Tenant-ID: your-tenant-id" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"categories": [
|
||||
{ "id": "necessary", "name": "Notwendig", "isRequired": true, "defaultEnabled": true }
|
||||
],
|
||||
"config": {
|
||||
"position": "bottom",
|
||||
"style": "bar",
|
||||
"primaryColor": "#6366f1",
|
||||
"banner_texts": {
|
||||
"title": "Wir verwenden Cookies",
|
||||
"description": "...",
|
||||
"privacyLink": "/datenschutz"
|
||||
}
|
||||
}
|
||||
}'`}
|
||||
</CodeBlock>
|
||||
|
||||
<ApiEndpoint method="PUT" path="/einwilligungen/cookies" description="Cookie-Banner-Konfiguration speichern (Upsert, inkl. Banner-Texte)" />
|
||||
</DevPortalLayout>
|
||||
)
|
||||
}
|
||||
@@ -126,6 +126,22 @@ export default function ApiReferencePage() {
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<h3>Consent Management</h3>
|
||||
<p>
|
||||
Verwalten Sie Einwilligungen, rechtliche Dokumente und Cookie-Banner-Konfigurationen.
|
||||
</p>
|
||||
|
||||
<ApiEndpoint method="GET" path="/einwilligungen/consents" description="Consent-Liste mit Pagination und Filtern" />
|
||||
<ApiEndpoint method="POST" path="/einwilligungen/consents" description="Neue Einwilligung erfassen" />
|
||||
<ApiEndpoint method="PUT" path="/einwilligungen/consents/{id}/revoke" description="Einwilligung widerrufen" />
|
||||
<ApiEndpoint method="GET" path="/einwilligungen/consents/{id}/history" description="Änderungshistorie einer Einwilligung" />
|
||||
|
||||
<p>
|
||||
<Link href="/api/einwilligungen" className="text-blue-600 hover:underline">
|
||||
→ Vollständige Consent Management API Dokumentation
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<h2>Response Format</h2>
|
||||
<p>
|
||||
Alle Responses folgen einem einheitlichen Format:
|
||||
|
||||
Reference in New Issue
Block a user