feat(deploy): single-origin path-based deployment + project site
Build · Site / build (push) Successful in 8s
Tests · Go / test (push) Successful in 2m22s
Tests · UI / test (push) Failing after 2m42s

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>
This commit is contained in:
Ilia Denisov
2026-05-23 18:19:07 +02:00
parent fa0df5183a
commit 8565942392
104 changed files with 2967 additions and 787 deletions
+28 -4
View File
@@ -24,6 +24,7 @@ on:
- 'game/**'
- 'pkg/**'
- 'ui/**'
- 'site/**'
- 'go.work'
- 'go.work.sum'
- 'tools/dev-deploy/**'
@@ -76,7 +77,11 @@ jobs:
- name: Build UI frontend
working-directory: ui/frontend
env:
VITE_GATEWAY_BASE_URL: https://api.galaxy.lan
# Single-origin deployment: an empty base URL means the
# gateway shares the document origin (REST at /api, Connect at
# /rpc). The game UI is served under the /game/ base path.
VITE_GATEWAY_BASE_URL: ""
BASE_PATH: /game
# Surface the synthetic-report loader and similar dev-only
# affordances in the long-lived dev bundle. The prod build
# path (`prod-build.yaml`) leaves this flag unset so the
@@ -91,6 +96,14 @@ jobs:
export VITE_GATEWAY_RESPONSE_PUBLIC_KEY="$(grep -E '^VITE_GATEWAY_RESPONSE_PUBLIC_KEY=' .env.development | cut -d= -f2)"
pnpm build
- name: Install site dependencies
working-directory: site
run: pnpm install --frozen-lockfile
- name: Build project site
working-directory: site
run: pnpm build
- name: Build galaxy-engine image
working-directory: ${{ gitea.workspace }}
run: |
@@ -112,6 +125,14 @@ jobs:
-v "${{ gitea.workspace }}/ui/frontend/build:/src:ro" \
alpine sh -c 'rm -rf /dst/* /dst/.??* 2>/dev/null; cp -a /src/. /dst/'
- name: Seed site volume
run: |
docker volume create galaxy-dev-site-dist >/dev/null
docker run --rm \
-v galaxy-dev-site-dist:/dst \
-v "${{ gitea.workspace }}/site/.vitepress/dist:/src:ro" \
alpine sh -c 'rm -rf /dst/* /dst/.??* 2>/dev/null; cp -a /src/. /dst/'
- name: Seed geoip volume
run: |
# Copy the GeoIP test fixture into a named volume so the
@@ -162,9 +183,12 @@ jobs:
# `tls internal`) terminates and forwards into the edge
# network. We accept the host's internal CA via -k because
# the runner image has no reason to trust it.
curl -sk --max-time 10 https://api.galaxy.lan/healthz \
curl -sk --max-time 10 https://galaxy.lan/healthz \
| tee /tmp/healthz
test -s /tmp/healthz
curl -sk --max-time 10 -o /dev/null -w '%{http_code}\n' \
https://www.galaxy.lan/ | tee /tmp/www_status
grep -qE '^(200|304)$' /tmp/www_status
https://galaxy.lan/ | tee /tmp/site_status
grep -qE '^(200|304)$' /tmp/site_status
curl -sk --max-time 10 -o /dev/null -w '%{http_code}\n' \
https://galaxy.lan/game/ | tee /tmp/game_status
grep -qE '^(200|304)$' /tmp/game_status