feat(visualizer): add runtime architecture diagram
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { defineConfig } from 'vite';
|
||||
import { defineConfig, loadEnv } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
const DIR = path.dirname(fileURLToPath(import.meta.url));
|
||||
@@ -14,8 +14,8 @@ function stripTrailingSlashes(p: string): string {
|
||||
return out || '/';
|
||||
}
|
||||
|
||||
function readApiReadToken(): string | undefined {
|
||||
if (process.env.API_READ_TOKEN) return process.env.API_READ_TOKEN;
|
||||
function readApiReadToken(env: Record<string, string | undefined>): string | undefined {
|
||||
if (env.API_READ_TOKEN) return env.API_READ_TOKEN;
|
||||
const p = path.join(ROOT, 'tokens', 'read.json');
|
||||
if (!fs.existsSync(p)) return undefined;
|
||||
try {
|
||||
@@ -38,11 +38,11 @@ function parseBasicAuth(value: string | undefined): BasicAuth | undefined {
|
||||
return { username, password };
|
||||
}
|
||||
|
||||
function readProxyBasicAuth(): BasicAuth | undefined {
|
||||
const fromEnv = parseBasicAuth(process.env.API_PROXY_BASIC_AUTH);
|
||||
function readProxyBasicAuth(env: Record<string, string | undefined>): BasicAuth | undefined {
|
||||
const fromEnv = parseBasicAuth(env.API_PROXY_BASIC_AUTH);
|
||||
if (fromEnv) return fromEnv;
|
||||
|
||||
const fileRaw = String(process.env.API_PROXY_BASIC_AUTH_FILE || '').trim();
|
||||
const fileRaw = String(env.API_PROXY_BASIC_AUTH_FILE || '').trim();
|
||||
if (!fileRaw) return undefined;
|
||||
|
||||
const p = path.isAbsolute(fileRaw) ? fileRaw : path.join(ROOT, fileRaw);
|
||||
@@ -59,10 +59,6 @@ function readProxyBasicAuth(): BasicAuth | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
const apiReadToken = readApiReadToken();
|
||||
const proxyBasicAuth = readProxyBasicAuth();
|
||||
const apiProxyTarget = process.env.API_PROXY_TARGET || 'http://localhost:8787';
|
||||
|
||||
function parseUrl(v: string): URL | undefined {
|
||||
try {
|
||||
return new URL(v);
|
||||
@@ -71,10 +67,6 @@ function parseUrl(v: string): URL | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
const apiProxyTargetUrl = parseUrl(apiProxyTarget);
|
||||
const apiProxyTargetPath = stripTrailingSlashes(apiProxyTargetUrl?.pathname || '/');
|
||||
const apiProxyTargetEndsWithApi = apiProxyTargetPath.endsWith('/api');
|
||||
|
||||
function inferUiProxyTarget(apiTarget: string): string | undefined {
|
||||
try {
|
||||
const u = new URL(apiTarget);
|
||||
@@ -91,15 +83,6 @@ function inferUiProxyTarget(apiTarget: string): string | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
const uiProxyTarget =
|
||||
process.env.FRONTEND_PROXY_TARGET ||
|
||||
process.env.UI_PROXY_TARGET ||
|
||||
process.env.AUTH_PROXY_TARGET ||
|
||||
inferUiProxyTarget(apiProxyTarget) ||
|
||||
(apiProxyTargetUrl && apiProxyTargetPath === '/' ? stripTrailingSlashes(apiProxyTargetUrl.toString()) : undefined);
|
||||
|
||||
const graphqlProxyTarget = process.env.GRAPHQL_PROXY_TARGET || process.env.HASURA_PROXY_TARGET || uiProxyTarget;
|
||||
|
||||
function applyProxyBasicAuth(proxyReq: any) {
|
||||
if (!proxyBasicAuth) return false;
|
||||
const b64 = Buffer.from(`${proxyBasicAuth.username}:${proxyBasicAuth.password}`, 'utf8').toString('base64');
|
||||
@@ -119,60 +102,88 @@ function rewriteSetCookieForLocalDevHttp(proxyRes: any) {
|
||||
proxyRes.headers['set-cookie'] = Array.isArray(v) ? v.map(rewrite) : rewrite(String(v));
|
||||
}
|
||||
|
||||
const proxy: Record<string, any> = {
|
||||
'/api': {
|
||||
target: apiProxyTarget,
|
||||
changeOrigin: true,
|
||||
rewrite: (p: string) => (apiProxyTargetEndsWithApi ? p.replace(/^\/api/, '') : p),
|
||||
configure: (p: any) => {
|
||||
p.on('proxyReq', (proxyReq: any) => {
|
||||
if (applyProxyBasicAuth(proxyReq)) return;
|
||||
if (apiReadToken) proxyReq.setHeader('Authorization', `Bearer ${apiReadToken}`);
|
||||
});
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = { ...process.env, ...loadEnv(mode, DIR, '') } as Record<string, string | undefined>;
|
||||
const apiReadToken = readApiReadToken(env);
|
||||
const proxyBasicAuth = readProxyBasicAuth(env);
|
||||
const apiProxyTarget = env.API_PROXY_TARGET || 'http://localhost:8787';
|
||||
const apiProxyTargetUrl = parseUrl(apiProxyTarget);
|
||||
const apiProxyTargetPath = stripTrailingSlashes(apiProxyTargetUrl?.pathname || '/');
|
||||
const apiProxyTargetEndsWithApi = apiProxyTargetPath.endsWith('/api');
|
||||
const uiProxyTarget =
|
||||
env.FRONTEND_PROXY_TARGET ||
|
||||
env.UI_PROXY_TARGET ||
|
||||
env.AUTH_PROXY_TARGET ||
|
||||
inferUiProxyTarget(apiProxyTarget) ||
|
||||
(apiProxyTargetUrl && apiProxyTargetPath === '/'
|
||||
? stripTrailingSlashes(apiProxyTargetUrl.toString())
|
||||
: undefined);
|
||||
const graphqlProxyTarget = env.GRAPHQL_PROXY_TARGET || env.HASURA_PROXY_TARGET || uiProxyTarget;
|
||||
const portRaw = env.VITE_DEV_PORT || env.DEV_PORT || '5173';
|
||||
const port = Number.parseInt(String(portRaw), 10);
|
||||
|
||||
function applyProxyBasicAuth(proxyReq: any) {
|
||||
if (!proxyBasicAuth) return false;
|
||||
const b64 = Buffer.from(`${proxyBasicAuth.username}:${proxyBasicAuth.password}`, 'utf8').toString('base64');
|
||||
proxyReq.setHeader('Authorization', `Basic ${b64}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
const proxy: Record<string, any> = {
|
||||
'/api': {
|
||||
target: apiProxyTarget,
|
||||
changeOrigin: true,
|
||||
rewrite: (p: string) => (apiProxyTargetEndsWithApi ? p.replace(/^\/api/, '') : p),
|
||||
configure: (p: any) => {
|
||||
p.on('proxyReq', (proxyReq: any) => {
|
||||
if (applyProxyBasicAuth(proxyReq)) return;
|
||||
if (apiReadToken) proxyReq.setHeader('Authorization', `Bearer ${apiReadToken}`);
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
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) => {
|
||||
applyProxyBasicAuth(proxyReq);
|
||||
});
|
||||
p.on('proxyReqWs', (proxyReq: any) => {
|
||||
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) => {
|
||||
applyProxyBasicAuth(proxyReq);
|
||||
});
|
||||
p.on('proxyReqWs', (proxyReq: any) => {
|
||||
applyProxyBasicAuth(proxyReq);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (uiProxyTarget) {
|
||||
for (const prefix of ['/whoami', '/auth', '/logout']) {
|
||||
proxy[prefix] = {
|
||||
target: uiProxyTarget,
|
||||
changeOrigin: true,
|
||||
configure: (p: any) => {
|
||||
p.on('proxyReq', (proxyReq: any) => {
|
||||
applyProxyBasicAuth(proxyReq);
|
||||
});
|
||||
p.on('proxyRes', (proxyRes: any) => {
|
||||
rewriteSetCookieForLocalDevHttp(proxyRes);
|
||||
});
|
||||
},
|
||||
};
|
||||
if (uiProxyTarget) {
|
||||
for (const prefix of ['/whoami', '/auth', '/logout']) {
|
||||
proxy[prefix] = {
|
||||
target: uiProxyTarget,
|
||||
changeOrigin: true,
|
||||
configure: (p: any) => {
|
||||
p.on('proxyReq', (proxyReq: any) => {
|
||||
applyProxyBasicAuth(proxyReq);
|
||||
});
|
||||
p.on('proxyRes', (proxyRes: any) => {
|
||||
rewriteSetCookieForLocalDevHttp(proxyRes);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 5173,
|
||||
strictPort: true,
|
||||
proxy,
|
||||
},
|
||||
return {
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: Number.isInteger(port) && port > 0 ? port : 5173,
|
||||
strictPort: true,
|
||||
proxy,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user