feat: Package 4 Phase 3 — Finale Fixes + Dokumentation (MkDocs, SDK Flow, StepHeader)
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 48s
CI / test-python-backend-compliance (push) Successful in 40s
CI / test-python-document-crawler (push) Successful in 23s
CI / test-python-dsms-gateway (push) Successful in 18s

Finale Fixes (5 Bugs):
- workflow/page.tsx: Array-Format-Fix fuer loadVersions — Array.isArray statt data.versions
- einwilligungen_routes.py: ip_address + user_agent in GET /consents Response ergaenzt
- consent/page.tsx: Bearbeiten/Vorschau/Veroeffentlichen + Quick Actions verdrahtet (useRouter + Preview Modal)
- cookie-banner/page.tsx: BannerTexts State + Controlled Inputs + DB-Persistenz (banner_texts)
- embed-code/route.ts: In-Memory configStorage → DB-fetch aus Backend, embed_code Key korrigiert

Dokumentation:
- docs-src/services/sdk-modules/rechtliche-texte.md: Neue MkDocs-Seite fuer Paket 4
  (Einwilligungen, Rechtliche Vorlagen, Cookie Banner, Document Workflow)
- mkdocs.yml: Nav-Eintrag 'Rechtliche Texte (Paket 4)' ergaenzt
- dokumentations-module.md: Datenfluss-Diagramm um Paket-4-Module erweitert
- flow-data.ts: Paket-4-Steps mit korrekten dbTables/dbMode und aktualisierten Beschreibungen
- StepHeader.tsx: cookie-banner + workflow STEP_EXPLANATIONS auf Persistenz und Funktionsumfang aktualisiert

Tests: 24/24 bestanden (test_einwilligungen_routes.py)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-03 10:37:41 +01:00
parent 3570dd10ea
commit c0b179510d
10 changed files with 491 additions and 88 deletions

View File

@@ -5,20 +5,20 @@
*/
import { NextRequest, NextResponse } from 'next/server'
import { CookieBannerConfig, CookieBannerEmbedCode } from '@/lib/sdk/einwilligungen/types'
import { CookieBannerConfig } from '@/lib/sdk/einwilligungen/types'
import {
generateCookieBannerConfig,
generateEmbedCode,
} from '@/lib/sdk/einwilligungen/generator/cookie-banner'
import { PREDEFINED_DATA_POINTS } from '@/lib/sdk/einwilligungen/catalog/loader'
// In-Memory Storage (in Produktion mit configStorage aus config/route.ts teilen)
const configStorage = new Map<string, CookieBannerConfig>()
const BACKEND_URL = process.env.BACKEND_URL || 'http://backend-compliance:8002'
const DEFAULT_TENANT_ID = process.env.DEFAULT_TENANT_ID || '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e'
/**
* GET /api/sdk/v1/einwilligungen/cookie-banner/embed-code
*
* Generiert den Embed-Code fuer den Cookie Banner
* Generiert den Embed-Code fuer den Cookie Banner (DB-backed)
*
* Query Parameters:
* - privacyPolicyUrl: string - URL zur Datenschutzerklaerung (default: /datenschutz)
@@ -26,25 +26,37 @@ const configStorage = new Map<string, CookieBannerConfig>()
*/
export async function GET(request: NextRequest) {
try {
const tenantId = request.headers.get('X-Tenant-ID')
if (!tenantId) {
return NextResponse.json(
{ error: 'Tenant ID required' },
{ status: 400 }
)
}
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
const clientTenantId = request.headers.get('X-Tenant-ID') || request.headers.get('x-tenant-id')
const tenantId = (clientTenantId && uuidRegex.test(clientTenantId)) ? clientTenantId : DEFAULT_TENANT_ID
const { searchParams } = new URL(request.url)
const privacyPolicyUrl = searchParams.get('privacyPolicyUrl') || '/datenschutz'
const format = searchParams.get('format') || 'combined'
// Hole oder erstelle Konfiguration
let config = configStorage.get(tenantId)
if (!config) {
// Lade Konfiguration aus DB (Backend), fallback auf generierte Standardkonfiguration
let config: CookieBannerConfig
try {
const configRes = await fetch(
`${BACKEND_URL}/api/compliance/einwilligungen/cookies?tenant_id=${tenantId}`,
{ signal: AbortSignal.timeout(5000) }
)
if (configRes.ok) {
const configData = await configRes.json()
config = generateCookieBannerConfig(tenantId, PREDEFINED_DATA_POINTS)
if (configData.config && Object.keys(configData.config).length > 0) {
const saved = configData.config
config = {
...config,
styling: { ...config.styling, ...(saved.styling || {}) },
texts: { ...config.texts, ...(saved.banner_texts || saved.texts || {}) },
}
}
} else {
config = generateCookieBannerConfig(tenantId, PREDEFINED_DATA_POINTS)
}
} catch {
config = generateCookieBannerConfig(tenantId, PREDEFINED_DATA_POINTS)
configStorage.set(tenantId, config)
}
// Generiere Embed-Code
@@ -110,6 +122,7 @@ ${embedCode.js}
`.trim()
return NextResponse.json({
embed_code: combinedCode,
embedCode: combinedCode,
scriptTag: embedCode.scriptTag,
config: {
@@ -143,14 +156,9 @@ ${embedCode.js}
*/
export async function POST(request: NextRequest) {
try {
const tenantId = request.headers.get('X-Tenant-ID')
if (!tenantId) {
return NextResponse.json(
{ error: 'Tenant ID required' },
{ status: 400 }
)
}
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
const clientTenantId = request.headers.get('X-Tenant-ID') || request.headers.get('x-tenant-id')
const tenantId = (clientTenantId && uuidRegex.test(clientTenantId)) ? clientTenantId : DEFAULT_TENANT_ID
const body = await request.json()
const {
@@ -160,25 +168,13 @@ export async function POST(request: NextRequest) {
language = 'de',
} = body
// Hole oder erstelle Konfiguration
let config = configStorage.get(tenantId)
if (!config) {
config = generateCookieBannerConfig(tenantId, PREDEFINED_DATA_POINTS, texts, styling)
} else {
// Wende temporaere Anpassungen an
if (styling) {
config = {
...config,
styling: { ...config.styling, ...styling },
}
}
if (texts) {
config = {
...config,
texts: { ...config.texts, ...texts },
}
}
// Erstelle Konfiguration mit optionalen Overrides
let config: CookieBannerConfig = generateCookieBannerConfig(tenantId, PREDEFINED_DATA_POINTS, texts, styling)
if (styling) {
config = { ...config, styling: { ...config.styling, ...styling } }
}
if (texts) {
config = { ...config, texts: { ...config.texts, ...texts } }
}
const embedCode = generateEmbedCode(config, privacyPolicyUrl)