import { randomUUID } from "crypto"; import { NextRequest, NextResponse } from "next/server"; import { GeocodingClient } from "@/lib/geocoding-client"; // Module-level singleton — cache persists across requests const client = new GeocodingClient(); /** * Maximum allowed query length. Station/place names are typically * < 100 characters. This prevents abuse with extremely long inputs. */ const MAX_QUERY_LENGTH = 256; /** * Allowed country codes for narrowing the search. * Two-letter ISO 3166-1 alpha-2 codes. */ const ALLOWED_COUNTRY_CODES = /^[a-z]{2}(,[a-z]{2}){0,4}$/; export async function GET(request: NextRequest) { try { const { searchParams } = new URL(request.url); const name = searchParams.get("name"); const countrycodes = searchParams.get("countrycodes"); if (!name) { return NextResponse.json({ error: "Missing 'name' parameter" }, { status: 400 }); } if (name.length > MAX_QUERY_LENGTH) { return NextResponse.json( { error: "Query too long (max 256 characters)" }, { status: 400 }, ); } if (countrycodes && !ALLOWED_COUNTRY_CODES.test(countrycodes.toLowerCase())) { return NextResponse.json( { error: "Invalid countrycodes (use up to 5 two-letter codes, comma-separated)" }, { status: 400 }, ); } const results = await client.geocode(name, countrycodes || undefined); if (results.length === 0) { return NextResponse.json({ error: "No results found" }, { status: 404 }); } return NextResponse.json(results[0]); } catch (error) { const corrId = randomUUID().slice(0, 8); console.error(`[${corrId}] Geocode API error:`, error); return NextResponse.json({ error: "Internal server error", correlationId: corrId }, { status: 500 }); } }