export const prerender = false; import type { APIRoute } from "astro"; import { and, desc, eq, inArray, sql } from "drizzle-orm"; import { createDb } from "@/db"; import { events, eventTags, markets } from "@/db/schema"; export const GET: APIRoute = async ({ url, locals }) => { const runtime = locals.runtime as { env: { DATABASE_URL: string } }; const db = createDb(runtime.env.DATABASE_URL); const tag = url.searchParams.get("tag"); const limit = Math.min(Math.max(Number(url.searchParams.get("limit")) || 50, 1), 100); const offset = Math.max(Number(url.searchParams.get("offset")) || 0, 0); let eventIds: string[] | undefined; if (tag) { const tagged = await db.select({ eventId: eventTags.eventId }).from(eventTags).where(eq(eventTags.tag, tag as never)); eventIds = tagged.map((t) => t.eventId); if (eventIds.length === 0) { return Response.json( { events: [], total: 0 }, { headers: { "Cache-Control": "public, s-maxage=30" } }, ); } } const conditions = [eq(events.active, true)]; if (eventIds) { conditions.push(inArray(events.id, eventIds)); } const randomCount = Math.max(Math.floor(limit * 0.8), limit - 2); const trendingCount = limit - randomCount; const [randomRows, trendingRows, countResult] = await Promise.all([ db .select({ slug: events.slug, title: events.title, description: events.description, startDate: events.startDate, endDate: events.endDate, closedAt: events.closedAt, createdAt: events.createdAt, }) .from(events) .where(and(...conditions)) .orderBy(sql`md5(${events.id}::text || date_trunc('hour', now())::text)`) .limit(randomCount) .offset(offset), db .select({ slug: events.slug, title: events.title, description: events.description, startDate: events.startDate, endDate: events.endDate, closedAt: events.closedAt, createdAt: events.createdAt, }) .from(events) .innerJoin(markets, eq(markets.eventId, events.id)) .where(and(...conditions)) .groupBy(events.id) .orderBy(desc(sql`sum(${markets.volume}::numeric)`)) .limit(trendingCount), db .select({ count: sql`count(*)::int` }) .from(events) .where(and(...conditions)), ]); const trendingSlugs = new Set(trendingRows.map((r) => r.slug)); const rows = [ ...randomRows.filter((r) => !trendingSlugs.has(r.slug)), ...trendingRows, ].slice(0, limit); const slugs = rows.map((r) => r.slug); const [marketData, tagData] = await Promise.all([ slugs.length > 0 ? db .select({ eventSlug: events.slug, volume: markets.volume, }) .from(markets) .innerJoin(events, eq(markets.eventId, events.id)) .where(inArray(events.slug, slugs)) : Promise.resolve([]), slugs.length > 0 ? db .select({ eventSlug: events.slug, tag: eventTags.tag, }) .from(eventTags) .innerJoin(events, eq(eventTags.eventId, events.id)) .where(inArray(events.slug, slugs)) : Promise.resolve([]), ]); const volumeBySlug = new Map(); for (const m of marketData) { volumeBySlug.set(m.eventSlug, (volumeBySlug.get(m.eventSlug) ?? 0) + parseFloat(m.volume)); } const tagsBySlug = new Map(); for (const t of tagData) { const existing = tagsBySlug.get(t.eventSlug) ?? []; existing.push(t.tag); tagsBySlug.set(t.eventSlug, existing); } const result = rows.map((row) => ({ ...row, volume: volumeBySlug.get(row.slug) ?? 0, tags: tagsBySlug.get(row.slug) ?? [], })); return Response.json( { events: result, total: countResult[0]?.count ?? 0 }, { headers: { "Cache-Control": "public, s-maxage=30" } }, ); };