mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +00:00
CCTV Street View fallback: catalog cameras only, at their catalog position, inside the Google budget
This commit is contained in:
1 parent
a33bc379cb
commit
7af0453f34
3 files changed
+46
-18
No files matched your search
+17
-16
@@ -19,6 +19,8 @@ import { admitSameSiteKeyless } from './common/same-site.js';
|
|||||||
import { PROXY_SIGNALS } from '../../src/localRequestGate.mjs';
|
import { PROXY_SIGNALS } from '../../src/localRequestGate.mjs';
|
||||||
import { createHlsPuller } from './cctv/stream.js';
|
import { createHlsPuller } from './cctv/stream.js';
|
||||||
import { googleServerApiKey } from './places/google-key.js';
|
import { googleServerApiKey } from './places/google-key.js';
|
||||||
|
import { googleRateLimiter } from './places/google.js';
|
||||||
|
import { clientKey } from './common/rate-limit.js';
|
||||||
export { CCTV_FRAME_FETCH_TIMEOUT_MS, fetchCctvImageFromUpstream };
|
export { CCTV_FRAME_FETCH_TIMEOUT_MS, fetchCctvImageFromUpstream };
|
||||||
/**
|
/**
|
||||||
* Vite plugin: CCTV camera proxy with source registry, frame/media serving,
|
* Vite plugin: CCTV camera proxy with source registry, frame/media serving,
|
||||||
@@ -509,13 +511,6 @@ export function cctvProxy({ sourceRoot = process.cwd() } = {}) {
|
|||||||
const source = sourceById.get(cameraId);
|
const source = sourceById.get(cameraId);
|
||||||
const label = url.searchParams.get('label') || source?.name || cameraId;
|
const label = url.searchParams.get('label') || source?.name || cameraId;
|
||||||
const city = url.searchParams.get('city') || source?.city || '';
|
const city = url.searchParams.get('city') || source?.city || '';
|
||||||
const lat = Number(url.searchParams.get('lat') || source?.lat);
|
|
||||||
const lon = Number(url.searchParams.get('lon') || source?.lon);
|
|
||||||
const heading = Number(
|
|
||||||
url.searchParams.get('heading') || source?.headingDeg,
|
|
||||||
);
|
|
||||||
const fov = Number(url.searchParams.get('fov') || source?.fovDeg);
|
|
||||||
const pitch = Number(url.searchParams.get('pitch') || source?.pitchDeg);
|
|
||||||
|
|
||||||
// Only use server-registered upstream URLs — never accept client-supplied URLs
|
// Only use server-registered upstream URLs — never accept client-supplied URLs
|
||||||
// (prevents SSRF via ?upstream= query parameter)
|
// (prevents SSRF via ?upstream= query parameter)
|
||||||
@@ -549,18 +544,24 @@ export function cctvProxy({ sourceRoot = process.cwd() } = {}) {
|
|||||||
// request (whose client this server cannot see) gets the placeholder,
|
// request (whose client this server cannot see) gets the placeholder,
|
||||||
// as if no key were configured. Any forwarding header counts, even an
|
// as if no key were configured. Any forwarding header counts, even an
|
||||||
// empty one.
|
// empty one.
|
||||||
|
// It is also only for catalog cameras, framed from the catalog (never
|
||||||
|
// a spot the query names), within the per-IP Google budget.
|
||||||
const forwarded = PROXY_SIGNALS.some(
|
const forwarded = PROXY_SIGNALS.some(
|
||||||
(name) => req.headers?.[name] !== undefined,
|
(name) => req.headers?.[name] !== undefined,
|
||||||
);
|
);
|
||||||
const sv = forwarded
|
const googleBudget = googleServerApiKey() ? googleRateLimiter() : null;
|
||||||
? null
|
const sv =
|
||||||
: await streetViewFallback({
|
forwarded ||
|
||||||
lat,
|
!source ||
|
||||||
lon,
|
(googleBudget && !googleBudget(clientKey(req)))
|
||||||
heading,
|
? null
|
||||||
fov,
|
: await streetViewFallback({
|
||||||
pitch,
|
lat: Number(source.lat),
|
||||||
});
|
lon: Number(source.lon),
|
||||||
|
heading: Number(source.headingDeg),
|
||||||
|
fov: Number(source.fovDeg),
|
||||||
|
pitch: Number(source.pitchDeg ?? -10),
|
||||||
|
});
|
||||||
if (sv?.ok) {
|
if (sv?.ok) {
|
||||||
setHealth(cameraId, {
|
setHealth(cameraId, {
|
||||||
status: 'degraded',
|
status: 'degraded',
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ let _googleRateLimiter;
|
|||||||
*/
|
*/
|
||||||
export const GOOGLE_DEFAULT_PER_MIN = 120;
|
export const GOOGLE_DEFAULT_PER_MIN = 120;
|
||||||
|
|
||||||
/** Google cost endpoints (nearby-places, text-search, geocoding). Null only when set to 0. */
|
/** Google cost endpoints (nearby-places, text-search, geocoding, the CCTV Street View fallback). Null only when set to 0. */
|
||||||
function googleRateLimiter() {
|
export function googleRateLimiter() {
|
||||||
if (_googleRateLimiter === undefined)
|
if (_googleRateLimiter === undefined)
|
||||||
_googleRateLimiter = makeCostRateLimiter(
|
_googleRateLimiter = makeCostRateLimiter(
|
||||||
process.env.GEV_RATELIMIT_GOOGLE_PER_MIN,
|
process.env.GEV_RATELIMIT_GOOGLE_PER_MIN,
|
||||||
|
|||||||
@@ -528,3 +528,30 @@ test('a forwarded same-origin request reaches every CCTV route but not the Stree
|
|||||||
assert.equal(direct.body, 'street-view');
|
assert.equal(direct.body, 'street-view');
|
||||||
assert.equal(streetViewCalls(), 1);
|
assert.equal(streetViewCalls(), 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('the Street View fallback frames catalog cameras only, from the catalog position', async (t) => {
|
||||||
|
const app = mount(t, ({ url }) =>
|
||||||
|
url.startsWith('https://maps.googleapis.com/')
|
||||||
|
? trackedUpstream({
|
||||||
|
headers: { 'content-type': 'image/jpeg' },
|
||||||
|
body: 'street-view',
|
||||||
|
}).response
|
||||||
|
: upstreamResponse({ status: 503 }),
|
||||||
|
);
|
||||||
|
process.env.GOOGLE_MAPS_SERVER_API_KEY = 'fixture-key';
|
||||||
|
const streetView = () =>
|
||||||
|
app.requests.filter(({ url }) =>
|
||||||
|
url.startsWith('https://maps.googleapis.com/'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const unknown = await app.call('/frame/not-a-camera?lat=48.85&lon=2.29');
|
||||||
|
assert.equal(unknown.headers['X-CCTV-Source'], 'synthetic');
|
||||||
|
assert.deepEqual(streetView(), [], 'no Google call for an unknown id');
|
||||||
|
|
||||||
|
const steered = await app.call(
|
||||||
|
`/frame/${STILL.id}?lat=48.85&lon=2.29&heading=90`,
|
||||||
|
);
|
||||||
|
assert.equal(steered.headers['X-CCTV-Source'], 'streetview');
|
||||||
|
const location = new URL(streetView()[0].url).searchParams.get('location');
|
||||||
|
assert.equal(location, `${STILL.lat},${STILL.lon}`);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user