diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index acf0a7a53..07226a8f0 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -422,14 +422,21 @@ export function apiAccountMastodon(router: Router): void { } }); router.get("/v1/favourites", async (ctx) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); try { - const data = await client.getFavourites( - convertTimelinesArgsId(limitToInt(ctx.query as any)), - ); - ctx.body = data.data.map((status) => convertStatus(status)); + const auth = await authenticate(ctx.headers.authorization, null); + const user = auth[0] ?? null; + + if (!user) { + ctx.status = 401; + return; + } + + const cache = UserHelpers.getFreshAccountCache(); + const args = normalizeUrlQuery(convertTimelinesArgsId(limitToInt(ctx.query as any))); + const favorites = await UserHelpers.getUserFavorites(user, args.max_id, args.since_id, args.min_id, args.limit) + .then(n => NoteConverter.encodeMany(n, user, cache)); + + ctx.body = favorites.map(s => convertStatus(s)); } catch (e: any) { console.error(e); console.error(e.response.data); diff --git a/packages/backend/src/server/api/mastodon/helpers/user.ts b/packages/backend/src/server/api/mastodon/helpers/user.ts index 89a4c7bbd..06b529c95 100644 --- a/packages/backend/src/server/api/mastodon/helpers/user.ts +++ b/packages/backend/src/server/api/mastodon/helpers/user.ts @@ -1,6 +1,6 @@ import { Note } from "@/models/entities/note.js"; import { ILocalUser, User } from "@/models/entities/user.js"; -import { Followings, NoteFavorites, Notes, UserProfiles } from "@/models/index.js"; +import { Followings, NoteFavorites, NoteReactions, Notes, UserProfiles } from "@/models/index.js"; import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js"; import { generateRepliesQuery } from "@/server/api/common/generate-replies-query.js"; import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js"; @@ -110,6 +110,37 @@ export class UserHelpers { return PaginationHelpers.execQuery(query, limit, minId !== undefined); } + public static async getUserFavorites(localUser: ILocalUser, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 20): Promise { + if (limit > 40) limit = 40; + + const favoriteQuery = NoteReactions.createQueryBuilder("reaction") + .select("reaction.noteId") + .where("reaction.userId = :meId"); + + const query = PaginationHelpers.makePaginationQuery( + Notes.createQueryBuilder("note"), + sinceId, + maxId, + minId + ) + .andWhere(`note.id IN (${favoriteQuery.getQuery()})`) + .innerJoinAndSelect("note.user", "user") + .leftJoinAndSelect("user.avatar", "avatar") + .leftJoinAndSelect("user.banner", "banner") + .leftJoinAndSelect("note.reply", "reply") + .leftJoinAndSelect("note.renote", "renote") + .leftJoinAndSelect("reply.user", "replyUser") + .leftJoinAndSelect("replyUser.avatar", "replyUserAvatar") + .leftJoinAndSelect("replyUser.banner", "replyUserBanner") + .leftJoinAndSelect("renote.user", "renoteUser") + .leftJoinAndSelect("renoteUser.avatar", "renoteUserAvatar") + .leftJoinAndSelect("renoteUser.banner", "renoteUserBanner"); + + generateVisibilityQuery(query, localUser); + + query.setParameters({ meId: localUser.id }); + return PaginationHelpers.execQuery(query, limit, minId !== undefined); + } private static async getUserRelationships(type: RelationshipType, user: User, localUser: ILocalUser | null, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 40): Promise> { if (limit > 80) limit = 80;