import { NextRequest, NextResponse } from "next/server"; import { getCurrentUser } from "@/lib/auth"; import { getOrderFeed, getOrderFacets, canViewOrders } from "@/lib/order-feed"; /** * GET /api/orders — the order browse list. * * Note this route is a sibling of /api/orders/ingest (the n8n webhook). Next * resolves static segments before dynamic ones, so `ingest` is unaffected by * the [entityKey] route next to it — but it does mean "ingest" is now a * reserved order key. Every entity key starts "order_", so no real collision. */ export async function GET(req: NextRequest) { const user = await getCurrentUser(req); if (!user) return NextResponse.json({ error: "unauthorized" }, { status: 403 }); // The spine has no owner column — this is a participant gate, not a row // filter. See ORDER_VIEWERS in lib/order-feed.ts for why it is an allowlist. if (!canViewOrders(user.id)) { return NextResponse.json({ error: "forbidden" }, { status: 403 }); } const p = req.nextUrl.searchParams; const list = (k: string) => p.get(k)?.split(",").filter(Boolean); const filters = { lane: p.get("lane") ?? undefined, platforms: list("platforms"), statuses: list("statuses"), from: p.get("from") ?? undefined, to: p.get("to") ?? undefined, search: p.get("search") ?? undefined, currency: p.get("currency") ?? undefined, has_transaction: p.get("has_transaction") ?? undefined, // buildParams encodes booleans as "1" and omits them when false, so an // absent param means "on" here — the default hides lifecycle-only rows. hide_lifecycle_only: p.get("show_lifecycle_only") !== "1", sort_by: p.get("sort_by") ?? undefined, sort_dir: p.get("sort_dir") ?? undefined, limit: p.get("limit") ? Number(p.get("limit")) : undefined, offset: p.get("offset") ? Number(p.get("offset")) : undefined, }; const [result, facets] = await Promise.all([ getOrderFeed(filters), getOrderFacets(filters), ]); return NextResponse.json({ ...result, facets }); }