support for 202507 endpoints (#37)

* [auth][api] accept the 20250424.01 client

* [2025] unstable

* 20250718.0

* correct one this time

* stubs

* more stubs

* more stubs

* [lists] add worker

* [ai] route stubs

* [api] player photo setting

* [econ] add roomEconConfig route

* [infra] update worker generators

* [worker] add cards/moderation/platformnotification workers

* [lists] updates to some endpoints

* [clubs] stub out announcement endpoint, for now

* [econ] stub out season endpoints for now

* [chat] apps/chat stub out party endpoint not sure the shape yet

* [api] stub out statsig and lockeditems

* [doc] new services

* [lists] stub the bulk endpoint

* [datacollection] add placeholder service until we can kill it

* [api] set gifting to lvl5

* update lock

* [cdn] enable cache

* [match] matchmake v2

* [lists] stub some lists

* [ai] stubs

* [rooms] new subroom save endpoint

* [econ] add bulk purchase endpoint

* [discovery] update featured creator to 1 for fun

* [api] add photo settings flag

* [chat] fixup chat permissions (sorta)

* [auth] restrictions endpoint

* [rooms] contributed endpoint

* [api] fix outfit endpoint

* [discovery] attempt to fix store

* [chat] privacy endpoints

* [api] cheered images

* [rooms] add xp endpoint (disbaled)

* [rooms] add xp endpoint (disabled)

* update images-db for cheers

* [rooms] add autocomplete endpoint

* [cdn/img] increase cache ttl for statics

* [api] bulk route for images

* [accounts] add banner image

* [api] add misc missing endpoints

* [discovery] remove AI tab

* [platformnotifications] stub some endpoints

* [lists] add some more lists

* [rooms] additional endpoints

* [chat] stub a few privacy endpoints

* [econ] stub some endpoints

* misc db fixes

* [api] tweak shape for images v6

* [rooms] dont show trending RROs
This commit is contained in:
devin
2026-08-18 23:07:24 -04:00
committed by Devin Zuczek
parent 66c09806f9
commit 178d3b5b0e
162 changed files with 114930 additions and 469 deletions
+118
View File
@@ -0,0 +1,118 @@
import { Hono } from 'hono'
import { describeRoute, openAPIRouteHandler } from 'hono-openapi'
import { useWorkersLogger } from 'workers-tagged-logger'
import { withCleanSpec, withNotFound, withOnError } from '@repo/hono-helpers'
import { DiscoverySections, json, PAGE_SOURCE_PARAM, ServiceStatus } from './openapi'
import { fetchPageSource } from './page-sources'
import type { App } from './context'
/**
* Discovery Worker. Serves the layout of the client's discovery pages — which carousels a
* page shows and in what order — out of `static/`, one file per page source, through the
* ASSETS binding (see `page-sources.ts`). It does not serve the carousels' CONTENTS: each
* section names a client-side feed the client resolves against the `rooms`/`api` workers
* itself.
*
* Unauthenticated: every client gets the same layout, and the client fetches this before
* anything player-specific.
*/
const app = new Hono<App>()
.use(
'*',
// middleware
(c, next) =>
useWorkersLogger(c.env.NAME, {
environment: c.env.ENVIRONMENT,
release: c.env.SENTRY_RELEASE,
})(c, next)
)
.onError(withOnError())
.notFound(withNotFound())
// Root health check.
.get(
'/',
describeRoute({
tags: ['Service'],
summary: 'Health check',
description: 'Liveness probe for the discovery worker. No auth.',
responses: { 200: json(ServiceStatus, 'Service is up') },
}),
(c) => c.json({ service: 'discovery', status: 'ok' })
)
// One discovery page's section layout, served verbatim from `static/<type>.json`.
.get(
'/sections/pagesource/:type',
describeRoute({
tags: ['Discovery'],
summary: 'Section layout for a page source',
description: [
'The sections of one discovery page, in the order the client draws them. `{type}` IS',
'the filename — the body is `static/<type>.json` served verbatim — so the page sources',
'that exist are whichever files are published (`WatchHome`, `PlayHighlight`,',
'`CommunityBoard`, `PlayMenuTabs`, `PlayCategories`, `StoreCategories`,',
'`StoreFeatured`, `StoreClothing`, `StoreConsumables` and `bulk` at the time of',
'writing). The match is exact, case included.',
'',
'This replaces the `Discovery.DiscoveryPageContent.*` game configs, which carried the',
'same layouts as embedded JSON strings: with `Discovery.UseNewDiscoveryServerAPI` set',
'to `True` the client asks this service instead. The two are not the same shape — the',
'configs wrapped the list in `{ pageSource, sections }` with PascalCase fields, while',
'this answers the bare ARRAY with camelCase ones.',
'',
'A section only NAMES a feed (`source`/`sourceMetadata`); its rooms, items and accounts',
'are fetched separately by the client. Nothing here is player-specific, so there is no',
'auth and every client gets the same layout.',
].join('\n'),
parameters: [PAGE_SOURCE_PARAM],
responses: {
200: json(DiscoverySections, 'The pages sections'),
304: { description: '`If-None-Match` matched the files etag (no body)' },
404: { description: 'No file is published under that name' },
},
}),
async (c) => {
const res = await fetchPageSource(c, c.req.param('type'))
return res ?? c.notFound()
}
)
// The generated spec. Documentation only — no request is validated against it (see
// openapi.ts). `hide: true` keeps this route out of its own output.
app.get(
'/openapi.json',
describeRoute({ hide: true }),
withCleanSpec(
openAPIRouteHandler(app, {
documentation: {
info: {
title: 'recflare discovery',
version: '1.0.0',
description: [
'Discovery page layouts for recflare, a private-server reimplementation of the Rec',
'Room backend. The client asks this service which sections each of its discovery',
'pages shows — Watch home, the play menu and its tabs, the community board, the store',
'pages — and draws them in the order given.',
'',
'Each layout is a file in `static/`, published as a Workers static asset and served',
'verbatim by filename, so the set of page sources is whatever is published rather',
'than anything the code enumerates. Nothing is editable at runtime and every client',
'gets the same answer, so the routes are unauthenticated.',
'',
'A section names a feed rather than carrying its contents: the client resolves the',
'rooms, items and accounts behind each carousel against the `rooms` and `api` workers',
'itself.',
].join('\n'),
},
servers: [{ url: 'https://discovery.recflare.net', description: 'Production' }],
},
})
)
)
export default app