mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-25 19:42:45 +00:00
c55ea6bb5a
## What Game creation no longer requires the caller to pick the `gameID` or compute its owning worker. The client POSTs to a prefix-less `/api/create_game`; **nginx (prod) and the vite dev proxy randomly route it to a worker**, which **mints an id that hashes back to itself** and returns it along with its `workerIndex`. ## Why it stays correct The minted id still hashes to the creating worker (via the existing `generateGameIdForWorker`), so everything downstream that derives the worker from the gameID — websocket connect, share URL, join flow — keeps working unchanged. The only thing that moved is *who picks the id and worker*. ## Changes - **`src/server/Worker.ts`** — factor create into a shared `createGameForId`; add `POST /api/create_game` (no id) that mints a self-owned id and returns `gameInfo` + `workerIndex`/`workerPath`. The existing `POST /api/create_game/:id` stays. - **`nginx.conf`** — `location = /api/create_game` proxies to a `random` worker upstream. - **`generate-nginx-upstream.sh` + `Dockerfile`** — the entrypoint generates that upstream from `NUM_WORKERS` at container **start** time. `NUM_WORKERS` isn't known at image build time (the image is built once and deployed with different env), so it can't be baked into `nginx.conf` — hence runtime generation of exactly the live worker ports (no dead-server padding). - **`vite.config.ts`** — dev-only middleware forwards `POST /api/create_game` to a random worker. Vite's `http-proxy` can't pick a per-request random target, so this is a small middleware plugin (same pattern as the existing `serveProprietaryDir`), registered before the `/api` proxy. - **`src/client/HostLobbyModal.ts`** — stop generating the id client-side; use the server's. ## Behavior change to note The host's share link used to be copied **instantly** from a client-generated id. Now the id comes from the server, so the copy waits one create round-trip — I moved the URL build/copy into the create `.then` (and kept the failure path that clears the clipboard). Brief empty-link state in the modal until create resolves. ## Verification - tsc + eslint clean; full suite green (1543 tests). - nginx additions validated with `nginx -t` in isolation (the full file references container-only paths like `/etc/nginx/mime.types`); upstream + `proxy_pass` resolve. - `generate-nginx-upstream.sh` tested with `NUM_WORKERS` set and unset (defaults to 1). Not yet exercised live end-to-end (needs a dev-server restart — `vite.config.ts` + `Worker.ts` changes aren't hot-reloaded). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
65 lines
1.5 KiB
TypeScript
65 lines
1.5 KiB
TypeScript
import { execFileSync } from "child_process";
|
|
import fs from "fs";
|
|
import os from "os";
|
|
import path from "path";
|
|
import { fileURLToPath } from "url";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const SCRIPT = path.resolve(__dirname, "../generate-nginx-upstream.sh");
|
|
|
|
// Run the script with the given NUM_WORKERS (undefined = unset) and return the
|
|
// generated config text.
|
|
function generate(numWorkers?: string): string {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "nginx-upstream-"));
|
|
const out = path.join(dir, "00-workers.conf");
|
|
const env = { ...process.env };
|
|
if (numWorkers === undefined) {
|
|
delete env.NUM_WORKERS;
|
|
} else {
|
|
env.NUM_WORKERS = numWorkers;
|
|
}
|
|
try {
|
|
execFileSync("sh", [SCRIPT, out], { env });
|
|
return fs.readFileSync(out, "utf8");
|
|
} finally {
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
describe("generate-nginx-upstream.sh", () => {
|
|
it("generates the upstream + worker port map for NUM_WORKERS=3", () => {
|
|
expect(generate("3")).toBe(
|
|
`upstream openfront_workers {
|
|
random;
|
|
server 127.0.0.1:3001;
|
|
server 127.0.0.1:3002;
|
|
server 127.0.0.1:3003;
|
|
}
|
|
|
|
map $worker $worker_port {
|
|
default 3001;
|
|
0 3001;
|
|
1 3002;
|
|
2 3003;
|
|
}
|
|
`,
|
|
);
|
|
});
|
|
|
|
it("defaults to a single worker when NUM_WORKERS is unset", () => {
|
|
expect(generate(undefined)).toBe(
|
|
`upstream openfront_workers {
|
|
random;
|
|
server 127.0.0.1:3001;
|
|
}
|
|
|
|
map $worker $worker_port {
|
|
default 3001;
|
|
0 3001;
|
|
}
|
|
`,
|
|
);
|
|
});
|
|
});
|