mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 03:19:46 +00:00
Let paginationMiddleware be configurable, add pagination to reactions handler
This commit is contained in:
parent
36ffd4283a
commit
88ef8087a5
8 changed files with 122 additions and 97 deletions
|
|
@ -633,7 +633,7 @@ const zappedByController: AppController = async (c) => {
|
||||||
const { db, relay } = c.var;
|
const { db, relay } = c.var;
|
||||||
|
|
||||||
const id = c.req.param('id');
|
const id = c.req.param('id');
|
||||||
const { offset, limit } = paginationSchema.parse(c.req.query());
|
const { offset, limit } = paginationSchema().parse(c.req.query());
|
||||||
|
|
||||||
const zaps = await db.kysely.selectFrom('event_zaps')
|
const zaps = await db.kysely.selectFrom('event_zaps')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { accountFromPubkey, renderAccount } from '@/views/mastodon/accounts.ts';
|
||||||
|
|
||||||
export const suggestionsV1Controller: AppController = async (c) => {
|
export const suggestionsV1Controller: AppController = async (c) => {
|
||||||
const { signal } = c.var;
|
const { signal } = c.var;
|
||||||
const { offset, limit } = paginationSchema.parse(c.req.query());
|
const { offset, limit } = paginationSchema().parse(c.req.query());
|
||||||
const suggestions = await renderV2Suggestions(c, { offset, limit }, signal);
|
const suggestions = await renderV2Suggestions(c, { offset, limit }, signal);
|
||||||
const accounts = suggestions.map(({ account }) => account);
|
const accounts = suggestions.map(({ account }) => account);
|
||||||
return paginatedList(c, { offset, limit }, accounts);
|
return paginatedList(c, { offset, limit }, accounts);
|
||||||
|
|
@ -17,7 +17,7 @@ export const suggestionsV1Controller: AppController = async (c) => {
|
||||||
|
|
||||||
export const suggestionsV2Controller: AppController = async (c) => {
|
export const suggestionsV2Controller: AppController = async (c) => {
|
||||||
const { signal } = c.var;
|
const { signal } = c.var;
|
||||||
const { offset, limit } = paginationSchema.parse(c.req.query());
|
const { offset, limit } = paginationSchema().parse(c.req.query());
|
||||||
const suggestions = await renderV2Suggestions(c, { offset, limit }, signal);
|
const suggestions = await renderV2Suggestions(c, { offset, limit }, signal);
|
||||||
return paginatedList(c, { offset, limit }, suggestions);
|
return paginatedList(c, { offset, limit }, suggestions);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ async function getTrendingLinks(conf: DittoConf, relay: NStore): Promise<Trendin
|
||||||
|
|
||||||
const trendingStatusesController: AppController = async (c) => {
|
const trendingStatusesController: AppController = async (c) => {
|
||||||
const { conf, relay } = c.var;
|
const { conf, relay } = c.var;
|
||||||
const { limit, offset, until } = paginationSchema.parse(c.req.query());
|
const { limit, offset, until } = paginationSchema().parse(c.req.query());
|
||||||
|
|
||||||
const [label] = await relay.query([{
|
const [label] = await relay.query([{
|
||||||
kinds: [1985],
|
kinds: [1985],
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { userMiddleware } from '@ditto/mastoapi/middleware';
|
import { paginationMiddleware, userMiddleware } from '@ditto/mastoapi/middleware';
|
||||||
import { DittoRoute } from '@ditto/mastoapi/router';
|
import { DittoRoute } from '@ditto/mastoapi/router';
|
||||||
|
|
||||||
import { DittoEvent } from '@/interfaces/DittoEvent.ts';
|
import { DittoEvent } from '@/interfaces/DittoEvent.ts';
|
||||||
|
|
@ -109,14 +109,18 @@ route.delete('/:id{[0-9a-f]{64}}/reactions/:emoji', userMiddleware(), async (c)
|
||||||
* Get an object of emoji to account mappings with accounts that reacted to the post.
|
* Get an object of emoji to account mappings with accounts that reacted to the post.
|
||||||
* https://docs.pleroma.social/backend/development/API/pleroma_api/#get-apiv1pleromastatusesidreactions
|
* https://docs.pleroma.social/backend/development/API/pleroma_api/#get-apiv1pleromastatusesidreactions
|
||||||
*/
|
*/
|
||||||
route.get('/:id{[0-9a-f]{64}}/reactions/:emoji?', userMiddleware({ required: false }), async (c) => {
|
route.get(
|
||||||
const { relay, user } = c.var;
|
'/:id{[0-9a-f]{64}}/reactions/:emoji?',
|
||||||
|
paginationMiddleware({ limit: 100 }),
|
||||||
|
userMiddleware({ required: false }),
|
||||||
|
async (c) => {
|
||||||
|
const { relay, user, pagination, paginate } = c.var;
|
||||||
|
|
||||||
const params = c.req.param();
|
const params = c.req.param();
|
||||||
const result = params.emoji ? parseEmojiParam(params.emoji) : undefined;
|
const result = params.emoji ? parseEmojiParam(params.emoji) : undefined;
|
||||||
const pubkey = await user?.signer.getPublicKey();
|
const pubkey = await user?.signer.getPublicKey();
|
||||||
|
|
||||||
const events = await relay.query([{ kinds: [7], '#e': [params.id], limit: 100 }])
|
const events = await relay.query([{ kinds: [7], '#e': [params.id], ...pagination }])
|
||||||
.then((events) =>
|
.then((events) =>
|
||||||
events.filter((event) => {
|
events.filter((event) => {
|
||||||
if (!result) return true;
|
if (!result) return true;
|
||||||
|
|
@ -193,8 +197,9 @@ route.get('/:id{[0-9a-f]{64}}/reactions/:emoji?', userMiddleware({ required: fal
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
return c.json(results);
|
return paginate(events, results);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
/** Determine if the input is a native or custom emoji, returning a structured object or throwing an error. */
|
/** Determine if the input is a native or custom emoji, returning a structured object or throwing an error. */
|
||||||
function parseEmojiParam(input: string):
|
function parseEmojiParam(input: string):
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ async function renderEventAccounts(c: AppContext, filters: NostrFilter[], opts?:
|
||||||
}
|
}
|
||||||
|
|
||||||
async function renderAccounts(c: AppContext, pubkeys: string[]) {
|
async function renderAccounts(c: AppContext, pubkeys: string[]) {
|
||||||
const { offset, limit } = paginationSchema.parse(c.req.query());
|
const { offset, limit } = paginationSchema().parse(c.req.query());
|
||||||
const authors = pubkeys.reverse().slice(offset, offset + limit);
|
const authors = pubkeys.reverse().slice(offset, offset + limit);
|
||||||
|
|
||||||
const { relay, signal } = c.var;
|
const { relay, signal } = c.var;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { paginated, paginatedList } from '../pagination/paginate.ts';
|
import { paginated, paginatedList } from '../pagination/paginate.ts';
|
||||||
import { paginationSchema } from '../pagination/schema.ts';
|
import { paginationSchema, type PaginationSchemaOpts } from '../pagination/schema.ts';
|
||||||
|
|
||||||
import type { DittoMiddleware } from '@ditto/mastoapi/router';
|
import type { DittoMiddleware } from '@ditto/mastoapi/router';
|
||||||
import type { NostrEvent } from '@nostrify/nostrify';
|
import type { NostrEvent } from '@nostrify/nostrify';
|
||||||
|
|
@ -19,19 +19,26 @@ type HeaderRecord = Record<string, string | string[]>;
|
||||||
type PaginateFn = (events: NostrEvent[], body: object | unknown[], headers?: HeaderRecord) => Response;
|
type PaginateFn = (events: NostrEvent[], body: object | unknown[], headers?: HeaderRecord) => Response;
|
||||||
type ListPaginateFn = (params: ListPagination, body: object | unknown[], headers?: HeaderRecord) => Response;
|
type ListPaginateFn = (params: ListPagination, body: object | unknown[], headers?: HeaderRecord) => Response;
|
||||||
|
|
||||||
|
interface PaginationMiddlewareOpts extends PaginationSchemaOpts {
|
||||||
|
type?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/** Fixes compatibility with Mastodon apps by that don't use `Link` headers. */
|
/** Fixes compatibility with Mastodon apps by that don't use `Link` headers. */
|
||||||
// @ts-ignore Types are right.
|
// @ts-ignore Types are right.
|
||||||
export function paginationMiddleware(): DittoMiddleware<{ pagination: Pagination; paginate: PaginateFn }>;
|
export function paginationMiddleware(): DittoMiddleware<{ pagination: Pagination; paginate: PaginateFn }>;
|
||||||
export function paginationMiddleware(
|
export function paginationMiddleware(
|
||||||
type: 'list',
|
opts: PaginationMiddlewareOpts & { type: 'list' },
|
||||||
): DittoMiddleware<{ pagination: ListPagination; paginate: ListPaginateFn }>;
|
): DittoMiddleware<{ pagination: ListPagination; paginate: ListPaginateFn }>;
|
||||||
export function paginationMiddleware(
|
export function paginationMiddleware(
|
||||||
type?: string,
|
opts?: PaginationMiddlewareOpts,
|
||||||
|
): DittoMiddleware<{ pagination: Pagination; paginate: PaginateFn }>;
|
||||||
|
export function paginationMiddleware(
|
||||||
|
opts: PaginationMiddlewareOpts = {},
|
||||||
): DittoMiddleware<{ pagination?: Pagination | ListPagination; paginate: PaginateFn | ListPaginateFn }> {
|
): DittoMiddleware<{ pagination?: Pagination | ListPagination; paginate: PaginateFn | ListPaginateFn }> {
|
||||||
return async (c, next) => {
|
return async (c, next) => {
|
||||||
const { relay } = c.var;
|
const { relay } = c.var;
|
||||||
|
|
||||||
const pagination = paginationSchema.parse(c.req.query());
|
const pagination = paginationSchema(opts).parse(c.req.query());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
max_id: maxId,
|
max_id: maxId,
|
||||||
|
|
@ -59,7 +66,7 @@ export function paginationMiddleware(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'list') {
|
if (opts.type === 'list') {
|
||||||
c.set('pagination', {
|
c.set('pagination', {
|
||||||
limit: pagination.limit,
|
limit: pagination.limit,
|
||||||
offset: pagination.offset,
|
offset: pagination.offset,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { assertEquals } from '@std/assert';
|
||||||
import { paginationSchema } from './schema.ts';
|
import { paginationSchema } from './schema.ts';
|
||||||
|
|
||||||
Deno.test('paginationSchema', () => {
|
Deno.test('paginationSchema', () => {
|
||||||
const pagination = paginationSchema.parse({
|
const pagination = paginationSchema().parse({
|
||||||
limit: '10',
|
limit: '10',
|
||||||
offset: '20',
|
offset: '20',
|
||||||
max_id: '1',
|
max_id: '1',
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,20 @@ export interface Pagination {
|
||||||
offset: number;
|
offset: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PaginationSchemaOpts {
|
||||||
|
limit?: number;
|
||||||
|
max?: number;
|
||||||
|
}
|
||||||
|
|
||||||
/** Schema to parse pagination query params. */
|
/** Schema to parse pagination query params. */
|
||||||
export const paginationSchema: z.ZodType<Pagination> = z.object({
|
export function paginationSchema(opts: PaginationSchemaOpts = {}): z.ZodType<Pagination> {
|
||||||
|
let { limit = 20, max = 40 } = opts;
|
||||||
|
|
||||||
|
if (limit > max) {
|
||||||
|
max = limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return z.object({
|
||||||
max_id: z.string().transform((val) => {
|
max_id: z.string().transform((val) => {
|
||||||
if (!val.includes('-')) return val;
|
if (!val.includes('-')) return val;
|
||||||
return val.split('-')[1];
|
return val.split('-')[1];
|
||||||
|
|
@ -18,6 +30,7 @@ export const paginationSchema: z.ZodType<Pagination> = z.object({
|
||||||
min_id: z.string().optional().catch(undefined),
|
min_id: z.string().optional().catch(undefined),
|
||||||
since: z.coerce.number().nonnegative().optional().catch(undefined),
|
since: z.coerce.number().nonnegative().optional().catch(undefined),
|
||||||
until: 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)),
|
limit: z.coerce.number().catch(limit).transform((value) => Math.min(Math.max(value, 0), max)),
|
||||||
offset: z.coerce.number().nonnegative().catch(0),
|
offset: z.coerce.number().nonnegative().catch(0),
|
||||||
}) as z.ZodType<Pagination>;
|
}) as z.ZodType<Pagination>;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue