Files
galaxy-game/ui/frontend/vite.config.ts
T
Ilia Denisov 8565942392
Build · Site / build (push) Successful in 8s
Tests · Go / test (push) Successful in 2m22s
Tests · UI / test (push) Failing after 2m42s
feat(deploy): single-origin path-based deployment + project site
Serve the whole stack behind one host: site at /, game UI at /game/,
gateway REST at /api + /healthz, Connect at /rpc (prefix stripped by the
edge Caddy). The built artifact is domain-agnostic — the UI talks to the
gateway same-origin via relative URLs, so the same bundle runs under any
host with no rebuild and with CORS disabled.

- Rename the Connect proto service galaxy.gateway.v1.EdgeGateway ->
  edge.v1.Gateway; regenerate Go + TS; public path /rpc/edge.v1.Gateway.
- Move the game UI under base path /game (env BASE_PATH); make the
  manifest, service-worker scope, WASM loader, and all navigation
  base-aware via a withBase helper.
- Relative API + /rpc Connect prefix; Vite dev proxy mirrors the strip.
- Rewrite the edge Caddy (dev + prod) for path-based routing; empty CORS
  allow-lists (same-origin); single host.
- New VitePress project site (site/): i18n en/ru with switcher, LaTeX
  math, minimal monospace theme; built and served at /.
- dev-deploy compose/Makefile + CI (dev-deploy, prod-build, new
  site-build) build and seed the site; probes hit /, /game/, /healthz.
- Sync docs (ARCHITECTURE, gateway README/openapi, dev-deploy &
  local-dev READMEs, CLAUDE.md, ui/PLAN).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 18:19:07 +02:00

95 lines
3.6 KiB
TypeScript

import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig, loadEnv } from "vite";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
const pkg = JSON.parse(
readFileSync(
fileURLToPath(new URL("./package.json", import.meta.url)),
"utf8",
),
) as { version: string };
// Parse the VITE_DEV_HOST override into the shape `server.host`
// expects. `""` / `"false"` keeps Vite's safe default (loopback
// only); `"true"` / `"1"` flips to "all interfaces" (`0.0.0.0` plus
// IPv6); any other string is passed through verbatim so a developer
// can pin a single LAN address (e.g. `"192.168.1.5"`). Returning
// `undefined` lets Vite stay on its built-in default.
function parseDevHost(raw: string | undefined): string | boolean | undefined {
if (raw === undefined || raw === "") return undefined;
const normalised = raw.toLowerCase();
if (normalised === "true" || normalised === "1" || normalised === "yes") {
return true;
}
if (normalised === "false" || normalised === "0" || normalised === "no") {
return false;
}
return raw;
}
export default defineConfig(({ mode }) => {
// `loadEnv("", ...)` matches every `.env*` entry regardless of
// the customary `VITE_` prefix so the config sees the same view
// that client code sees via `import.meta.env`. Without this
// `process.env` would carry only the shell's exports, and
// per-developer files like `.env.development.local` would
// silently miss the config — every override would have to be
// passed on the `pnpm dev` command line.
const env = loadEnv(mode, process.cwd(), "");
// Default upstream gateway addresses used by the dev proxy.
// Override by pointing `VITE_DEV_PROXY_TARGET` (REST surface)
// and `VITE_DEV_GRPC_PROXY_TARGET` (Connect-Web surface) at a
// different gateway when working with a remote stack instead of
// `tools/local-dev/`. In production the two surfaces sit behind
// a single host; the split here exists only because local-dev
// runs the REST listener on :8080 and the authenticated
// Connect-Web listener on :9090.
const DEV_PROXY_TARGET =
env.VITE_DEV_PROXY_TARGET || "http://localhost:8080";
const DEV_GRPC_PROXY_TARGET =
env.VITE_DEV_GRPC_PROXY_TARGET || "http://localhost:9090";
// `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,
},
"/rpc": {
target: DEV_GRPC_PROXY_TARGET,
changeOrigin: false,
rewrite: (path) => path.replace(/^\/rpc/, ""),
// 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.
},
},
},
};
});