fix: render live V2X vehicles in Cesium preview

This commit is contained in:
2026-08-25 12:57:35 +08:00
parent bc845444bb
commit 25cf82e7c7
23 changed files with 198 additions and 58 deletions

View File

@@ -34,11 +34,15 @@ function parseArgs(argv) {
return values;
}
function createV2xPreviewServer({ root, upstream }) {
function createV2xPreviewServer({ root, upstream, wsUpstream }) {
const staticRoot = path.resolve(root);
const upstreamUrl = new URL(upstream);
// Deployments front the API and the WebSocket with different prefixes (e.g.
// /dashboardApi and /dashboardWebsocket), so the two targets are separate.
const wsUpstreamUrl = wsUpstream ? new URL(wsUpstream) : upstreamUrl;
if (!fs.statSync(staticRoot).isDirectory()) throw new Error(`Preview root is not a directory: ${staticRoot}`);
if (!/^https?:$/.test(upstreamUrl.protocol)) throw new Error("V2X upstream must use http or https");
if (!/^https?:$/.test(wsUpstreamUrl.protocol)) throw new Error("V2X websocket upstream must use http or https");
const server = http.createServer((request, response) => {
if (isProxyPath(request.url)) {
@@ -53,7 +57,7 @@ function createV2xPreviewServer({ root, upstream }) {
socket.destroy();
return;
}
proxyWebSocket(request, socket, head, upstreamUrl);
proxyWebSocket(request, socket, head, wsUpstreamUrl);
});
return server;
}
@@ -64,10 +68,15 @@ function isProxyPath(url) {
pathname === "/websocket" || pathname.startsWith("/websocket/");
}
function upstreamPath(url) {
// The browser calls /api/... and /websocket/...; the local prefix is dropped and
// whatever base path the upstream URL carries is prepended, so an upstream of
// http://host:7862/dashboardApi maps /api/facilities/... onto
// /dashboardApi/facilities/... instead of the bare backend path.
function upstreamPath(url, basePath) {
const parsed = new URL(url, "http://preview.local");
const pathname = parsed.pathname.replace(/^\/(api|websocket)(?=\/|$)/, "") || "/";
return `${pathname}${parsed.search}`;
const base = String(basePath || "").replace(/\/+$/, "");
return `${base}${pathname}${parsed.search}`;
}
function upstreamRequestOptions(request, upstreamUrl) {
@@ -76,7 +85,7 @@ function upstreamRequestOptions(request, upstreamUrl) {
hostname: upstreamUrl.hostname,
port: upstreamUrl.port || undefined,
method: request.method,
path: upstreamPath(request.url),
path: upstreamPath(request.url, upstreamUrl.pathname),
headers: { ...request.headers, host: upstreamUrl.host },
};
}
@@ -155,12 +164,13 @@ if (require.main === module) {
const args = parseArgs(process.argv.slice(2));
const root = args.root || process.cwd();
const upstream = args.upstream || process.env.V2X_UPSTREAM;
const wsUpstream = args.wsUpstream || process.env.V2X_WS_UPSTREAM;
const port = Number(args.port || process.env.PORT || 7862);
const host = args.host || process.env.HOST || "0.0.0.0";
if (!upstream) throw new Error("Set V2X_UPSTREAM or pass --upstream http://host:port");
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error("Port must be an integer between 1 and 65535");
const server = createV2xPreviewServer({ root, upstream });
server.listen(port, host, () => console.log(`V2X preview server: http://${host}:${port} -> ${upstream}`));
const server = createV2xPreviewServer({ root, upstream, wsUpstream });
server.listen(port, host, () => console.log(`V2X preview server: http://${host}:${port} -> ${upstream} (ws -> ${wsUpstream || upstream})`));
}
module.exports = { createV2xPreviewServer, isProxyPath, upstreamPath };