feat(cmp): Phase 3 — backend consent withdrawal + consent_id tracking

- ConsentBanner: save consent_id to localStorage after successful POST
- Footer: DELETE /api/consent/{id} on consent re-open (Art. 17 DSGVO)
- New proxy route: DELETE /api/consent/[id] → backend withdrawal endpoint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-12 17:55:29 +02:00
parent f5d4e3bd95
commit ae937a35d7
3 changed files with 36 additions and 4 deletions
@@ -127,12 +127,12 @@ function detectCookies(): CookieEntry[] {
return cookies
}
async function sendConsent(consent: ConsentState, method: ConsentMethod, vendorConsents?: Record<string, boolean>) {
async function sendConsent(consent: ConsentState, method: ConsentMethod, vendorConsents?: Record<string, boolean>): Promise<string | null> {
try {
const { device_type, browser, os } = detectDevice()
const { blocked, released } = detectScripts()
const cookies_set = detectCookies()
await fetch('/api/consent', {
const res = await fetch('/api/consent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -161,8 +161,14 @@ async function sendConsent(consent: ConsentState, method: ConsentMethod, vendorC
cookies_set,
}),
})
const result = await res.json().catch(() => null)
if (result?.id) {
localStorage.setItem('bp_consent_id', result.id)
return result.id
}
return null
} catch {
// Consent API not reachable — store locally anyway
return null
}
}
@@ -4,7 +4,12 @@ import { Cookie } from 'lucide-react'
import { t } from '@/lib/content'
import { useApp } from '@/lib/context'
function reopenConsentBanner() {
async function reopenConsentBanner() {
const consentId = localStorage.getItem('bp_consent_id')
if (consentId) {
fetch(`/api/consent/${consentId}`, { method: 'DELETE' }).catch(() => {})
localStorage.removeItem('bp_consent_id')
}
localStorage.removeItem('bp_consent')
window.location.reload()
}