diff --git a/lib/routes/follow/namespace.ts b/lib/routes/follow/namespace.ts new file mode 100644 index 0000000000000..02fd4ac9fd401 --- /dev/null +++ b/lib/routes/follow/namespace.ts @@ -0,0 +1,6 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Follow', + url: 'app.follow.is', +}; diff --git a/lib/routes/follow/profile.ts b/lib/routes/follow/profile.ts new file mode 100644 index 0000000000000..a136ef30bece6 --- /dev/null +++ b/lib/routes/follow/profile.ts @@ -0,0 +1,38 @@ +import type { Data, Route } from '@/types'; +import { Context } from 'hono'; +import ofetch from '@/utils/ofetch'; +import type { FollowResponse, Profile, Subscription } from './types'; + +export const route: Route = { + name: 'User subscriptions', + path: '/profile/:uid', + example: '/profile/41279032429549568', + radar: [ + { + source: ['app.follow.is/profile/:uid'], + target: '/follow/profile/:uid', + }, + ], + handler, + maintainers: ['KarasuShin'], + features: { + supportRadar: true, + }, +}; + +async function handler(ctx: Context): Promise { + const uid = ctx.req.param('uid'); + const host = 'https://api.follow.is'; + + const [profile, subscriptions] = await Promise.all([ofetch>(`${host}/profiles?id=${uid}`), ofetch>(`${host}/subscriptions?userId=${uid}`)]); + + return { + title: `${profile.data.name} - User subscriptions`, + item: subscriptions.data.map((subscription) => ({ + title: subscription.feeds.title, + description: subscription.feeds.description, + link: `https://app.follow.is/feed/${subscription.feedId}`, + })), + link: `https://app.follow.is/profile/${uid}`, + }; +} diff --git a/lib/routes/follow/types.ts b/lib/routes/follow/types.ts new file mode 100644 index 0000000000000..5fb2ee0be39e2 --- /dev/null +++ b/lib/routes/follow/types.ts @@ -0,0 +1,38 @@ +export interface FollowResponse { + code: number; + data: T; +} + +export interface Subscription { + category: string; + feedId: string; + feeds: { + checkAt: string; + description: string; + errorAt: unknown; + errorMessage: unknown; + etagHeader: string; + id: string; + image: unknown; + lastModifiedHeader: string; + ownerUserId: string | null; + siteUrl: string; + title: string; + ttl: number; + url: string; + }; + isPrivate: boolean; + title: string | null; + userId: string; + view: number; +} + +export interface Profile { + id: string; + name: string; + email: string; + emailVerified: unknown; + image: string; + handle: unknown; + createdAt: string; +}