Redirect large/non-inline lesson files to CDN instead of proxying
ZIP/DOCX etc. download natively — no need to force attachment; and the 157 MB Second Brain Vault archive must stream from Caddy with Range, not through the app. Proxy attachment stays for small inline types (PDF/images).
This commit is contained in:
@@ -29,6 +29,17 @@ function extFromUrl(url: string): string {
|
|||||||
return dot >= 0 ? clean.slice(dot + 1).toLowerCase() : "";
|
return dot >= 0 ? clean.slice(dot + 1).toLowerCase() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Types a browser renders inline (so the direct link "opens in a tab" instead of
|
||||||
|
// downloading). Only these need proxying with a forced attachment header. ZIP,
|
||||||
|
// DOCX, etc. already download natively → redirect straight to the CDN.
|
||||||
|
const INLINE_EXT = new Set([
|
||||||
|
"pdf", "png", "jpg", "jpeg", "webp", "gif", "svg", "avif",
|
||||||
|
"txt", "htm", "html", "mp4", "mov", "webm", "mp3", "wav",
|
||||||
|
]);
|
||||||
|
// Above this, don't proxy through the app — redirect so the file streams from
|
||||||
|
// the CDN/Caddy with Range support (e.g. the 157 MB Second Brain Vault archive).
|
||||||
|
const PROXY_MAX_BYTES = 50 * 1024 * 1024;
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
_req: NextRequest,
|
_req: NextRequest,
|
||||||
{ params }: { params: Promise<{ id: string }> }
|
{ params }: { params: Promise<{ id: string }> }
|
||||||
@@ -59,12 +70,26 @@ export async function GET(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ext = extFromUrl(file.url);
|
||||||
|
|
||||||
|
// Non-inline types download natively — send the browser straight to the CDN
|
||||||
|
// (native download, Range/resume, no app load). Auth is already enforced above.
|
||||||
|
if (!INLINE_EXT.has(ext)) {
|
||||||
|
return NextResponse.redirect(file.url, 302);
|
||||||
|
}
|
||||||
|
|
||||||
const upstream = await fetch(file.url);
|
const upstream = await fetch(file.url);
|
||||||
if (!upstream.ok || !upstream.body) {
|
if (!upstream.ok || !upstream.body) {
|
||||||
return NextResponse.json({ error: "file_unavailable" }, { status: 502 });
|
return NextResponse.json({ error: "file_unavailable" }, { status: 502 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const ext = extFromUrl(file.url);
|
// Guard: a very large inline file still shouldn't stream through the app.
|
||||||
|
const upstreamLen = Number(upstream.headers.get("content-length") ?? "0");
|
||||||
|
if (upstreamLen > PROXY_MAX_BYTES) {
|
||||||
|
await upstream.body.cancel();
|
||||||
|
return NextResponse.redirect(file.url, 302);
|
||||||
|
}
|
||||||
|
|
||||||
const contentType =
|
const contentType =
|
||||||
upstream.headers.get("content-type") ??
|
upstream.headers.get("content-type") ??
|
||||||
CONTENT_TYPES[ext] ??
|
CONTENT_TYPES[ext] ??
|
||||||
|
|||||||
Reference in New Issue
Block a user