import "server-only"; import { admin, required } from "./supabase"; import { developerResponse } from "./developer-response"; import { developerPage, type DeveloperPagination, } from "@/shared/developer-api"; import type { Database } from "@/shared/database"; const mapColumns = "id,name,description,source,state,bounds,panorama_count,created_at,updated_at" as const; type MapRecord = Pick< Database["public"]["Tables"]["datasets"]["Row"], | "id" | "name" | "description" | "source" | "state" | "bounds" | "panorama_count" | "created_at" | "updated_at" >; function mapView(map: MapRecord) { return { id: map.id, name: map.name, description: map.description, source: map.source, state: map.state, bounds: map.bounds, panoramaCount: map.panorama_count, createdAt: map.created_at, updatedAt: map.updated_at, url: `/servers/${map.id}`, }; } async function maps(page: DeveloperPagination, owner?: string) { let query = admin().from("datasets").select(mapColumns, { count: "exact" }); // Public requests cannot reveal private, rejected or banned maps by guessing IDs. query = owner ? query.eq("owner_id", owner) : query.eq("state", "published").is("banned_at", null); if (page.q) query = query.ilike("name", `%${page.q.replace(/[\\%_]/g, "\\$&")}%`); const result = await query .order("created_at", { ascending: false }) .order("id") .range(page.offset, page.offset + page.limit - 1); const rows = required(result); return { maps: rows.map(mapView), pagination: developerPage(result.count ?? 0, page), }; } async function map(id: string) { const result = await admin() .from("datasets") .select(mapColumns) .eq("id", id) .eq("state", "published") .is("banned_at", null) .maybeSingle(); if (result.error) required(result); return result.data ? mapView(result.data) : null; } async function account(owner: string) { const result = await admin() .from("profiles") .select( "id,display_name,created_at,name_glow,glow_color,bug_finder,custom_prefix,custom_prefix_icon", ) .eq("id", owner) .maybeSingle(); if (result.error) required(result); const p = result.data; return p ? { id: p.id, displayName: p.display_name, createdAt: p.created_at, appearance: { nameGlow: p.name_glow, glowColor: p.glow_color, bugFinder: p.bug_finder, customPrefix: p.custom_prefix, customPrefixIcon: p.custom_prefix_icon, }, } : null; } async function games(owner: string, page: DeveloperPagination) { const result = await admin() .from("game_sessions") .select("id,difficulty,created_at,current_round", { count: "exact" }) .eq("owner_id", owner) .order("created_at", { ascending: false }) .order("id") .range(page.offset, page.offset + page.limit - 1); const rows = required(result); const complete = rows.filter((s) => s.current_round === 5).map((s) => s.id); const totals = new Map(); if (complete.length) { const rounds = required( await admin() .from("game_rounds") .select("session_id,result") .in("session_id", complete), ); for (const round of rounds) { const result = round.result as { score?: unknown } | null; if ( !Number.isInteger(result?.score) || Number(result?.score) < 0 || Number(result?.score) > 5000 ) continue; const previous = totals.get(round.session_id) ?? { score: 0, count: 0 }; totals.set(round.session_id, { score: previous.score + Number(result?.score), count: previous.count + 1, }); } } return { games: rows.map((s) => ({ id: s.id, difficulty: s.difficulty, createdAt: s.created_at, currentRound: s.current_round, complete: s.current_round === 5, score: totals.get(s.id)?.count === 5 ? totals.get(s.id)!.score : null, })), pagination: developerPage(result.count ?? 0, page), }; } export function developerApi(request: Request, path: string[]) { return developerResponse(request, path, { consume: async (hash) => required( await admin().rpc("developer_key_consume", { p_token_hash: hash }), ), maps, map, account, games, }); }