syncova-policies/app/api/privacy-policies/[id]/route.ts
Weapie 328f042307 Syncova Policies: Portal für DSGVO-Datenschutzerklärungen
Next.js 15 App Router mit öffentlicher Übersicht und geschütztem
Admin-Bereich zur Pflege von Datenschutzerklärungen nach Art. 12ff. DSGVO.

- Oberfläche auf Basis von shadcn/ui, Inter lokal eingebunden
- Prisma/SQLite, Auth.js mit Credentials-Provider und Rollen
- Massenimport der Merkblätter des Amtes Leezen aus PDF
- Docker-Image (standalone) und Gitea-Workflow zur Veröffentlichung

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 16:23:51 +02:00

191 lines
5.7 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { PrismaClient } from '@prisma/client'
import { requireAdmin } from '@/lib/require-admin'
// Prisma client instance
const prisma = new PrismaClient()
// Validation function for privacy policy data
function validatePrivacyPolicyData(data: any) {
const errors: string[] = []
// Required fields validation
if (!data.title?.trim()) errors.push('Title ist erforderlich')
if (!data.responsible?.trim()) errors.push('Verantwortlicher ist erforderlich')
if (!data.contactDPO?.trim()) errors.push('Kontaktdaten des Datenschutzbeauftragten sind erforderlich')
if (!data.dataSubjectsRights?.trim()) errors.push('Betroffenen-Rechte sind erforderlich')
if (!data.complaintRight?.trim()) errors.push('Beschwerderecht ist erforderlich')
if (!data.withdrawalRight?.trim()) errors.push('Widerrufsrecht ist erforderlich')
if (!data.processingDescription?.trim()) errors.push('Verarbeitungstätigkeit ist erforderlich')
if (!data.recipients?.trim()) errors.push('Empfänger sind erforderlich')
// Optional boolean validation
if (data.isPublic !== undefined && typeof data.isPublic !== 'boolean') {
errors.push('isPublic muss ein Boolean-Wert sein')
}
return errors
}
// GET /api/privacy-policies/[id] - returns a single privacy policy
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id: rawId } = await params
const id = parseInt(rawId)
// Validate ID is a number
if (isNaN(id)) {
return NextResponse.json(
{ error: 'Ungültige ID' },
{ status: 400 }
)
}
// Find privacy policy by ID
const policy = await prisma.privacyPolicy.findUnique({
where: { id }
})
if (!policy) {
return NextResponse.json(
{ error: 'Datenschutzerklärung nicht gefunden' },
{ status: 404 }
)
}
return NextResponse.json(policy)
} catch (error) {
console.error('Error fetching privacy policy:', error)
return NextResponse.json(
{ error: 'Fehler beim Abrufen der Datenschutzerklärung' },
{ status: 500 }
)
}
}
// PUT /api/privacy-policies/[id] - updates a privacy policy (Admin)
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const denied = await requireAdmin()
if (denied) return denied
const { id: rawId } = await params
const id = parseInt(rawId)
const data = await request.json()
// Validate ID is a number
if (isNaN(id)) {
return NextResponse.json(
{ error: 'Ungültige ID' },
{ status: 400 }
)
}
// Validate required fields
const validationErrors = validatePrivacyPolicyData(data)
if (validationErrors.length > 0) {
return NextResponse.json(
{ error: 'Validierungsfehler', details: validationErrors },
{ status: 400 }
)
}
// Check if policy exists
const existingPolicy = await prisma.privacyPolicy.findUnique({
where: { id }
})
if (!existingPolicy) {
return NextResponse.json(
{ error: 'Datenschutzerklärung nicht gefunden' },
{ status: 404 }
)
}
// Update privacy policy
const updatedPolicy = await prisma.privacyPolicy.update({
where: { id },
data: {
title: data.title.trim(),
responsible: data.responsible.trim(),
contactDPO: data.contactDPO.trim(),
dataSubjectsRights: data.dataSubjectsRights.trim(),
complaintRight: data.complaintRight.trim(),
withdrawalRight: data.withdrawalRight.trim(),
processingDescription: data.processingDescription.trim(),
processingPurposes: data.processingPurposes?.trim() || null,
legalBasis: data.legalBasis?.trim() || null,
recipients: data.recipients.trim(),
storageDuration: data.storageDuration?.trim() || null,
dataSource: data.dataSource?.trim() || null,
provisionObligation: data.provisionObligation?.trim() || null,
provisionConsequences: data.provisionConsequences?.trim() || null,
isPublic: data.isPublic !== undefined ? data.isPublic : existingPolicy.isPublic
}
})
return NextResponse.json(updatedPolicy)
} catch (error) {
console.error('Error updating privacy policy:', error)
return NextResponse.json(
{ error: 'Fehler beim Aktualisieren der Datenschutzerklärung' },
{ status: 500 }
)
}
}
// DELETE /api/privacy-policies/[id] - deletes a privacy policy (Admin)
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const denied = await requireAdmin()
if (denied) return denied
const { id: rawId } = await params
const id = parseInt(rawId)
// Validate ID is a number
if (isNaN(id)) {
return NextResponse.json(
{ error: 'Ungültige ID' },
{ status: 400 }
)
}
// Check if policy exists
const existingPolicy = await prisma.privacyPolicy.findUnique({
where: { id }
})
if (!existingPolicy) {
return NextResponse.json(
{ error: 'Datenschutzerklärung nicht gefunden' },
{ status: 404 }
)
}
// Delete privacy policy
await prisma.privacyPolicy.delete({
where: { id }
})
return NextResponse.json(
{ message: 'Datenschutzerklärung erfolgreich gelöscht' },
{ status: 200 }
)
} catch (error) {
console.error('Error deleting privacy policy:', error)
return NextResponse.json(
{ error: 'Fehler beim Löschen der Datenschutzerklärung' },
{ status: 500 }
)
}
}