← 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 });
  }
}