ui: read dev-server config from .env files and add VITE_DEV_HOST opt-in
`vite.config.ts` read `VITE_DEV_PROXY_TARGET` / `VITE_DEV_GRPC_PROXY_TARGET` straight from `process.env`, so the gateway-override knob only worked when the variable was exported in the shell that ran `pnpm dev`. Per-developer `.env.development.local` files (the documented way to override) were silently ignored by the config: Vite auto-populates `import.meta.env` for client code from those files, but the config itself runs in Node and has to call `loadEnv` explicitly. Switch the config to the function-form + `loadEnv` so every `VITE_*` entry in any `.env*` file reaches both client code and the config. Now adding `VITE_DEV_PROXY_TARGET=http://localhost:18080` to `.env.development.local` actually retargets the proxy, no shell gymnastics required. While there, introduce `VITE_DEV_HOST` as an opt-in for wider listener binding: unset (default) keeps Vite's loopback-only behaviour; `true`/`1`/`yes` flips to "all interfaces" (`0.0.0.0` + IPv6); any other string is passed through verbatim to pin a specific LAN address. Useful when reaching the dev server through SSH port forwarding, a VM, or a container needs a non-loopback bind, and intentionally opt-in so an unattended `pnpm dev` on a laptop never exposes the unauthenticated dev surface to the LAN by accident. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+78
-37
@@ -1,5 +1,5 @@
|
|||||||
import { sveltekit } from "@sveltejs/kit/vite";
|
import { sveltekit } from "@sveltejs/kit/vite";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig, loadEnv } from "vite";
|
||||||
import { readFileSync } from "node:fs";
|
import { readFileSync } from "node:fs";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
@@ -10,43 +10,84 @@ const pkg = JSON.parse(
|
|||||||
),
|
),
|
||||||
) as { version: string };
|
) as { version: string };
|
||||||
|
|
||||||
// Default upstream gateway addresses used by the dev proxy. Override
|
// Parse the VITE_DEV_HOST override into the shape `server.host`
|
||||||
// by pointing `VITE_DEV_PROXY_TARGET` (REST surface) and
|
// expects. `""` / `"false"` keeps Vite's safe default (loopback
|
||||||
// `VITE_DEV_GRPC_PROXY_TARGET` (Connect-Web surface) at a different
|
// only); `"true"` / `"1"` flips to "all interfaces" (`0.0.0.0` plus
|
||||||
// gateway when working with a remote stack instead of
|
// IPv6); any other string is passed through verbatim so a developer
|
||||||
// `tools/local-dev/`. In production the two surfaces sit behind a
|
// can pin a single LAN address (e.g. `"192.168.1.5"`). Returning
|
||||||
// single host; the split here exists only because local-dev runs the
|
// `undefined` lets Vite stay on its built-in default.
|
||||||
// REST listener on :8080 and the authenticated Connect-Web listener
|
function parseDevHost(raw: string | undefined): string | boolean | undefined {
|
||||||
// on :9090.
|
if (raw === undefined || raw === "") return undefined;
|
||||||
const DEV_PROXY_TARGET =
|
const normalised = raw.toLowerCase();
|
||||||
process.env.VITE_DEV_PROXY_TARGET ?? "http://localhost:8080";
|
if (normalised === "true" || normalised === "1" || normalised === "yes") {
|
||||||
const DEV_GRPC_PROXY_TARGET =
|
return true;
|
||||||
process.env.VITE_DEV_GRPC_PROXY_TARGET ?? "http://localhost:9090";
|
}
|
||||||
|
if (normalised === "false" || normalised === "0" || normalised === "no") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig(({ mode }) => {
|
||||||
plugins: [sveltekit()],
|
// `loadEnv("", ...)` matches every `.env*` entry regardless of
|
||||||
define: {
|
// the customary `VITE_` prefix so the config sees the same view
|
||||||
__APP_VERSION__: JSON.stringify(pkg.version),
|
// that client code sees via `import.meta.env`. Without this
|
||||||
},
|
// `process.env` would carry only the shell's exports, and
|
||||||
server: {
|
// per-developer files like `.env.development.local` would
|
||||||
// Same-origin proxy so the browser sees only `localhost:5173`
|
// silently miss the config — every override would have to be
|
||||||
// and never trips a cross-origin preflight against the
|
// passed on the `pnpm dev` command line.
|
||||||
// gateway's REST + Connect-Web surfaces. Production deployments
|
const env = loadEnv(mode, process.cwd(), "");
|
||||||
// serve the UI and the gateway behind a single host, so the
|
|
||||||
// proxy is purely a dev-time convenience.
|
// Default upstream gateway addresses used by the dev proxy.
|
||||||
proxy: {
|
// Override by pointing `VITE_DEV_PROXY_TARGET` (REST surface)
|
||||||
"/api": {
|
// and `VITE_DEV_GRPC_PROXY_TARGET` (Connect-Web surface) at a
|
||||||
target: DEV_PROXY_TARGET,
|
// different gateway when working with a remote stack instead of
|
||||||
changeOrigin: false,
|
// `tools/local-dev/`. In production the two surfaces sit behind
|
||||||
},
|
// a single host; the split here exists only because local-dev
|
||||||
"/galaxy.gateway.v1.EdgeGateway": {
|
// runs the REST listener on :8080 and the authenticated
|
||||||
target: DEV_GRPC_PROXY_TARGET,
|
// Connect-Web listener on :9090.
|
||||||
changeOrigin: false,
|
const DEV_PROXY_TARGET =
|
||||||
// Connect-Web server-streaming (`SubscribeEvents`) uses
|
env.VITE_DEV_PROXY_TARGET || "http://localhost:8080";
|
||||||
// chunked HTTP responses; http-proxy passes them through
|
const DEV_GRPC_PROXY_TARGET =
|
||||||
// transparently as long as buffering stays off, which is
|
env.VITE_DEV_GRPC_PROXY_TARGET || "http://localhost:9090";
|
||||||
// the default.
|
|
||||||
|
// `VITE_DEV_HOST` opts the dev server into wider listener
|
||||||
|
// binding. Default stays at Vite's safe loopback-only behaviour
|
||||||
|
// so an unattended `pnpm dev` on someone's laptop never exposes
|
||||||
|
// the unauthenticated dev surface to the LAN by accident. Set
|
||||||
|
// the value in `.env.development.local` (untracked) when
|
||||||
|
// reaching the server through SSH port forwarding, a VM, or a
|
||||||
|
// container needs a non-loopback bind.
|
||||||
|
const devHost = parseDevHost(env.VITE_DEV_HOST);
|
||||||
|
|
||||||
|
return {
|
||||||
|
plugins: [sveltekit()],
|
||||||
|
define: {
|
||||||
|
__APP_VERSION__: JSON.stringify(pkg.version),
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
...(devHost !== undefined ? { host: devHost } : {}),
|
||||||
|
// Same-origin proxy so the browser sees only
|
||||||
|
// `localhost:5173` and never trips a cross-origin
|
||||||
|
// preflight against the gateway's REST + Connect-Web
|
||||||
|
// surfaces. Production deployments serve the UI and the
|
||||||
|
// gateway behind a single host, so the proxy is purely a
|
||||||
|
// dev-time convenience.
|
||||||
|
proxy: {
|
||||||
|
"/api": {
|
||||||
|
target: DEV_PROXY_TARGET,
|
||||||
|
changeOrigin: false,
|
||||||
|
},
|
||||||
|
"/galaxy.gateway.v1.EdgeGateway": {
|
||||||
|
target: DEV_GRPC_PROXY_TARGET,
|
||||||
|
changeOrigin: false,
|
||||||
|
// Connect-Web server-streaming
|
||||||
|
// (`SubscribeEvents`) uses chunked HTTP
|
||||||
|
// responses; http-proxy passes them through
|
||||||
|
// transparently as long as buffering stays off,
|
||||||
|
// which is the default.
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user