feat(map): add cache-first Cesium gateway and AMD egress
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { once } from "node:events";
|
||||
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
|
||||
import { createServer } from "node:net";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { spawn } from "node:child_process";
|
||||
|
||||
const root = await mkdtemp(join(tmpdir(), "nodedc-map-gateway-credential-smoke-"));
|
||||
const cacheDir = join(root, "cache");
|
||||
const port = await freePort();
|
||||
let child;
|
||||
|
||||
try {
|
||||
await mkdir(join(cacheDir, "ion-endpoints"), { recursive: true });
|
||||
await writeFile(join(cacheDir, "index.json"), `${JSON.stringify({ version: 1, entries: {} })}\n`);
|
||||
await writeFile(join(cacheDir, "ion-endpoints", "1.json"), `${JSON.stringify({
|
||||
assetId: "1",
|
||||
type: "TERRAIN",
|
||||
url: "https://assets.ion.cesium.com/asset/1/",
|
||||
accessToken: "terrain-private-token",
|
||||
attributions: [],
|
||||
savedAt: Date.now(),
|
||||
})}\n`);
|
||||
await writeFile(join(cacheDir, "ion-endpoints", "2.json"), `${JSON.stringify({
|
||||
assetId: "2",
|
||||
type: "IMAGERY",
|
||||
externalType: "BING",
|
||||
options: {
|
||||
url: "https://dev.virtualearth.net/",
|
||||
key: "bing-private-key",
|
||||
mapStyle: "Aerial",
|
||||
},
|
||||
attributions: [],
|
||||
savedAt: Date.now(),
|
||||
})}\n`);
|
||||
const expiredBuildingsToken = fakeJwt("expired-buildings", Math.floor(Date.now() / 1000) - 60);
|
||||
await writeFile(join(cacheDir, "ion-endpoints", "96188.json"), `${JSON.stringify({
|
||||
assetId: "96188",
|
||||
type: "3DTILES",
|
||||
url: "https://assets.ion.cesium.com/us-east-1/asset_depot/96188/OpenStreetMap/CWT/2025-04-01/tileset.json?v=expired",
|
||||
accessToken: expiredBuildingsToken,
|
||||
attributions: [],
|
||||
savedAt: Date.now(),
|
||||
})}\n`);
|
||||
|
||||
child = spawn(process.execPath, ["src/server.mjs"], {
|
||||
cwd: new URL("..", import.meta.url),
|
||||
env: {
|
||||
...process.env,
|
||||
PORT: String(port),
|
||||
MAP_CACHE_DIR: cacheDir,
|
||||
MAP_GATEWAY_ALLOW_ANONYMOUS: "true",
|
||||
MAP_CACHE_MODE: "readwrite",
|
||||
CESIUM_ION_TOKEN: "",
|
||||
CESIUM_ION_ASSET_ALLOWLIST: "1,2,96188",
|
||||
MAP_GATEWAY_UPSTREAM_ALLOWLIST: "assets.ion.cesium.com,dev.virtualearth.net",
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
|
||||
await waitForGateway(port, child);
|
||||
for (const assetId of ["1", "2"]) {
|
||||
const response = await fetch(`http://127.0.0.1:${port}/api/map/ion/assets/${assetId}/endpoint`);
|
||||
assert.equal(response.status, 200);
|
||||
const body = await response.text();
|
||||
assert.equal(body.includes("terrain-private-token"), false);
|
||||
assert.equal(body.includes("bing-private-key"), false);
|
||||
assert.equal(body.includes("accessToken"), false);
|
||||
assert.equal(body.includes('"key"'), false);
|
||||
const endpoint = JSON.parse(body);
|
||||
assert.equal(endpoint.credentialMode, "gateway");
|
||||
}
|
||||
const expired = await fetch(`http://127.0.0.1:${port}/api/map/ion/assets/96188/endpoint`);
|
||||
const expiredRaw = await expired.text();
|
||||
assert.equal(expired.status, 503);
|
||||
assert.equal(expiredRaw.includes(expiredBuildingsToken), false);
|
||||
assert.equal(expiredRaw.includes("accessToken"), false);
|
||||
console.log("ok: endpoint responses contain no credentials and a known-expired Ion JWT is never served");
|
||||
} finally {
|
||||
if (child && !child.killed) {
|
||||
child.kill("SIGTERM");
|
||||
await once(child, "exit").catch(() => undefined);
|
||||
}
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
async function freePort() {
|
||||
const server = createServer();
|
||||
server.listen(0, "127.0.0.1");
|
||||
await once(server, "listening");
|
||||
const address = server.address();
|
||||
assert(address && typeof address === "object");
|
||||
const port = address.port;
|
||||
server.close();
|
||||
await once(server, "close");
|
||||
return port;
|
||||
}
|
||||
|
||||
async function waitForGateway(port, child) {
|
||||
let output = "";
|
||||
child.stderr.on("data", (chunk) => { output += String(chunk); });
|
||||
for (let attempt = 0; attempt < 60; attempt += 1) {
|
||||
if (child.exitCode !== null) throw new Error(`gateway_exited:${child.exitCode}:${output}`);
|
||||
try {
|
||||
const response = await fetch(`http://127.0.0.1:${port}/healthz`);
|
||||
if (response.ok) return;
|
||||
} catch { /* service is still starting */ }
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
}
|
||||
throw new Error(`gateway_start_timeout:${output}`);
|
||||
}
|
||||
|
||||
function fakeJwt(subject, exp) {
|
||||
const encode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
|
||||
return `${encode({ alg: "HS256", typ: "JWT" })}.${encode({ sub: subject, exp })}.test-signature`;
|
||||
}
|
||||
Reference in New Issue
Block a user