skip Nominatim lookups when the server has none configured

Add GET /api/geocode/status ({configured}), like /api/overpass/status. The outline client and the last-resort place search ask it once per page and send no lookup a server without a Nominatim endpoint would refuse, so a default install no longer logs a 503 on its first named landmark or grounds ask. An older server or a failed probe leaves the capability unknown and lookups proceed as before.
This commit is contained in:
manjunath committed 2026-10-30 22:33:55 +05:30
1 parent 24228b145e
commit f32ab5446b
10 files changed
+157 -6

No files matched your search

+11 -1
View File
@@ -2,6 +2,7 @@ import { createNominatimProvider } from './nominatim.js';
import { createGeospatialServices } from './geospatial.js';
import { createHttpGeospatialProvider } from './http.js';
import { createPlaceSearch } from './placeSearch.js';
import { createNominatimStatus } from '../sources/nominatimStatus.js';
import { createGoogleGeocoder } from './google.js';
import { createPhotonGeocoder } from '../keylessGeocoder.js';
import { createCoordinateGeocoder } from './coordinateGeocoder.js';
@@ -28,6 +29,9 @@ export function createDefaultPlaceSearch({
} = {}) {
if (geocoding && geocoding.provider !== 'nominatim')
throw new TypeError('Unsupported geocoding provider');
const nominatimConfigured = endpoints.nominatim
? null
: createNominatimStatus({ fetchImpl });
const selected = geocoding
? createNominatimProvider({ ...geocoding, fetchImpl })
: null;
@@ -74,7 +78,13 @@ export function createDefaultPlaceSearch({
// neither of the two above did. It speaks the same result shape, so it
// rides the existing Google adapter rather than needing its own.
createGoogleGeocoder({
request(query, { bias, signal }) {
async request(query, { bias, signal }) {
// The app's own route says up front when Nominatim is off.
if (
nominatimConfigured &&
(await nominatimConfigured()) === false
)
return null;
const params = new URLSearchParams({ q: query });
if (bias) params.set('bounds', bias);
return fetchImpl(
+23
View File
@@ -30,3 +30,26 @@ test('place search follows the Photon setting', async () => {
const off = await hosts({ photon: null });
assert.ok(!off.some((host) => host.includes('photon')), off.join(','));
});
test('the last-resort route is skipped when the server reports Nominatim unconfigured', async () => {
const run = async (configured) => {
const asked = [];
const search = createDefaultPlaceSearch({
endpoints: { photon: null },
fetchImpl: async (url) => {
const path = new URL(String(url), 'http://local.test').pathname;
asked.push(path);
if (path === '/api/geocode/status')
return Response.json({ configured });
return Response.json({ status: 'ZERO_RESULTS', results: [] });
},
});
await search.geocode(`nowhere-nominatim-${configured}`);
return asked;
};
const off = await run(false);
assert.ok(off.includes('/api/geocode/status'));
assert.ok(!off.includes('/api/geocode'), off.join(','));
const on = await run(true);
assert.ok(on.includes('/api/geocode'), on.join(','));
});