import { desc, eq, inArray } from "drizzle-orm"; import { apiJson } from "@/lib/api"; import { db, User, UsersCurrency } from "@/lib/db"; import { apiCacheKey, redisCache } from "@/lib/redis-cache"; // Public REST API — leaderboard. Mirrors src/app/leaderboard/page.tsx. // AtomCMS-faithful currency type ids (see UsersCurrency): // Credits = -1 (lives on users.credits), Duckets = 0, Diamonds = 5. type LeaderboardType = "credits" | "diamonds" | "duckets"; const CURRENCY_TYPE: Record, number> = { diamonds: 5, duckets: 0, }; type Row = { rank: number; username: string; look: string; value: number }; async function loadCreditsRows(): Promise { const users = await db .select({ username: User.username, look: User.look, credits: User.credits, }) .from(User) .orderBy(desc(User.credits)) .limit(20); return users.map((u, i) => ({ rank: i + 1, username: u.username, look: u.look, value: u.credits, })); } async function loadCurrencyRows(type: number): Promise { const top = await db .select({ userId: UsersCurrency.userId, amount: UsersCurrency.amount, }) .from(UsersCurrency) .where(eq(UsersCurrency.type, type)) .orderBy(desc(UsersCurrency.amount)) .limit(20); if (top.length === 0) return []; const users = await db .select({ id: User.id, username: User.username, look: User.look }) .from(User) .where( inArray( User.id, top.map((t) => t.userId), ), ); const byId = new Map(users.map((u) => [u.id, u])); return top .map((t) => { const u = byId.get(t.userId); if (!u) return null; return { username: u.username, look: u.look, value: t.amount }; }) .filter((r): r is Omit => r !== null) .map((r, i) => ({ rank: i + 1, ...r })); } export async function GET(req: Request) { try { const sp = new URL(req.url).searchParams; const requested = sp.get("type"); const type: LeaderboardType = requested === "diamonds" || requested === "duckets" ? requested : "credits"; const rows = await redisCache( apiCacheKey(`leaderboard:${type}`), 60, async () => type === "credits" ? await loadCreditsRows() : // eslint-disable-next-line security/detect-object-injection -- type validated to "diamonds"|"duckets" await loadCurrencyRows(CURRENCY_TYPE[type]), ); return apiJson({ type, data: rows }, { status: 200 }); } catch { return apiJson({ type: "credits", data: [] }, { status: 200 }); } }