Files
gods-eye-view/server/providers/military-installations.js
T
f4fcf267c6 fix(osm): stop using public Overpass by default (#648) (#742)
Traffic roads come from OpenFreeMap vector tiles, or TomTom flow tiles with a
key (Hybrid by default). Military areas use OpenFreeMap polygons with a bundled
worldwide name index; wide views show named points. ALPR reads an hourly
OpenStreetMap extract. Cockpit place context and country, state and US county
outlines use bundled Natural Earth and US Census data. Overpass is used only
when an operator configures their own endpoint.

Also: camera arrivals no longer land below ground, keyless terrain retries on
429, bounded relation geometry for configured Overpass, one shared
OpenStreetMap credit, and separate tile and API transports for embedders.

Co-authored-by: theGaryLarson <[email protected]>
Co-authored-by: Milan Khanal <[email protected]>
2026-09-28 19:07:57 -05:00

247 lines
9.2 KiB
JavaScript

import { makeRateLimiter, clientKey } from './common/rate-limit.js';
import {
MILITARY_INSTALLATION_ELEMENT_CAP,
MILITARY_INSTALLATION_MAX_RESPONSE_BYTES,
MILITARY_INSTALLATION_DISK_TTL_MS,
MILITARY_INSTALLATION_STALE_MS,
} from './military-installations/constants.js';
import {
fetchOverpassPayload,
overpassNotConfigured,
} from './overpass/transport.js';
import { resolveOverpassUpstreams } from './overpass/constants.js';
import {
_militaryInstallationCache,
trimMilitaryInstallationCache,
writeMilitaryInstallationDisk,
resolveMilitaryInstallationTier,
readMilitaryInstallationDisk,
} from './military-installations/cache.js';
import {
validMilitaryInstallationBox,
quantizeMilitaryInstallationBox,
militaryInstallationCacheKey,
militaryInstallationFailureReason,
} from './military-installations/query.js';
import { coalesceProxyRequest } from './common/http.js';
const _militaryInstallationsRateLimiter = makeRateLimiter({
windowMs: 60_000,
max: 90,
globalMax: 300,
});
const _militaryInstallationInFlight = new Map();
function militaryInstallationsProxy() {
async function refresh(box, key) {
const bbox = `${box.south},${box.west},${box.north},${box.east}`;
const ql = `[out:json][timeout:20];(nwr["military"~"^(airfield|naval_base|range|barracks|base)$"](${bbox});nwr["landuse"="military"](${bbox}););out center tags geom ${MILITARY_INSTALLATION_ELEMENT_CAP};`;
const upstream = await fetchOverpassPayload(
`data=${encodeURIComponent(ql)}`,
MILITARY_INSTALLATION_MAX_RESPONSE_BYTES,
);
if (
upstream.status >= 400 ||
upstream.rateLimited ||
upstream.runtimeError
) {
throw Object.assign(
new Error('Mapped installation upstream unavailable'),
{
retryAfterMs: upstream.retryAfterMs,
installationReason: upstream.rateLimited
? 'rate_limited'
: upstream.status === 504
? 'timeout'
: upstream.runtimeError
? 'query_failed'
: 'unavailable',
},
);
}
const parsed = JSON.parse(upstream.body);
const elements = Array.isArray(parsed?.elements)
? parsed.elements.slice(0, MILITARY_INSTALLATION_ELEMENT_CAP)
: [];
const payload = {
elements,
// Honest truncation flag — the client re-asks for its exact viewport so
// off-view features can never starve in-view ones. The cap travels with
// the payload so the client never has to hard-code it.
saturated: elements.length >= MILITARY_INSTALLATION_ELEMENT_CAP,
elementCap: MILITARY_INSTALLATION_ELEMENT_CAP,
retrievedAt: new Date().toISOString(),
status: 'ready',
};
const entry = { payload, cachedAt: Date.now() };
_militaryInstallationCache.set(key, entry);
trimMilitaryInstallationCache();
writeMilitaryInstallationDisk(key, entry);
return payload;
}
function install(middlewares) {
middlewares.use('/api/military-installations', async (req, res) => {
if (req.method !== 'GET') {
res.writeHead(405, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Method Not Allowed' }));
return;
}
const url = new URL(req.url, 'http://localhost');
const requested = validMilitaryInstallationBox(url.searchParams);
if (!requested) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(
JSON.stringify({
error: 'A non-dateline bbox no larger than 10 degrees is required',
}),
);
return;
}
// Query the SNAPPED box, not the raw viewport: neighbouring views then
// share one cache entry, and an outward snap always covers what was asked.
// `exact=1` opts out — the client sends it after a SATURATED snapped
// response, so a truncated tile can never starve the actual viewport. It
// is keyed separately so exact and snapped answers never collide.
const exact = url.searchParams.get('exact') === '1';
const box = exact
? requested
: quantizeMilitaryInstallationBox(requested);
// Key at the precision the query actually uses (see militaryInstallationCacheKey).
const key = exact
? `exact:${militaryInstallationCacheKey(box, 5)}`
: militaryInstallationCacheKey(box);
const now = Date.now();
const cached = _militaryInstallationCache.get(key);
if (!resolveOverpassUpstreams().length) {
const stale =
cached || (await readMilitaryInstallationDisk(key, Infinity));
const disabled = overpassNotConfigured();
res.writeHead(200, {
'Content-Type': 'application/json',
'Cache-Control': 'no-store',
'X-Military-Installations': stale ? 'STALE' : 'DISABLED',
});
res.end(
stale
? JSON.stringify({
...stale.payload,
status: 'stale',
retrievedAt:
stale.payload.retrievedAt ||
new Date(stale.cachedAt).toISOString(),
})
: disabled.body,
);
return;
}
if (!_militaryInstallationsRateLimiter(clientKey(req))) {
res.writeHead(429, {
'Content-Type': 'application/json',
'Retry-After': '5',
});
res.end(JSON.stringify({ error: 'Rate limit exceeded' }));
return;
}
const preflight = await resolveMilitaryInstallationTier({
cacheKey: key,
memoryCache: _militaryInstallationCache,
inFlight: _militaryInstallationInFlight,
readDisk: () =>
readMilitaryInstallationDisk(key, MILITARY_INSTALLATION_DISK_TTL_MS),
now,
});
if (preflight.source !== 'UPSTREAM') {
if (preflight.source === 'DISK') {
_militaryInstallationCache.set(key, preflight.entry);
trimMilitaryInstallationCache();
}
res.writeHead(200, {
'Content-Type': 'application/json',
'Cache-Control': 'public, max-age=60',
'X-Military-Installations': preflight.source,
});
res.end(
JSON.stringify({ ...preflight.entry.payload, status: 'cached' }),
);
return;
}
const request = coalesceProxyRequest(
_militaryInstallationInFlight,
key,
() => refresh(box, key),
);
try {
const payload = await request.promise;
res.writeHead(200, {
'Content-Type': 'application/json',
'Cache-Control': 'public, max-age=60',
'X-Military-Installations': request.shared ? 'INFLIGHT' : 'MISS',
});
res.end(JSON.stringify(payload));
} catch (error) {
if (cached && now - cached.cachedAt <= MILITARY_INSTALLATION_STALE_MS) {
res.writeHead(200, {
'Content-Type': 'application/json',
'Cache-Control': 'no-store',
'X-Military-Installations': 'STALE',
});
res.end(JSON.stringify({ ...cached.payload, status: 'stale' }));
return;
}
// Overpass is down: last-good mapped context at ANY age beats an empty
// layer (the same serve-stale rule the Overpass proxy applies).
const stale = await readMilitaryInstallationDisk(key, Infinity);
if (stale) {
res.writeHead(200, {
'Content-Type': 'application/json',
'Cache-Control': 'no-store',
'X-Military-Installations': 'STALE-DISK',
});
res.end(JSON.stringify({ ...stale.payload, status: 'stale' }));
return;
}
res.writeHead(503, {
...(error?.retryAfterMs
? { 'Retry-After': String(Math.ceil(error.retryAfterMs / 1000)) }
: {}),
'Content-Type': 'application/json',
'Cache-Control': 'no-store',
});
res.end(
JSON.stringify({
error: 'Mapped installation context is temporarily unavailable',
reason: militaryInstallationFailureReason(error),
retryAfterMs: error?.retryAfterMs,
}),
);
}
});
}
return {
name: 'military-installations-proxy',
configureServer(server) {
install(server.middlewares);
},
configurePreviewServer(server) {
install(server.middlewares);
},
};
}
export { militaryInstallationsProxy };
export { MILITARY_INSTALLATION_ELEMENT_CAP } from './military-installations/constants.js';
export { quantizeMilitaryInstallationBox } from './military-installations/query.js';
export { militaryInstallationCacheKey } from './military-installations/query.js';
export { resolveMilitaryInstallationTier } from './military-installations/cache.js';
export { migrateMilitaryInstallationEntry } from './military-installations/cache.js';
export { militaryInstallationDiskFresh } from './military-installations/cache.js';
export { militaryInstallationDiskPath } from './military-installations/cache.js';
export { readMilitaryInstallationDisk } from './military-installations/cache.js';
export { writeMilitaryInstallationDisk } from './military-installations/cache.js';
export { validMilitaryInstallationBox } from './military-installations/query.js';
export { militaryInstallationFailureReason } from './military-installations/query.js';