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>
This commit is contained in:
@@ -1,51 +1,68 @@
|
||||
# Application-routing Caddy for the long-lived dev environment.
|
||||
# Listens only on the `edge` Docker network; TLS termination and the
|
||||
# real `:80`/`:443` listeners belong to the host Caddy in front of us.
|
||||
# Single-origin, path-based: the project site, the game UI, and both
|
||||
# gateway surfaces live behind one host. TLS termination and the real
|
||||
# `:80`/`:443` listeners belong to the host Caddy in front of us.
|
||||
#
|
||||
# `/srv/galaxy-ui` is mounted from the `galaxy-dev-ui-dist` named volume,
|
||||
# refreshed on every dev-deploy run.
|
||||
{
|
||||
# / -> project site (galaxy-dev-site-dist -> /srv/galaxy-site)
|
||||
# /game/* -> game UI (galaxy-dev-ui-dist -> /srv/galaxy-ui)
|
||||
# /api/*, /healthz -> gateway public REST (galaxy-api:8080)
|
||||
# /rpc/* -> gateway Connect/gRPC-web (galaxy-api:9090)
|
||||
#
|
||||
# The same artifact is domain-agnostic: nothing here names a host, so an
|
||||
# identical bundle serves galaxy.lan, galaxy.iliadenisov.ru, or any
|
||||
# other domain the host Caddy terminates.
|
||||
{
|
||||
}
|
||||
auto_https off
|
||||
}
|
||||
|
||||
:80 {
|
||||
handle @frontend {
|
||||
root * /srv/galaxy-ui
|
||||
|
||||
# Authenticated Connect-Web edge. The browser calls
|
||||
# `/rpc/edge.v1.Gateway/<Method>`; strip the `/rpc` prefix so the
|
||||
# gateway sees the proto-derived service path on its :9090 listener.
|
||||
handle_path /rpc/* {
|
||||
reverse_proxy galaxy-api:9090
|
||||
}
|
||||
|
||||
# `_app/immutable/`; the file name changes whenever the
|
||||
# content changes, so the browser can cache them forever.
|
||||
# Without an explicit Cache-Control, Caddy falls back to
|
||||
# heuristic caching that revalidates on every reload —
|
||||
# measurably slow on Safari + the long-lived dev stack
|
||||
# when the cache is warm. Everything else (index.html
|
||||
# fallback, env.js, version.json, core.wasm,
|
||||
# wasm_exec.js, favicon.svg) must revalidate so a fresh
|
||||
# deploy lands without the user having to clear the
|
||||
# cache by hand.
|
||||
@immutable path /_app/immutable/*
|
||||
header @immutable Cache-Control "public, max-age=31536000, immutable"
|
||||
@dynamic not path /_app/immutable/*
|
||||
header @dynamic Cache-Control "no-cache, must-revalidate"
|
||||
|
||||
# Gateway public REST (auth) and the health probe on :8080.
|
||||
@api path /api/* /healthz
|
||||
handle @api {
|
||||
reverse_proxy galaxy-api:8080
|
||||
}
|
||||
|
||||
file_server
|
||||
encode zstd gzip
|
||||
}
|
||||
|
||||
# Bare `/game` (no trailing slash) -> `/game/` so the SPA root
|
||||
# resolves before the site catch-all can claim it.
|
||||
handle /game {
|
||||
redir * /game/ 308
|
||||
}
|
||||
|
||||
handle @api {
|
||||
# Connect-Web (authenticated) lives on a separate listener
|
||||
# (`GATEWAY_AUTHENTICATED_GRPC_ADDR=:9090`). Anything else —
|
||||
# public auth, healthz — is the public REST listener on
|
||||
# `:8080`. The split mirrors the Vite dev-server proxy in
|
||||
# `ui/frontend/vite.config.ts`.
|
||||
@connect path /galaxy.gateway.v1.EdgeGateway/*
|
||||
handle @connect {
|
||||
reverse_proxy galaxy-api:9090
|
||||
}
|
||||
reverse_proxy galaxy-api:8080
|
||||
}
|
||||
}
|
||||
# Game UI under `/game/`. The bundle is built with base=/game, so it
|
||||
# references `/game/_app/...`; strip the prefix to serve the build
|
||||
# whose files sit at the volume root. SPA fallback to index.html.
|
||||
handle_path /game/* {
|
||||
root * /srv/galaxy-ui
|
||||
# Hash-named, content-addressed chunks: cache forever.
|
||||
@immutable path /_app/immutable/*
|
||||
header @immutable Cache-Control "public, max-age=31536000, immutable"
|
||||
# index.html, env.js, version.json, core.wasm, wasm_exec.js,
|
||||
# favicon, manifest, service-worker.js must revalidate so a
|
||||
# fresh deploy lands without a manual cache clear.
|
||||
@dynamic not path /_app/immutable/*
|
||||
header @dynamic Cache-Control "no-cache, must-revalidate"
|
||||
try_files {path} /index.html
|
||||
file_server
|
||||
encode zstd gzip
|
||||
}
|
||||
|
||||
# Project site at the root (VitePress static output).
|
||||
handle {
|
||||
root * /srv/galaxy-site
|
||||
# VitePress emits hash-named assets under `/assets/`.
|
||||
@immutable path /assets/*
|
||||
header @immutable Cache-Control "public, max-age=31536000, immutable"
|
||||
@dynamic not path /assets/*
|
||||
header @dynamic Cache-Control "no-cache, must-revalidate"
|
||||
try_files {path} {path}.html {path}/index.html /404.html
|
||||
file_server
|
||||
encode zstd gzip
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,44 @@
|
||||
# Production placeholder. Mirrors `Caddyfile.dev` but uses real
|
||||
# hostnames and lets Caddy auto-provision TLS certificates. Not used
|
||||
# until prod-deploy plumbing exists; kept under version control so the
|
||||
# dev/prod surface stays symmetric.
|
||||
# Production placeholder. Single-origin, path-based — mirrors
|
||||
# `Caddyfile.dev` but binds the real public host and lets Caddy
|
||||
# auto-provision TLS. Not used until prod-deploy plumbing exists; kept
|
||||
# in version control so the dev/prod surface stays identical.
|
||||
#
|
||||
# The host is supplied at deploy time via `GALAXY_PUBLIC_HOST` so the
|
||||
# same image is domain-agnostic (the fallback is only a placeholder).
|
||||
|
||||
www.galaxy.com {
|
||||
root * /srv/galaxy-ui
|
||||
{$GALAXY_PUBLIC_HOST:galaxy.example} {
|
||||
handle_path /rpc/* {
|
||||
reverse_proxy galaxy-api:9090
|
||||
}
|
||||
|
||||
# Mirrors the cache policy `Caddyfile.dev` documents in detail:
|
||||
# SvelteKit's hash-named `_app/immutable/*` is safe to cache
|
||||
# forever; everything else must revalidate so a deploy reaches
|
||||
# the browser without a manual cache clear.
|
||||
@immutable path /_app/immutable/*
|
||||
header @immutable Cache-Control "public, max-age=31536000, immutable"
|
||||
@dynamic not path /_app/immutable/*
|
||||
header @dynamic Cache-Control "no-cache, must-revalidate"
|
||||
@api path /api/* /healthz
|
||||
handle @api {
|
||||
reverse_proxy galaxy-api:8080
|
||||
}
|
||||
|
||||
try_files {path} /index.html
|
||||
file_server
|
||||
encode zstd gzip
|
||||
}
|
||||
|
||||
api.galaxy.com {
|
||||
reverse_proxy galaxy-api:8080
|
||||
handle /game {
|
||||
redir * /game/ 308
|
||||
}
|
||||
|
||||
handle_path /game/* {
|
||||
root * /srv/galaxy-ui
|
||||
@immutable path /_app/immutable/*
|
||||
header @immutable Cache-Control "public, max-age=31536000, immutable"
|
||||
@dynamic not path /_app/immutable/*
|
||||
header @dynamic Cache-Control "no-cache, must-revalidate"
|
||||
try_files {path} /index.html
|
||||
file_server
|
||||
encode zstd gzip
|
||||
}
|
||||
|
||||
handle {
|
||||
root * /srv/galaxy-site
|
||||
@immutable path /assets/*
|
||||
header @immutable Cache-Control "public, max-age=31536000, immutable"
|
||||
@dynamic not path /assets/*
|
||||
header @dynamic Cache-Control "no-cache, must-revalidate"
|
||||
try_files {path} {path}.html {path}/index.html /404.html
|
||||
file_server
|
||||
encode zstd gzip
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.PHONY: help up down rebuild logs status clean-data health psql build-engine seed-ui seed-geoip
|
||||
.PHONY: help up down rebuild logs status clean-data health psql build-engine seed-ui seed-site seed-geoip
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
@@ -6,6 +6,9 @@ REPO_ROOT := $(realpath $(CURDIR)/../..)
|
||||
ENGINE_IMAGE := galaxy-engine:dev
|
||||
STACK_LABEL := galaxy.stack=dev-deploy
|
||||
ENGINE_LABEL := org.opencontainers.image.title=galaxy-game-engine
|
||||
# Public host the in-front host Caddy serves the single-origin stack on.
|
||||
# Used only by `make health` probes; override for a different domain.
|
||||
GALAXY_DEV_HOST ?= galaxy.lan
|
||||
# Game-state root lives under the invoking user's home by default so
|
||||
# `make up` works without sudo. Override `GALAXY_DEV_GAME_STATE_DIR`
|
||||
# in the environment or `.env` to relocate (e.g. /var/lib/galaxy-dev/
|
||||
@@ -17,11 +20,12 @@ export GALAXY_DEV_GAME_STATE_DIR ?= $(HOME)/.galaxy-dev/game-state
|
||||
COMPOSE := docker compose
|
||||
|
||||
help:
|
||||
@echo "Long-lived Galaxy dev environment (https://*.galaxy.lan):"
|
||||
@echo "Long-lived Galaxy dev environment (single-origin, e.g. https://galaxy.lan):"
|
||||
@echo " make up Build images, ensure engine image, seed geoip, bring stack up"
|
||||
@echo " make rebuild Force rebuild of backend / gateway images and bring up"
|
||||
@echo " make build-engine Build $(ENGINE_IMAGE) from game/Dockerfile (no-op if present)"
|
||||
@echo " make seed-ui Build ui/frontend and load into galaxy-dev-ui-dist volume"
|
||||
@echo " make seed-site Build site/ (VitePress) and load into galaxy-dev-site-dist volume"
|
||||
@echo " make seed-geoip Copy GeoIP fixture into galaxy-dev-geoip-data volume"
|
||||
@echo " make down Stop containers, keep named volumes"
|
||||
@echo " make logs Tail all logs"
|
||||
@@ -33,7 +37,7 @@ help:
|
||||
@echo "Requires:"
|
||||
@echo " - external Docker network '$${GALAXY_EDGE_NETWORK:-edge}'"
|
||||
@echo " (docker network create edge)"
|
||||
@echo " - host Caddy proxying *.galaxy.lan into that network"
|
||||
@echo " - host Caddy proxying the public host into that network"
|
||||
@echo " - game-state dir: $(GALAXY_DEV_GAME_STATE_DIR) (auto-created)"
|
||||
|
||||
up: build-engine seed-geoip
|
||||
@@ -76,7 +80,8 @@ seed-ui:
|
||||
fi
|
||||
@echo "building UI (vite build)…"
|
||||
(cd $(REPO_ROOT)/ui/frontend && \
|
||||
VITE_GATEWAY_BASE_URL=https://api.galaxy.lan \
|
||||
VITE_GATEWAY_BASE_URL= \
|
||||
BASE_PATH=/game \
|
||||
VITE_GALAXY_DEV_AFFORDANCES=true \
|
||||
VITE_GATEWAY_RESPONSE_PUBLIC_KEY=$$(cat $(REPO_ROOT)/ui/frontend/.env.development \
|
||||
| sed -n 's/^VITE_GATEWAY_RESPONSE_PUBLIC_KEY=//p') \
|
||||
@@ -88,6 +93,23 @@ seed-ui:
|
||||
-v $(REPO_ROOT)/ui/frontend/build:/src:ro \
|
||||
alpine sh -c 'rm -rf /dst/* /dst/.??* 2>/dev/null; cp -a /src/. /dst/'
|
||||
|
||||
# Build the project site (VitePress) and load the static output into the
|
||||
# named volume Caddy serves at the root. Used by the dev-deploy workflow
|
||||
# and by anyone bringing the stack up by hand.
|
||||
seed-site:
|
||||
@if [ ! -d $(REPO_ROOT)/site/node_modules ]; then \
|
||||
echo "installing site dependencies…"; \
|
||||
(cd $(REPO_ROOT)/site && pnpm install --frozen-lockfile); \
|
||||
fi
|
||||
@echo "building project site (vitepress build)…"
|
||||
(cd $(REPO_ROOT)/site && pnpm build)
|
||||
@echo "loading site dist into galaxy-dev-site-dist volume…"
|
||||
docker volume create galaxy-dev-site-dist >/dev/null
|
||||
docker run --rm \
|
||||
-v galaxy-dev-site-dist:/dst \
|
||||
-v $(REPO_ROOT)/site/.vitepress/dist:/src:ro \
|
||||
alpine sh -c 'rm -rf /dst/* /dst/.??* 2>/dev/null; cp -a /src/. /dst/'
|
||||
|
||||
down:
|
||||
$(COMPOSE) down
|
||||
|
||||
@@ -98,10 +120,12 @@ status:
|
||||
$(COMPOSE) ps
|
||||
|
||||
health:
|
||||
@echo "Frontend (https://www.galaxy.lan):"
|
||||
@curl -sS -o /dev/null -w " HTTP %{http_code}\n" https://www.galaxy.lan/ || echo " unreachable"
|
||||
@echo "API healthz (https://api.galaxy.lan/healthz):"
|
||||
@curl -sS -o /dev/null -w " HTTP %{http_code}\n" https://api.galaxy.lan/healthz || echo " unreachable"
|
||||
@echo "Site (https://$(GALAXY_DEV_HOST)/):"
|
||||
@curl -sS -o /dev/null -w " HTTP %{http_code}\n" https://$(GALAXY_DEV_HOST)/ || echo " unreachable"
|
||||
@echo "Game (https://$(GALAXY_DEV_HOST)/game/):"
|
||||
@curl -sS -o /dev/null -w " HTTP %{http_code}\n" https://$(GALAXY_DEV_HOST)/game/ || echo " unreachable"
|
||||
@echo "API healthz (https://$(GALAXY_DEV_HOST)/healthz):"
|
||||
@curl -sS -o /dev/null -w " HTTP %{http_code}\n" https://$(GALAXY_DEV_HOST)/healthz || echo " unreachable"
|
||||
|
||||
psql:
|
||||
$(COMPOSE) exec galaxy-postgres psql -U galaxy -d galaxy_backend
|
||||
|
||||
+57
-29
@@ -2,11 +2,26 @@
|
||||
|
||||
A docker-compose stack that runs the Galaxy backend, gateway, supporting
|
||||
services, and a small Caddy in front of them, reachable through the host
|
||||
Caddy at `https://www.galaxy.lan` and `https://api.galaxy.lan`. Used by
|
||||
the `dev-deploy.yaml` Gitea Actions workflow as the canonical dev target
|
||||
on every merge into the `development` branch, and runnable by hand
|
||||
through this Makefile for local debugging of the deploy plumbing
|
||||
itself.
|
||||
Caddy at a single origin (`https://galaxy.lan` in dev). The stack is
|
||||
single-origin and path-based: the project site, the game UI, and both
|
||||
gateway surfaces live behind one host with no host name baked into the
|
||||
artifacts. Used by the `dev-deploy.yaml` Gitea Actions workflow as the
|
||||
canonical dev target on every merge into the `development` branch, and
|
||||
runnable by hand through this Makefile for local debugging of the deploy
|
||||
plumbing itself.
|
||||
|
||||
The application Caddy (`Caddyfile.dev`) is the authoritative routing
|
||||
source; its header comment documents the exact topology:
|
||||
|
||||
```text
|
||||
/ -> project site (galaxy-dev-site-dist -> /srv/galaxy-site)
|
||||
/game/* -> game UI (galaxy-dev-ui-dist -> /srv/galaxy-ui)
|
||||
/api/*, /healthz -> gateway public REST (galaxy-api:8080)
|
||||
/rpc/* -> gateway Connect/gRPC-web (galaxy-api:9090)
|
||||
```
|
||||
|
||||
The `/rpc` prefix is stripped before the gateway, and the game UI bundle
|
||||
is built with base path `/game`.
|
||||
|
||||
This stack is **not** the developer's primary playground for UI work —
|
||||
that role still belongs to [`tools/local-dev/`](../local-dev/README.md),
|
||||
@@ -38,11 +53,13 @@ The host must already provide:
|
||||
```
|
||||
|
||||
- A host Caddy listening on `:80`/`:443`, attached to the `edge`
|
||||
network, and proxying `www.galaxy.lan` and `api.galaxy.lan` to
|
||||
`galaxy-caddy:80`. Example fragment for the host Caddyfile:
|
||||
network, and proxying the single dev host `galaxy.lan` to
|
||||
`galaxy-caddy:80`. The host Caddy only needs that one host;
|
||||
`Caddyfile.dev` does the path-based fan-out behind it. Example
|
||||
fragment for the host Caddyfile:
|
||||
|
||||
```caddy
|
||||
www.galaxy.lan, api.galaxy.lan {
|
||||
galaxy.lan {
|
||||
tls internal
|
||||
reverse_proxy galaxy-caddy:80
|
||||
}
|
||||
@@ -62,25 +79,28 @@ make -C tools/dev-deploy up
|
||||
|
||||
`up` (re)builds the local-dev backend and gateway images, makes sure the
|
||||
engine image `galaxy-engine:dev` exists, and waits for healthchecks. It
|
||||
does **not** seed the UI volume — that is normally done by CI. The first
|
||||
time you run by hand:
|
||||
does **not** seed the UI or site volumes — that is normally done by CI.
|
||||
The first time you run by hand:
|
||||
|
||||
```sh
|
||||
make -C tools/dev-deploy seed-site
|
||||
make -C tools/dev-deploy seed-ui
|
||||
make -C tools/dev-deploy up
|
||||
make -C tools/dev-deploy health
|
||||
```
|
||||
|
||||
`seed-ui` runs `pnpm build` in `ui/frontend/`, then copies the resulting
|
||||
`build/` tree into the `galaxy-dev-ui-dist` volume. Subsequent CI deploys
|
||||
overwrite this volume automatically.
|
||||
`seed-ui` runs `pnpm build` in `ui/frontend/` (base path `/game`), then
|
||||
copies the resulting `build/` tree into the `galaxy-dev-ui-dist` volume.
|
||||
`seed-site` builds the VitePress project site in `site/` and copies its
|
||||
`.vitepress/dist/` output into the `galaxy-dev-site-dist` volume.
|
||||
Subsequent CI deploys overwrite both volumes automatically.
|
||||
|
||||
## Daily flow
|
||||
|
||||
```sh
|
||||
make -C tools/dev-deploy rebuild # rebuild backend/gateway images + up
|
||||
make -C tools/dev-deploy logs # tail compose logs
|
||||
make -C tools/dev-deploy health # probe https://*.galaxy.lan
|
||||
make -C tools/dev-deploy health # probe https://galaxy.lan/ , /game/ , /healthz
|
||||
make -C tools/dev-deploy down # stop, keep state
|
||||
```
|
||||
|
||||
@@ -109,14 +129,16 @@ cannot leak into the prod environment.
|
||||
|
||||
```
|
||||
Browser
|
||||
│ https://www.galaxy.lan, https://api.galaxy.lan
|
||||
│ https://galaxy.lan/ (one origin, path-based)
|
||||
▼
|
||||
host-Caddy (:80, :443, TLS, attached to `edge` network)
|
||||
│ reverse_proxy *.galaxy.lan → galaxy-caddy:80
|
||||
│ reverse_proxy galaxy.lan → galaxy-caddy:80
|
||||
▼
|
||||
galaxy-caddy (networks: edge + galaxy-dev-internal)
|
||||
│ www.galaxy.lan → file_server /srv/galaxy-ui (volume galaxy-dev-ui-dist)
|
||||
│ api.galaxy.lan → reverse_proxy galaxy-api:8080
|
||||
│ / -> file_server /srv/galaxy-site (volume galaxy-dev-site-dist)
|
||||
│ /game/* -> file_server /srv/galaxy-ui (volume galaxy-dev-ui-dist)
|
||||
│ /api/*, /healthz -> reverse_proxy galaxy-api:8080
|
||||
│ /rpc/* -> reverse_proxy galaxy-api:9090 (strips /rpc)
|
||||
▼
|
||||
galaxy-dev-internal
|
||||
├─ galaxy-api (gateway: :8080 REST, :9090 gRPC)
|
||||
@@ -155,13 +177,14 @@ The same volume-persistence model applies to `tools/local-dev/`.
|
||||
```text
|
||||
make up Build images, ensure engine image, seed geoip, bring stack up
|
||||
make rebuild Rebuild backend / gateway images (ignores cache), then up
|
||||
make seed-ui pnpm build + load build/ into galaxy-dev-ui-dist volume
|
||||
make seed-ui pnpm build (base /game) + load build/ into galaxy-dev-ui-dist volume
|
||||
make seed-site vitepress build + load site dist into galaxy-dev-site-dist volume
|
||||
make seed-geoip Copy pkg/geoip fixture into galaxy-dev-geoip-data volume
|
||||
make build-engine Build galaxy-engine:dev (no-op if image already present)
|
||||
make down Stop containers, keep named volumes
|
||||
make logs Tail compose logs
|
||||
make status docker compose ps
|
||||
make health curl https://www.galaxy.lan + https://api.galaxy.lan/healthz
|
||||
make health curl https://galaxy.lan/ , /game/ , and /healthz
|
||||
make psql psql as galaxy@galaxy_backend
|
||||
make clean-data Stop everything and wipe volumes + game-state dir
|
||||
```
|
||||
@@ -169,15 +192,20 @@ make clean-data Stop everything and wipe volumes + game-state dir
|
||||
## Files
|
||||
|
||||
- `docker-compose.yml` — six services: postgres, redis, mailpit,
|
||||
galaxy-backend, galaxy-api, galaxy-caddy. Reuses the alpine-runtime
|
||||
Dockerfiles from `../local-dev/` so the backend healthcheck can run
|
||||
`wget`. Reuses the dev keypair from `../local-dev/keys/`.
|
||||
- `Caddyfile.dev` — the application-routing Caddy config, mounted into
|
||||
`galaxy-caddy` at `/etc/caddy/Caddyfile`.
|
||||
galaxy-backend, galaxy-api, galaxy-caddy. `galaxy-caddy` mounts both
|
||||
the `galaxy-dev-site-dist` (`/srv/galaxy-site`) and
|
||||
`galaxy-dev-ui-dist` (`/srv/galaxy-ui`) volumes and reverse-proxies
|
||||
both gateway tiers (REST/health on `:8080`, Connect/gRPC-web on
|
||||
`:9090`). Reuses the alpine-runtime Dockerfiles from `../local-dev/`
|
||||
so the backend healthcheck can run `wget`. Reuses the dev keypair
|
||||
from `../local-dev/keys/`.
|
||||
- `Caddyfile.dev` — the application-routing Caddy config and the
|
||||
authoritative single-origin path topology, mounted into `galaxy-caddy`
|
||||
at `/etc/caddy/Caddyfile`.
|
||||
- `Caddyfile.prod` — placeholder for a future prod deployment; not used
|
||||
by this compose.
|
||||
- `Makefile` — wrapper over `docker compose` with helpers for engine,
|
||||
UI seeding, health probes, and full wipe.
|
||||
site/UI seeding, health probes, and full wipe.
|
||||
- `.env.example` — non-secret defaults for the compose `${VAR:-}`
|
||||
expansions. Copy to `.env` if you want host-local overrides.
|
||||
|
||||
@@ -212,6 +240,6 @@ behind. There is no separate state to clean up between the two paths.
|
||||
- `tools/local-dev/` — single-developer playground, host-port mapped,
|
||||
Vite dev server on the side. Recommended for active UI work.
|
||||
- `.gitea/workflows/dev-deploy.yaml` — the CI side of this stack:
|
||||
builds images, seeds the UI volume, runs `docker compose up -d` on
|
||||
every merge into `development`. The Makefile in this directory is
|
||||
what that workflow ultimately calls into.
|
||||
builds images, seeds the site and UI volumes, runs `docker compose
|
||||
up -d` on every merge into `development`. The Makefile in this
|
||||
directory is what that workflow ultimately calls into.
|
||||
|
||||
@@ -186,11 +186,14 @@ services:
|
||||
GATEWAY_RESPONSE_SIGNER_PRIVATE_KEY_PEM_PATH: /run/secrets/gateway-response.pem
|
||||
GATEWAY_REDIS_MASTER_ADDR: "galaxy-redis:6379"
|
||||
GATEWAY_REDIS_PASSWORD: galaxy-dev
|
||||
# UI lives on https://www.galaxy.lan; the API is on
|
||||
# https://api.galaxy.lan. Browsers therefore issue cross-origin
|
||||
# requests to the gateway and need an explicit allow-list.
|
||||
GATEWAY_PUBLIC_HTTP_CORS_ALLOWED_ORIGINS: "https://www.galaxy.lan"
|
||||
GATEWAY_AUTHENTICATED_GRPC_CORS_ALLOWED_ORIGINS: "https://www.galaxy.lan"
|
||||
# Single-origin deployment: the UI, public REST, and Connect-Web
|
||||
# edge share one host, so browser requests are same-origin and
|
||||
# CORS is not needed. An empty allow-list disables the CORS
|
||||
# middleware (requests pass through without Access-Control-*
|
||||
# headers). Re-populate these only if a future deploy fronts the
|
||||
# gateway on a different host than the UI.
|
||||
GATEWAY_PUBLIC_HTTP_CORS_ALLOWED_ORIGINS: ""
|
||||
GATEWAY_AUTHENTICATED_GRPC_CORS_ALLOWED_ORIGINS: ""
|
||||
# Anti-abuse defaults are looser than production: the dev
|
||||
# environment is shared by a handful of trusted testers who
|
||||
# frequently hammer the same identity to reproduce flows.
|
||||
@@ -237,6 +240,7 @@ services:
|
||||
- ./Caddyfile.dev:/etc/caddy/Caddyfile:ro
|
||||
- galaxy-dev-caddy-data:/data
|
||||
- galaxy-dev-ui-dist:/srv/galaxy-ui:ro
|
||||
- galaxy-dev-site-dist:/srv/galaxy-site:ro
|
||||
networks:
|
||||
- galaxy-internal
|
||||
- edge
|
||||
@@ -266,5 +270,7 @@ volumes:
|
||||
name: galaxy-dev-caddy-data
|
||||
galaxy-dev-ui-dist:
|
||||
name: galaxy-dev-ui-dist
|
||||
galaxy-dev-site-dist:
|
||||
name: galaxy-dev-site-dist
|
||||
galaxy-dev-geoip-data:
|
||||
name: galaxy-dev-geoip-data
|
||||
|
||||
@@ -14,8 +14,8 @@ This stack is **not** a CI gate (the per-stage CI gate now lives on
|
||||
`gitea.lan`; see project-level `CLAUDE.md`). It is also distinct from
|
||||
the **long-lived dev environment** at
|
||||
[`tools/dev-deploy/`](../dev-deploy/README.md), which is redeployed on
|
||||
every merge into `development` and is reachable as
|
||||
`https://www.galaxy.lan` / `https://api.galaxy.lan`. The two stacks
|
||||
every merge into `development` and is reachable at the single origin
|
||||
`https://galaxy.lan` (site at `/`, game UI at `/game/`). The two stacks
|
||||
(`tools/local-dev/` and `tools/dev-deploy/`) coexist on the same host
|
||||
because every name — compose project, container, network, volume — is
|
||||
distinct.
|
||||
@@ -131,7 +131,7 @@ host compose network "galaxy-local-de
|
||||
┌────────────────────────────────┐ ┌──────────────────────────────┐
|
||||
│ browser localhost:5173 │── pnpm dev (Vite, host) ──┐ │
|
||||
│ ↳ /api/* proxied ───┼──────────────────────────▶│ gateway:8080 │
|
||||
│ ↳ /galaxy.gateway... ┼──────────────────────────▶│ │
|
||||
│ ↳ /rpc/* proxied ───┼──────────────────────────▶│ gateway:9090 │
|
||||
│ browser localhost:8025 │─────────────────────────▶│ mailpit:8025 │
|
||||
│ psql localhost:5433 │─────────────────────────▶│ postgres:5432 │
|
||||
│ redis-cli localhost:6380 │─────────────────────────▶│ redis:6379 │
|
||||
@@ -141,8 +141,9 @@ host compose network "galaxy-local-de
|
||||
└────────────────────────────────┘
|
||||
```
|
||||
|
||||
Vite's dev server proxies `/api` and `/galaxy.gateway.v1.EdgeGateway`
|
||||
to the gateway, so every browser request stays same-origin (no CORS
|
||||
Vite's dev server proxies `/api` (to the gateway REST listener) and
|
||||
`/rpc` (to the authenticated Connect/gRPC-Web listener, stripping the
|
||||
`/rpc` prefix), so every browser request stays same-origin (no CORS
|
||||
preflight). The gateway is therefore reachable only through Vite at
|
||||
<http://localhost:5173>, not at <http://localhost:8080> from the
|
||||
browser tab. Direct curl/wget against <http://localhost:8080> still
|
||||
@@ -291,9 +292,9 @@ make status docker compose ps
|
||||
## Relationship to other infrastructure
|
||||
|
||||
- `tools/dev-deploy/` — long-lived dev environment redeployed on every
|
||||
merge into `development`; reachable at `https://www.galaxy.lan` /
|
||||
`https://api.galaxy.lan`. Distinct compose project, container names,
|
||||
network and volumes.
|
||||
merge into `development`; reachable at the single origin
|
||||
`https://galaxy.lan` (site at `/`, game UI at `/game/`). Distinct
|
||||
compose project, container names, network and volumes.
|
||||
- `integration/testenv/` — testcontainers harness used by
|
||||
`make -C integration integration`. Uses the canonical
|
||||
`backend/Dockerfile` / `gateway/Dockerfile` at production defaults;
|
||||
|
||||
@@ -180,7 +180,7 @@ services:
|
||||
GATEWAY_PUBLIC_HTTP_ANTI_ABUSE_SEND_EMAIL_CODE_IDENTITY_RATE_LIMIT_BURST: "1000"
|
||||
GATEWAY_PUBLIC_HTTP_ANTI_ABUSE_CONFIRM_EMAIL_CODE_IDENTITY_RATE_LIMIT_REQUESTS: "10000"
|
||||
GATEWAY_PUBLIC_HTTP_ANTI_ABUSE_CONFIRM_EMAIL_CODE_IDENTITY_RATE_LIMIT_BURST: "1000"
|
||||
# public_misc class wraps the authenticated EdgeGateway gRPC
|
||||
# public_misc class wraps the authenticated edge.v1.Gateway gRPC
|
||||
# endpoints (ExecuteCommand, SubscribeEvents). The gateway's
|
||||
# default for this class is 0 bytes, which rejects every
|
||||
# non-empty body with HTTP 413; override with a generous limit
|
||||
@@ -200,7 +200,7 @@ services:
|
||||
GATEWAY_AUTHENTICATED_GRPC_ANTI_ABUSE_MESSAGE_CLASS_RATE_LIMIT_BURST: "1000"
|
||||
ports:
|
||||
- "${LOCAL_DEV_GATEWAY_REST_PORT:-8080}:8080"
|
||||
# Authenticated EdgeGateway connect-web/gRPC listener. The
|
||||
# Authenticated edge.v1.Gateway connect-web/gRPC listener. The
|
||||
# browser reaches it via the Vite dev proxy in
|
||||
# ui/frontend/vite.config.ts.
|
||||
- "${LOCAL_DEV_GATEWAY_GRPC_PORT:-9090}:9090"
|
||||
|
||||
Reference in New Issue
Block a user