Add public timeline, fix limit param
This commit is contained in:
parent
cacf51ea36
commit
d4eef9c2af
|
@ -11,7 +11,7 @@ import {
|
|||
} from './controllers/api/accounts.ts';
|
||||
import { appCredentialsController, createAppController } from './controllers/api/apps.ts';
|
||||
import { emptyArrayController, emptyObjectController } from './controllers/api/fallback.ts';
|
||||
import { homeController } from './controllers/api/timelines.ts';
|
||||
import { homeController, publicController } from './controllers/api/timelines.ts';
|
||||
import instanceController from './controllers/api/instance.ts';
|
||||
import { createTokenController, oauthAuthorizeController, oauthController } from './controllers/api/oauth.ts';
|
||||
import { frontendConfigController } from './controllers/api/pleroma.ts';
|
||||
|
@ -75,6 +75,7 @@ app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/favourite', favouriteController);
|
|||
app.post('/api/v1/statuses', requireAuth, createStatusController);
|
||||
|
||||
app.get('/api/v1/timelines/home', requireAuth, homeController);
|
||||
app.get('/api/v1/timelines/public', publicController);
|
||||
|
||||
app.get('/api/v1/preferences', preferencesController);
|
||||
app.get('/api/v1/search', searchController);
|
||||
|
@ -92,7 +93,6 @@ app.get('/api/v1/blocks', emptyArrayController);
|
|||
app.get('/api/v1/mutes', emptyArrayController);
|
||||
app.get('/api/v1/domain_blocks', emptyArrayController);
|
||||
app.get('/api/v1/markers', emptyObjectController);
|
||||
app.get('/api/v1/timelines/public', emptyArrayController);
|
||||
app.get('/api/v1/conversations', emptyArrayController);
|
||||
app.get('/api/v1/favourites', emptyArrayController);
|
||||
app.get('/api/v1/lists', emptyArrayController);
|
||||
|
|
|
@ -3,7 +3,7 @@ import { type Event, type SignedEvent } from '@/event.ts';
|
|||
|
||||
import { Conf } from './config.ts';
|
||||
|
||||
import { eventDateComparator, nostrNow } from './utils.ts';
|
||||
import { eventDateComparator, type PaginationParams } from './utils.ts';
|
||||
|
||||
const db = await Deno.openKv();
|
||||
|
||||
|
@ -99,9 +99,14 @@ const getEvent = async <K extends number = number>(id: string, kind?: K): Promis
|
|||
};
|
||||
|
||||
/** Get a Nostr `set_medatadata` event for a user's pubkey. */
|
||||
const getAuthor = async (pubkey: string): Promise<SignedEvent<0> | undefined> => {
|
||||
const getAuthor = async (pubkey: string, timeout = 1000): Promise<SignedEvent<0> | undefined> => {
|
||||
const author = new Author(getPool(), Conf.poolRelays, pubkey);
|
||||
const event: SignedEvent<0> | null = await new Promise((resolve) => author.metaData(resolve, 0));
|
||||
|
||||
const event: SignedEvent<0> | null = await new Promise((resolve) => {
|
||||
setTimeout(resolve, timeout, null);
|
||||
return author.metaData(resolve, 0);
|
||||
});
|
||||
|
||||
return event?.pubkey === pubkey ? event : undefined;
|
||||
};
|
||||
|
||||
|
@ -119,16 +124,8 @@ const getFollows = async (pubkey: string): Promise<SignedEvent<3> | undefined> =
|
|||
}
|
||||
};
|
||||
|
||||
interface PaginationParams {
|
||||
since?: number;
|
||||
until?: number;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
/** Get events from people the user follows. */
|
||||
async function getFeed(event3: Event<3>, params: PaginationParams = {}): Promise<SignedEvent<1>[]> {
|
||||
const limit = Math.max(params.limit ?? 20, 40);
|
||||
|
||||
async function getFeed(event3: Event<3>, params: PaginationParams): Promise<SignedEvent<1>[]> {
|
||||
const authors = event3.tags
|
||||
.filter((tag) => tag[0] === 'p')
|
||||
.map((tag) => tag[1]);
|
||||
|
@ -138,15 +135,19 @@ async function getFeed(event3: Event<3>, params: PaginationParams = {}): Promise
|
|||
const filter: Filter = {
|
||||
authors,
|
||||
kinds: [1],
|
||||
since: params.since,
|
||||
until: params.until ?? nostrNow(),
|
||||
limit,
|
||||
...params,
|
||||
};
|
||||
|
||||
const results = await getFilter(filter, { timeout: 5000 }) as SignedEvent<1>[];
|
||||
return results.sort(eventDateComparator);
|
||||
}
|
||||
|
||||
/** Get a feed of all known text notes. */
|
||||
async function getPublicFeed(params: PaginationParams): Promise<SignedEvent<1>[]> {
|
||||
const results = await getFilter({ kinds: [1], ...params }, { timeout: 5000 });
|
||||
return results.sort(eventDateComparator);
|
||||
}
|
||||
|
||||
async function getAncestors(event: Event<1>, result = [] as Event<1>[]): Promise<Event<1>[]> {
|
||||
if (result.length < 100) {
|
||||
const replyTag = findReplyTag(event);
|
||||
|
@ -179,4 +180,4 @@ function publish(event: SignedEvent, relays = Conf.publishRelays): void {
|
|||
}
|
||||
}
|
||||
|
||||
export { getAncestors, getAuthor, getDescendants, getEvent, getFeed, getFilter, getFollows, publish };
|
||||
export { getAncestors, getAuthor, getDescendants, getEvent, getFeed, getFilter, getFollows, getPublicFeed, publish };
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { getFeed, getFollows } from '@/client.ts';
|
||||
import { getFeed, getFollows, getPublicFeed } from '@/client.ts';
|
||||
import { toStatus } from '@/transmute.ts';
|
||||
import { buildLinkHeader, paginationSchema } from '@/utils.ts';
|
||||
|
||||
import type { AppController } from '@/app.ts';
|
||||
|
||||
const homeController: AppController = async (c) => {
|
||||
const { since, until } = paginationSchema.parse(c.req.query());
|
||||
const params = paginationSchema.parse(c.req.query());
|
||||
const pubkey = c.get('pubkey')!;
|
||||
|
||||
const follows = await getFollows(pubkey);
|
||||
|
@ -13,7 +13,7 @@ const homeController: AppController = async (c) => {
|
|||
return c.json([]);
|
||||
}
|
||||
|
||||
const events = await getFeed(follows, { since, until });
|
||||
const events = await getFeed(follows, params);
|
||||
if (!events.length) {
|
||||
return c.json([]);
|
||||
}
|
||||
|
@ -24,4 +24,13 @@ const homeController: AppController = async (c) => {
|
|||
return c.json(statuses, 200, link ? { link } : undefined);
|
||||
};
|
||||
|
||||
export { homeController };
|
||||
const publicController: AppController = async (c) => {
|
||||
const params = paginationSchema.parse(c.req.query());
|
||||
const events = await getPublicFeed(params);
|
||||
const statuses = (await Promise.all(events.map(toStatus))).filter(Boolean);
|
||||
const link = buildLinkHeader(c.req.url, events);
|
||||
|
||||
return c.json(statuses, 200, link ? { link } : undefined);
|
||||
};
|
||||
|
||||
export { homeController, publicController };
|
||||
|
|
|
@ -78,9 +78,12 @@ async function parseBody(req: Request): Promise<unknown> {
|
|||
|
||||
const paginationSchema = z.object({
|
||||
since: z.coerce.number().optional().catch(undefined),
|
||||
until: z.coerce.number().optional().catch(undefined),
|
||||
until: z.coerce.number().catch(nostrNow()),
|
||||
limit: z.coerce.number().min(0).max(40).catch(20),
|
||||
});
|
||||
|
||||
type PaginationParams = z.infer<typeof paginationSchema>;
|
||||
|
||||
function buildLinkHeader(url: string, events: Event[]): string | undefined {
|
||||
if (!events.length) return;
|
||||
const firstEvent = events[0];
|
||||
|
@ -103,6 +106,7 @@ export {
|
|||
lookupAccount,
|
||||
type Nip05,
|
||||
nostrNow,
|
||||
type PaginationParams,
|
||||
paginationSchema,
|
||||
parseBody,
|
||||
parseNip05,
|
||||
|
|
Loading…
Reference in New Issue