Paginate list events (kind 3, 10000)
This commit is contained in:
parent
1290aeeb5e
commit
b7943669b2
|
@ -138,8 +138,8 @@ async function parseBody(req: Request): Promise<unknown> {
|
|||
|
||||
/** Schema to parse pagination query params. */
|
||||
const paginationSchema = z.object({
|
||||
since: z.coerce.number().optional().catch(undefined),
|
||||
until: z.coerce.number().optional().catch(undefined),
|
||||
since: z.coerce.number().nonnegative().optional().catch(undefined),
|
||||
until: z.coerce.number().nonnegative().optional().catch(undefined),
|
||||
limit: z.coerce.number().catch(20).transform((value) => Math.min(Math.max(value, 0), 40)),
|
||||
});
|
||||
|
||||
|
@ -179,6 +179,47 @@ function paginated(c: AppContext, events: NostrEvent[], entities: (Entity | unde
|
|||
return c.json(results, 200, headers);
|
||||
}
|
||||
|
||||
/** Query params for paginating a list. */
|
||||
const listPaginationSchema = z.object({
|
||||
offset: z.coerce.number().nonnegative().catch(0),
|
||||
limit: z.coerce.number().catch(20).transform((value) => Math.min(Math.max(value, 0), 40)),
|
||||
});
|
||||
|
||||
/** Build HTTP Link header for paginating Nostr lists. */
|
||||
function buildListLinkHeader(url: string, params: { offset: number; limit: number }): string | undefined {
|
||||
const { origin } = Conf.url;
|
||||
const { pathname, search } = new URL(url);
|
||||
const { offset, limit } = params;
|
||||
const next = new URL(pathname + search, origin);
|
||||
const prev = new URL(pathname + search, origin);
|
||||
|
||||
next.searchParams.set('offset', String(offset + limit));
|
||||
prev.searchParams.set('offset', String(Math.max(offset - limit, 0)));
|
||||
|
||||
next.searchParams.set('limit', String(limit));
|
||||
prev.searchParams.set('limit', String(limit));
|
||||
|
||||
return `<${next}>; rel="next", <${prev}>; rel="prev"`;
|
||||
}
|
||||
|
||||
/** paginate a list of tags. */
|
||||
function paginatedList(
|
||||
c: AppContext,
|
||||
params: { offset: number; limit: number },
|
||||
entities: (Entity | undefined)[],
|
||||
headers: HeaderRecord = {},
|
||||
) {
|
||||
const link = buildListLinkHeader(c.req.url, params);
|
||||
|
||||
if (link) {
|
||||
headers.link = link;
|
||||
}
|
||||
|
||||
// Filter out undefined entities.
|
||||
const results = entities.filter((entity): entity is Entity => Boolean(entity));
|
||||
return c.json(results, 200, headers);
|
||||
}
|
||||
|
||||
/** JSON-LD context. */
|
||||
type LDContext = (string | Record<string, string | Record<string, string>>)[];
|
||||
|
||||
|
@ -209,8 +250,10 @@ export {
|
|||
createAdminEvent,
|
||||
createEvent,
|
||||
type EventStub,
|
||||
listPaginationSchema,
|
||||
localRequest,
|
||||
paginated,
|
||||
paginatedList,
|
||||
type PaginationParams,
|
||||
paginationSchema,
|
||||
parseBody,
|
||||
|
|
12
src/views.ts
12
src/views.ts
|
@ -4,7 +4,7 @@ import { AppContext } from '@/app.ts';
|
|||
import { Storages } from '@/storages.ts';
|
||||
import { renderAccount } from '@/views/mastodon/accounts.ts';
|
||||
import { renderStatus } from '@/views/mastodon/statuses.ts';
|
||||
import { paginated, paginationSchema } from '@/utils/api.ts';
|
||||
import { listPaginationSchema, paginated, paginatedList, paginationSchema } from '@/utils/api.ts';
|
||||
import { hydrateEvents } from '@/storages/hydrate.ts';
|
||||
import { accountFromPubkey } from '@/views/mastodon/accounts.ts';
|
||||
|
||||
|
@ -42,12 +42,14 @@ async function renderEventAccounts(c: AppContext, filters: NostrFilter[], opts?:
|
|||
return paginated(c, events, accounts);
|
||||
}
|
||||
|
||||
async function renderAccounts(c: AppContext, authors: string[], signal = AbortSignal.timeout(1000)) {
|
||||
const { since, until, limit } = paginationSchema.parse(c.req.query());
|
||||
async function renderAccounts(c: AppContext, pubkeys: string[]) {
|
||||
const { offset, limit } = listPaginationSchema.parse(c.req.query());
|
||||
const authors = pubkeys.slice(offset, offset + limit);
|
||||
|
||||
const store = await Storages.db();
|
||||
const signal = c.req.raw.signal;
|
||||
|
||||
const events = await store.query([{ kinds: [0], authors, since, until, limit }], { signal })
|
||||
const events = await store.query([{ kinds: [0], authors }], { signal })
|
||||
.then((events) => hydrateEvents({ events, store, signal }));
|
||||
|
||||
const accounts = await Promise.all(
|
||||
|
@ -61,7 +63,7 @@ async function renderAccounts(c: AppContext, authors: string[], signal = AbortSi
|
|||
}),
|
||||
);
|
||||
|
||||
return paginated(c, events, accounts);
|
||||
return paginatedList(c, { offset, limit }, accounts);
|
||||
}
|
||||
|
||||
/** Render statuses by event IDs. */
|
||||
|
|
Loading…
Reference in New Issue