TypeScript 50.6%
JavaScript 46.1%
SQL 2.1%
Shell 0.6%
CSS 0.5%
1import { NextRequest, NextResponse } from "next/server";2import { getDb } from "@/lib/db";3import type { ListingRow, ListingCard } from "@/lib/types";4import { directCardsByUids } from "@/lib/listings/public";5type Row = ListingRow & { estimate: number | null; ratio: number | null; verdict: string | null; confidence: string | null };6export const dynamic = "force-dynamic";7export async function GET(req: NextRequest) {8 const uids = (req.nextUrl.searchParams.get("uids") ?? "").split(",").filter(Boolean).slice(0, 50);9 if (!uids.length) return NextResponse.json({ items: [] });10 try {11 const agg = uids.filter((u) => !u.startsWith("toveli:"));12 const rows = agg.length ? getDb().prepare(`SELECT l.*, v.estimate, v.ratio, v.verdict, v.confidence FROM listing l LEFT JOIN valuation v ON v.listing_uid = l.uid WHERE l.uid IN (${agg.map(() => "?").join(",")})`).all(...agg) as Row[] : [];13 const items: ListingCard[] = rows.map((r) => { let image: string | null = null; try { const a = JSON.parse(r.images ?? "[]"); image = a[0] ?? null; } catch { /* */ } return { uid: r.uid, propertyId: r.property_id, market: r.market, price: r.price, address: r.address, city: r.city, region: r.region, category: r.category, typeLabel: r.type_label, bedrooms: r.bedrooms, bathrooms: r.bathrooms, areaSqft: r.area_sqft, lat: r.lat, lng: r.lng, image, imageCount: r.image_count ?? 0, connector: r.connector, sourceId: r.source_id, url: r.url, firstSeen: r.first_seen, estimate: r.estimate, ratio: r.ratio, verdict: r.verdict, confidence: r.confidence }; });14 items.push(...(await directCardsByUids(uids)));15 items.sort((a, b) => uids.indexOf(a.uid) - uids.indexOf(b.uid));16 return NextResponse.json({ items });17 } catch (e) { return NextResponse.json({ items: [], error: (e as Error).message }, { status: 503 }); }18}19