[studio] beta studio (#114)

* rrs stuff

* studio service yay

* rrs whitelist

* moree studiooooooo fellowwww

* rec room studio room loading yay

* fixes yay

* fix 2 because idk  yayuh

* Update cdn.app.ts

* ccontent length fix?

* Preserve content-length and upgrade metadata

Return a real zero-filled byte array for CDN HEAD responses so the runtime preserves Content-Length while still sending no payload. In notify, re-create the Request with its original headers, set OWNER_HEADER from the validated token, and forward that Request to hub.fetch to avoid losing WebSocket upgrade metadata (which could trigger 500s in Durable Objects). These changes ensure the editor's size checks and WebSocket upgrades remain reliable.

* fix x524234

* shirt thing?

* [auth] remove grant table as we already have audit_log for now

* rename table to generic device_grant

* cleanup/lint

---------

Co-authored-by: Devin Zuczek <devin.zuczek@gmail.com>
This commit is contained in:
Crasho
2026-10-05 19:39:22 +01:00
committed by GitHub
parent 0900bcac25
commit 26270e9c25
52 changed files with 18231 additions and 103 deletions
+24
View File
@@ -157,6 +157,17 @@ export interface Account {
* real: the website's claim page warns about it, and so does `grant-plus`.
*/
hasPlus?: boolean
/**
* Whether this account may upload from Rec Room Studio. The editor decides Full access
* by reading the token for a `role` claim of exactly `betastudio`, which `auth` stamps
* from this flag alone — `developer` does not imply it. Staff set and clear it from the
* website (`www` `/settings/recroomstudio`); an operator can too, via
* `runx admin grant-studio`. Absent/false means Limited.
*
* Like the other flags it reaches the editor on the NEXT token: a Studio session already
* open keeps its old access until it signs in again or refreshes.
*/
hasStudio?: boolean
}
interface AccountRow {
@@ -414,6 +425,19 @@ export async function getAccountsByIds(db: D1Database, ids: number[]): Promise<A
return parseAll(pages.flatMap((page) => page.results))
}
/**
* Every account flagged `hasStudio`, lowest id first — the website's staff list. A JSON
* scan rather than a generated column: the list is short and only staff read it.
*/
export async function listStudioAccounts(db: D1Database): Promise<Account[]> {
const { results } = await db
.prepare(
`SELECT data FROM account WHERE json_extract(data, '$.hasStudio') = 1 ORDER BY account_id`
)
.all<AccountRow>()
return parseAll(results)
}
/**
* Merge `overrides` into the account row for `id` and persist it. Reads the
* current account (falling back to a synthesized default), applies the