mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-09 07:01:27 -07:00
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:
@@ -0,0 +1,101 @@
|
||||
import { resolver } from 'hono-openapi'
|
||||
import { z } from 'zod'
|
||||
|
||||
import type { OpenAPIV3_1 } from 'openapi-types'
|
||||
|
||||
/**
|
||||
* OpenAPI schemas for the discovery worker.
|
||||
*
|
||||
* IMPORTANT: these are DESCRIPTIVE ONLY. They are passed to `describeRoute` to generate
|
||||
* the spec and are never wired into `hono-openapi`'s `validator()`. Same rationale as
|
||||
* the other workers: a reverse-engineered protocol, lenient handlers, no runtime
|
||||
* validation.
|
||||
*
|
||||
* Do NOT add `.meta({ id })` to these schemas — with this hono-openapi + zod v4 setup a
|
||||
* meta'd schema used in a response emits a `$ref` the framework doesn't always hoist into
|
||||
* `components.schemas`, leaving a dangling reference. Leaving meta off makes every schema
|
||||
* inline, which renders correctly in any tool.
|
||||
*/
|
||||
|
||||
/** Emit a zod schema as an `application/json` response body. */
|
||||
export function json(schema: z.ZodType, description: string) {
|
||||
return { description, content: { 'application/json': { schema: resolver(schema) } } }
|
||||
}
|
||||
|
||||
/** The `{type}` path parameter, which is the filename in `static/`. */
|
||||
export const PAGE_SOURCE_PARAM: OpenAPIV3_1.ParameterObject = {
|
||||
name: 'type',
|
||||
in: 'path',
|
||||
required: true,
|
||||
description: [
|
||||
'The page source — `WatchHome`, `PlayHighlight`, `CommunityBoard`, `PlayMenuTabs`,',
|
||||
'`PlayCategories`, `StoreCategories`, `StoreFeatured`, `StoreClothing`,',
|
||||
'`StoreConsumables`, `bulk` at the time of writing. It names a file in `static/`',
|
||||
'(`<type>.json`) and is matched exactly, case included, so the set is whatever is',
|
||||
'published rather than anything this worker enumerates.',
|
||||
].join(' '),
|
||||
// Deliberately not an `enum`: the accepted values are the published files, and a spec
|
||||
// that froze today's list would be wrong the moment one is added.
|
||||
schema: { type: 'string', example: 'WatchHome' },
|
||||
}
|
||||
|
||||
// ---- Response schemas ------------------------------------------------------
|
||||
|
||||
/** `GET /` — the liveness probe body. */
|
||||
export const ServiceStatus = z.object({
|
||||
service: z.literal('discovery'),
|
||||
status: z.literal('ok'),
|
||||
})
|
||||
|
||||
/**
|
||||
* How one carousel on a discovery page is filled and drawn. `source`/`sourceMetadata`
|
||||
* name a feed (`Hot`, `Recent`, `PlaylistById` + an id, `CarouselEndpoint` + a slug, …)
|
||||
* that the client resolves against the `rooms`/`api` workers itself — this worker only
|
||||
* says WHICH carousels a page has and in what order, never their contents.
|
||||
*/
|
||||
export const DiscoverySection = z.object({
|
||||
id: z.string().describe('Unique id of this section on this page, e.g. `Rooms_RRO_WatchHome`'),
|
||||
sectionType: z
|
||||
.int()
|
||||
.describe(
|
||||
'What the section lists: 0 RoomsSection · 1 AccountsSection · 2 InventionsSection · ' +
|
||||
'3 ClubsSection · 4 StoreItemsSection · 5 EventsSection · 6 RoomBanner · 7 Top5Section · ' +
|
||||
'8 CustomAvatarItemsSection · 9 AdsSection · 10 SkusSection · 11 RoomCategorySection · ' +
|
||||
'12 RoomCategoryListSection · 13 DiscoverySection'
|
||||
),
|
||||
sectionSubType: z.string().describe('The section’s kind, shared across pages, e.g. `Rooms_RRO`'),
|
||||
source: z.string().describe('The feed that fills the section'),
|
||||
sourceMetadata: z
|
||||
.string()
|
||||
.nullable()
|
||||
.describe('Argument to `source` — a carousel slug, a playlist id, … `null` when it takes none'),
|
||||
displayMetadata: z
|
||||
.string()
|
||||
.nullable()
|
||||
.describe(
|
||||
'How the section is drawn (`DisplayTitle`, `numRows`, `backgroundColor`, …), as an ' +
|
||||
'embedded JSON *string* the client parses itself — not an object. Its booleans and ' +
|
||||
'numbers are quoted in most sections and bare in some; both forms are live in the ' +
|
||||
'captures, so the client evidently takes either. `null` where a section is drawn ' +
|
||||
'however its type says (several store and play-highlight sections).'
|
||||
),
|
||||
})
|
||||
|
||||
/**
|
||||
* `GET /sections/pagesource/{type}` — a page's sections, in the order they are drawn.
|
||||
*
|
||||
* The DTO accepts anything (its validator is a no-op), but the STORE page builder is much
|
||||
* stricter and drops a section it doesn't like SILENTLY — no error reaches the client, the
|
||||
* carousel simply isn't there. A section it keeps must have:
|
||||
*
|
||||
* - a non-empty `displayMetadata` that parses, and whose gating (platform, junior account,
|
||||
* account age) says it's supported;
|
||||
* - `sectionType` 4 (StoreItemsSection → the product carousel) or 13 (DiscoverySection).
|
||||
* Anything else logs "Unhandled SectionType {0}, skipping";
|
||||
* - for 13 only, a `source` of exactly `CuratedList` or `PageSource`, with `sourceMetadata`
|
||||
* carrying the argument — a curated-list id, or another page source to recurse into.
|
||||
*
|
||||
* So a store section that renders nothing is worth checking against these before assuming
|
||||
* the feed behind it is empty.
|
||||
*/
|
||||
export const DiscoverySections = DiscoverySection.array()
|
||||
Reference in New Issue
Block a user