mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-08 14:41:28 -07:00
fix service subdomains env var not working
This commit is contained in:
+11
-4
@@ -7,11 +7,18 @@ discover every service host (Accounts, API, Auth, Econ, Matchmaking,
|
|||||||
Notifications, …).
|
Notifications, …).
|
||||||
|
|
||||||
Each host is built at runtime from the `DOMAIN` var (the base domain) plus the
|
Each host is built at runtime from the `DOMAIN` var (the base domain) plus the
|
||||||
service → subdomain map in `src/endpoints.ts`. `DOMAIN` is injected at deploy
|
service → subdomain map in `src/endpoints.ts`, with the `SUBDOMAINS` var applied
|
||||||
time from `RECFLARE_DOMAIN` (see `run-wrangler-deploy`) and defaults to
|
on top. Both vars are injected at deploy time from `RECFLARE_DOMAIN` and
|
||||||
`rec.example.com` in `wrangler.jsonc` for local dev.
|
`RECFLARE_SUBDOMAINS` (see `run-wrangler-deploy`) and default to
|
||||||
|
`rec.example.com` / `{}` in `wrangler.jsonc` for local dev.
|
||||||
|
|
||||||
## Updating endpoints
|
## Updating endpoints
|
||||||
|
|
||||||
- To change the base domain, set `RECFLARE_DOMAIN` (in `.env`) and redeploy.
|
- To change the base domain, set `RECFLARE_DOMAIN` (in `.env`) and redeploy.
|
||||||
- To add or rename a service host, edit the map in `src/endpoints.ts`.
|
- To point one service at a different host, add it to `RECFLARE_SUBDOMAINS` (in
|
||||||
|
`.env`) and redeploy. It's keyed by the service's _default_ subdomain — the
|
||||||
|
same object `run-wrangler-deploy` reads to pick a worker's host, so an entry
|
||||||
|
moves the deployed worker and the advertised host together. An entry for a
|
||||||
|
service with no worker (e.g. `{"moderation":"api"}`) is a pure client-side
|
||||||
|
redirect onto a host another worker already serves.
|
||||||
|
- To add or rename a service, edit the map in `src/endpoints.ts`.
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ export type Env = SharedHonoEnv & {
|
|||||||
* for local dev and tests.
|
* for local dev and tests.
|
||||||
*/
|
*/
|
||||||
DOMAIN: string
|
DOMAIN: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-service subdomain overrides as a raw JSON object keyed by default subdomain,
|
||||||
|
* e.g. `{"moderation":"api"}`. The operator's `RECFLARE_SUBDOMAINS`, injected at deploy
|
||||||
|
* time via `--var SUBDOMAINS`; defaults to `{}` in `wrangler.jsonc`. See
|
||||||
|
* `parseOverrides` in `endpoints.ts`.
|
||||||
|
*/
|
||||||
|
SUBDOMAINS: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Variables can be extended */
|
/** Variables can be extended */
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Service-discovery map: service label → subdomain. The game client fetches the
|
* Service-discovery map: service label → default subdomain. The game client fetches the
|
||||||
* generated `{ label: "https://<subdomain>.<domain>" }` document from `/`.
|
* generated `{ label: "https://<subdomain>.<domain>" }` document from `/`.
|
||||||
*
|
*
|
||||||
* The base domain is injected at deploy time via the `DOMAIN` var (see
|
* The base domain is injected at deploy time via the `DOMAIN` var (see
|
||||||
* `run-wrangler-deploy`), so the real domain never lives in a versioned file.
|
* `run-wrangler-deploy`), so the real domain never lives in a versioned file. The
|
||||||
|
* subdomains here are defaults — an operator redirects any of them from `.env`, see
|
||||||
|
* `applyOverrides` below.
|
||||||
*/
|
*/
|
||||||
const SERVICE_SUBDOMAINS = {
|
const SERVICE_SUBDOMAINS = {
|
||||||
Accounts: 'accounts',
|
Accounts: 'accounts',
|
||||||
@@ -44,9 +46,48 @@ const SERVICE_SUBDOMAINS = {
|
|||||||
WWW: 'www',
|
WWW: 'www',
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
/** Builds the endpoints document for `domain`, e.g. `rec.example.com`. */
|
/**
|
||||||
export function buildEndpoints(domain: string): Record<string, string> {
|
* Parses the `SUBDOMAINS` var — the operator's `RECFLARE_SUBDOMAINS` object, injected at
|
||||||
|
* deploy time by `run-wrangler-deploy`.
|
||||||
|
*
|
||||||
|
* It is keyed by the DEFAULT subdomain above, not by the service label, because the deploy
|
||||||
|
* script reads the very same object keyed by a worker's directory name — and every worker's
|
||||||
|
* directory name is its default subdomain. So one `.env` entry moves both sides at once:
|
||||||
|
* `{"playersettings":"settings"}` both deploys the `playersettings` worker onto
|
||||||
|
* `settings.<domain>` and advertises that host to the client. Entries naming a service with
|
||||||
|
* no worker of its own are pure client-side redirects — `{"moderation":"api"}` points the
|
||||||
|
* client's Moderation calls at the `api` worker, which is where the
|
||||||
|
* `/api/PlayerReporting/…` routes actually live.
|
||||||
|
*
|
||||||
|
* A malformed value is ignored rather than thrown: this document is the first thing the
|
||||||
|
* client fetches, so a typo in `.env` should cost one redirect, not every service host.
|
||||||
|
*/
|
||||||
|
function parseOverrides(subdomains: string | undefined): Record<string, string> {
|
||||||
|
if (!subdomains) return {}
|
||||||
|
let parsed: unknown
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(subdomains)
|
||||||
|
} catch {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {}
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(SERVICE_SUBDOMAINS).map(([label, sub]) => [label, `https://${sub}.${domain}`])
|
Object.entries(parsed).filter(
|
||||||
|
(entry): entry is [string, string] => typeof entry[1] === 'string' && entry[1] !== ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the endpoints document for `domain`, e.g. `rec.example.com`, applying any
|
||||||
|
* subdomain overrides from `subdomains` (the raw `SUBDOMAINS` var JSON).
|
||||||
|
*/
|
||||||
|
export function buildEndpoints(domain: string, subdomains?: string): Record<string, string> {
|
||||||
|
const overrides = parseOverrides(subdomains)
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(SERVICE_SUBDOMAINS).map(([label, sub]) => [
|
||||||
|
label,
|
||||||
|
`https://${overrides[sub] ?? sub}.${domain}`,
|
||||||
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,6 @@ const app = new Hono<App>()
|
|||||||
.notFound(withNotFound())
|
.notFound(withNotFound())
|
||||||
|
|
||||||
// Endpoints document, derived from the deploy-time base domain.
|
// Endpoints document, derived from the deploy-time base domain.
|
||||||
.get('/', (c) => c.json(buildEndpoints(c.env.DOMAIN)))
|
.get('/', (c) => c.json(buildEndpoints(c.env.DOMAIN, c.env.SUBDOMAINS)))
|
||||||
|
|
||||||
export default app
|
export default app
|
||||||
|
|||||||
@@ -18,6 +18,19 @@ describe('ns endpoints', () => {
|
|||||||
expect(body).toEqual(buildEndpoints(TEST_DOMAIN))
|
expect(body).toEqual(buildEndpoints(TEST_DOMAIN))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('a subdomain override redirects that service only', () => {
|
||||||
|
const endpoints = buildEndpoints(TEST_DOMAIN, '{"moderation":"api"}')
|
||||||
|
expect(endpoints.Moderation).toBe(`https://api.${TEST_DOMAIN}`)
|
||||||
|
expect(endpoints.API).toBe(`https://api.${TEST_DOMAIN}`)
|
||||||
|
expect(endpoints.Accounts).toBe(`https://accounts.${TEST_DOMAIN}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('a malformed override object is ignored', () => {
|
||||||
|
for (const bad of ['', '{', 'null', '[]', '{"moderation":42}', '{"moderation":""}']) {
|
||||||
|
expect(buildEndpoints(TEST_DOMAIN, bad)).toEqual(buildEndpoints(TEST_DOMAIN))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
test('unknown path returns 404', async () => {
|
test('unknown path returns 404', async () => {
|
||||||
const res = await exports.default.fetch(`${ORIGIN}/nope`)
|
const res = await exports.default.fetch(`${ORIGIN}/nope`)
|
||||||
expect(res.status).toBe(404)
|
expect(res.status).toBe(404)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"vars": {
|
"vars": {
|
||||||
"ENVIRONMENT": "development", // overridden during deployment
|
"ENVIRONMENT": "development", // overridden during deployment
|
||||||
"SENTRY_RELEASE": "unknown", // overridden during deployment
|
"SENTRY_RELEASE": "unknown", // overridden during deployment
|
||||||
"DOMAIN": "rec.example.com" // base domain; overridden during deployment
|
"DOMAIN": "rec.example.com", // base domain; overridden during deployment
|
||||||
|
"SUBDOMAINS": "{}" // per-service subdomain overrides; overridden during deployment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user