← back to Govarbitrage

src/app/api/listings/[id]/buyer-lead/route.ts

38 lines

import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/db";
import { z } from "zod";
import { rateLimit, clientIp, tooManyRequests } from "@/lib/rate-limit";

export const dynamic = "force-dynamic";

const LeadSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  phone: z.string().optional(),
  offer: z.number().nonnegative().optional(),
  notes: z.string().optional(),
});

// Record a CONTINGENT, non-binding buyer lead against a listing.
export async function POST(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
  // Public endpoint — throttle anonymous submissions to curb spam/abuse.
  const limit = rateLimit(`lead:ip:${clientIp(req)}`, 5, 10 * 60_000);
  if (!limit.allowed) return tooManyRequests(limit);

  const { id } = await params;
  const listing = await prisma.listing.findUnique({ where: { id } });
  if (!listing) return NextResponse.json({ error: "Not found" }, { status: 404 });

  const parsed = LeadSchema.safeParse(await req.json().catch(() => ({})));
  if (!parsed.success) {
    return NextResponse.json({ error: "Invalid lead", issues: parsed.error.issues }, { status: 400 });
  }
  const lead = await prisma.buyerLead.create({
    data: { listingId: id, ...parsed.data, contingent: true },
  });
  await prisma.listingEvent.create({
    data: { listingId: id, type: "BUYER_LEAD_ADDED", message: `Contingent lead from ${parsed.data.name}` },
  });
  return NextResponse.json(lead);
}