45 lines
1.4 KiB
TypeScript
45 lines
1.4 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
import { extname, join } from 'node:path';
|
|
import { randomUUID } from 'node:crypto';
|
|
import { UPLOADS_DIR } from '@/lib/db/store';
|
|
|
|
export const runtime = 'nodejs';
|
|
|
|
const ALLOWED = new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif', '.svg', '.avif']);
|
|
const MAX_BYTES = 8 * 1024 * 1024; // 8 MB
|
|
|
|
export async function POST(req: Request) {
|
|
let form: FormData;
|
|
try {
|
|
form = await req.formData();
|
|
} catch {
|
|
return NextResponse.json({ error: 'expected multipart form' }, { status: 400 });
|
|
}
|
|
|
|
const file = form.get('file');
|
|
if (!(file instanceof File)) {
|
|
return NextResponse.json({ error: 'no file' }, { status: 400 });
|
|
}
|
|
if (file.size > MAX_BYTES) {
|
|
return NextResponse.json({ error: 'file too large (max 8MB)' }, { status: 413 });
|
|
}
|
|
|
|
const ext = extname(file.name).toLowerCase();
|
|
if (!ALLOWED.has(ext)) {
|
|
return NextResponse.json({ error: `unsupported type ${ext}` }, { status: 415 });
|
|
}
|
|
|
|
const name = `${Date.now()}-${randomUUID().slice(0, 8)}${ext}`;
|
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
|
|
try {
|
|
await mkdir(UPLOADS_DIR, { recursive: true });
|
|
await writeFile(join(UPLOADS_DIR, name), buffer);
|
|
} catch {
|
|
return NextResponse.json({ error: 'write failed' }, { status: 500 });
|
|
}
|
|
|
|
return NextResponse.json({ url: `/api/uploads/${name}`, name });
|
|
}
|