import { NextResponse } from 'next/server' import { z } from 'zod' import { auditSchreiben } from '@/server/audit' import { verlangeAnmeldungFuerApi } from '@/server/berechtigung' import { geraetAbmelden, geraetAnmelden } from '@/server/benachrichtigung/push' import { umgebung } from '@/server/umgebung' export const dynamic = 'force-dynamic' /** * An- und Abmelden eines Browsers für Push-Nachrichten. * * Der öffentliche VAPID-Schlüssel kommt über GET; er ist nicht geheim, wird * aber nur an Angemeldete herausgegeben — es gibt keinen Grund, die * Einrichtung nach außen zu zeigen. */ const anmeldung = z.object({ endpunkt: z.url(), p256dh: z.string().min(1).max(500), auth: z.string().min(1).max(500), bezeichnung: z.string().trim().max(60).nullable().default(null), }) export async function GET() { const benutzer = await verlangeAnmeldungFuerApi() if (!benutzer) { return NextResponse.json({ fehler: 'Nicht angemeldet.' }, { status: 401 }) } const einst = umgebung() return NextResponse.json( { aktiv: einst.pushAktiv, oeffentlicherSchluessel: einst.pushAktiv ? einst.VAPID_OEFFENTLICH : null, }, { headers: { 'Cache-Control': 'no-store' } }, ) } export async function POST(anfrage: Request) { const benutzer = await verlangeAnmeldungFuerApi() if (!benutzer) { return NextResponse.json({ fehler: 'Nicht angemeldet.' }, { status: 401 }) } if (!umgebung().pushAktiv) { return NextResponse.json( { fehler: 'Push ist auf diesem Server nicht eingerichtet.' }, { status: 400 }, ) } let inhalt: unknown try { inhalt = await anfrage.json() } catch { return NextResponse.json({ fehler: 'Ungültige Anfrage.' }, { status: 400 }) } const gelesen = anmeldung.safeParse(inhalt) if (!gelesen.success) { return NextResponse.json( { fehler: 'Die Anmeldung war unvollständig.' }, { status: 400 }, ) } const geraet = await geraetAnmelden( benutzer.id, { endpunkt: gelesen.data.endpunkt, p256dh: gelesen.data.p256dh, auth: gelesen.data.auth, }, gelesen.data.bezeichnung, ) await auditSchreiben({ akteur: { id: benutzer.id, email: benutzer.email }, aktion: 'push.angemeldet', objektTyp: 'PushGeraet', objektId: geraet.id, // Weder Endpunkt noch Schlüssel – beides taugt zum Zustellen. details: { bezeichnung: gelesen.data.bezeichnung }, }) return NextResponse.json({ ok: true }) } const abmeldung = z.object({ endpunkt: z.url() }) export async function DELETE(anfrage: Request) { const benutzer = await verlangeAnmeldungFuerApi() if (!benutzer) { return NextResponse.json({ fehler: 'Nicht angemeldet.' }, { status: 401 }) } let inhalt: unknown try { inhalt = await anfrage.json() } catch { return NextResponse.json({ fehler: 'Ungültige Anfrage.' }, { status: 400 }) } const gelesen = abmeldung.safeParse(inhalt) if (!gelesen.success) { return NextResponse.json({ fehler: 'Ungültige Anfrage.' }, { status: 400 }) } await geraetAbmelden(gelesen.data.endpunkt) await auditSchreiben({ akteur: { id: benutzer.id, email: benutzer.email }, aktion: 'push.abgemeldet', objektTyp: 'PushGeraet', objektId: null, }) return NextResponse.json({ ok: true }) }