import { type NextRequest, NextResponse } from "next/server"; import { clientIp, rateLimit } from "@/lib/rate-limit"; import { siteSettings } from "@/lib/services/site-settings"; /** * GET /api/imaging/badge?code=ADM * * Resolves a Habbo badge code to its image URL and redirects there. */ const CODE_RE = /^[A-Za-z0-9_+.-]+$/; const CODE_MAX_LEN = 50; const DEFAULT_BASE = "https://images.habbo.com/c_images/album1584"; export async function GET(request: NextRequest) { const ip = await clientIp(); const limited = await rateLimit(`badge-imaging:${ip}`, 60, 60_000); if (!limited.ok) { return new NextResponse(null, { status: 429, headers: { "Retry-After": String(limited.retryAfter) }, }); } const code = request.nextUrl.searchParams.get("code")?.trim(); if (!code || code.length > CODE_MAX_LEN || !CODE_RE.test(code)) { return NextResponse.json({ error: "Invalid badge code" }, { status: 400 }); } const configured = ( (await siteSettings.get("badge_base_url", "")) ?? "" ).trim(); const base = (configured || DEFAULT_BASE).replace(/\/+$/, ""); const isAbsolute = /^https?:\/\//i.test(base); const target = `${base}/${code}.gif`; const absoluteTarget = isAbsolute ? target : new URL(target, request.nextUrl.origin).toString(); return NextResponse.redirect(absoluteTarget, { status: 302, headers: { "Cache-Control": "public, max-age=300", }, }); }