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