// POST /api/upload — accept multipart file upload, store via saveArtifact, return Artifact. import { NextRequest, NextResponse } from "next/server"; import { saveArtifact, artifactId } from "@/server/storage"; import type { Artifact } from "@/lib/types"; export const dynamic = "force-dynamic"; const ALLOWED_EXTENSIONS = new Set([".fasta", ".pdb", ".csv", ".nwk"]); const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50 MB function extFromName(name: string): string { const idx = name.lastIndexOf("."); return idx >= 0 ? name.slice(idx).toLowerCase() : ""; } function mimeFromExt(ext: string): string { const map: Record = { ".fasta": "chemical/seq-aa-fasta", ".pdb": "chemical/x-pdb", ".csv": "text/csv", ".nwk": "text/x-newick", }; return map[ext] || "application/octet-stream"; } export async function POST(req: NextRequest): Promise { try { const formData = await req.formData(); const file = formData.get("file"); const taskIdParam = formData.get("taskId"); if (!file || !(file instanceof File)) { return NextResponse.json( { error: "Missing file in multipart body" }, { status: 400 }, ); } if (!taskIdParam || typeof taskIdParam !== "string") { return NextResponse.json( { error: "Missing taskId in multipart body" }, { status: 400 }, ); } const ext = extFromName(file.name); if (!ALLOWED_EXTENSIONS.has(ext)) { return NextResponse.json( { error: `File extension "${ext}" not allowed. Accepted: ${Array.from(ALLOWED_EXTENSIONS).join(", ")}` }, { status: 400 }, ); } if (file.size > MAX_FILE_SIZE) { return NextResponse.json( { error: `File too large (${(file.size / 1024 / 1024).toFixed(1)} MB). Max: 50 MB` }, { status: 400 }, ); } const id = artifactId(); const artifact: Artifact = { id, name: file.name, bytes: file.size, mime: mimeFromExt(ext), url: `/api/artifacts/${id}`, createdAt: Date.now(), }; const buffer = Buffer.from(await file.arrayBuffer()); await saveArtifact(taskIdParam, artifact, buffer); return NextResponse.json({ artifact }, { status: 201 }); } catch (err) { const message = err instanceof Error ? err.message : "unknown error"; return NextResponse.json({ error: message }, { status: 500 }); } }