/** * API Route: Consent Management * * Proxies to backend-compliance for DB persistence. * POST - Consent erfassen * GET - Consent-Status und Statistiken abrufen * PUT - Batch-Update von Consents */ import { NextRequest, NextResponse } from 'next/server' const BACKEND_URL = process.env.BACKEND_URL || 'http://backend-compliance:8002' function getHeaders(request: NextRequest): HeadersInit { 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 : (process.env.DEFAULT_TENANT_ID || '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e') return { 'Content-Type': 'application/json', 'X-Tenant-ID': tenantId, } } /** * POST /api/sdk/v1/einwilligungen/consent * Erfasst eine neue Einwilligung */ export async function POST(request: NextRequest) { try { const headers = getHeaders(request) const body = await request.json() const { userId, dataPointId, granted, consentVersion = '1.0.0', source } = body if (!userId || !dataPointId || typeof granted !== 'boolean') { return NextResponse.json( { error: 'userId, dataPointId, and granted required' }, { status: 400 } ) } const ipAddress = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || null const userAgent = request.headers.get('user-agent') || null const response = await fetch( `${BACKEND_URL}/api/compliance/einwilligungen/consents`, { method: 'POST', headers, body: JSON.stringify({ user_id: userId, data_point_id: dataPointId, granted, consent_version: consentVersion, source: source || null, ip_address: ipAddress, user_agent: userAgent, }), signal: AbortSignal.timeout(30000), } ) if (!response.ok) { const errorText = await response.text() return NextResponse.json({ error: errorText }, { status: response.status }) } const data = await response.json() return NextResponse.json({ success: true, consent: { id: data.id, dataPointId: data.data_point_id, granted: data.granted, grantedAt: data.granted_at, }, }) } catch (error) { console.error('Error recording consent:', error) return NextResponse.json({ error: 'Failed to record consent' }, { status: 500 }) } } /** * GET /api/sdk/v1/einwilligungen/consent * Ruft Consent-Status und Statistiken ab */ export async function GET(request: NextRequest) { try { const headers = getHeaders(request) const { searchParams } = new URL(request.url) const userId = searchParams.get('userId') const includeStats = searchParams.get('stats') === 'true' if (includeStats) { const statsResponse = await fetch( `${BACKEND_URL}/api/compliance/einwilligungen/consents/stats`, { method: 'GET', headers, signal: AbortSignal.timeout(30000) } ) if (!statsResponse.ok) { return NextResponse.json({ error: 'Failed to fetch stats' }, { status: statsResponse.status }) } const stats = await statsResponse.json() return NextResponse.json({ statistics: stats }) } // Fetch consents — forward pagination params from frontend const limit = searchParams.get('limit') || '50' const offset = searchParams.get('offset') || '0' const queryParams = new URLSearchParams() if (userId) queryParams.set('user_id', userId) queryParams.set('limit', limit) queryParams.set('offset', offset) const response = await fetch( `${BACKEND_URL}/api/compliance/einwilligungen/consents?${queryParams.toString()}`, { method: 'GET', headers, signal: AbortSignal.timeout(30000) } ) if (!response.ok) { const errorText = await response.text() return NextResponse.json({ error: errorText }, { status: response.status }) } const data = await response.json() return NextResponse.json({ total: data.total || 0, totalConsents: data.total || 0, offset: data.offset || 0, limit: data.limit || parseInt(limit), consents: data.consents || [], }) } catch (error) { console.error('Error fetching consents:', error) return NextResponse.json({ error: 'Failed to fetch consents' }, { status: 500 }) } } /** * PUT /api/sdk/v1/einwilligungen/consent * Batch-Update oder Revoke einzelner Consents */ export async function PUT(request: NextRequest) { try { const headers = getHeaders(request) const body = await request.json() const { consentId, action } = body // Single consent revoke if (consentId && action === 'revoke') { const response = await fetch( `${BACKEND_URL}/api/compliance/einwilligungen/consents/${consentId}/revoke`, { method: 'PUT', headers, body: '{}', signal: AbortSignal.timeout(30000) } ) if (!response.ok) { const errorText = await response.text() return NextResponse.json({ error: errorText }, { status: response.status }) } const data = await response.json() return NextResponse.json({ success: true, ...data }) } // Batch update: { userId, consents: { dataPointId: boolean } } const { userId, consents, consentVersion = '1.0.0' } = body if (!userId || !consents) { return NextResponse.json({ error: 'userId and consents required' }, { status: 400 }) } const ipAddress = request.headers.get('x-forwarded-for') || null const userAgent = request.headers.get('user-agent') || null const results = [] for (const [dataPointId, granted] of Object.entries(consents)) { if (typeof granted !== 'boolean') continue const resp = await fetch( `${BACKEND_URL}/api/compliance/einwilligungen/consents`, { method: 'POST', headers, body: JSON.stringify({ user_id: userId, data_point_id: dataPointId, granted, consent_version: consentVersion, ip_address: ipAddress, user_agent: userAgent, }), signal: AbortSignal.timeout(30000), } ) if (resp.ok) { results.push(await resp.json()) } } return NextResponse.json({ success: true, userId, updated: results.length }) } catch (error) { console.error('Error updating consents:', error) return NextResponse.json({ error: 'Failed to update consents' }, { status: 500 }) } }