Files
OpenSquawk/server/utils/radioBackend.ts
itsrubberduck d70fffdd68 refactor(tools): forward the airport endpoints to the backend
/api/service/tools/* was a second implementation of the OSM geocoding and taxi
routing the Python backend already owns — its own Overpass client, alias
matching and scoring. It drifted: it still asked Overpass for `out center tags`,
so a runway resolved to the middle of the strip rather than a threshold, which
is the runway-endpoint bug the backend fixed. Teaching the copy about runway
endpoints would mean maintaining the geometry twice, so the copy goes instead.

Forwarding exposes origin_runway_point / dest_runway_point and
include_connectors, and inherits the backend's Overpass cache and its radius
fallback for aerodromes with no generated OSM area (EDDM), which the copy
answered with an empty feature list. Failures now carry an HTTP status as well
as the error code in the body; the old copy answered every error with 200.

The frequencies call sites needed real types: airportGeocode.ts sat under
server/api with no default export, so Nitro registered it as a route whose
return type collapsed to any and poisoned the whole API type map. The typecheck
was green because of it, not despite it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 11:30:52 +02:00

60 lines
2.1 KiB
TypeScript

import { useRuntimeConfig } from '#imports'
/**
* Forwarding for the public `/api/service/tools/*` endpoints.
*
* The OSM airport geocoder and the taxi router live in the Python backend,
* which owns the Overpass client and its cache, the airport dataset, and the
* runway-threshold logic. Nuxt used to carry a second copy of all of it; the
* copy drifted and started routing to runway centres. These helpers keep the
* documented public paths while leaving exactly one implementation.
*/
/** Build the upstream URL, forwarding every supplied query parameter. */
export function backendToolUrl(
baseUrl: string,
path: string,
query: Record<string, unknown>
): string {
const params = new URLSearchParams()
for (const [key, raw] of Object.entries(query)) {
// h3 yields an array when a parameter is repeated; the backend takes one.
const value = Array.isArray(raw) ? raw[0] : raw
if (value === undefined || value === null) continue
const text = String(value).trim()
if (!text) continue
params.set(key, text)
}
const base = baseUrl.replace(/\/+$/, '')
const search = params.toString()
return search ? `${base}${path}?${search}` : `${base}${path}`
}
export function radioBackendBaseUrl(): string {
const config = useRuntimeConfig() as any
return config?.public?.radioBackendUrl || 'http://127.0.0.1:8000'
}
/**
* Forward a tools request and hand back what the backend answered.
*
* The backend reports failures with real status codes and keeps the old error
* code in the body (`{"error": "origin_not_found", …}`). Both are passed
* through: rewriting a 404 back into the legacy HTTP 200 would hide a failed
* lookup from every caller that checks the status.
*/
export async function forwardToRadioBackend(
path: string,
query: Record<string, unknown>
): Promise<Record<string, any>> {
const url = backendToolUrl(radioBackendBaseUrl(), path, query)
const fetcher = (globalThis as any).$fetch as (
target: string,
options?: Record<string, unknown>
) => Promise<Record<string, any>>
return await fetcher(url, { method: 'GET' })
}