← back to Govarbitrage

src/app/api/listings/route.ts

53 lines

import { NextRequest, NextResponse } from "next/server";
import { queryListings, type ListingRow, type QueryParams } from "@/lib/listings";
import { getCurrentTier } from "@/lib/current-user";
import { tierDef } from "@/lib/tiers";

export const dynamic = "force-dynamic";

// Money-math fields the FREE tier does NOT see — the arbitrage intelligence a
// reseller pays for (recommended max bid, ROI, valuations, opportunity scores).
const PAID_FIELDS: (keyof ListingRow)[] = [
  "recommendedMaxBid", "retailLow", "retailAverage", "retailHigh",
  "usedLow", "usedAverage", "usedHigh", "wholesale", "liquidation", "sellNow",
  "value7Day", "value30Day", "value90Day", "expectedSale", "netProfit", "roi",
  "opportunityScore", "arbitrageScore", "demandScore", "velocityScore",
  "logisticsScore", "conditionScore", "competitionScore", "buyerScore",
];

export async function GET(req: NextRequest) {
  const sp = req.nextUrl.searchParams;
  const params: QueryParams = {
    search: sp.get("search") || undefined,
    source: sp.get("source") || undefined,
    category: sp.get("category") || undefined,
    condition: sp.get("condition") || undefined,
    risk: sp.get("risk") || undefined,
    closingWithinHours: sp.get("closingWithinHours")
      ? Number(sp.get("closingWithinHours"))
      : undefined,
    sort: (sp.get("sort") as keyof ListingRow) || undefined,
    dir: (sp.get("dir") as "asc" | "desc") || undefined,
    page: sp.get("page") ? Number(sp.get("page")) : undefined,
    pageSize: sp.get("pageSize") ? Number(sp.get("pageSize")) : undefined,
    profile: sp.get("profile") || undefined,
  };
  const tier = await getCurrentTier();
  const limits = tierDef(tier).limits;

  // Enforce the tier's listing cap.
  const capped = { ...params, pageSize: Math.min(params.pageSize ?? 50, limits.maxListings) };
  const result = await queryListings(capped);

  // FREE tier: redact the money-math (they see the catalog, not the intelligence).
  if (!limits.showMoneyMath) {
    result.rows = result.rows.map((r) => {
      const row: Record<string, unknown> = { ...r };
      for (const f of PAID_FIELDS) row[f] = null;
      return row as unknown as ListingRow;
    });
  }

  return NextResponse.json({ ...result, tier, gated: !limits.showMoneyMath });
}