mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-09 07:01:27 -07:00
more stubs
This commit is contained in:
@@ -312,6 +312,9 @@ export async function cloneRoom(
|
||||
clonedSubRooms.push(await insertSubRoom(db, newRoomId, { ...sub, CreatorAccountId: accountId }))
|
||||
}
|
||||
cloned.SubRooms = clonedSubRooms
|
||||
// Inherited from the (parsed) source in practice; defaulted here too so a clone is
|
||||
// never the one room shape missing them.
|
||||
attachRoomDtoDefaults(cloned)
|
||||
return cloned
|
||||
}
|
||||
|
||||
@@ -869,6 +872,24 @@ interface RoomRow {
|
||||
*/
|
||||
const ROOM_COLUMNS = 'data, visits'
|
||||
|
||||
/**
|
||||
* Two keys on the client's room DTO that nothing here stores, defaulted on every read so
|
||||
* the key is PRESENT rather than absent — the seed blobs and every room written since
|
||||
* predate them, so they can't come from the data:
|
||||
*
|
||||
* - `BoostCount` — how many boosts the room is carrying. No boost feature exists here, so
|
||||
* it is 0 for every room.
|
||||
* - `CurrentSnapshotId` — the room's published snapshot. Nothing takes snapshots, so it is
|
||||
* null, which is also what the reference serves for a room that has none.
|
||||
*
|
||||
* Defaulted rather than assigned, so a stored value wins if either is ever really written
|
||||
* (a blob keeps whatever `serializeRoom` last put in it).
|
||||
*/
|
||||
function attachRoomDtoDefaults(room: Room): void {
|
||||
room.BoostCount ??= 0
|
||||
room.CurrentSnapshotId ??= null
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a room row: the stored blob with the counters the columns own folded back in.
|
||||
* `visits` is a real column, so a room read straight from the DB carries the live count.
|
||||
@@ -876,6 +897,7 @@ const ROOM_COLUMNS = 'data, visits'
|
||||
const parseRow = (row: RoomRow): Room => {
|
||||
const room = JSON.parse(row.data) as Room
|
||||
room.Stats = { ...storedStats(room.Stats), VisitCount: row.visits ?? 0 }
|
||||
attachRoomDtoDefaults(room)
|
||||
return room
|
||||
}
|
||||
|
||||
@@ -2066,5 +2088,7 @@ export async function getOrCreateDormRoom(db: D1Database, accountId: number): Pr
|
||||
await db.prepare('INSERT INTO room (data) VALUES (?1)').bind(serializeRoom(room)).run()
|
||||
const subRoom = await insertSubRoom(db, roomId, { ...templateSub, CreatorAccountId: accountId })
|
||||
room.SubRooms = [subRoom]
|
||||
// The template carries these (it was parsed), but a dorm minted without one wouldn't.
|
||||
attachRoomDtoDefaults(room)
|
||||
return room
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export {
|
||||
validateAndGetAccountId,
|
||||
validateAndGetRoles,
|
||||
validateAndGetVersion,
|
||||
generateToken,
|
||||
generatePhotonAuthToken,
|
||||
TOKEN_TTL_SECONDS,
|
||||
|
||||
+32
-2
@@ -82,6 +82,31 @@ export async function validateAndGetRoles(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a request's bearer token and return its `rn.ver` claim — the game build the
|
||||
* caller posted to `/connect/token`, stamped by {@link generateToken}. `null` when the
|
||||
* request carries no valid token, and `null` too when a valid token has no `rn.ver` (an
|
||||
* older token, issued before the claim carried the client's own value): callers fall back
|
||||
* to what they stored or to GAME_VERSION rather than writing an empty version, which
|
||||
* breaks the client's presence handling.
|
||||
*/
|
||||
export async function validateAndGetVersion(
|
||||
request: Request,
|
||||
secret: string
|
||||
): Promise<string | null> {
|
||||
const authHeader = request.headers.get('Authorization')
|
||||
if (!authHeader || !authHeader.toLowerCase().startsWith('bearer ')) return null
|
||||
|
||||
const token = authHeader.slice('bearer '.length)
|
||||
try {
|
||||
const payload = await verify(token, secret, 'HS256') // checks exp/nbf/signature
|
||||
const version = payload['rn.ver']
|
||||
return typeof version === 'string' && version !== '' ? version : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/** Scopes stamped onto every token (as a claim array). */
|
||||
const TOKEN_SCOPES = [
|
||||
'profile',
|
||||
@@ -163,7 +188,8 @@ export async function generateToken(
|
||||
platform: number,
|
||||
secret: string,
|
||||
extraRoles: string[] = [],
|
||||
privileges: string[] = []
|
||||
privileges: string[] = [],
|
||||
version: string = GAME_VERSION
|
||||
): Promise<string> {
|
||||
const now = Math.floor(Date.now() / 1000)
|
||||
// The client reads `role`/`scope` (and expects a well-formed iss/aud) to
|
||||
@@ -182,7 +208,11 @@ export async function generateToken(
|
||||
idp: 'local',
|
||||
platform,
|
||||
platform_id: platformId,
|
||||
'rn.ver': GAME_VERSION,
|
||||
// The CLIENT's build, as it posted it to /connect/token (`ver`) — not this
|
||||
// server's GAME_VERSION, which is only the fallback for a grant that names none
|
||||
// (a refresh, or a caller that isn't the game). Presence reads it back off the
|
||||
// token, so a player's reported version is the build they are actually running.
|
||||
'rn.ver': version,
|
||||
'rn.plat': platform,
|
||||
role: [...BASE_ROLES, ...extraRoles],
|
||||
// `rn.privilege` LOOKS like a scope but is a claim: the client reads it out of
|
||||
|
||||
Reference in New Issue
Block a user