fix: separate Google browser and server keys (#110)

Use a separate optional server key for Places and Street View, with consistent local and Pinokio configuration, server-first CLI selection, and credential isolation checks.

Original contribution by Tom-Neverwinter; reviewed and completed by Sameh Khamis.

Co-authored-by: Tom-Neverwinter <[email protected]>
Co-authored-by: Sameh Khamis <[email protected]>
This commit is contained in:
Tom-NeverwinterandSameh Khamis authored and GitHub committed 2026-09-11 15:08:23 -07:00
1 parent e9b6214ec4
commit 205c58efbd
21 files changed
+282 -28

No files matched your search

+23 -4
View File
@@ -25,6 +25,7 @@
* @module vite.config
*/
import { resolveGoogleServerKey } from './scripts/google-server-key.mjs';
import fs from 'node:fs';
import os from 'node:os';
import { promises as fsp } from 'node:fs';
@@ -4563,9 +4564,15 @@ function cctvProxy() {
};
};
/** Fetch a Google Street View static image as a fallback frame. Requires GOOGLE_MAPS_API_KEY. */
/**
* Fetch a Google Street View static image as a fallback frame. Server-side
* call, never reaches the browser — prefers GOOGLE_MAPS_SERVER_API_KEY
* (#33: a key scoped to Street View Static/Places, restricted by server IP
* rather than HTTP referrer) and falls back to the browser-exposed
* GOOGLE_MAPS_API_KEY for setups that haven't split the two yet.
*/
const streetViewFallback = async ({ lat, lon, heading, fov, pitch }) => {
const streetViewKey = process.env.GOOGLE_MAPS_API_KEY;
const streetViewKey = googleServerApiKey();
if (!streetViewKey || !Number.isFinite(lat) || !Number.isFinite(lon)) return null;
try {
const sv = new URL('https://maps.googleapis.com/maps/api/streetview');
@@ -5388,6 +5395,18 @@ export function keylessGooglePlacesResponse(apiKey) {
};
}
/**
* Google API key for the SERVER-SIDE calls (Places nearby/text search, the
* CCTV Street View fallback). These never reach the browser, so this key can
* be restricted by server IP and scoped to Places API + Street View Static
* API — while GOOGLE_MAPS_API_KEY stays referrer-restricted to Map Tiles +
* Geocoding for the browser (#33). Splitting them is opt-in: unset, this
* falls back to the shared browser key and nothing changes.
*/
export function googleServerApiKey() {
return resolveGoogleServerKey(process.env);
}
/**
* Vite plugin: nearby Google place labels for Realtime scene context.
*
@@ -5408,7 +5427,7 @@ export function googlePlacesContextProxy() {
// Keyless place context has no provider cost, so it resolves before the
// paid-endpoint limiter can consume or exhaust quota (mirrors the HUD
// summary route).
const apiKey = process.env.GOOGLE_MAPS_API_KEY;
const apiKey = googleServerApiKey();
const keyless = keylessGooglePlacesResponse(apiKey);
if (keyless) {
res.statusCode = keyless.statusCode;
@@ -5527,7 +5546,7 @@ export function googlePlacesContextProxy() {
// Keyless place context has no provider cost, so it resolves before the
// paid-endpoint limiter can consume or exhaust quota (mirrors the HUD
// summary route).
const apiKey = process.env.GOOGLE_MAPS_API_KEY;
const apiKey = googleServerApiKey();
const keyless = keylessGooglePlacesResponse(apiKey);
if (keyless) {
res.statusCode = keyless.statusCode;