Files
manyangles/apps/web/src/server/location-search.ts
T

44 lines
1.8 KiB
TypeScript

import { z } from "zod";
import { locationSuggestionSchema, type LocationSuggestion } from "@album/contracts";
const photonSchema = z.object({
features: z.array(z.object({
geometry: z.object({ coordinates: z.tuple([z.number(), z.number()]) }),
properties: z.record(z.unknown()),
})),
});
export function parseLocationResults(payload: unknown): LocationSuggestion[] {
const parsed = photonSchema.parse(payload);
const seen = new Set<string>();
return parsed.features.flatMap(({ geometry, properties }) => {
const text = (key: string) => typeof properties[key] === "string" ? properties[key] as string : "";
const street = [text("housenumber"), text("street")].filter(Boolean).join(" ");
const address = [...new Set([
text("name"), street, text("city") || text("district") || text("county"),
text("state"), text("postcode"), text("country"),
].filter(Boolean))].join(", ");
const result = locationSuggestionSchema.safeParse({
address, longitude: geometry.coordinates[0], latitude: geometry.coordinates[1],
});
const key = `${address}:${geometry.coordinates.join(",")}`;
if (!address || !result.success || seen.has(key)) return [];
seen.add(key);
return [result.data];
});
}
export async function searchLocations(query: string) {
const url = new URL("https://photon.komoot.io/api/");
url.searchParams.set("q", query);
url.searchParams.set("limit", "6");
url.searchParams.set("lang", "en");
const response = await fetch(url, {
headers: { Accept: "application/json", "User-Agent": "Manyangles event location search" },
next: { revalidate: 86_400 },
signal: AbortSignal.timeout(8000),
});
if (!response.ok) throw new Error("Location search unavailable");
return parseLocationResults(await response.json());
}