[plus] discord role verifier to grant RR plus

This commit is contained in:
Devin Zuczek
2026-08-31 15:44:33 -04:00
parent 740e9efa09
commit 8260c5abcd
23 changed files with 1746 additions and 98 deletions
+47 -3
View File
@@ -7,9 +7,11 @@ export type Env = SharedHonoEnv & {
/** Static-asset fetcher for the built React SPA (see wrangler.jsonc `assets`). */
ASSETS: Fetcher
/**
* The shared `recflare` D1, bound READ-ONLY in practice: the only thing www asks it
* is the live presence head-count behind `/server-status`. Every table it can see is
* owned (and migrated) by another worker.
* The shared `recflare` D1. www asks it two things: the live presence head-count
* behind `/server-status`, and the caller's `account` row on the benefits claim —
* which is also the one place www WRITES (the `hasPlus`/`discordUserId` pair, through
* `@repo/domain`'s `updateAccount`, so the blob's shape stays in one module). Every
* table it can see is owned (and migrated) by another worker; www never migrates.
*/
DB: D1Database
/**
@@ -39,6 +41,48 @@ export type Env = SharedHonoEnv & {
* failing to resolve closes web signup — see src/turnstile.ts.
*/
TURNSTILE_SECRET_KEY: SecretsStoreSecret
/**
* The HS256 signing key every worker shares, out of the same account-level Secrets
* Store. www needs it for ONE thing: the benefits claim is the only route here that
* acts on behalf of a specific account (it writes `hasPlus` onto it), so it has to
* establish WHICH account is calling rather than take the SPA's word for it. Every
* other www route is either anonymous or hands the token straight to another worker.
*
* Resolve with `await env.JWT_SECRET.get()`; validate through `@repo/jwt` so the
* signature/exp checks are the ones every other worker runs.
*/
JWT_SECRET: SecretsStoreSecret
/**
* The Discord application's client id. PUBLIC — it ships to the browser, which needs
* it to build the authorize URL — but kept in the Secrets Store beside its secret so
* one place configures the claim, exactly as TURNSTILE_SITE_KEY is.
*/
DISCORD_CLIENT_ID: SecretsStoreSecret
/**
* The Discord application's client secret — what turns an authorization code into an
* access token. Never leaves this worker (see src/discord.ts).
*/
DISCORD_CLIENT_SECRET: SecretsStoreSecret
/**
* The guild (Discord server) whose membership the benefits claim checks, and the roles
* within it that entitle a player to the benefits. Both hold Discord SNOWFLAKES — all
* digits, never a role's display name — kept as strings because a snowflake exceeds
* 2^53. Plain vars rather than secrets: any member of the server can read these off
* their own client, and none of them authorizes anything on its own.
*
* OPTIONAL because an operator who hasn't set up Discord has neither, and that is a
* supported state: it CLOSES the claim (see `discordConfig`) rather than opening an
* unverified one.
*/
DISCORD_GUILD_ID?: string
/**
* The role ids inside DISCORD_GUILD_ID that grant Rec Room Plus — numeric snowflakes,
* separated by commas and/or whitespace, e.g. `"1077000000000000001,1077000000000000002"`.
* ANY one of them qualifies, so several tiers (a supporter role, a booster role, staff)
* can share the same benefit. Parsed by `parseRoleIds`; a value that parses to no ids at
* all closes the claim, exactly as an unset one does.
*/
DISCORD_BENEFITS_ROLE_IDS?: string
}
/** Variables can be extended */