@@ -72,7 +72,31 @@ export async function GET(
|
||||
// Not on local disk and not a video served above — try Hetzner as last resort
|
||||
try {
|
||||
const hetznerUrl = await generateHetznerStreamUrl(relativePath, 3600);
|
||||
|
||||
if (isVideo) {
|
||||
// Videos aren't rendered through next/image — a redirect lets the
|
||||
// browser/<video> stream directly from Hetzner (range requests,
|
||||
// no extra hop through this server).
|
||||
return NextResponse.redirect(hetznerUrl, 302);
|
||||
}
|
||||
|
||||
// Images/other assets may be requested by Next's built-in image
|
||||
// optimizer (/_next/image) for thumbnails. That optimizer does NOT
|
||||
// follow redirects on this Next.js version (`images.maximumRedirects`
|
||||
// only exists in much newer Next releases) and returns a bare 400 for
|
||||
// any 3xx response. Proxy the bytes through instead so the optimizer
|
||||
// (and any other consumer) sees a normal 200 response.
|
||||
const upstream = await fetch(hetznerUrl);
|
||||
if (!upstream.ok || !upstream.body) {
|
||||
return new NextResponse("Not found", { status: 404 });
|
||||
}
|
||||
const proxyHeaders = new Headers();
|
||||
const upstreamType = upstream.headers.get("content-type");
|
||||
const upstreamLength = upstream.headers.get("content-length");
|
||||
if (upstreamType) proxyHeaders.set("Content-Type", upstreamType);
|
||||
if (upstreamLength) proxyHeaders.set("Content-Length", upstreamLength);
|
||||
proxyHeaders.set("Cache-Control", "public, max-age=3600, immutable");
|
||||
return new NextResponse(upstream.body, { status: 200, headers: proxyHeaders });
|
||||
} catch {
|
||||
return new NextResponse("Not found", { status: 404 });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user