Files
gods-eye-view/server/providers/overpass/constants.js
T
Bilawal SidhuandCyber Knight fb5ee5aa36 fix(installations): send an identifying User-Agent to Overpass (#570)
Supersedes #420 by @GladiatorrX9, ported onto the current component layout with fixes from review.

Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.

Co-authored-by: Cyber Knight <[email protected]>
2026-09-14 21:11:32 -05:00

149 lines
6.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import path from 'node:path';
// ---------------------------------------------------------------------------
// Overpass API proxy constants and cache state
// ---------------------------------------------------------------------------
/**
* User-Agent sent to every Overpass mirror.
*
* The OSM API usage policy asks for a "Valid User-Agent identifying application
* and version"; a generic proxy label is not one. A mirror is free to refuse a
* client it cannot identify, and `src/overpassProxy.test.mjs` pins what that
* costs: a refusal is never data, so the query falls through to whatever
* mirrors are left. Keep this honest and stable — if it is ever refused, the
* answer is less query volume, not a new name.
*/
const OVERPASS_USER_AGENT =
'gods-eye-view/0.1 (+https://github.com/bilawalsidhu/gods-eye-view)';
/** 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_USER_AGENT,
OVERPASS_TIMEOUT_MS,
};