| |
| 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; |
|
|
| function extFromName(name: string): string { |
| const idx = name.lastIndexOf("."); |
| return idx >= 0 ? name.slice(idx).toLowerCase() : ""; |
| } |
|
|
| function mimeFromExt(ext: string): string { |
| const map: Record<string, string> = { |
| ".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<NextResponse> { |
| 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 }); |
| } |
| } |
|
|