import { randomUUID } from "node:crypto"; import { NextResponse } from "next/server"; import { getCurrentSession } from "@/lib/session"; import { ensureBucket, minioClient, MINIO_BUCKET } from "@/lib/minio"; export const runtime = "nodejs"; export const maxDuration = 60; export const config = { api: { bodyParser: { sizeLimit: "100mb" } }, }; const MAX_FILE_SIZE = 100 * 1024 * 1024; export async function POST(request: Request) { let session: Awaited>; try { session = await getCurrentSession(); } catch { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } let formData: FormData; try { formData = await request.formData(); } catch { return NextResponse.json({ error: "Invalid multipart body" }, { status: 400 }); } const file = formData.get("file"); if (!(file instanceof File)) { return NextResponse.json({ error: "No file field in request" }, { status: 400 }); } const url = new URL(request.url); const scopeParam = url.searchParams.get("scope"); const scope = scopeParam === "notes" ? "notes" : scopeParam === "assistant" ? "assistant" : "garden"; if (!file.type.startsWith("image/")) { return NextResponse.json({ error: "Only image files are allowed" }, { status: 415 }); } if (file.size > MAX_FILE_SIZE) { return NextResponse.json({ error: "File exceeds 100 MB limit" }, { status: 413 }); } const rawExt = file.name.split(".").pop() ?? "bin"; const safeExt = rawExt .replace(/[^a-z0-9]/gi, "") .toLowerCase() .slice(0, 8); const key = `${scope}/${session.household.id}/${randomUUID()}.${safeExt}`; try { await ensureBucket(); const buffer = Buffer.from(await file.arrayBuffer()); await minioClient.putObject(MINIO_BUCKET, key, buffer, buffer.length, { "Content-Type": file.type, }); } catch (err) { console.error("Upload failed", err); return NextResponse.json({ error: "Upload service unavailable" }, { status: 503 }); } return NextResponse.json({ url: `/api/uploads/${key}` }); }