- planner dashboard: replace Overpass panels with POI index freshness + serving panels (age, rows/category, import status, API request rate/errors) - alerts: replace overpass-upstream-unhealthy with poi-index-stale (>6 weeks) - architecture.md: POI data flow now the self-hosted index - privacy manifest (DE+EN): Overpass is Journal-surface-backfill-only; Planner POIs served same-origin from /api/pois - self-host-overpass README + roadmap: superseded for POIs by poi-index - poi-extract README: self-hoster story (optional pipeline, regional extract, graceful empty index) - map-core tsconfig: exclude *.sync.test.ts from tsc to keep it zero-dep Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.1 KiB
5.1 KiB
1. Category selectors (single source of truth)
- 1.1 Refactor
packages/map-core/src/poi.ts: replace Overpass QLquerystrings with structuredselectors: Array<{key, value}>per category; updatepoi.test.ts - 1.2 Add a helper that renders the selectors as an osmium tag-filter expression list (used by the pipeline; unit-tested against the nine categories)
2. Schema
- 2.1 Add
planner.poistopackages/db(osm_type, osm_id, category, name, geom Point 4326, tags jsonb, imported_at; PK (osm_type, osm_id, category); GiST on geom, btree on category) + migration - 2.2
pnpm db:pushlocally and verify bbox+category query plans use the indexes (needs a local PostGIS; runpnpm dev:services && pnpm db:push)
3. Extract pipeline (BRouter host)
- 3.1 Create
infrastructure/brouter-host/poi-extract/: script that downloads the configured PBF (POI_PBF_URL, planet by default), runsosmium tags-filter+osmium export(centroids) into gzipped NDJSON, writes a manifest (sha256 + timestamp + row count), and cleans up working files (NDJSON is{osm_type, osm_id, name, lat, lon, tags}; the importer derivescategoriesfrom tags via map-core, so selector logic isn't duplicated on the Node-free host) - 3.2 Publish artifact + manifest via the existing Caddy sidecar at a vSwitch-only address (
/poi/*on:17777, sameX-BRouter-Authgate) (public-unreachability follows from the existing vSwitch-only bind + UFW; verify on the host) - 3.3 Add a monthly systemd timer under the
trailsuser; document disk/bandwidth footprint ininfrastructure/brouter-host/poi-extract/README.md - 3.4 Dry-run on a small Geofabrik extract first; then a full planet run — record row counts per category and artifact size (operational: run on the BRouter host)
4. Import job (flagship)
- 4.1 Create the import script: fetch manifest + artifact over the vSwitch, verify checksum, COPY into
planner.pois_staging, build indexes, atomic rename swap in one transaction - 4.2 Implement the 70% row-count guard with a
--bootstrapoverride; abort loudly (log + metric) when tripped - 4.3 Schedule via systemd timer (
poi-import.timer, offset a day after the extract); wire intocd-infra.ymldeploy sources (infrastructure/scriptsadded to the SCP list) - 4.4 First production import with
--bootstrap; spot-check several viewports against live Overpass results for category parity (operational: run on the flagship)
5. Serving route
- 5.1 Create
apps/planner/app/routes/api.pois.ts(GET bbox + categories): same-origin + session checks, 120/IP/min rate limit (no DB query on rejection), 100-result cap, short Cache-Control; register inapps/planner/app/routes.ts - 5.2 Unit tests: happy path, invalid bbox/categories 400, 429 path, empty-table graceful response
6. Client switch
- 6.1 Rewrite
apps/planner/app/lib/overpass.tsas the/api/poisclient (keepPoishape,quantizeBbox, error mapping; drop QL building); rename file topois.tsand update imports (use-pois,use-nearby-pois, snap-to-poi) - 6.2 Update client tests; verify the existing rate-limit banner and unavailable message fire on 429/5xx from
/api/pois - 6.3 Delete
apps/planner/app/routes/api.overpass.tsand its tests; remove the route registration and the private.coffee configuration (OVERPASS_URLSkept for the Journal surface backfill; removed from the planner service in both compose files)
7. Observability
- 7.1 Add
poi_index_rows{category},poi_index_age_seconds, import outcome, andpoi_api_requests_total{status}metrics; removeoverpass_upstream_*metrics frommetrics.server.ts(planner exposes rows/age via scrape-time DB collect +poi_api_requests_total; import outcome is emitted by the import job via node_exporter textfile — see Task 4) - 7.2 Update the Grafana planner dashboard: replace Overpass upstream panels with index freshness + serving panels; add the stale-index alert (~6 weeks) (
poi-index-stalein alerts.yml)
8. Docs & cleanup
- 8.1 Update
docs/architecture.md(POI data flow) and the privacy documentation (Overpass now Journal-surface-backfill-only; Planner POIs served same-origin) - 8.2 Document the self-hoster story: optional pipeline, regional extract URL, graceful behavior with an empty index (poi-extract README)
- 8.3 Update
docs/ideas/self-host-overpass/README.md: superseded bypoi-index, with the revive-criteria note kept for the QL-compatibility case (+ roadmap pointer)
9. Verification
- 9.1 E2E: POI overlay flow (enable category → markers appear) —
e2e/planner-overlays.test.ts+ nearby-snap inplanner-routing.test.tsnow mock/api/pois(network-mock is the house pattern; a DB-seeded variant is possible but the route's SQL path is covered byapi.pois.test.ts) - 9.2 Run
pnpm typecheck && pnpm lint && pnpm test && pnpm test:e2e(typecheck + lint + unit all green; e2e needs the local stack — run before merge) - 9.3 Post-cutover production check: enable each of the nine categories on a busy viewport and a rural viewport; confirm results and dashboard metrics (operational: after the first production import)