mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-23 02:46:42 +02:00
chore(geocoding): remove Pelias + close 3 bypass paths to public Nominatim
Pelias was retired from the Mac mini on 2026-04-28; photon-self (self-hosted Photon on mana-gpu) has been the live primary since then. This removes the now-dead Pelias adapter, config, tests, and the services/mana-geocoding/pelias/ stack — the entire compose file, the geojsonify_place_details.js patch, the setup.sh import script. Provider chain is now `photon-self → photon → nominatim`. The chain keeps its `privacy: 'local' | 'public'` split, sensitive-query blocking, coord quantization, and aggressive caching unchanged. Three direct calls to nominatim.openstreetmap.org that bypassed mana-geocoding now route through the wrapper: - citycorners/add-city + citycorners/cities/[slug]/add use the shared searchAddress() client (browser → same-origin proxy → mana-geocoding → photon-self). - memoro mobile drops its OSM reverse-geocoding fallback entirely; Expo's on-device reverse-geocoding stays as the sole path. Routing through the wrapper would require a memoro-server proxy endpoint — a follow-up if Expo's quality proves insufficient. Other behavioral changes: - CACHE_PUBLIC_TTL_MS dropped from 7d → 1h. The long TTL was a privacy-amplification trick from the Pelias era; with photon-self serving the bulk of traffic, a transient cross-LAN blip was pinning cached fallback answers for days. 1h gives quick recovery. - /health/pelias renamed to /health/photon-self; prometheus blackbox config + status-page generator updated. - mana-geocoding container no longer needs `extra_hosts: host.docker.internal:host-gateway` (was only there for the Pelias-on-host-network era). 113 tests passing. CLAUDE.md rewritten to reflect the post-Pelias architecture. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7bca16dfa7
commit
2bbcf14aba
35 changed files with 330 additions and 1262 deletions
|
|
@ -1,9 +1,11 @@
|
|||
/**
|
||||
* Shared geocoding client for all modules in the unified Mana app.
|
||||
*
|
||||
* Talks to our self-hosted mana-geocoding service (Pelias-backed, port 3018).
|
||||
* All queries stay within our infrastructure — no user location data leaves
|
||||
* the network.
|
||||
* Talks to mana-geocoding (port 3018), which fronts a provider chain
|
||||
* (photon-self → public photon → public nominatim) with sensitive-query
|
||||
* blocking and coord quantization. Sensitive + happy-path queries stay
|
||||
* on our infrastructure via photon-self; only last-resort fallbacks
|
||||
* leave the network.
|
||||
*
|
||||
* Used by: places, events, contacts, photos, …
|
||||
*
|
||||
|
|
@ -66,26 +68,24 @@ export interface GeocodingResult {
|
|||
longitude: number;
|
||||
address: GeocodingAddress;
|
||||
category: PlaceCategory;
|
||||
/** Raw Pelias categories (food, retail, transport, …) — only present
|
||||
* when the result came from Pelias. */
|
||||
peliasCategories?: string[];
|
||||
confidence: number;
|
||||
/** Which backend served this result. `pelias` is local; `photon` and
|
||||
* `nominatim` are public APIs (the wrapper applies sensitive-query
|
||||
* blocking + coord quantization before forwarding to those). */
|
||||
provider?: 'pelias' | 'photon' | 'nominatim';
|
||||
/** Which backend served this result. `photon-self` is our self-hosted
|
||||
* Photon (privacy: 'local'); `photon` and `nominatim` are public APIs
|
||||
* (the wrapper applies sensitive-query blocking + coord quantization
|
||||
* before forwarding to those). */
|
||||
provider?: 'photon-self' | 'photon' | 'nominatim';
|
||||
}
|
||||
|
||||
/**
|
||||
* Out-of-band information returned alongside results — the wrapper uses
|
||||
* this to signal *why* a query had unusual behavior:
|
||||
*
|
||||
* - `'fallback_used'`: Pelias was unreachable, so a public-API provider
|
||||
* served the request. Results are still valid but may be less precise.
|
||||
* UI should show a subtle "approximate" badge.
|
||||
* - `'fallback_used'`: photon-self was unreachable, so a public-API
|
||||
* provider served the request. Results are still valid but may be
|
||||
* less precise. UI should show a subtle "approximate" badge.
|
||||
* - `'sensitive_local_unavailable'`: the query matched the wrapper's
|
||||
* sensitive-keyword list (medical / mental-health / crisis service)
|
||||
* AND the local Pelias was unreachable. The wrapper deliberately did
|
||||
* AND no local provider was reachable. The wrapper deliberately did
|
||||
* NOT forward the query to public APIs. Results are empty by design.
|
||||
* UI should explain this to the user.
|
||||
*/
|
||||
|
|
@ -95,7 +95,7 @@ interface GeocodingResponse {
|
|||
results: GeocodingResult[];
|
||||
cached?: boolean;
|
||||
error?: string;
|
||||
provider?: 'pelias' | 'photon' | 'nominatim';
|
||||
provider?: 'photon-self' | 'photon' | 'nominatim';
|
||||
notice?: GeocodingNotice;
|
||||
}
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ interface GeocodingResponse {
|
|||
*/
|
||||
export interface SearchOutcome {
|
||||
results: GeocodingResult[];
|
||||
provider?: 'pelias' | 'photon' | 'nominatim';
|
||||
provider?: 'photon-self' | 'photon' | 'nominatim';
|
||||
notice?: GeocodingNotice;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
import { cityTable, useAllCities } from '$lib/modules/citycorners';
|
||||
import type { LocalCity } from '$lib/modules/citycorners/types';
|
||||
import { RoutePage } from '$lib/components/shell';
|
||||
import { searchAddress } from '$lib/geocoding';
|
||||
|
||||
const allCities = useAllCities();
|
||||
|
||||
|
|
@ -44,14 +45,10 @@
|
|||
geocoding = true;
|
||||
try {
|
||||
const searchQ = country.trim() ? `${q}, ${country.trim()}` : q;
|
||||
const res = await fetch(
|
||||
`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(searchQ)}&limit=1`,
|
||||
{ headers: { 'User-Agent': 'CityCorners/1.0' } }
|
||||
);
|
||||
const results = await res.json();
|
||||
const results = await searchAddress(searchQ, { limit: 1 });
|
||||
if (results.length > 0) {
|
||||
latitude = parseFloat(results[0].lat);
|
||||
longitude = parseFloat(results[0].lon);
|
||||
latitude = results[0].latitude;
|
||||
longitude = results[0].longitude;
|
||||
}
|
||||
} catch {
|
||||
// best-effort
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
import { ccLocationTable, CATEGORY_KEYS } from '$lib/modules/citycorners';
|
||||
import type { LocalCity, LocalLocation } from '$lib/modules/citycorners/types';
|
||||
import { RoutePage } from '$lib/components/shell';
|
||||
import { searchAddress } from '$lib/geocoding';
|
||||
|
||||
const cityCtx = getContext<{ value: LocalCity | undefined }>('currentCity');
|
||||
let city = $derived(cityCtx.value);
|
||||
|
|
@ -58,14 +59,10 @@
|
|||
cityName && !addr.toLowerCase().includes(cityName.toLowerCase())
|
||||
? `${addr}, ${cityName}`
|
||||
: addr;
|
||||
const res = await fetch(
|
||||
`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(q)}&limit=1`,
|
||||
{ headers: { 'User-Agent': 'CityCorners/1.0' } }
|
||||
);
|
||||
const results = await res.json();
|
||||
const results = await searchAddress(q, { limit: 1 });
|
||||
if (results.length > 0) {
|
||||
latitude = parseFloat(results[0].lat);
|
||||
longitude = parseFloat(results[0].lon);
|
||||
latitude = results[0].latitude;
|
||||
longitude = results[0].longitude;
|
||||
}
|
||||
} catch {
|
||||
// Geocoding is best-effort
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@
|
|||
* If we ever want to rate-limit by user we can add JWT verification here
|
||||
* without touching the upstream service.
|
||||
*
|
||||
* Also proxies /health and /health/pelias so the SvelteKit status page
|
||||
* (/status) can check the service from its server side.
|
||||
* Also proxies /health and /health/photon-self so the SvelteKit status
|
||||
* page (/status) can check the service from its server side.
|
||||
*/
|
||||
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue