← back to Govarbitrage
src/app/api/import/csv/route.ts
45 lines
import { NextRequest, NextResponse } from "next/server";
import { parseListingsCsv } from "@/importers/csv";
import { ingestMany } from "@/importers/ingest";
import { requireWrite } from "@/lib/auth";
export const dynamic = "force-dynamic";
export const maxDuration = 60;
// Accepts either raw CSV text (Content-Type: text/csv) or a multipart file upload
// under the "file" field. Set ?research=false to skip the research pipeline.
export async function POST(req: NextRequest) {
const denied = await requireWrite(req);
if (denied) return denied;
try {
const research = req.nextUrl.searchParams.get("research") !== "false";
let text: string;
const ct = req.headers.get("content-type") || "";
if (ct.includes("multipart/form-data")) {
const form = await req.formData();
const file = form.get("file");
if (!(file instanceof File)) {
return NextResponse.json({ error: "No file field" }, { status: 400 });
}
text = await file.text();
} else {
text = await req.text();
}
if (!text.trim()) return NextResponse.json({ error: "Empty CSV" }, { status: 400 });
const rows = parseListingsCsv(text);
if (rows.length === 0) return NextResponse.json({ error: "No rows parsed" }, { status: 400 });
const results = await ingestMany(rows, { research });
const created = results.filter((r) => r.ok && r.created).length;
const updated = results.filter((r) => r.ok && !r.created).length;
const failed = results.filter((r) => !r.ok);
return NextResponse.json({ total: rows.length, created, updated, failed, results });
} catch (e) {
return NextResponse.json({ error: (e as Error).message }, { status: 500 });
}
}