From 2a95b5ba9b8081d533e9085d80c5cb21300422db Mon Sep 17 00:00:00 2001 From: Devin Zuczek Date: Tue, 7 Jul 2026 00:57:16 -0400 Subject: [PATCH] remove old /roomserver/ endpoints --- apps/api/src/api.app.ts | 71 +---------------------- apps/api/src/context.ts | 2 +- apps/api/src/rooms-db.ts | 34 +---------- apps/api/src/test/integration/api.test.ts | 71 +---------------------- 4 files changed, 8 insertions(+), 170 deletions(-) diff --git a/apps/api/src/api.app.ts b/apps/api/src/api.app.ts index fe6d5f3..a5da1cf 100644 --- a/apps/api/src/api.app.ts +++ b/apps/api/src/api.app.ts @@ -17,7 +17,7 @@ import { getPlayerFeed, } from './images-db' import { validateAndGetAccountId } from './jwt' -import { getRoomById, getRoomByName, getRoomsByCreator, getRoomsByIds } from './rooms-db' +import { getRoomById } from './rooms-db' import type { Context } from 'hono' import type { App } from './context' @@ -85,39 +85,6 @@ function queryIds(c: Context): number[] { ) } -/** - * Photon access-token response (`/roomserver/photon_access_token`). The 2023 - * client calls this to get its room permissions + the instance id it's spawning - * into; a 404 here leaves the player stuck on a black screen. `PhotonAccessToken` - * is empty — the client uses its baked-in Photon credentials. Our synthesized - * instances always use roomInstanceId 1. - */ -function photonAccessToken() { - const perm = (Permission: string, Role: number, Override: boolean) => ({ - Override, - Permission, - Role, - Type: 0, - Value: 'True', - }) - return { - Permissions: [ - perm('CAN_USE_ROOM_RESET_BUTTON', 0, true), - perm('CAN_USE_DELETE_ALL_BUTTON', 0, true), - perm('CAN_SAVE_INVENTIONS', 0, true), - perm('CAN_SPAWN_INVENTIONS', 0, true), - perm('CAN_USE_PLAY_GIZMOS_TOGGLE', 0, true), - perm('CAN_USE_MAKER_PEN', 30, false), - perm('CAN_USE_ROOM_RESET_BUTTON', 30, true), - perm('CAN_USE_DELETE_ALL_BUTTON', 30, true), - perm('CAN_SAVE_INVENTIONS', 30, true), - perm('CAN_SPAWN_INVENTIONS', 30, true), - perm('CAN_USE_PLAY_GIZMOS_TOGGLE', 30, true), - ], - PhotonAccessToken: '', - RoomInstanceId: 1, - } -} /** Default reputation for an account — the fallback used with no DB. */ function defaultReputation(id: number) { @@ -606,40 +573,4 @@ const app = new Hono({ strict: false }) return c.json(hasRole) }) - // ---- Room server ---------------------------------------------------------- - // Room data is read from the shared `recflare` D1 (owned by the rooms worker). - // Register specific paths before the `/:id` param route. - .get('/roomserver/rooms/bulk', async (c) => { - const idParam = c.req.query('id') - const nameParam = c.req.query('name') - if (!idParam && !nameParam) { - return c.text("Either 'id' or 'name' query parameter is required", 400) - } - if (idParam) { - const ids = idParam - .split(',') - .map((s) => Number.parseInt(s.trim(), 10)) - .filter((n) => !Number.isNaN(n)) - return c.json(await getRoomsByIds(c.env.DB, ids)) - } - const room = await getRoomByName(c.env.DB, nameParam ?? '') - return c.json(room ? [room] : []) - }) - // Photon access token + room permissions the client needs to spawn into a room. - .get('/roomserver/photon_access_token', (c) => c.json(photonAccessToken())) - .get('/roomserver/rooms/hot', (c) => c.json({ Results: [], TotalResults: 0 })) - .get('/roomserver/roomsandplaylists/hot', (c) => c.json({ Results: [], TotalResults: 0 })) - .get('/roomserver/rooms/createdby/me', async (c) => - c.json(await getRoomsByCreator(c.env.DB, (await authedId(c)) ?? 1)) - ) - .get('/roomserver/rooms/:id/interactionby/me', (c) => - c.json({ Cheered: false, Favorited: false }) - ) - .get('/roomserver/rooms/:id', async (c) => { - const roomId = Number.parseInt(c.req.param('id'), 10) - if (Number.isNaN(roomId)) return c.notFound() - const room = await getRoomById(c.env.DB, roomId) - return room ? c.json(room) : c.notFound() - }) - export default app diff --git a/apps/api/src/context.ts b/apps/api/src/context.ts index c883cf8..a8a68d3 100644 --- a/apps/api/src/context.ts +++ b/apps/api/src/context.ts @@ -13,7 +13,7 @@ export type Env = SharedHonoEnv & { */ DOMAIN: string // Shared rooms database (schema/migrations owned by the `rooms` worker). Used - // read-only here for the /roomserver/rooms/* endpoints. + // read-only here to resolve room roles for `/api/rooms/v1/verifyRole`. DB: D1Database // Image bucket (shared with the `img` worker, which serves objects back by // key). Uploaded saved images are written here. diff --git a/apps/api/src/rooms-db.ts b/apps/api/src/rooms-db.ts index ce48b78..3235fe8 100644 --- a/apps/api/src/rooms-db.ts +++ b/apps/api/src/rooms-db.ts @@ -1,9 +1,9 @@ /** * Read helpers for the shared `recflare` D1 database. The schema, migrations, * and seed are owned by the `rooms` worker (apps/rooms/src/rooms-db.ts + - * migrations); this worker binds the same database read-only for its - * `/roomserver/rooms/*` endpoints. Keep these queries in sync with the rooms - * worker's. + * migrations); this worker binds the same database read-only to resolve room + * roles for the `/api/rooms/v1/verifyRole` endpoint. Keep these queries in sync + * with the rooms worker's. */ /** A stored room — the parsed JSON blob (full client-facing room response). */ @@ -14,37 +14,9 @@ interface RoomRow { } const parseOne = (row: RoomRow | null): Room | null => (row ? (JSON.parse(row.data) as Room) : null) -const parseAll = (rows: RoomRow[]): Room[] => rows.map((r) => JSON.parse(r.data) as Room) export async function getRoomById(db: D1Database, roomId: number): Promise { return parseOne( await db.prepare('SELECT data FROM rooms WHERE room_id = ?1').bind(roomId).first() ) } - -export async function getRoomByName(db: D1Database, name: string): Promise { - return parseOne( - await db - .prepare('SELECT data FROM rooms WHERE name_lower = ?1') - .bind(name.toLowerCase()) - .first() - ) -} - -export async function getRoomsByIds(db: D1Database, ids: number[]): Promise { - if (ids.length === 0) return [] - const placeholders = ids.map((_, i) => `?${i + 1}`).join(',') - const { results } = await db - .prepare(`SELECT data FROM rooms WHERE room_id IN (${placeholders})`) - .bind(...ids) - .all() - return parseAll(results) -} - -export async function getRoomsByCreator(db: D1Database, accountId: number): Promise { - const { results } = await db - .prepare('SELECT data FROM rooms WHERE creator_account_id = ?1') - .bind(accountId) - .all() - return parseAll(results) -} diff --git a/apps/api/src/test/integration/api.test.ts b/apps/api/src/test/integration/api.test.ts index f607186..67b5123 100644 --- a/apps/api/src/test/integration/api.test.ts +++ b/apps/api/src/test/integration/api.test.ts @@ -15,16 +15,9 @@ declare module 'cloudflare:test' { const ORIGIN = 'https://example.com' -// The /roomserver/rooms/* routes read from the shared recflare D1. Set up the -// schema (matching the rooms worker's migration) + a couple of rooms for tests. +// `/api/rooms/v1/verifyRole` reads room roles from the shared recflare D1. Set +// up the schema (matching the rooms worker's migration) + a couple of rooms. const TEST_ROOMS = [ - { - RoomId: 1, - Name: 'DormRoom', - IsDorm: true, - CreatorAccountId: 1, - SubRooms: [{ SubRoomId: 1, UnitySceneId: '76d98498-60a1-430c-ab76-b54a29b7a163' }], - }, { RoomId: 2, Name: 'RecCenter', @@ -307,25 +300,7 @@ describe('auth-gated endpoints', () => { }) }) -describe('room server', () => { - test('GET /roomserver/rooms/bulk requires id or name', async () => { - const res = await exports.default.fetch(`${ORIGIN}/roomserver/rooms/bulk`) - expect(res.status).toBe(400) - }) - - test('GET /roomserver/rooms/bulk with id returns rooms from D1', async () => { - const res = await exports.default.fetch(`${ORIGIN}/roomserver/rooms/bulk?id=1,2`) - expect(res.status).toBe(200) - const rooms = (await res.json()) as Array<{ RoomId: number; Name: string }> - expect(rooms.map((r) => r.RoomId).sort((a, b) => a - b)).toEqual([1, 2]) - }) - - test('GET /roomserver/rooms/bulk?name= resolves from D1', async () => { - const res = await exports.default.fetch(`${ORIGIN}/roomserver/rooms/bulk?name=reccenter`) - const rooms = (await res.json()) as Array<{ Name: string }> - expect(rooms.map((r) => r.Name)).toEqual(['RecCenter']) - }) - +describe('rooms', () => { test('POST /api/rooms/v1/verifyRole checks creator + room roles', async () => { const verify = async (fields: Record, sub?: string): Promise => { const res = await exports.default.fetch(`${ORIGIN}/api/rooms/v1/verifyRole`, { @@ -353,46 +328,6 @@ describe('room server', () => { // Unknown room → false. expect(await verify({ roomId: '99999', role: '0' }, '42')).toBe(false) }) - - test('GET /roomserver/photon_access_token returns permissions + instance id', async () => { - const res = await exports.default.fetch(`${ORIGIN}/roomserver/photon_access_token`) - expect(res.status).toBe(200) - const body = (await res.json()) as { - Permissions: unknown[] - PhotonAccessToken: string - RoomInstanceId: number - } - expect(Array.isArray(body.Permissions)).toBe(true) - expect(body.Permissions.length).toBeGreaterThan(0) - expect(body).toMatchObject({ PhotonAccessToken: '', RoomInstanceId: 1 }) - }) - - test('GET /roomserver/rooms/hot returns an empty result set', async () => { - const res = await exports.default.fetch(`${ORIGIN}/roomserver/rooms/hot`) - expect(await res.json()).toEqual({ Results: [], TotalResults: 0 }) - }) - - test('GET /roomserver/rooms/:id returns the room from D1', async () => { - const res = await exports.default.fetch(`${ORIGIN}/roomserver/rooms/1`) - expect(res.status).toBe(200) - const room = (await res.json()) as { - RoomId: number - IsDorm: boolean - SubRooms: Array<{ UnitySceneId: string }> - } - expect(room).toMatchObject({ RoomId: 1, IsDorm: true }) - expect(room.SubRooms[0].UnitySceneId).toBe('76d98498-60a1-430c-ab76-b54a29b7a163') - }) - - test('GET /roomserver/rooms/:id 404s for an unknown room', async () => { - const res = await exports.default.fetch(`${ORIGIN}/roomserver/rooms/99999`) - expect(res.status).toBe(404) - }) - - test('GET /roomserver/rooms/:id/interactionby/me', async () => { - const res = await exports.default.fetch(`${ORIGIN}/roomserver/rooms/5/interactionby/me`) - expect(await res.json()).toEqual({ Cheered: false, Favorited: false }) - }) }) describe('images', () => {