- Add 11 missing server action files: badges, bulk-users, catalog, catalog-bc, catalog-items, import-badges, import-furni, multi-account-detect, permissions, rooms, soundtracks - Add missing admin navigation links: tickets, sounds, translations, import, radio sub-pages - Add translation keys for all new navigation items
74 lines
2.3 KiB
TypeScript
74 lines
2.3 KiB
TypeScript
import { apiJson } from "@/lib/api";
|
|
import { prisma } from "@/lib/prisma";
|
|
|
|
// Public REST API — leaderboard. Mirrors src/app/leaderboard/page.tsx.
|
|
// AtomCMS-faithful currency type ids (see prisma/schema.prisma UsersCurrency):
|
|
// Credits = -1 (lives on users.credits), Duckets = 0, Diamonds = 5.
|
|
export const dynamic = "force-dynamic";
|
|
|
|
type LeaderboardType = "credits" | "diamonds" | "duckets";
|
|
|
|
const CURRENCY_TYPE: Record<Exclude<LeaderboardType, "credits">, number> = {
|
|
diamonds: 5,
|
|
duckets: 0,
|
|
};
|
|
|
|
type Row = { rank: number; username: string; look: string; value: number };
|
|
|
|
async function loadCreditsRows(): Promise<Row[]> {
|
|
const users = await prisma.user.findMany({
|
|
orderBy: { credits: "desc" },
|
|
take: 20,
|
|
select: { username: true, look: true, credits: true },
|
|
});
|
|
return users.map((u, i) => ({
|
|
rank: i + 1,
|
|
username: u.username,
|
|
look: u.look,
|
|
value: u.credits,
|
|
}));
|
|
}
|
|
|
|
async function loadCurrencyRows(type: number): Promise<Row[]> {
|
|
const top = await prisma.usersCurrency.findMany({
|
|
where: { type },
|
|
orderBy: { amount: "desc" },
|
|
take: 20,
|
|
select: { userId: true, amount: true },
|
|
});
|
|
if (top.length === 0) return [];
|
|
|
|
const users = await prisma.user.findMany({
|
|
where: { id: { in: top.map((t) => t.userId) } },
|
|
select: { id: true, username: true, look: true },
|
|
});
|
|
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<Row, "rank"> => 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 =
|
|
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 });
|
|
}
|
|
}
|