Ein DGS-Video liess sich bisher gar nicht einbinden: Der Markdown- Prozessor laeuft mit Sanitizing, und remark reicht rohes HTML dann von vornherein nicht durch - <video> kam als leeres <p> heraus. Eine erweiterte Freigabeliste allein half deshalb nicht. Statt rohes HTML fuer alle Textfelder zu oeffnen - auch fuer die 121 Erklaerungen - bekommt die Gebaerdensprach-Seite zwei eigene Felder fuer Video- und Untertiteladresse. Ausgegeben wird ein natives <video controls> mit <track kind="captions"> und Bildunterschrift. Adressen werden zweimal geprueft, beim Speichern und vor der Ausgabe: zugelassen sind nur der eigene Server und https. javascript:, data:, protokollrelative und einfache http-Adressen werden abgewiesen. iframe bleibt bewusst gesperrt - eine YouTube-Einbindung wuerde Besucherdaten an Dritte uebertragen. Erklaerung zur Barrierefreiheit entsprechend gekuerzt: Die beiden Luecken nach § 4 BITV 2.0 entfallen, es bleibt die ungepruefte Druckansicht. Geprueft: Video wird mit Untertitelspur ausgeliefert und ist per Tastatur erreichbar, axe-core meldet auf beiden neuen Seiten in hell und dunkel keine Verstoesse, Adresspruefung weist alle acht Testfaelle korrekt zu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
||
import { PrismaClient } from '@prisma/client'
|
||
import { requireAdmin } from '@/lib/require-admin'
|
||
import { SITE_MEDIA, SITE_PAGES, isSafeMediaUrl } from '@/lib/site-pages'
|
||
import { readSitePages } from '@/lib/site-pages.server'
|
||
|
||
// Prisma client instance
|
||
const prisma = new PrismaClient()
|
||
|
||
// GET /api/site-pages - liefert die Inhalte der rechtlichen Seiten
|
||
export async function GET() {
|
||
try {
|
||
return NextResponse.json(await readSitePages())
|
||
} catch (error) {
|
||
console.error('Error fetching site pages:', error)
|
||
return NextResponse.json(
|
||
{ error: 'Fehler beim Abrufen der Seiteninhalte' },
|
||
{ status: 500 }
|
||
)
|
||
}
|
||
}
|
||
|
||
// PUT /api/site-pages - speichert die Inhalte der rechtlichen Seiten (Admin)
|
||
export async function PUT(request: NextRequest) {
|
||
try {
|
||
const denied = await requireAdmin()
|
||
if (denied) return denied
|
||
|
||
const data = await request.json()
|
||
|
||
// Nur die bekannten Seiten übernehmen, jeweils getrimmt. Ein leerer Text
|
||
// ist zulässig und bedeutet: Seite nicht veröffentlichen.
|
||
const values = Object.fromEntries(
|
||
[...SITE_PAGES, ...SITE_MEDIA].map((key) => [
|
||
key,
|
||
String(data[key] ?? '').trim(),
|
||
])
|
||
)
|
||
|
||
// Medienadressen landen in einem src-Attribut. Nur eigener Server oder
|
||
// https – alles andere wird abgewiesen, statt still gespeichert zu werden.
|
||
const ungueltig = SITE_MEDIA.filter((key) => !isSafeMediaUrl(values[key]))
|
||
if (ungueltig.length > 0) {
|
||
return NextResponse.json(
|
||
{
|
||
error: 'Ungültige Adresse',
|
||
details: ungueltig.map(
|
||
(key) =>
|
||
`${key}: nur eine Adresse auf diesem Server (beginnt mit /) oder https ist zulässig`
|
||
),
|
||
},
|
||
{ status: 400 }
|
||
)
|
||
}
|
||
|
||
const saved = await prisma.sitePages.upsert({
|
||
where: { id: 1 },
|
||
update: values,
|
||
create: { id: 1, ...values },
|
||
})
|
||
|
||
return NextResponse.json(saved)
|
||
} catch (error) {
|
||
console.error('Error updating site pages:', error)
|
||
return NextResponse.json(
|
||
{ error: 'Fehler beim Speichern der Seiteninhalte' },
|
||
{ status: 500 }
|
||
)
|
||
}
|
||
}
|