mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
Let the app run under a panel page's document base
A panel page loads the app with <base> pointing at the app's server, so addresses the app builds resolve against the document base instead of the page location, and hash updates stay on the page's own address. The dev server answers panel origins with CORS on the API routes too.
This commit is contained in:
1 parent
63341b947e
commit
d9ecd126c8
10 files changed
+96
-11
No files matched your search
@@ -5,6 +5,9 @@ import { createAisStreamSource } from '../sources/live/standalone.js';
|
||||
const aisLiveVesselsLayer = createApplicationVessels({
|
||||
source: createAisStreamSource({
|
||||
apiUrl: import.meta.env?.VITE_AIS_LIVE_API_URL || '/api/ais-live',
|
||||
// The app's server, also when a panel page loads the app under <base>.
|
||||
origin: () =>
|
||||
new URL(globalThis.document?.baseURI ?? 'http://localhost').origin,
|
||||
}),
|
||||
options: {
|
||||
maxRows: import.meta.env?.VITE_AIS_LIVE_MAX_ROWS,
|
||||
|
||||
+5
-1
@@ -318,7 +318,11 @@ export async function initKeySetup({
|
||||
globalThis.location?.hash?.slice(1) || '',
|
||||
);
|
||||
if (next !== null)
|
||||
globalThis.history?.replaceState?.(null, '', `#${next}`);
|
||||
globalThis.history?.replaceState?.(
|
||||
null,
|
||||
'',
|
||||
new URL(`#${next}`, globalThis.location.href).href,
|
||||
);
|
||||
} catch {
|
||||
// Continuity is a nicety, never a blocker.
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ function uiBlock(start, end) {
|
||||
/** A ShareLinkManager over a synthetic hash — enough surface for parseInitialHash. */
|
||||
function managerForHash(hash) {
|
||||
globalThis.window = { location: { hash, href: `http://localhost/${hash}` } };
|
||||
globalThis.history = { replaceState(_s, _t, next) { window.location.hash = next; } };
|
||||
globalThis.history = { replaceState(_s, _t, next) { window.location.hash = new URL(next, window.location.href).hash; } };
|
||||
const viewer = {
|
||||
camera: {
|
||||
changed: { addEventListener() {} },
|
||||
|
||||
@@ -34,8 +34,8 @@ function assertClaimsBefore(block, mutation, label) {
|
||||
function makeManager(hash = '') {
|
||||
globalThis.window = { location: { hash, href: `http://localhost/${hash}` } };
|
||||
globalThis.history = {
|
||||
replaceState(_state, _title, nextHash) {
|
||||
window.location.hash = nextHash;
|
||||
replaceState(_state, _title, next) {
|
||||
window.location.hash = new URL(next, window.location.href).hash;
|
||||
},
|
||||
};
|
||||
const viewer = {
|
||||
|
||||
+7
-1
@@ -529,7 +529,13 @@ export class ShareLinkManager {
|
||||
if (this._destroyed || this._initialRestorePending) return;
|
||||
const params = this._buildHashParams();
|
||||
if (!params) return;
|
||||
history.replaceState(null, '', `#${params.toString()}`);
|
||||
// The hash belongs to this page's address, not the document base the
|
||||
// app may load under.
|
||||
history.replaceState(
|
||||
null,
|
||||
'',
|
||||
new URL(`#${params.toString()}`, window.location.href).href,
|
||||
);
|
||||
}
|
||||
|
||||
/** Build a deterministic snapshot without mutating history. */
|
||||
|
||||
@@ -29,6 +29,9 @@ export function createStandaloneLayerSources() {
|
||||
military: createAdsbLolSource(),
|
||||
vessels: createAisStreamSource({
|
||||
apiUrl: import.meta.env?.VITE_AIS_LIVE_API_URL || '/api/ais-live',
|
||||
// The app's server, also when a panel page loads the app under <base>.
|
||||
origin: () =>
|
||||
new URL(globalThis.document?.baseURI ?? 'http://localhost').origin,
|
||||
}),
|
||||
cctv: createCctvSource(),
|
||||
radio: createRadioSource(),
|
||||
|
||||
@@ -16,7 +16,7 @@ export function loadToolCatalog() {
|
||||
tools: coreTools,
|
||||
services: createToolServices({
|
||||
fetchImpl: (...args) => globalThis.fetch(...args),
|
||||
appUrl: globalThis.location.origin,
|
||||
appUrl: new URL(globalThis.document.baseURI).origin,
|
||||
}),
|
||||
}),
|
||||
'voice',
|
||||
|
||||
@@ -8,7 +8,7 @@ export function createStandaloneTools(options) {
|
||||
sceneDataPacks: {
|
||||
sources: {
|
||||
assets: createAssetDirectorySource({
|
||||
baseUrl: new URL('/scene-assets/', window.location.href).href,
|
||||
baseUrl: new URL('/scene-assets/', document.baseURI).href,
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -12,7 +12,8 @@ test('explicit build inputs preserve browser-only defines, plugin order and loop
|
||||
googleApiKey: 'browser-fixture',
|
||||
cesiumToken: 'ion-fixture',
|
||||
});
|
||||
assert.equal(config.plugins[2], plugin);
|
||||
assert.equal(config.plugins[0].name, 'panel-cors');
|
||||
assert.equal(config.plugins[3], plugin);
|
||||
assert.equal(config.server.host, 'localhost');
|
||||
assert.equal(config.server.port, 4173);
|
||||
assert.deepEqual(config.server.allowedHosts, [
|
||||
@@ -42,6 +43,40 @@ test('explicit build inputs preserve browser-only defines, plugin order and loop
|
||||
);
|
||||
});
|
||||
|
||||
test('panel origins may read every dev server path, other sites may not', () => {
|
||||
let middleware;
|
||||
createBrowserViteConfig().plugins[0].configureServer({
|
||||
middlewares: { use: (fn) => (middleware = fn) },
|
||||
});
|
||||
const request = (origin, method = 'GET') => {
|
||||
const headers = {};
|
||||
let ended = false;
|
||||
let passed = false;
|
||||
middleware(
|
||||
{ method, headers: origin ? { origin } : {} },
|
||||
{
|
||||
setHeader: (name, value) => (headers[name] = value),
|
||||
end: () => (ended = true),
|
||||
},
|
||||
() => (passed = true),
|
||||
);
|
||||
return { headers, ended, passed };
|
||||
};
|
||||
const panel = request('https://abc123.claudemcpcontent.com');
|
||||
assert.equal(
|
||||
panel.headers['Access-Control-Allow-Origin'],
|
||||
'https://abc123.claudemcpcontent.com',
|
||||
);
|
||||
assert.equal(panel.passed, true);
|
||||
assert.equal(
|
||||
request('https://abc123.claudemcpcontent.com', 'OPTIONS').ended,
|
||||
true,
|
||||
);
|
||||
const other = request('https://example.com');
|
||||
assert.deepEqual(other.headers, {});
|
||||
assert.equal(other.passed, true);
|
||||
});
|
||||
|
||||
test('build helper does not discover environment values or construct local providers', () => {
|
||||
const before = process.env.GOOGLE_MAPS_API_KEY;
|
||||
process.env.GOOGLE_MAPS_API_KEY = 'environment-fixture';
|
||||
@@ -52,7 +87,7 @@ test('build helper does not discover environment values or construct local provi
|
||||
undefined,
|
||||
);
|
||||
assert.deepEqual(
|
||||
config.plugins.slice(2).map((plugin) => plugin.name),
|
||||
config.plugins.slice(3).map((plugin) => plugin.name),
|
||||
['embed-framing'],
|
||||
);
|
||||
} finally {
|
||||
@@ -66,7 +101,7 @@ test('root config retains existing named exports and standalone provider order',
|
||||
assert.equal(compatibility[name], value, name);
|
||||
const config = standaloneConfig({ mode: 'test' });
|
||||
assert.deepEqual(
|
||||
config.plugins.slice(2, -3).map((plugin) => plugin.name),
|
||||
config.plugins.slice(3, -3).map((plugin) => plugin.name),
|
||||
providers.localProviderPlugins().map((plugin) => plugin.name),
|
||||
);
|
||||
assert.equal(config.plugins.at(-4).name, 'gev-key-setup');
|
||||
|
||||
Reference in new issue
Block a user