From c83dc7a5e5f9a6f8f8228cd9bc0958e11b3dedcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=B6rdle=20Bot?= Date: Sun, 7 Dec 2025 13:03:43 +0100 Subject: [PATCH] =?UTF-8?q?Fix:=20Cache-Control-Header=20f=C3=BCr=20Wavefo?= =?UTF-8?q?rm-Editor=20API-Route=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - API-Route sendet jetzt explizite No-Cache-Header - Frontend-Fetch verwendet cache: 'no-store' - Behebt Problem, dass neu hochgeladene Dateien erst nach Container-Neustart bearbeitbar waren --- app/api/curator/specials/[id]/route.ts | 40 +++++++++++++++++++++++--- app/curator/specials/[id]/page.tsx | 1 + 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/app/api/curator/specials/[id]/route.ts b/app/api/curator/specials/[id]/route.ts index da6e1e9..666289e 100644 --- a/app/api/curator/specials/[id]/route.ts +++ b/app/api/curator/specials/[id]/route.ts @@ -1,9 +1,14 @@ 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 }> } @@ -52,13 +57,40 @@ export async function GET( return NextResponse.json({ error: 'Special not found' }, { status: 404 }); } - // Filtere Songs ohne vollständige Song-Daten (song, song.filename) - // Dies verhindert Fehler im Frontend, wenn Songs gelöscht wurden oder Daten fehlen - const filteredSongs = special.songs.filter(ss => ss.song && ss.song.filename); + // 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: filteredSongs, + songs: availableSongs, + }, { + headers: { + 'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate', + 'Pragma': 'no-cache', + 'Expires': '0', + }, }); } diff --git a/app/curator/specials/[id]/page.tsx b/app/curator/specials/[id]/page.tsx index 686ed7d..e8d7386 100644 --- a/app/curator/specials/[id]/page.tsx +++ b/app/curator/specials/[id]/page.tsx @@ -32,6 +32,7 @@ export default function CuratorSpecialEditorPage() { } const res = await fetch(`/api/curator/specials/${specialId}`, { headers: getCuratorAuthHeaders(), + cache: 'no-store', }); if (res.status === 403) { setError(t('specialForbidden'));