mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
Google Street View registers beside Mapillary as provider `google`. It runs on the Maps JavaScript API with the browser GOOGLE_MAPS_API_KEY, loaded on first use (mapsLoader.js), and uses Google's own StreetViewPanorama as its viewer adapter, emitting provider-neutral poses (address and month in the caption, a link to Google Maps, steps along Google's arrows, FOLLOW on Google 3D). Its chip starts off because every panorama is billed to the key; the share option is `g`. Google offers no coverage layer for other maps, so the provider draws none: with it on, a click on bare ground at street zoom opens its nearest Google-collected outdoor panorama (groundClick.js). A key Google refuses reads KEY REJECTED and fails a pending open at once; a lookup Google never answers fails after 10 s. Core: providers may declare `groundClick` and `defaultOn`; poses may carry `capturedAtPrecision` and `title`; the coverage line keeps every provider's hint; opening one provider's image releases another's prewarmed viewer; FIT/FILL hides for viewers without render modes. CSP script-src admits https://maps.googleapis.com. The browser gate gains two Street View steps (the keyed one skips as 'no Google key' in CI). Co-Authored-By: Claude Opus 5.5 <[email protected]>
119 lines
5.1 KiB
JavaScript
119 lines
5.1 KiB
JavaScript
import { applicationHtmlPlugin } from './application-html.js';
|
|
import cesium from 'vite-plugin-cesium';
|
|
import { DEFAULT_ALLOWED_HOSTS, hostCheckPlugin } from './allowedHosts.js';
|
|
import { embedFramingPlugin } from './embed-framing.js';
|
|
import { panelBuildPlugin } from './panel.js';
|
|
|
|
/**
|
|
* Content-Security-Policy for every document the dev/preview server serves.
|
|
* No inline script is permitted, and the only foreign script origins are the
|
|
* three that the Bhote Koshi event's embedded media needs (YouTube, Facebook
|
|
* and X), which frame-src matches for their players and posts. 'unsafe-eval' is
|
|
* required: Knockout (bundled inside @cesium/widgets) resolves the global
|
|
* object with `(0, eval)("this")` at module load, and without it the Cesium
|
|
* widget never initializes (verified in headless Chrome). It also covers
|
|
* Cesium's WASM decoders. `blob:` is required in the built app: Cesium's
|
|
* bundled workers bootstrap through `importScripts(blob:...)`, which a worker
|
|
* checks against script-src (the dev server loads them by URL instead).
|
|
* Widen any other directive only for a real violation.
|
|
*/
|
|
export const BROWSER_CSP = [
|
|
"default-src 'self'",
|
|
// The Bhote Koshi event's embedded media: the YouTube player API, the
|
|
// Facebook video SDK and X's post widget (src/data/bhoteKoshiEmbeddedMedia.js);
|
|
// and the Maps JavaScript API that Street Level's Google Street View runs on.
|
|
"script-src 'self' 'unsafe-eval' blob: https://www.youtube.com https://connect.facebook.net https://platform.twitter.com https://maps.googleapis.com",
|
|
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
|
|
"font-src 'self' data: https://fonts.gstatic.com",
|
|
"img-src 'self' data: blob: https:",
|
|
"media-src 'self' blob: https:",
|
|
"connect-src 'self' blob: data: https: wss: ws:",
|
|
"worker-src 'self' blob:",
|
|
"child-src 'self' blob:",
|
|
// The same media's players and posts, which load in frames.
|
|
"frame-src 'self' blob: https://www.youtube-nocookie.com https://www.youtube.com https://www.facebook.com https://platform.twitter.com",
|
|
"manifest-src 'self'",
|
|
"object-src 'none'",
|
|
"base-uri 'self'",
|
|
"form-action 'self'",
|
|
"frame-ancestors 'none'",
|
|
].join('; ');
|
|
|
|
/** Response headers shared by the dev and preview servers. */
|
|
export const BROWSER_HEADERS = Object.freeze({
|
|
'X-Frame-Options': 'DENY',
|
|
'Content-Security-Policy': BROWSER_CSP,
|
|
// Responses are only ever used as the type the server declares.
|
|
'X-Content-Type-Options': 'nosniff',
|
|
});
|
|
|
|
/** Build browser assets with explicit inputs; never load environment or providers. */
|
|
export function createBrowserViteConfig({
|
|
plugins = [],
|
|
publicDir,
|
|
googleApiKey,
|
|
cesiumToken,
|
|
mapillaryToken,
|
|
host = 'localhost',
|
|
port = 4173,
|
|
allowedHosts = DEFAULT_ALLOWED_HOSTS,
|
|
command,
|
|
} = {}) {
|
|
return {
|
|
plugins: [
|
|
// First, so no provider route answers a Host the server does not allow.
|
|
hostCheckPlugin(),
|
|
cesium(),
|
|
applicationHtmlPlugin(),
|
|
...plugins,
|
|
embedFramingPlugin(),
|
|
panelBuildPlugin(),
|
|
],
|
|
...(publicDir === undefined ? {} : { publicDir }),
|
|
// A production build must not clean the dependency cache a running dev
|
|
// server is still serving optimized module URLs from.
|
|
...(command === 'build' ? { cacheDir: 'node_modules/.vite-build' } : {}),
|
|
optimizeDeps: {
|
|
// First reached through the SDR worker or a dynamic import. Pre-bundle
|
|
// them at startup so first use cannot invalidate already-transformed
|
|
// URLs with Vite's "Outdated Optimize Dep" 504 response.
|
|
include: [
|
|
'@jtarrio/signals/demod/demodulator.js',
|
|
'@jtarrio/signals/demod/modes.js',
|
|
'@jtarrio/webrtlsdr/rtlsdr.js',
|
|
'egm96-universal',
|
|
// Loaded on demand when the first street-level image opens.
|
|
'mapillary-js',
|
|
],
|
|
},
|
|
server: {
|
|
host: host || 'localhost',
|
|
port: parseInt(port, 10) || 4173,
|
|
// A wildcard bind only chooses interfaces; it never disables Vite's
|
|
// Host-header check. LAN hostnames are added explicitly (see
|
|
// build/allowedHosts.js); IP addresses are always accepted.
|
|
allowedHosts: [...allowedHosts],
|
|
fs: {
|
|
deny: ['.env', '.env.*', '*.{crt,pem}', '**/.git/**', '**/ENVIRONMENT'],
|
|
},
|
|
// These headers protect the document containing Provider Settings and
|
|
// give the whole page a real Content-Security-Policy (BROWSER_CSP).
|
|
// Embed-mode documents are framable instead: embed-framing.js rewrites
|
|
// only the frame-ancestors directive and keeps the rest of the policy.
|
|
headers: BROWSER_HEADERS,
|
|
},
|
|
// The preview server serves the same documents, so it carries the same
|
|
// framing + CSP hardening (one constant, no drift).
|
|
preview: { headers: BROWSER_HEADERS },
|
|
define: {
|
|
'import.meta.env.GOOGLE_MAPS_API_KEY': JSON.stringify(googleApiKey),
|
|
'import.meta.env.CESIUM_ION_TOKEN': JSON.stringify(cesiumToken),
|
|
// Mapillary client tokens are public by design (see SECURITY.md).
|
|
'import.meta.env.MAPILLARY_CLIENT_TOKEN': JSON.stringify(
|
|
mapillaryToken ?? '',
|
|
),
|
|
},
|
|
build: { chunkSizeWarningLimit: 1500 },
|
|
};
|
|
}
|