diff --git a/src/app.ts b/src/app.ts index 80e260e..a9509b4 100644 --- a/src/app.ts +++ b/src/app.ts @@ -79,7 +79,11 @@ import { publicTimelineController, suggestedTimelineController, } from '@/controllers/api/timelines.ts'; -import { trendingStatusesController, trendingTagsController } from '@/controllers/api/trends.ts'; +import { + trendingLinksController, + trendingStatusesController, + trendingTagsController, +} from '@/controllers/api/trends.ts'; import { indexController } from '@/controllers/site.ts'; import { nodeInfoController, nodeInfoSchemaController } from '@/controllers/well-known/nodeinfo.ts'; import { nostrController } from '@/controllers/well-known/nostr.ts'; @@ -207,6 +211,7 @@ app.get('/api/v2/search', searchController); app.get('/api/pleroma/frontend_configurations', frontendConfigController); app.get('/api/v1/trends/statuses', trendingStatusesController); +app.get('/api/v1/trends/links', trendingLinksController); app.get('/api/v1/trends/tags', trendingTagsController); app.get('/api/v1/trends', trendingTagsController); diff --git a/src/controllers/api/trends.ts b/src/controllers/api/trends.ts index 13075d1..349d86d 100644 --- a/src/controllers/api/trends.ts +++ b/src/controllers/api/trends.ts @@ -6,6 +6,7 @@ import { Conf } from '@/config.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; import { Storages } from '@/storages.ts'; import { generateDateRange, Time } from '@/utils/time.ts'; +import { unfurlCardCached } from '@/utils/unfurl.ts'; import { renderStatus } from '@/views/mastodon/statuses.ts'; let trendingHashtagsCache = getTrendingHashtags(); @@ -47,6 +48,54 @@ async function getTrendingHashtags() { }); } +let trendingLinksCache = getTrendingLinks(); + +Deno.cron('update trending links cache', { minute: { every: 15 } }, async () => { + const trends = await getTrendingLinks(); + trendingLinksCache = Promise.resolve(trends); +}); + +const trendingLinksController: AppController = async (c) => { + const { limit, offset } = trendingTagsQuerySchema.parse(c.req.query()); + const trends = await trendingLinksCache; + return c.json(trends.slice(offset, offset + limit)); +}; + +async function getTrendingLinks() { + const store = await Storages.db(); + const trends = await getTrendingTags(store, 'r'); + + return Promise.all(trends.map(async (trend) => { + const link = trend.value; + const card = await unfurlCardCached(link); + + const history = trend.history.map(({ day, authors, uses }) => ({ + day: String(day), + accounts: String(authors), + uses: String(uses), + })); + + return { + url: link, + title: '', + description: '', + type: 'link', + author_name: '', + author_url: '', + provider_name: '', + provider_url: '', + html: '', + width: 0, + height: 0, + image: null, + embed_url: '', + blurhash: null, + ...card, + history, + }; + })); +} + const trendingStatusesQuerySchema = z.object({ limit: z.coerce.number().catch(20).transform((value) => Math.min(Math.max(value, 0), 40)), offset: z.number().nonnegative().catch(0), @@ -153,4 +202,4 @@ export async function getTrendingTags(store: NStore, tagName: string): Promise