mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-08 14:41:28 -07:00
addd a few more endpoints
This commit is contained in:
+33
-1
@@ -386,6 +386,15 @@ const app = new Hono<App>({ strict: false })
|
||||
})
|
||||
.post('/api/sanitize/v1/isPure', (c) => c.json({ IsPure: true }))
|
||||
|
||||
// Keepsakes (room mementos). Shapes from the 2025 reference; categories isn't
|
||||
// in any reference, so it's stubbed empty. The client fetches these on room
|
||||
// entry — a 404 stalls the load.
|
||||
.get('/api/keepsakes/globalconfig', (c) =>
|
||||
c.json({ KeepsakeFeatureEnabled: true, KeepsakeRoomLimit: 10, SocialXpBoostEnabled: false })
|
||||
)
|
||||
.get('/api/keepsakes/rooms/:roomId', (c) => c.body(null, 204))
|
||||
.get('/api/keepsakes/categories', (c) => c.json([]))
|
||||
|
||||
// ---- Player reporting -----------------------------------------------------
|
||||
.get('/api/PlayerReporting/v1/moderationBlockDetails', (c) =>
|
||||
c.json({
|
||||
@@ -489,7 +498,30 @@ const app = new Hono<App>({ strict: false })
|
||||
})
|
||||
|
||||
// ---- Rooms ----------------------------------------------------------------
|
||||
.get('/api/rooms/v1/filters', (c) => c.json([])) // TODO: hydrate from JSON/roomfilters.json
|
||||
// Room search filters. The client deserializes this into an object (not an
|
||||
// array) — shape from the 2025 reference.
|
||||
.get('/api/rooms/v1/filters', (c) =>
|
||||
c.json({
|
||||
PinnedFilters: [
|
||||
'recroomoriginal',
|
||||
'community',
|
||||
'featured',
|
||||
'quest',
|
||||
'pvp',
|
||||
'hangout',
|
||||
'game',
|
||||
'art',
|
||||
'store',
|
||||
'tutorial',
|
||||
'fandom',
|
||||
'performance',
|
||||
'action',
|
||||
'horror',
|
||||
],
|
||||
PopularFilters: ['pvp', 'quest', 'game', 'hangout', 'art'],
|
||||
TrendingFilters: ['roleplay', 'nomp', 'rp', 'casual', 'fun', 'action', 'military', 'sports'],
|
||||
})
|
||||
)
|
||||
|
||||
// ---- Room server ----------------------------------------------------------
|
||||
// Register specific paths before the `/:id` param route.
|
||||
|
||||
@@ -148,6 +148,28 @@ describe('public endpoints', () => {
|
||||
expect(await res.json()).toBe(true)
|
||||
})
|
||||
|
||||
test('GET /api/rooms/v1/filters returns an object with filter arrays', async () => {
|
||||
const res = await exports.default.fetch(`${ORIGIN}/api/rooms/v1/filters`)
|
||||
expect(res.status).toBe(200)
|
||||
const body = (await res.json()) as { PinnedFilters: string[]; PopularFilters: string[] }
|
||||
expect(Array.isArray(body.PinnedFilters)).toBe(true)
|
||||
expect(Array.isArray(body.PopularFilters)).toBe(true)
|
||||
})
|
||||
|
||||
test('GET /api/keepsakes/globalconfig returns the keepsake config', async () => {
|
||||
const res = await exports.default.fetch(`${ORIGIN}/api/keepsakes/globalconfig`)
|
||||
expect(res.status).toBe(200)
|
||||
expect(await res.json()).toMatchObject({ KeepsakeFeatureEnabled: true })
|
||||
})
|
||||
|
||||
test('GET /api/keepsakes/rooms/:id returns 204; categories returns []', async () => {
|
||||
const room = await exports.default.fetch(`${ORIGIN}/api/keepsakes/rooms/1`)
|
||||
expect(room.status).toBe(204)
|
||||
const cats = await exports.default.fetch(`${ORIGIN}/api/keepsakes/categories`)
|
||||
expect(cats.status).toBe(200)
|
||||
expect(await cats.json()).toEqual([])
|
||||
})
|
||||
|
||||
test('GET /voice/config returns an object', async () => {
|
||||
const res = await exports.default.fetch(`${ORIGIN}/voice/config`)
|
||||
expect(res.status).toBe(200)
|
||||
|
||||
@@ -165,6 +165,8 @@ const app = new Hono<App>()
|
||||
|
||||
// The player's room keys. The C# returns "[]".
|
||||
.get('/api/roomkeys/v1/mine', (c) => c.json([]))
|
||||
// Room keys for a given room (client calls this on the econ host). [] with no DB.
|
||||
.get('/api/roomkeys/v1/room', (c) => c.json([]))
|
||||
|
||||
// Subscription lookup. The C# returns both fields null with no auth.
|
||||
.post('/api/CampusCard/v1/UpdateAndGetSubscription', (c) =>
|
||||
|
||||
@@ -146,6 +146,12 @@ describe('econ endpoints', () => {
|
||||
expect(await res.json()).toEqual([])
|
||||
})
|
||||
|
||||
test('GET /api/roomkeys/v1/room returns []', async () => {
|
||||
const res = await exports.default.fetch(`${ORIGIN}/api/roomkeys/v1/room?roomId=1`)
|
||||
expect(res.status).toBe(200)
|
||||
expect(await res.json()).toEqual([])
|
||||
})
|
||||
|
||||
test('GET /api/roomconsumables/v1/roomConsumable/room/:id/me returns []', async () => {
|
||||
const res = await exports.default.fetch(
|
||||
`${ORIGIN}/api/roomconsumables/v1/roomConsumable/room/1/me`
|
||||
|
||||
@@ -349,6 +349,10 @@ const app = new Hono<App>()
|
||||
return c.json({ errorCode: 0, roomInstance: instance })
|
||||
})
|
||||
|
||||
// Region ping reports — accept-and-ack (the reference returns Ok()).
|
||||
.put('/player/photonregionpings', (c) => c.body(null, 200))
|
||||
.put('/player/gameserverregionpings', (c) => c.body(null, 200))
|
||||
|
||||
// ---- Room instance -------------------------------------------------------
|
||||
.post('/roominstance/:id/reportjoinresult', (c) => c.body(null, 200))
|
||||
|
||||
|
||||
@@ -118,6 +118,11 @@ describe('public endpoints', () => {
|
||||
expect(res.status).toBe(200)
|
||||
})
|
||||
|
||||
test('PUT /player/photonregionpings returns 200', async () => {
|
||||
const res = await exports.default.fetch(`${ORIGIN}/player/photonregionpings`, { method: 'PUT' })
|
||||
expect(res.status).toBe(200)
|
||||
})
|
||||
|
||||
test('POST /roominstance/:id/reportjoinresult returns 200', async () => {
|
||||
const res = await exports.default.fetch(`${ORIGIN}/roominstance/5/reportjoinresult`, {
|
||||
method: 'POST',
|
||||
|
||||
+52
-27
@@ -24,6 +24,34 @@ import type { App } from './context'
|
||||
/** Unity scene id for the dorm (also the matchmake/heartbeat instance location). */
|
||||
const DORM_SCENE_ID = '76d98498-60a1-430c-ab76-b54a29b7a163'
|
||||
|
||||
/** Room permissions + (empty) Photon token the client needs to spawn into a room. */
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
function buildRoomResponse(roomId: number) {
|
||||
const isDorm = roomId === 1
|
||||
return {
|
||||
@@ -113,6 +141,26 @@ const app = new Hono<App>()
|
||||
return c.json(buildRoomResponse(1))
|
||||
})
|
||||
|
||||
// Bulk room lookup by `id` (synthesized per id) or `name`. The client calls
|
||||
// this bare on the rooms host. We have no named-room data, so a name lookup
|
||||
// returns [] — the client treats that as NoSuchRoom (a non-fatal warning),
|
||||
// which is the honest answer since we can't actually host that room.
|
||||
.get('/rooms/bulk', (c) => {
|
||||
const idParam = c.req.query('id')
|
||||
const nameParam = c.req.query('name')
|
||||
if (!idParam && !nameParam) {
|
||||
return c.json("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(ids.map(buildRoomResponse))
|
||||
}
|
||||
return c.json([])
|
||||
})
|
||||
|
||||
// Single room by id. The C# 404s when the row is missing; with no DB we
|
||||
// synthesize the room so the client can load it (ignores the include/
|
||||
// unityAsset* query params, same as the C#).
|
||||
@@ -127,32 +175,9 @@ const app = new Hono<App>()
|
||||
// Photon access token + room permissions the client needs to spawn into a
|
||||
// room. Without it the player is stuck on a black screen. PhotonAccessToken is
|
||||
// empty (the client uses its baked-in Photon credentials); roomInstanceId is
|
||||
// our constant 1.
|
||||
.get('/roomserver/photon_access_token', (c) => {
|
||||
const perm = (Permission: string, Role: number, Override: boolean) => ({
|
||||
Override,
|
||||
Permission,
|
||||
Role,
|
||||
Type: 0,
|
||||
Value: 'True',
|
||||
})
|
||||
return c.json({
|
||||
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,
|
||||
})
|
||||
})
|
||||
// our constant 1. The client calls it on the rooms host both bare and under
|
||||
// `/roomserver`, so both are registered.
|
||||
.get('/photon_access_token', (c) => c.json(photonAccessToken()))
|
||||
.get('/roomserver/photon_access_token', (c) => c.json(photonAccessToken()))
|
||||
|
||||
export default app
|
||||
|
||||
@@ -55,6 +55,23 @@ describe('rooms endpoints', () => {
|
||||
expect(body.RoomInstanceId).toBe(1)
|
||||
})
|
||||
|
||||
it('GET /rooms/bulk?id= returns an array; ?name= returns []', async () => {
|
||||
const byId = await SELF.fetch(`${ORIGIN}/rooms/bulk?id=1,2`)
|
||||
expect(byId.status).toBe(200)
|
||||
expect(((await byId.json()) as unknown[]).length).toBe(2)
|
||||
const byName = await SELF.fetch(`${ORIGIN}/rooms/bulk?name=RecCenter`)
|
||||
expect(byName.status).toBe(200)
|
||||
expect(await byName.json()).toEqual([])
|
||||
})
|
||||
|
||||
it('GET /photon_access_token (bare) also returns permissions', async () => {
|
||||
const res = await SELF.fetch(`${ORIGIN}/photon_access_token`)
|
||||
expect(res.status).toBe(200)
|
||||
const body = (await res.json()) as { Permissions: unknown[]; RoomInstanceId: number }
|
||||
expect(body.Permissions.length).toBeGreaterThan(0)
|
||||
expect(body.RoomInstanceId).toBe(1)
|
||||
})
|
||||
|
||||
it('GET /roomserver/rooms/createdby/me returns the owned rooms array', async () => {
|
||||
const res = await SELF.fetch(`${ORIGIN}/roomserver/rooms/createdby/me`)
|
||||
expect(res.status).toBe(200)
|
||||
|
||||
Reference in New Issue
Block a user