* Add navi-geo service (extraction #6) Faithful port of recon's geocode/reverse family to a new :8426 service: GET /api/geocode?q=&limit=&lat=&lon=&zoom= Photon-first ranked search GET /api/reverse?lat=&lon= reverse geocode (Photon) GET /api/reverse/<lat>/<lon> reverse enrichment bundle (Central) Ported modules: geocode.py (engine), netsyms.py (address SQLite), dem.py (planet-DEM reader), address_book.py (reader copy), and the three handlers + four bundle helpers from netsyms_api.py. All three routes public, behaviour- identical to recon. Behaviour-changing edges (both pre-decided in Phase A/B, called out in the PR): - landclass: in-process call replaced with HTTP GET to navi-landclass :8424, reading .summary (the same most-specific unit-name string). First navi→navi edge after landclass itself. - hardcoded paths/URLs → env vars (PHOTON_URL, NAVI_NETSYMS_DB, NAVI_TIMEZONE_DB, NAVI_DEM_PMTILES, NAVI_ADDRESS_BOOK_YAML, NAVI_LANDCLASS_URL); rerank trace log opt-in (NAVI_GEO_RERANK_TRACE_LOG, default off — recon always wrote /tmp). No secrets in this service: PADUS_DB_* disappears because landclass is HTTP- delegated (Phase A §10). Address book uses Option B (shared-file read), the same pattern navi-contacts already uses. Bundle 9-key contract preserved exactly (name/city/county/state/country/ postal_code/timezone/landclass/elevation_m), same null-on-component-failure semantics, same in-memory TTLCache(10_000, 86_400) per worker. Tests: 28 passing, 1 skipped (real timezone DB, off-box). Ported the 9 recon reverse-bundle tests + added the HTTP-landclass coupling tests + hermetic geocode reranker/intent-classifier tests (recon's geocode_test.py was a live smoke test). Adds usaddress/rapidfuzz/cachetools/shapely/numpy/Pillow/pmtiles to deps. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PR #6 review fixes 1. Rename geocode._setup_trace_logger → setup_trace_logger (public hook) 2. Hoist `import requests as http_requests` to module level in geo_route.py 3. Wire netsyms.health() into admin.py (enriches the netsyms filesystem entry with row_count/file_size_bytes/indexed_countries; no shared-builder change) 4. Fix misleading LANDCLASS_TIMEOUT_S comment (recon had no timeout) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: zvx-echo6 <mj@k7zvx.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| config | ||
| deploy | ||
| services | ||
| shared | ||
| .gitignore | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
navi-backend
Monorepo of small, single-responsibility HTTP services extracted from the recon
codebase as part of the recon ↔ Navi decoupling project. Each service owns a
slice of the /api/* surface that navi.echo6.co depends on, runs behind the
existing Caddy/Authentik edge, and is fronted by the navi.echo6.co nginx vhost.
See HANDOFF-recon-navi-decoupling-v3.md for the full plan. This repo is
extraction #1: navi-traffic.
Layout
navi-backend/
├── shared/ # cross-service helpers, imported by every service
│ ├── auth.py # get_user_id(req), require_auth decorator (Authentik header)
│ └── admin_info.py # build_info_response(), mask_key(), time_dependency()
├── services/
│ └── navi_traffic/ # extraction #1 — TomTom traffic tile proxy (:8421)
│ ├── app.py # Flask factory (create_app) + gunicorn entry
│ ├── traffic.py # /api/traffic/flow/<z>/<x>/<y>.png (ported from recon)
│ ├── admin.py # /api/admin/navi-traffic/info (§4.5 admin convention)
│ └── tests/
└── deploy/
├── systemd/navi-traffic.service
└── nginx/navi-traffic.conf.snippet
Service directories use an underscore (navi_traffic) so they're importable
Python packages; the service name stays navi-traffic (hyphen) in systemd,
nginx, and the admin-info service field.
Setup
Single workspace, single virtualenv:
python -m venv .venv
.venv/bin/pip install -e .
Test
.venv/bin/pytest services/navi_traffic/tests/ -v
Run (local)
TOMTOM_API_KEY=... .venv/bin/gunicorn 'services.navi_traffic.app:create_app()' \
--bind 127.0.0.1:8421 --workers 2
Run (local) — navi-geo (extraction #6)
.venv/bin/pytest services/navi_geo/tests/ -v
# All paths/URLs are env-overridable (see deploy/env/navi-geo.env.example).
# No secrets — landclass is HTTP-delegated to navi-landclass (:8424).
.venv/bin/gunicorn 'services.navi_geo.app:create_app()' \
--bind 127.0.0.1:8426 --workers 2
navi-geo serves /api/geocode, /api/reverse?lat=&lon=, and the reverse
enrichment bundle /api/reverse/<lat>/<lon> (Central's 9-key contract). All
public. The reverse bundle fans out to Photon, the SpatiaLite timezone DB,
navi-landclass (HTTP), and the planet-DEM PMTiles — each degrading to null
independently, never 5xx.
The admin-info convention (§4.5)
Every service exposes GET /api/admin/<service-name>/info, gated by require_auth,
returning a uniform shape: service, version (git SHA), port, config, env
(names + masked values), dependencies (upstream health checks), filesystem,
runtime (uptime / request count / last error). No aggregator — a future admin
panel fans out to each service in parallel.