Files
RetroBlog/src/app/admin/(panel)/integrations/games/search/route.ts
T

43 lines
1.7 KiB
TypeScript

import { NextResponse, type NextRequest } from "next/server";
import { isAdmin } from "@/lib/auth";
// Game search for the admin "favorite games" picker. Backed by Steam's public
// store-search endpoint: keyless, returns capsule cover art, and its catalog is
// broad enough to cover PC plus the countless console/retro titles that have
// since been re-released on Steam. The picker also allows free-text entry for
// anything the search can't find, so console-only games are never blocked.
//
// Admin-gated (it lives under the panel layout, but route handlers don't inherit
// that guard, so we check here too) to avoid turning the blog into an open proxy.
const STORE_SEARCH = "https://store.steampowered.com/api/storesearch/";
const TIMEOUT = 6000;
type StoreItem = { id: number; name: string; tiny_image?: string };
export async function GET(req: NextRequest) {
if (!(await isAdmin())) {
return NextResponse.json({ error: "unauthorized" }, { status: 401 });
}
const q = (new URL(req.url).searchParams.get("q") ?? "").trim();
if (q.length < 2) return NextResponse.json({ games: [] });
const url = `${STORE_SEARCH}?term=${encodeURIComponent(q)}&cc=us&l=en`;
try {
const res = await fetch(url, { signal: AbortSignal.timeout(TIMEOUT) });
if (!res.ok) {
return NextResponse.json({ games: [], error: `Steam ${res.status}` });
}
const json = (await res.json()) as { items?: StoreItem[] };
const games = (json.items ?? []).slice(0, 10).map((it) => ({
name: it.name,
image: it.tiny_image,
url: `https://store.steampowered.com/app/${it.id}`,
}));
return NextResponse.json({ games });
} catch {
return NextResponse.json({ games: [], error: "Search timed out." });
}
}