66 lines
2.2 KiB
TypeScript
66 lines
2.2 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
|
|
const REVERSE_GEOCODING_URL = "https://nominatim.openstreetmap.org/reverse";
|
|
const USER_AGENT = "wtr./1.0 (https://git.zvcloud.net/zv/wtr)";
|
|
|
|
interface RawReverseLocation {
|
|
name?: string;
|
|
display_name?: string;
|
|
address?: {
|
|
city?: string;
|
|
town?: string;
|
|
village?: string;
|
|
municipality?: string;
|
|
state?: string;
|
|
};
|
|
}
|
|
|
|
function parseCoordinate(value: string | null, min: number, max: number) {
|
|
if (!value?.trim()) return null;
|
|
const coordinate = Number(value);
|
|
return Number.isFinite(coordinate) && coordinate >= min && coordinate <= max ? Number(coordinate.toFixed(3)) : null;
|
|
}
|
|
|
|
export async function GET(request: Request) {
|
|
const { searchParams } = new URL(request.url);
|
|
const latitude = parseCoordinate(searchParams.get("latitude"), -90, 90);
|
|
const longitude = parseCoordinate(searchParams.get("longitude"), -180, 180);
|
|
const language = searchParams.get("language") === "en" ? "en" : "pl";
|
|
if (latitude === null || longitude === null) {
|
|
return NextResponse.json({ error: "Invalid coordinates." }, { status: 400 });
|
|
}
|
|
|
|
const params = new URLSearchParams({
|
|
format: "jsonv2",
|
|
lat: String(latitude),
|
|
lon: String(longitude),
|
|
zoom: "10",
|
|
addressdetails: "1",
|
|
"accept-language": language,
|
|
});
|
|
|
|
try {
|
|
const response = await fetch(`${REVERSE_GEOCODING_URL}?${params}`, {
|
|
next: { revalidate: 86400 },
|
|
headers: { Accept: "application/json", "User-Agent": USER_AGENT },
|
|
});
|
|
if (!response.ok) return NextResponse.json({ error: "Reverse geocoding is unavailable." }, { status: 502 });
|
|
const data = await response.json() as RawReverseLocation;
|
|
const name = data.address?.city
|
|
?? data.address?.town
|
|
?? data.address?.village
|
|
?? data.address?.municipality
|
|
?? data.name
|
|
?? data.display_name?.split(",")[0]?.trim();
|
|
if (!name) return NextResponse.json({ error: "Place name is unavailable." }, { status: 404 });
|
|
return NextResponse.json({
|
|
name,
|
|
province: data.address?.state ?? null,
|
|
}, {
|
|
headers: { "Cache-Control": "public, s-maxage=86400, stale-while-revalidate=604800" },
|
|
});
|
|
} catch {
|
|
return NextResponse.json({ error: "Reverse geocoding is unavailable." }, { status: 502 });
|
|
}
|
|
}
|