From 275fd0dccc4da57ef5104c83d394c0773b15744c Mon Sep 17 00:00:00 2001 From: Evan Date: Mon, 11 May 2026 19:24:01 -0700 Subject: [PATCH] refactor: collapse per-env Configs into ClientEnv + ServerEnv (#3906) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: This is a refactor to simplify config handling. Replaces the per-environment DevConfig/PreprodConfig/ProdConfig class hierarchy with two static classes: ClientEnv (browser main thread, reads from window.BOOTSTRAP_CONFIG) and ServerEnv (Node server, reads from process.env). The four config classes are deleted, the abstract DefaultServerConfig is gone, and DefaultConfig is renamed to Config. The values that flow server → client (gameEnv, numWorkers, turnstileSiteKey, jwtAudience, instanceId) used to be baked into the hardcoded per-env classes. They're now real env vars on the server, embedded into a single window.BOOTSTRAP_CONFIG object in index.html at request time (alongside the existing gitCommit/assetManifest/cdnBase globals, which moved into the same object), and read back by ClientEnv on the client. The dev defaults previously hidden inside DevServerConfig are now explicit in start:server-dev (NUM_WORKERS=2, TURNSTILE_SITE_KEY=1x..., JWT_AUDIENCE=localhost, etc.) and in vite.config.ts's html plugin inject.data. Production deploys plumb NUM_WORKERS and TURNSTILE_SITE_KEY through deploy.yml (GitHub vars) into the remote env file; JWT_AUDIENCE is derived from DOMAIN in deploy.sh. The dynamic /api/instance endpoint is gone — INSTANCE_ID rides along in BOOTSTRAP_CONFIG now. ServerEnv is the only thing server code touches; ClientEnv is browser-only. The two classes have intentional overlap (env, numWorkers, jwtIssuer, gameCreationRate, workerIndex, etc.) since they derive identical logic from different sources — there's a TODO in each to consolidate via a shared helper later. The game-logic Config no longer stores a ServerConfig/ClientEnv reference and its serverConfig() getter is gone; the one caller (MultiTabModal) now reads ClientEnv.env() directly. Worker init no longer carries server-config values since nothing in the worker actually reads them. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan --- .github/workflows/deploy.yml | 2 + deploy.sh | 7 + index.html | 10 +- package.json | 2 +- src/client/AccountModal.ts | 5 +- src/client/ClientEnv.ts | 120 ++ src/client/ClientGameRunner.ts | 6 +- src/client/GameModeSelector.ts | 6 +- src/client/HostLobbyModal.ts | 8 +- src/client/JoinLobbyModal.ts | 11 +- src/client/LobbySocket.ts | 5 +- src/client/LocalServer.ts | 6 +- src/client/Main.ts | 22 +- src/client/Matchmaking.ts | 37 +- src/client/Transport.ts | 5 +- src/client/components/CopyButton.ts | 5 +- src/client/graphics/AnimatedSpriteLoader.ts | 2 +- src/client/graphics/SpriteLoader.ts | 2 +- src/client/graphics/fx/SpriteFx.ts | 2 +- src/client/graphics/layers/FxLayer.ts | 2 +- src/client/graphics/layers/MultiTabModal.ts | 3 +- src/client/graphics/layers/NameLayer.ts | 3 +- .../graphics/layers/StructureDrawingUtils.ts | 2 +- .../graphics/layers/StructureIconsLayer.ts | 2 +- src/client/graphics/layers/StructureLayer.ts | 2 +- src/client/graphics/layers/TerrainLayer.ts | 3 +- src/client/graphics/layers/TerritoryLayer.ts | 2 +- src/client/graphics/layers/UILayer.ts | 2 +- src/client/graphics/layers/UnitLayer.ts | 2 +- src/core/AssetUrls.ts | 19 +- src/core/GameRunner.ts | 4 +- src/core/configuration/Config.ts | 1074 ++++++++++++++--- src/core/configuration/ConfigLoader.ts | 119 -- src/core/configuration/DefaultConfig.ts | 1014 ---------------- src/core/configuration/DevConfig.ts | 55 - src/core/configuration/Env.ts | 89 -- src/core/configuration/PastelTheme.ts | 2 +- src/core/configuration/PreprodConfig.ts | 26 - src/core/configuration/ProdConfig.ts | 17 - src/core/configuration/Theme.ts | 31 + src/core/worker/Worker.worker.ts | 2 +- src/server/Archive.ts | 18 +- src/server/GameManager.ts | 7 +- src/server/GamePreviewBuilder.ts | 3 +- src/server/GamePreviewRoute.ts | 20 +- src/server/GameServer.ts | 8 +- src/server/Logger.ts | 12 +- src/server/MapPlaylist.ts | 2 +- src/server/Master.ts | 17 +- src/server/MasterLobbyService.ts | 14 +- src/server/OtelResource.ts | 20 +- src/server/RenderHtml.ts | 11 +- src/server/ServerEnv.ts | 177 +++ src/server/Turnstile.ts | 7 +- src/server/Worker.ts | 46 +- src/server/WorkerMetrics.ts | 11 +- src/server/jwt.ts | 17 +- tests/client/clan/ClanModal.handlers.test.ts | 4 - tests/client/clan/ClanModal.rendering.test.ts | 4 - tests/client/clan/ClanModalTestUtils.ts | 6 - tests/core/configuration/ConfigLoader.test.ts | 95 +- tests/core/game/TrainStation.test.ts | 15 +- tests/core/pathfinding/_fixtures.ts | 9 +- tests/pathfinding/utils.ts | 2 - tests/server/Archive.test.ts | 6 +- tests/server/GameLifecycle.test.ts | 55 +- tests/server/KickPlayerAuthorization.test.ts | 19 - tests/server/MasterLobbyServiceHealth.test.ts | 7 +- tests/server/RenderHtml.test.ts | 9 +- tests/server/ServerEnv.test.ts | 109 ++ tests/util/Setup.ts | 10 +- tests/util/TestConfig.ts | 8 +- tests/util/TestServerConfig.ts | 91 -- vite.config.ts | 6 + 74 files changed, 1627 insertions(+), 1956 deletions(-) create mode 100644 src/client/ClientEnv.ts delete mode 100644 src/core/configuration/ConfigLoader.ts delete mode 100644 src/core/configuration/DefaultConfig.ts delete mode 100644 src/core/configuration/DevConfig.ts delete mode 100644 src/core/configuration/Env.ts delete mode 100644 src/core/configuration/PreprodConfig.ts delete mode 100644 src/core/configuration/ProdConfig.ts create mode 100644 src/core/configuration/Theme.ts create mode 100644 src/server/ServerEnv.ts create mode 100644 tests/server/ServerEnv.test.ts delete mode 100644 tests/util/TestServerConfig.ts diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f1284dcbf..cf3f6e556 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -139,6 +139,8 @@ jobs: OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }} OTEL_AUTH_HEADER: ${{ secrets.OTEL_AUTH_HEADER }} API_KEY: ${{ secrets.API_KEY }} + NUM_WORKERS: ${{ vars.NUM_WORKERS }} + TURNSTILE_SITE_KEY: ${{ vars.TURNSTILE_SITE_KEY }} SERVER_HOST_MASTERS: ${{ secrets.SERVER_HOST_MASTERS }} SERVER_HOST_FALK2: ${{ secrets.SERVER_HOST_FALK2 }} SERVER_HOST_STAGING: ${{ secrets.SERVER_HOST_STAGING }} diff --git a/deploy.sh b/deploy.sh index 53c90df21..1a78dbd0f 100755 --- a/deploy.sh +++ b/deploy.sh @@ -66,6 +66,11 @@ else GHCR_IMAGE="${GHCR_USERNAME}/${GHCR_REPO}:${VERSION_TAG}" fi +if [ -z "$DOMAIN" ]; then + echo "Error: DOMAIN not defined in .env file or environment" + exit 1 +fi + if [ "$HOST" == "staging" ]; then print_header "DEPLOYING TO STAGING HOST" SERVER_HOST=$SERVER_HOST_STAGING @@ -138,6 +143,8 @@ API_KEY=$API_KEY DOMAIN=$DOMAIN SUBDOMAIN=$SUBDOMAIN CDN_BASE=$CDN_BASE +NUM_WORKERS=$NUM_WORKERS +TURNSTILE_SITE_KEY=$TURNSTILE_SITE_KEY OTEL_EXPORTER_OTLP_ENDPOINT=$OTEL_EXPORTER_OTLP_ENDPOINT OTEL_AUTH_HEADER=$OTEL_AUTH_HEADER EOL diff --git a/index.html b/index.html index b196749fd..aca2853cd 100644 --- a/index.html +++ b/index.html @@ -60,11 +60,15 @@