import { NextRequest, NextResponse } from 'next/server'; import { PrismaClient } from '@prisma/client'; import { requireStaffAuth } from '@/lib/auth'; import { access } from 'fs/promises'; import path from 'path'; const prisma = new PrismaClient(); // Mark route as dynamic to prevent caching export const dynamic = 'force-dynamic'; export async function GET( request: NextRequest, { params }: { params: Promise<{ id: string }> } ) { const { error, context } = await requireStaffAuth(request); if (error || !context) return error!; if (context.role !== 'curator') { return NextResponse.json( { error: 'Only curators can access this endpoint' }, { status: 403 } ); } const { id } = await params; const specialId = Number(id); if (!specialId || Number.isNaN(specialId)) { return NextResponse.json({ error: 'Invalid special id' }, { status: 400 }); } // Prüfen, ob dieses Special dem Kurator zugeordnet ist const assignment = await prisma.curatorSpecial.findFirst({ where: { curatorId: context.curator.id, specialId }, }); if (!assignment) { return NextResponse.json( { error: 'Forbidden: You are not allowed to access this special' }, { status: 403 } ); } const special = await prisma.special.findUnique({ where: { id: specialId }, include: { songs: { include: { song: true, }, orderBy: { order: 'asc' }, }, }, }); if (!special) { return NextResponse.json({ error: 'Special not found' }, { status: 404 }); } // Filtere Songs ohne vollständige Song-Daten und prüfe Datei-Existenz // Dies verhindert Fehler im Frontend, wenn Songs gelöscht wurden, Daten fehlen // oder Dateien noch nicht im Container verfügbar sind (Volume Mount Delay) const uploadsDir = path.join(process.cwd(), 'public/uploads'); const filteredSongs = await Promise.all( special.songs .filter(ss => ss.song && ss.song.filename) .map(async (ss) => { const filePath = path.join(uploadsDir, ss.song.filename); try { // Prüfe ob Datei existiert und zugänglich ist await access(filePath); return ss; } catch (error) { // Datei existiert nicht oder ist nicht zugänglich console.warn(`[API] Song file not available: ${ss.song.filename} (may be syncing)`); return null; } }) ); // Entferne null-Werte (Songs ohne verfügbare Dateien) const availableSongs = filteredSongs.filter((ss): ss is typeof special.songs[0] => ss !== null); return NextResponse.json({ ...special, songs: availableSongs, }, { headers: { 'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate', 'Pragma': 'no-cache', 'Expires': '0', }, }); }