mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
fix(cctv): make the Lithuania pack opt-in until its licence is confirmed
The camera endpoints are the eismoinfo.lt website backend, which the Via Lietuva open-data statement does not list, so the pack now follows the NYC opt-in pattern (CCTV_LITHUANIA_ENABLED=1) and the docs say why. A failed catalog request now aborts its sibling instead of leaving it open until the timeout, and a redirect is logged as not followed.
This commit is contained in:
1 parent
b4d852587a
commit
bcfba77196
9 files changed
+125
-36
No files matched your search
@@ -26,15 +26,18 @@ import {
|
||||
/** Env kill switch: unset or anything but "0" means enabled. */
|
||||
const envEnabled = (name) => String(process.env[name] || '1').trim() !== '0';
|
||||
/** Env opt-in: only "1" enables. For packs whose feed access needs an
|
||||
* operator agreement, so an unset flag never reaches the upstream. */
|
||||
* operator agreement or whose terms are unconfirmed, so an unset flag never
|
||||
* reaches the upstream. */
|
||||
const envOptIn = (name) => String(process.env[name] || '').trim() === '1';
|
||||
|
||||
/**
|
||||
* Live open-data packs, in merge order. Adding a region is one entry here
|
||||
* plus its loader in sources.js; the catalog cap is shared across entries
|
||||
* round-robin (cap.js), so a new pack never silently evicts an older one.
|
||||
* plus its loader (in sources.js or its own module); the catalog cap is
|
||||
* shared across entries round-robin (cap.js), so a new pack never silently
|
||||
* evicts an older one.
|
||||
* Each pack fails independently (allSettled) and is gated by its own env
|
||||
* kill switch, or by an opt-in flag where access needs an agreement.
|
||||
* kill switch, or by an opt-in flag where access needs an agreement or the
|
||||
* terms are unconfirmed.
|
||||
*/
|
||||
const LIVE_PACKS = [
|
||||
{ name: 'austin', enabled: () => true, load: loadAustinSourcesFromOpenData },
|
||||
@@ -111,8 +114,10 @@ const LIVE_PACKS = [
|
||||
load: loadNycSourcesFromOpenData,
|
||||
},
|
||||
{
|
||||
// Opt-in until the Via Lietuva open-data terms are confirmed to cover the
|
||||
// camera photos (see DATA_SOURCES.md).
|
||||
name: 'lithuania',
|
||||
enabled: () => envEnabled('CCTV_LITHUANIA_ENABLED'),
|
||||
enabled: () => envOptIn('CCTV_LITHUANIA_ENABLED'),
|
||||
load: loadLithuaniaSources,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -81,26 +81,45 @@ export function lithuaniaCameraToSource(row, feature) {
|
||||
};
|
||||
}
|
||||
|
||||
async function readCatalog(url) {
|
||||
const signal = AbortSignal.timeout(CCTV_SOURCE_FETCH_TIMEOUT_MS);
|
||||
const response = await fetch(url, {
|
||||
headers: { Accept: 'application/json' },
|
||||
redirect: 'manual',
|
||||
signal,
|
||||
});
|
||||
if (!response.ok) {
|
||||
await response.body?.cancel();
|
||||
throw new Error(`Catalog HTTP ${response.status}`);
|
||||
async function readCatalog(url, sibling) {
|
||||
const signal = AbortSignal.any([
|
||||
AbortSignal.timeout(CCTV_SOURCE_FETCH_TIMEOUT_MS),
|
||||
sibling.signal,
|
||||
]);
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
headers: { Accept: 'application/json' },
|
||||
redirect: 'manual',
|
||||
signal,
|
||||
});
|
||||
if (!response.ok) {
|
||||
await response.body?.cancel().catch(() => {});
|
||||
throw new Error(
|
||||
response.status >= 300 && response.status < 400
|
||||
? 'catalog redirected; redirects are not followed'
|
||||
: `Catalog HTTP ${response.status}`,
|
||||
);
|
||||
}
|
||||
return await readResponseJsonCapped(
|
||||
response,
|
||||
LITHUANIA_MAX_CATALOG_BYTES,
|
||||
signal,
|
||||
);
|
||||
} catch (error) {
|
||||
// The join needs both catalogs; release the other request at once
|
||||
// rather than leaving its body open until the timeout.
|
||||
sibling.abort();
|
||||
throw error;
|
||||
}
|
||||
return readResponseJsonCapped(response, LITHUANIA_MAX_CATALOG_BYTES, signal);
|
||||
}
|
||||
|
||||
/** Two bounded requests per shared catalog refresh, no API key required. */
|
||||
export async function loadLithuaniaSources() {
|
||||
try {
|
||||
const sibling = new AbortController();
|
||||
const [rows, layers] = await Promise.all([
|
||||
readCatalog(LITHUANIA_CAMERAS_URL),
|
||||
readCatalog(LITHUANIA_LOCATIONS_URL),
|
||||
readCatalog(LITHUANIA_CAMERAS_URL, sibling),
|
||||
readCatalog(LITHUANIA_LOCATIONS_URL, sibling),
|
||||
]);
|
||||
if (!Array.isArray(rows) || !Array.isArray(layers)) return [];
|
||||
const features = layers.find((layer) => layer?.layer === 'VKR')?.features;
|
||||
|
||||
Reference in new issue
Block a user