first commit
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { readFile, stat } from 'node:fs/promises';
|
||||
import { extname, join, normalize } from 'node:path';
|
||||
import { UPLOADS_DIR } from '@/lib/db/store';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
const MIME: Record<string, string> = {
|
||||
'.png': 'image/png',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.jpeg': 'image/jpeg',
|
||||
'.webp': 'image/webp',
|
||||
'.gif': 'image/gif',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.avif': 'image/avif',
|
||||
};
|
||||
|
||||
// Serves admin-uploaded media from the DATA_DIR volume. Public (not gated by
|
||||
// middleware) so images render on the marketing site.
|
||||
export async function GET(
|
||||
_req: Request,
|
||||
{ params }: { params: { path: string[] } },
|
||||
) {
|
||||
const rel = normalize(params.path.join('/'));
|
||||
// Reject path traversal — the resolved file must stay inside UPLOADS_DIR.
|
||||
if (rel.includes('..') || rel.startsWith('/') || rel.startsWith('\\')) {
|
||||
return new NextResponse('bad path', { status: 400 });
|
||||
}
|
||||
|
||||
const filePath = join(UPLOADS_DIR, rel);
|
||||
try {
|
||||
const info = await stat(filePath);
|
||||
if (!info.isFile()) return new NextResponse('not found', { status: 404 });
|
||||
const buf = await readFile(filePath);
|
||||
const type = MIME[extname(filePath).toLowerCase()] ?? 'application/octet-stream';
|
||||
return new NextResponse(buf, {
|
||||
headers: {
|
||||
'content-type': type,
|
||||
'cache-control': 'public, max-age=31536000, immutable',
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
return new NextResponse('not found', { status: 404 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user