feat(visualizer): add layers + fast timeframe switching

This commit is contained in:
u1
2026-02-01 21:17:28 +01:00
parent fc92392705
commit 89415f6793
17 changed files with 2758 additions and 127 deletions

View File

@@ -61,7 +61,18 @@ function readProxyBasicAuth(): BasicAuth | undefined {
const apiReadToken = readApiReadToken();
const proxyBasicAuth = readProxyBasicAuth();
const apiProxyTarget = process.env.API_PROXY_TARGET || 'http://localhost:8787';
const apiProxyTarget =
process.env.API_PROXY_TARGET ||
process.env.VISUALIZER_PROXY_TARGET ||
process.env.TRADE_UI_URL ||
process.env.TRADE_VPS_URL ||
'https://trade.mpabi.pl';
function isLocalHost(hostname: string | undefined): boolean {
const h = String(hostname || '').trim().toLowerCase();
if (!h) return false;
return h === 'localhost' || h === '127.0.0.1' || h === '0.0.0.0';
}
function parseUrl(v: string): URL | undefined {
try {
@@ -71,9 +82,17 @@ function parseUrl(v: string): URL | undefined {
}
}
function toOrigin(u: URL | undefined): string | undefined {
if (!u) return undefined;
return `${u.protocol}//${u.host}`;
}
const apiProxyTargetUrl = parseUrl(apiProxyTarget);
const apiProxyOrigin = toOrigin(apiProxyTargetUrl);
const apiProxyTargetPath = stripTrailingSlashes(apiProxyTargetUrl?.pathname || '/');
const apiProxyTargetEndsWithApi = apiProxyTargetPath.endsWith('/api');
const apiProxyIsLocal = isLocalHost(apiProxyTargetUrl?.hostname);
const apiProxyForceBearer = process.env.API_PROXY_FORCE_BEARER === '1' || process.env.API_PROXY_USE_READ_TOKEN === '1';
function inferUiProxyTarget(apiTarget: string): string | undefined {
try {
@@ -97,7 +116,12 @@ const uiProxyTarget =
process.env.AUTH_PROXY_TARGET ||
inferUiProxyTarget(apiProxyTarget) ||
(apiProxyTargetUrl && apiProxyTargetPath === '/' ? stripTrailingSlashes(apiProxyTargetUrl.toString()) : undefined);
const uiProxyOrigin = toOrigin(parseUrl(uiProxyTarget || ''));
const graphqlProxyTarget = process.env.GRAPHQL_PROXY_TARGET || process.env.HASURA_PROXY_TARGET || uiProxyTarget;
const graphqlProxyOrigin = toOrigin(parseUrl(graphqlProxyTarget || ''));
const graphqlProxyBasicAuthEnabled =
process.env.GRAPHQL_PROXY_BASIC_AUTH === '1' || process.env.HASURA_PROXY_BASIC_AUTH === '1';
function applyProxyBasicAuth(proxyReq: any) {
if (!proxyBasicAuth) return false;
const b64 = Buffer.from(`${proxyBasicAuth.username}:${proxyBasicAuth.password}`, 'utf8').toString('base64');
@@ -105,6 +129,12 @@ function applyProxyBasicAuth(proxyReq: any) {
return true;
}
function applyProxyOrigin(proxyReq: any, origin: string | undefined) {
if (!origin) return;
// Some upstreams (notably WS endpoints) validate Origin and may drop the connection when it doesn't match.
proxyReq.setHeader('Origin', origin);
}
function rewriteSetCookieForLocalDevHttp(proxyRes: any) {
const v = proxyRes?.headers?.['set-cookie'];
if (!v) return;
@@ -124,13 +154,37 @@ const proxy: Record<string, any> = {
rewrite: (p: string) => (apiProxyTargetEndsWithApi ? p.replace(/^\/api/, '') : p),
configure: (p: any) => {
p.on('proxyReq', (proxyReq: any) => {
applyProxyOrigin(proxyReq, apiProxyOrigin);
if (applyProxyBasicAuth(proxyReq)) return;
if (apiReadToken) proxyReq.setHeader('Authorization', `Bearer ${apiReadToken}`);
if ((apiProxyIsLocal || apiProxyForceBearer) && apiReadToken) proxyReq.setHeader('Authorization', `Bearer ${apiReadToken}`);
});
p.on('proxyReqWs', (proxyReq: any) => {
applyProxyOrigin(proxyReq, apiProxyOrigin);
applyProxyBasicAuth(proxyReq);
});
},
},
};
if (graphqlProxyTarget) {
for (const prefix of ['/graphql', '/graphql-ws']) {
proxy[prefix] = {
target: graphqlProxyTarget,
changeOrigin: true,
ws: true,
configure: (p: any) => {
p.on('proxyReq', (proxyReq: any) => {
applyProxyOrigin(proxyReq, graphqlProxyOrigin);
if (graphqlProxyBasicAuthEnabled) applyProxyBasicAuth(proxyReq);
});
p.on('proxyReqWs', (proxyReq: any) => {
applyProxyOrigin(proxyReq, graphqlProxyOrigin);
if (graphqlProxyBasicAuthEnabled) applyProxyBasicAuth(proxyReq);
});
},
};
}
}
if (uiProxyTarget) {
for (const prefix of ['/whoami', '/auth', '/logout']) {
proxy[prefix] = {
@@ -138,6 +192,7 @@ if (uiProxyTarget) {
changeOrigin: true,
configure: (p: any) => {
p.on('proxyReq', (proxyReq: any) => {
applyProxyOrigin(proxyReq, uiProxyOrigin);
applyProxyBasicAuth(proxyReq);
});
p.on('proxyRes', (proxyRes: any) => {
@@ -152,7 +207,7 @@ export default defineConfig({
plugins: [react()],
server: {
port: 5173,
strictPort: true,
strictPort: false,
proxy,
},
});