mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-09 23:21:30 -07:00
[discovery][lists] semi working lists and discovery
This commit is contained in:
@@ -4,17 +4,23 @@ 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 {
|
||||
DiscoverySections,
|
||||
json,
|
||||
PAGE_SOURCE_PARAM,
|
||||
SECTION_IDS_PARAM,
|
||||
ServiceStatus,
|
||||
} from './openapi'
|
||||
import { fetchPageSource, readSections, SECTIONS_CATALOGUE } 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.
|
||||
* ASSETS binding (see `page-sources.ts`), plus `sections.json`, the id-keyed catalogue the
|
||||
* bulk lookup filters. 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.
|
||||
@@ -45,6 +51,57 @@ const app = new Hono<App>()
|
||||
(c) => c.json({ service: 'discovery', status: 'ok' })
|
||||
)
|
||||
|
||||
// A set of sections looked up by id, out of the catalogue in `static/sections.json`.
|
||||
//
|
||||
// This is the id-keyed counterpart to the page-source route: a page source hands back a
|
||||
// whole page's rows in draw order, while this hands back exactly the rows asked for,
|
||||
// which is how the client refreshes sections it already knows the ids of without
|
||||
// re-fetching every page they came from.
|
||||
//
|
||||
// The reference reads its catalogue file and filters it, so the failure modes are the
|
||||
// file's, not the query's: an id matching nothing is simply absent from the answer
|
||||
// rather than an error, and a query naming NO ids answers `[]` rather than the whole
|
||||
// catalogue — the client asks for nothing when it wants nothing. Only a missing
|
||||
// catalogue file is a 404.
|
||||
.get(
|
||||
'/sections/bulk',
|
||||
describeRoute({
|
||||
tags: ['Discovery'],
|
||||
summary: 'Look up sections by id',
|
||||
description: [
|
||||
'The sections named by the repeated `?id=` query, drawn from the catalogue in',
|
||||
'`static/sections.json` — the union of the rows the page sources are built from.',
|
||||
'',
|
||||
'The answer is that file FILTERED, which fixes the edges: rows come back in the',
|
||||
'catalogue’s order rather than the query’s, an id that matches nothing is left out',
|
||||
'instead of erroring, and repeating an id still yields it once. A query with no `id`',
|
||||
'at all answers `[]`. Rows are served exactly as stored, so a field this service',
|
||||
'doesn’t model survives the round trip.',
|
||||
'',
|
||||
'Same section shape as `/sections/pagesource/{type}`: a section NAMES a feed',
|
||||
'(`source`/`sourceMetadata`) that the client resolves itself. Nothing here is',
|
||||
'player-specific, so there is no auth.',
|
||||
].join('\n'),
|
||||
parameters: [SECTION_IDS_PARAM],
|
||||
responses: {
|
||||
200: json(DiscoverySections, 'The requested sections, in catalogue order'),
|
||||
404: { description: 'The catalogue file is not published' },
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
// `queries` and not `query`: the ids arrive as a repeated parameter, and `query`
|
||||
// would collapse them to the first one and silently drop the rest of the page.
|
||||
const ids = c.req.queries('id')
|
||||
if (ids === undefined || ids.length === 0) return c.json([])
|
||||
|
||||
const sections = await readSections(c, SECTIONS_CATALOGUE)
|
||||
if (sections === null) return c.notFound()
|
||||
|
||||
const wanted = new Set(ids)
|
||||
return c.json(sections.filter((s) => typeof s.id === 'string' && wanted.has(s.id)))
|
||||
}
|
||||
)
|
||||
|
||||
// One discovery page's section layout, served verbatim from `static/<type>.json`.
|
||||
.get(
|
||||
'/sections/pagesource/:type',
|
||||
@@ -104,6 +161,9 @@ app.get(
|
||||
'than anything the code enumerates. Nothing is editable at runtime and every client',
|
||||
'gets the same answer, so the routes are unauthenticated.',
|
||||
'',
|
||||
'Sections can also be fetched by id rather than by page: `/sections/bulk` filters',
|
||||
'`static/sections.json`, the catalogue those layouts draw their rows from.',
|
||||
'',
|
||||
'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.',
|
||||
|
||||
Reference in New Issue
Block a user