Route ground legs along real roads via an OSRM directions proxy

- GET /api/directions proxies OSRM (OSRM_URL env, default public demo
  server) with validation, Leaflet-order geometry, 24h capped cache
- Map draws straight lines first, then upgrades ground legs to
  road-following polylines with routed km (marked road) as directions
  arrive; silent fallback to great-circle on failure; air legs unchanged
- README documents OSRM_URL
This commit is contained in:
2026-07-20 10:54:04 +07:00
parent 65480fd892
commit 4cedab0cf6
7 changed files with 268 additions and 35 deletions
+11
View File
@@ -243,6 +243,17 @@ Computation (see Cost semantics above):
Proxies `https://nominatim.openstreetmap.org/search?format=jsonv2&limit=5&accept-language=en&q=…` server-side with header `User-Agent: trip-plan-app/0.1 (self-hosted)` (`accept-language=en` so results come back in Latin script, not the local language). Map Nominatim's `display_name``name`, parse lat/lon to numbers. On upstream failure return `502 {"error":"geocoding unavailable"}`. Cache identical queries in-memory for 10 minutes.
### Directions proxy (OSRM)
`GET /api/directions?from=<lat>,<lng>&to=<lat>,<lng>``200 {km, geometry}` (requires auth).
- Proxies `${OSRM_URL}/route/v1/driving/{fromLng},{fromLat};{toLng},{toLat}?overview=full&geometries=geojson` server-side. `OSRM_URL` env var, default `https://router.project-osrm.org` (the public demo server — fine for light personal use; self-hosters can point it at their own OSRM). Send the same `User-Agent` header as the geocode proxy.
- Validation: `from`/`to` must each be `lat,lng` with finite numbers in range (lat [-90,90], lng [-180,180]) → else `400`. Upstream failure, non-Ok OSRM code, or no route → `502 {"error":"directions unavailable"}`.
- Response `km` = route distance / 1000 rounded to 1 decimal; `geometry` = the GeoJSON coordinates converted to `[[lat,lng], …]` (Leaflet order).
- In-memory cache: key = both coord pairs rounded to 5 decimals, TTL 24 h, cap ~500 entries (evict oldest).
**Consumer contract (map)**: the frontend requests directions for each `mode: "ground"` route leg and, on success, draws the road-following polyline instead of the straight line and shows the routed km for that leg ("via road"); on 502/failure it keeps the straight great-circle line silently. Air legs stay straight/dashed. `/route` itself (and `summary.kmDriven`) remains great-circle — the server does not call OSRM during route computation.
## Frontend contract notes
- SPA served from `public/`; all non-`/api` GETs fall back to `public/index.html` is NOT required — a single `index.html` with hash-based routing (`#/login`, `#/trips`, `#/trip/:id`) is the expected design, so no server-side fallback is needed.