fix(military): retire unavailable history and keep local trails

This commit is contained in:
Said committed 2026-10-20 00:43:47 +01:00
1 parent de2138e84c
commit 5f3d1eaa2f
6 files changed
+40 -34

No files matched your search

+2
View File
@@ -1,5 +1,7 @@
# God's Eye View Current State
The standalone military source no longer requests the unavailable, undocumented ADSB.lol trace endpoint. Tracking retains its locally accumulated trail, and custom sources may still supply optional getTrack history. The old HTTP route validates the identifier then reports 503 history_unavailable without any upstream request, rather than forwarding a misleading 403. This does not restore historical coverage or download bulk daily archives.
Shared distance checks use `src/geoDistance.js` with `{ lat, lon }` points and
the IUGG mean spherical Earth radius, 6371.0088 km. `greatCircleKm` is
canonical; `greatCircleMeters` is its exact metre conversion. Missing,
+13 -8
View File
@@ -7,10 +7,9 @@ import { readCappedResponseText } from '../common/http.js';
* own credit bucket, 4 credits per call on the free tier). OAuth via the
* shared coalesced token. 60s per-icao cache; 404/429 forwarded so the
* client can fall back to its accumulated trail silently.
* /api/military/track?hex=<hex> — adsb.lol tar1090 readsb trace
* (undocumented but live; no browser CORS, hence this proxy). Up to ~24h
* of real history per aircraft. Treat as best-effort; data is ODbL —
* credit "adsb.lol (ODbL)" in the UI.
* /api/military/track?hex=<hex> — compatibility refusal for the retired,
* undocumented ADSB.lol trace route. The standalone military source uses
* locally accumulated trails; provider-supplied history remains optional.
*/
export function trackBackfillProxies() {
const TRACK_CACHE_MS = 60000;
@@ -107,10 +106,16 @@ export function trackBackfillProxies() {
);
return;
}
await proxyJson(
res,
`lol:${hex}`,
`https://adsb.lol/data/traces/${hex.slice(-2)}/trace_full_${hex}.json`,
res.statusCode = 503;
res.setHeader('Content-Type', 'application/json');
res.setHeader('Cache-Control', 'no-store');
res.setHeader('Retry-After', '1800');
res.end(
JSON.stringify({
error:
'Military history unavailable; locally accumulated trails remain available',
code: 'history_unavailable',
}),
);
} catch (error) {
res.statusCode = 502;
+1 -1
View File
@@ -593,7 +593,7 @@ export function createTracking({
}
/**
* Fire-and-forget adsb.lol readsb trace backfill (PRD F2). Trace points are
* Fire-and-forget optional source history backfill (PRD F2). Trace points are
* stride-thinned to the remaining vertex budget, then spliced strictly older
* than the oldest seeded fix AHEAD of the locally accumulated fine segment,
* capped at TRAIL_MAX_POINTS (newest kept). Any failure (404/timeout/
+4 -5
View File
@@ -214,11 +214,10 @@ test('construction is inert; adapters preserve routes, viewport query, cache epo
assert.equal(mil.observedAtMs, now - 60000);
assert.equal(mil.records[0].positionTimeMs, now - 75000);
assert.equal(mil.stale, true);
const track = await military.getTrack('abc123');
assert.equal(track.complete, false);
assert.equal(track.records[0].observedAtMs, now - 50000);
assert.equal(track.records[0].baroAltitudeM, 304.8);
assert.equal(track.records[0].ellipsoidAltitudeM, null);
assert.equal(military.getTrack, undefined);
assert.ok(
requests.every(({ url }) => !url.startsWith('/api/military/track')),
);
});
test('cancellation after slow body parsing rejects even with a transport that ignores abort', async () => {
+2 -20
View File
@@ -159,26 +159,8 @@ export function createMilitarySource({
status: response.status,
};
},
async getTrack(reference, { signal } = {}) {
const { response, payload } = await readResponse(
fetchImpl,
'/api/military/track?hex=' + encodeURIComponent(reference),
{ signal },
'Military aircraft',
);
if (!response.ok) throw httpError(response, 'Military aircraft');
const baseTimeMs = epoch(payload?.timestamp, 1000);
return {
records:
baseTimeMs == null
? []
: normalizeAircraftTrack(payload?.trace, {
baseTimeMs,
readsb: true,
}),
complete: false,
};
},
// ADSB.lol does not expose a supported live-history endpoint. Omitting
// optional getTrack keeps the tracking layer on its accumulated trail.
};
}
+18
View File
@@ -583,3 +583,21 @@ test('track backfill proxy returns 502 on an oversized upstream body and caches
assert.equal(res2.statusCode, 502);
assert.equal(callCount, 1);
});
test('retired military history refuses without upstream I/O while validating identifiers', async (t) => {
t.mock.method(globalThis, 'fetch', async () => {
assert.fail('retired history must not contact the upstream');
});
const route = install(providers.trackBackfillProxies());
for (const hex of ['abc123', 'abc456']) {
const response = await route('/api/military/track', '?hex=' + hex);
assert.equal(response.statusCode, 503);
assert.equal(JSON.parse(response.body).code, 'history_unavailable');
assert.equal(response.headers['retry-after'], '1800');
assert.equal(response.headers['cache-control'], 'no-store');
}
assert.equal(
(await route('/api/military/track', '?hex=invalid')).statusCode,
400,
);
});