refactor: separate Overpass and installation services

This commit is contained in:
Sameh Khamis committed 2026-09-12 12:59:39 -07:00
1 parent 781613fbb8
commit 213edd80ce
14 files changed
+1455 -841

No files matched your search

+108
View File
@@ -0,0 +1,108 @@
import path from 'node:path';
// ---------------------------------------------------------------------------
// Overpass API proxy constants and cache state
// ---------------------------------------------------------------------------
/** Ordered list of Overpass API mirrors; tried sequentially on failure/rate-limit. */
const OVERPASS_UPSTREAMS = [
'https://overpass-api.de/api/interpreter',
'https://overpass.kumi.systems/api/interpreter',
'https://lz4.overpass-api.de/api/interpreter',
// Community full-planet instance (privateforge nonprofit) — added 2026-07-30
// when all three mirrors above refused this IP (likely a dev-traffic rate
// ban; refused connections fail in ms, so healthy mirrors above still win).
// Verified: planet coverage (Texas query), CORS *, ~5-20 s cold latency.
'https://overpass.private.coffee/api/interpreter',
];
/**
* TTL for FRESH cached Overpass responses (ms). Road geometry is static for
* months — the original 45 s TTL forced a public-mirror round-trip on nearly
* every viewport revisit and left nothing to serve when the mirrors 502
* (field-test 2026-07-17: all three mirrors down during US morning peak =
* "traffic takes forever to load"). 24 h in memory; the disk layer below
* keeps 7 days and also survives dev-server restarts.
*/
const OVERPASS_CACHE_MS = 86_400_000;
/** Disk-cache TTL for Overpass responses (ms) — 7 days. */
const OVERPASS_DISK_TTL_MS = 7 * 86_400_000;
/**
* Disk-cache TTL for BOUNDARY-class queries (is_in / admin-relation pivots) — 30
* days. Admin boundaries change ≈never, and their pivots are the most expensive
* queries the app issues (multi-MB coastline geometry, 10–25 s on public mirrors —
* field test 2026-07-23: outline latency + the Sicily miss). Keeping them a month
* means each boundary is fetched roughly once per machine, ever.
*/
const OVERPASS_BOUNDARY_DISK_TTL_MS = 30 * 86_400_000;
/** Disk-cache directory for Overpass responses. */
const OVERPASS_DISK_DIR = path.join(process.cwd(), '.gev-cache', 'overpass');
/** Per-upstream fetch timeout (ms). */
const OVERPASS_TIMEOUT_MS = 22000;
/** Max entries in the Overpass response cache (LRU-like, oldest evicted first). */
const OVERPASS_CACHE_MAX_ENTRIES = 120;
// --- Abuse guards shared by the Overpass + route proxies --------------------
/** Max accepted POST body for the Overpass proxy (Overpass QL queries are tiny). */
const OVERPASS_MAX_BODY_BYTES = 24 * 1024;
// 24 KB
/**
* Hard cap on a single Overpass upstream response we will buffer into memory.
* 32 MB (was 12 MB): a dense island/state admin boundary at full `out geom`
* fidelity — Sicilia's Mediterranean coastline — can exceed 12 MB, and clipping
* it read as a permanent "transient" failure (field test 2026-07-23, Sicily
* never traced). The buffered payload is SIMPLIFIED server-side before it is
* cached or sent (simplifyOverpassPayloadBody), so the raised cap does not
* raise what clients receive or what the disk stores.
*/
const OVERPASS_MAX_RESPONSE_BYTES = 32 * 1024 * 1024;
// 32 MB
/** Only payloads at least this large go through geometry simplification. */
const OVERPASS_SIMPLIFY_MIN_BYTES = 1_500_000;
/** Only per-element geometry arrays with at least this many points are simplified. */
const OVERPASS_SIMPLIFY_MIN_POINTS = 1200;
/**
* Douglas-Peucker tolerance (degrees, ≈44 m of latitude). Region/state boundary
* rings are drawn at regional camera scale and the client simplifies again for
* draw, so ~44 m fidelity is invisible; building footprints never reach the
* point threshold above and pass through untouched.
*/
const OVERPASS_SIMPLIFY_TOLERANCE_DEG = 0.0004;
/** Max concurrent in-flight upstream Overpass fetches across all distinct queries. */
const OVERPASS_MAX_CONCURRENT = 6;
/** Server-side timeout ceiling (seconds) we allow inside an Overpass QL query. */
const OVERPASS_MAX_QL_TIMEOUT = 30;
/** Max `around:` radius (m) — every app caller uses <= 1800 m. */
const OVERPASS_MAX_AROUND_M = 50000;
/** Max bbox span (degrees) — app bboxes are small viewport tiles. */
const OVERPASS_MAX_BBOX_DEG = 12;
/**
* Every Overpass element-type specifier, including the combined shortcuts
* (nwr/nw/nr/wr) and `rel`. Shared by the selector + area-element-deny regexes so
* they can't drift (a missing shortcut like `wr` was an area-scan bypass).
*/
const OVERPASS_ELEMENT_TYPES = 'node|way|relation|nwr|nw|nr|wr|rel';
/** Element-selector (incl. `area`) whose statements must be individually bounded. */
const OVERPASS_SELECTOR_RE = new RegExp(`\\b(?:${OVERPASS_ELEMENT_TYPES}|area)\\b`);
/** An element selector bounded BY an area — the country-scan abuse shape. */
const OVERPASS_AREA_ELEMENT_RE = new RegExp(`\\b(?:${OVERPASS_ELEMENT_TYPES})\\s*\\(\\s*area\\b`, 'i');
/** A single bbox 4-tuple `(s,w,n,e)` (non-global so it does not advance lastIndex). */
const OVERPASS_BBOX_RE = /\(\s*-?\d+(?:\.\d+)?\s*,\s*-?\d+(?:\.\d+)?\s*,\s*-?\d+(?:\.\d+)?\s*,\s*-?\d+(?:\.\d+)?\s*\)/;
export { OVERPASS_BOUNDARY_DISK_TTL_MS, OVERPASS_DISK_TTL_MS, OVERPASS_DISK_DIR, OVERPASS_CACHE_MS, OVERPASS_CACHE_MAX_ENTRIES, OVERPASS_MAX_BODY_BYTES, OVERPASS_MAX_CONCURRENT, OVERPASS_MAX_AROUND_M, OVERPASS_MAX_BBOX_DEG, OVERPASS_AREA_ELEMENT_RE, OVERPASS_SELECTOR_RE, OVERPASS_BBOX_RE, OVERPASS_MAX_QL_TIMEOUT, OVERPASS_SIMPLIFY_MIN_BYTES, OVERPASS_SIMPLIFY_MIN_POINTS, OVERPASS_SIMPLIFY_TOLERANCE_DEG, OVERPASS_MAX_RESPONSE_BYTES, OVERPASS_UPSTREAMS, OVERPASS_TIMEOUT_MS };