mirror of
https://iceshrimp.dev/crimekillz/iceshrimp-161sh.git
synced 2024-11-22 04:03:49 +01:00
[mastodon-client] Fully move cache into ctx
This commit is contained in:
parent
8428f2efc4
commit
79c3e56989
@ -15,13 +15,14 @@ import { PollConverter } from "@/server/api/mastodon/converters/poll.js";
|
||||
import { populatePoll } from "@/models/repositories/note.js";
|
||||
import { FileConverter } from "@/server/api/mastodon/converters/file.js";
|
||||
import { awaitAll } from "@/prelude/await-all.js";
|
||||
import { AccountCache, UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { IsNull } from "typeorm";
|
||||
import { MfmHelpers } from "@/server/api/mastodon/helpers/mfm.js";
|
||||
import { MastoContext } from "@/server/api/mastodon/index.js";
|
||||
|
||||
export class NoteConverter {
|
||||
public static async encode(note: Note, user: ILocalUser | null, cache: AccountCache = UserHelpers.getFreshAccountCache(), recurse: boolean = true): Promise<MastodonEntity.Status> {
|
||||
const noteUser = note.user ?? UserHelpers.getUserCached(note.userId, cache);
|
||||
public static async encode(note: Note, user: ILocalUser | null, ctx: MastoContext, recurse: boolean = true): Promise<MastodonEntity.Status> {
|
||||
const noteUser = note.user ?? UserHelpers.getUserCached(note.userId, ctx);
|
||||
|
||||
if (!await Notes.isVisibleForMe(note, user?.id ?? null))
|
||||
throw new Error('Cannot encode note not visible for user');
|
||||
@ -78,7 +79,7 @@ export class NoteConverter {
|
||||
const files = DriveFiles.packMany(note.fileIds);
|
||||
|
||||
const mentions = Promise.all(note.mentions.map(p =>
|
||||
UserHelpers.getUserCached(p, cache)
|
||||
UserHelpers.getUserCached(p, ctx)
|
||||
.then(u => MentionConverter.encode(u, JSON.parse(note.mentionedRemoteUsers)))
|
||||
.catch(() => null)))
|
||||
.then(p => p.filter(m => m)) as Promise<MastodonEntity.Mention[]>;
|
||||
@ -105,10 +106,10 @@ export class NoteConverter {
|
||||
id: note.id,
|
||||
uri: note.uri ? note.uri : `https://${config.host}/notes/${note.id}`,
|
||||
url: note.uri ? note.uri : `https://${config.host}/notes/${note.id}`,
|
||||
account: Promise.resolve(noteUser).then(p => UserConverter.encode(p, cache)),
|
||||
account: Promise.resolve(noteUser).then(p => UserConverter.encode(p, ctx)),
|
||||
in_reply_to_id: note.replyId,
|
||||
in_reply_to_account_id: note.replyUserId,
|
||||
reblog: Promise.resolve(renote).then(renote => recurse && renote && note.text === null ? this.encode(renote, user, cache, false) : null),
|
||||
reblog: Promise.resolve(renote).then(renote => recurse && renote && note.text === null ? this.encode(renote, user, ctx, false) : null),
|
||||
content: text.then(text => text !== null ? MfmHelpers.toHtml(mfm.parse(text), JSON.parse(note.mentionedRemoteUsers)) ?? escapeMFM(text) : ""),
|
||||
text: text,
|
||||
created_at: note.createdAt.toISOString(),
|
||||
@ -134,13 +135,13 @@ export class NoteConverter {
|
||||
// Use emojis list to provide URLs for emoji reactions.
|
||||
reactions: [], //FIXME: this.mapReactions(n.emojis, n.reactions, n.myReaction),
|
||||
bookmarked: isBookmarked,
|
||||
quote: Promise.resolve(renote).then(renote => recurse && renote && note.text !== null ? this.encode(renote, user, cache, false) : null),
|
||||
quote: Promise.resolve(renote).then(renote => recurse && renote && note.text !== null ? this.encode(renote, user, ctx, false) : null),
|
||||
edited_at: note.updatedAt?.toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
public static async encodeMany(notes: Note[], user: ILocalUser | null, cache: AccountCache = UserHelpers.getFreshAccountCache()): Promise<MastodonEntity.Status[]> {
|
||||
const encoded = notes.map(n => this.encode(n, user, cache));
|
||||
public static async encodeMany(notes: Note[], user: ILocalUser | null, ctx: MastoContext): Promise<MastodonEntity.Status[]> {
|
||||
const encoded = notes.map(n => this.encode(n, user, ctx));
|
||||
return Promise.all(encoded);
|
||||
}
|
||||
}
|
||||
|
@ -2,20 +2,21 @@ import { ILocalUser } from "@/models/entities/user.js";
|
||||
import { Notification } from "@/models/entities/notification.js";
|
||||
import { notificationTypes } from "@/types.js";
|
||||
import { UserConverter } from "@/server/api/mastodon/converters/user.js";
|
||||
import { AccountCache, UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { awaitAll } from "@/prelude/await-all.js";
|
||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||
import { getNote } from "@/server/api/common/getters.js";
|
||||
import { MastoContext } from "@/server/api/mastodon/index.js";
|
||||
|
||||
type NotificationType = typeof notificationTypes[number];
|
||||
|
||||
export class NotificationConverter {
|
||||
public static async encode(notification: Notification, localUser: ILocalUser, cache: AccountCache = UserHelpers.getFreshAccountCache()): Promise<MastodonEntity.Notification> {
|
||||
public static async encode(notification: Notification, localUser: ILocalUser, ctx: MastoContext): Promise<MastodonEntity.Notification> {
|
||||
if (notification.notifieeId !== localUser.id) throw new Error('User is not recipient of notification');
|
||||
|
||||
const account = notification.notifierId
|
||||
? UserHelpers.getUserCached(notification.notifierId, cache).then(p => UserConverter.encode(p))
|
||||
: UserConverter.encode(localUser);
|
||||
? UserHelpers.getUserCached(notification.notifierId, ctx).then(p => UserConverter.encode(p, ctx))
|
||||
: UserConverter.encode(localUser, ctx);
|
||||
|
||||
let result = {
|
||||
id: notification.id,
|
||||
@ -27,8 +28,8 @@ export class NotificationConverter {
|
||||
if (notification.note) {
|
||||
const isPureRenote = notification.note.renoteId !== null && notification.note.text === null;
|
||||
const encodedNote = isPureRenote
|
||||
? getNote(notification.note.renoteId!, localUser).then(note => NoteConverter.encode(note, localUser, cache))
|
||||
: NoteConverter.encode(notification.note, localUser, cache);
|
||||
? getNote(notification.note.renoteId!, localUser).then(note => NoteConverter.encode(note, localUser, ctx))
|
||||
: NoteConverter.encode(notification.note, localUser, ctx);
|
||||
result = Object.assign(result, {
|
||||
status: encodedNote,
|
||||
});
|
||||
@ -44,8 +45,8 @@ export class NotificationConverter {
|
||||
return awaitAll(result);
|
||||
}
|
||||
|
||||
public static async encodeMany(notifications: Notification[], localUser: ILocalUser, cache: AccountCache = UserHelpers.getFreshAccountCache()): Promise<MastodonEntity.Notification[]> {
|
||||
const encoded = notifications.map(u => this.encode(u, localUser, cache));
|
||||
public static async encodeMany(notifications: Notification[], localUser: ILocalUser, ctx: MastoContext): Promise<MastodonEntity.Notification[]> {
|
||||
const encoded = notifications.map(u => this.encode(u, localUser, ctx));
|
||||
return Promise.all(encoded)
|
||||
.then(p => p.filter(n => n !== null) as MastodonEntity.Notification[]);
|
||||
}
|
||||
|
@ -6,8 +6,9 @@ import { populateEmojis } from "@/misc/populate-emojis.js";
|
||||
import { escapeMFM } from "@/server/api/mastodon/converters/mfm.js";
|
||||
import mfm from "mfm-js";
|
||||
import { awaitAll } from "@/prelude/await-all.js";
|
||||
import { AccountCache, UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { AccountCache } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { MfmHelpers } from "@/server/api/mastodon/helpers/mfm.js";
|
||||
import { MastoContext } from "@/server/api/mastodon/index.js";
|
||||
|
||||
type Field = {
|
||||
name: string;
|
||||
@ -16,7 +17,8 @@ type Field = {
|
||||
};
|
||||
|
||||
export class UserConverter {
|
||||
public static async encode(u: User, cache: AccountCache = UserHelpers.getFreshAccountCache()): Promise<MastodonEntity.Account> {
|
||||
public static async encode(u: User, ctx: MastoContext): Promise<MastodonEntity.Account> {
|
||||
const cache = ctx.cache as AccountCache;
|
||||
return cache.locks.acquire(u.id, async () => {
|
||||
const cacheHit = cache.accounts.find(p => p.id == u.id);
|
||||
if (cacheHit) return cacheHit;
|
||||
@ -90,8 +92,8 @@ export class UserConverter {
|
||||
});
|
||||
}
|
||||
|
||||
public static async encodeMany(users: User[], cache: AccountCache = UserHelpers.getFreshAccountCache()): Promise<MastodonEntity.Account[]> {
|
||||
const encoded = users.map(u => this.encode(u, cache));
|
||||
public static async encodeMany(users: User[], ctx: MastoContext): Promise<MastodonEntity.Account[]> {
|
||||
const encoded = users.map(u => this.encode(u, ctx));
|
||||
return Promise.all(encoded);
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ import { convertAccountId, convertListId, convertRelationshipId, convertStatusId
|
||||
import { UserConverter } from "@/server/api/mastodon/converters/user.js";
|
||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
|
||||
import { ListHelpers } from "@/server/api/mastodon/helpers/list.js";
|
||||
import { Files } from "formidable";
|
||||
import { auth } from "@/server/api/mastodon/middleware/auth.js";
|
||||
@ -14,7 +13,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
router.get("/v1/accounts/verify_credentials",
|
||||
auth(true, ['read:accounts']),
|
||||
async (ctx) => {
|
||||
const acct = await UserHelpers.verifyCredentials(ctx.user);
|
||||
const acct = await UserHelpers.verifyCredentials(ctx.user, ctx);
|
||||
ctx.body = convertAccountId(acct);
|
||||
}
|
||||
);
|
||||
@ -22,7 +21,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
auth(true, ['write:accounts']),
|
||||
async (ctx) => {
|
||||
const files = (ctx.request as any).files as Files | undefined;
|
||||
const acct = await UserHelpers.updateCredentials(ctx.user, (ctx.request as any).body as any, files);
|
||||
const acct = await UserHelpers.updateCredentials(ctx.user, (ctx.request as any).body as any, files, ctx);
|
||||
ctx.body = convertAccountId(acct)
|
||||
}
|
||||
);
|
||||
@ -30,7 +29,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(ctx.query);
|
||||
const user = await UserHelpers.getUserFromAcct(args.acct);
|
||||
const account = await UserConverter.encode(user);
|
||||
const account = await UserConverter.encode(user, ctx);
|
||||
ctx.body = convertAccountId(account);
|
||||
}
|
||||
);
|
||||
@ -47,7 +46,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
auth(false),
|
||||
async (ctx) => {
|
||||
const userId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const account = await UserConverter.encode(await UserHelpers.getUserOr404(userId));
|
||||
const account = await UserConverter.encode(await UserHelpers.getUserOr404(userId), ctx);
|
||||
ctx.body = convertAccountId(account);
|
||||
}
|
||||
);
|
||||
@ -56,10 +55,10 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
auth(false, ["read:statuses"]),
|
||||
async (ctx) => {
|
||||
const userId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const query = await UserHelpers.getUserCachedOr404(userId, ctx.cache);
|
||||
const query = await UserHelpers.getUserCachedOr404(userId, ctx);
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(argsToBools(limitToInt(ctx.query))));
|
||||
const res = await UserHelpers.getUserStatuses(query, ctx.user, args.max_id, args.since_id, args.min_id, args.limit, args['only_media'], args['exclude_replies'], args['exclude_reblogs'], args.pinned, args.tagged);
|
||||
const tl = await NoteConverter.encodeMany(res.data, ctx.user, ctx.cache);
|
||||
const tl = await NoteConverter.encodeMany(res.data, ctx.user, ctx);
|
||||
|
||||
ctx.body = tl.map(s => convertStatusIds(s));
|
||||
ctx.pagination = res.pagination;
|
||||
@ -76,10 +75,10 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
auth(false),
|
||||
async (ctx) => {
|
||||
const userId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const query = await UserHelpers.getUserCachedOr404(userId, ctx.cache);
|
||||
const query = await UserHelpers.getUserCachedOr404(userId, ctx);
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const res = await UserHelpers.getUserFollowers(query, ctx.user, args.max_id, args.since_id, args.min_id, args.limit);
|
||||
const followers = await UserConverter.encodeMany(res.data, ctx.cache);
|
||||
const followers = await UserConverter.encodeMany(res.data, ctx);
|
||||
|
||||
ctx.body = followers.map((account) => convertAccountId(account));
|
||||
ctx.pagination = res.pagination;
|
||||
@ -90,10 +89,10 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
auth(false),
|
||||
async (ctx) => {
|
||||
const userId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const query = await UserHelpers.getUserCachedOr404(userId, ctx.cache);
|
||||
const query = await UserHelpers.getUserCachedOr404(userId, ctx);
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const res = await UserHelpers.getUserFollowing(query, ctx.user, args.max_id, args.since_id, args.min_id, args.limit);
|
||||
const following = await UserConverter.encodeMany(res.data, ctx.cache);
|
||||
const following = await UserConverter.encodeMany(res.data, ctx);
|
||||
|
||||
ctx.body = following.map((account) => convertAccountId(account));
|
||||
ctx.pagination = res.pagination;
|
||||
@ -103,7 +102,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
"/v1/accounts/:id/lists",
|
||||
auth(true, ["read:lists"]),
|
||||
async (ctx) => {
|
||||
const member = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId));
|
||||
const member = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const results = await ListHelpers.getListsByMember(ctx.user, member);
|
||||
ctx.body = results.map(p => convertListId(p));
|
||||
},
|
||||
@ -112,7 +111,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
"/v1/accounts/:id/follow",
|
||||
auth(true, ["write:follows"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId));
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
//FIXME: Parse form data
|
||||
const result = await UserHelpers.followUser(target, ctx.user, true, false);
|
||||
ctx.body = convertRelationshipId(result);
|
||||
@ -122,7 +121,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
"/v1/accounts/:id/unfollow",
|
||||
auth(true, ["write:follows"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId));
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.unfollowUser(target, ctx.user);
|
||||
ctx.body = convertRelationshipId(result);
|
||||
},
|
||||
@ -131,7 +130,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
"/v1/accounts/:id/block",
|
||||
auth(true, ["write:blocks"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId));
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.blockUser(target, ctx.user);
|
||||
ctx.body = convertRelationshipId(result);
|
||||
},
|
||||
@ -140,7 +139,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
"/v1/accounts/:id/unblock",
|
||||
auth(true, ["write:blocks"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId));
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.unblockUser(target, ctx.user);
|
||||
ctx.body = convertRelationshipId(result)
|
||||
},
|
||||
@ -151,7 +150,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
async (ctx) => {
|
||||
//FIXME: parse form data
|
||||
const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query, ['duration']), ['notifications']));
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId));
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.muteUser(target, ctx.user, args.notifications, args.duration);
|
||||
ctx.body = convertRelationshipId(result)
|
||||
},
|
||||
@ -160,7 +159,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
"/v1/accounts/:id/unmute",
|
||||
auth(true, ["write:mutes"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId));
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.unmuteUser(target, ctx.user);
|
||||
ctx.body = convertRelationshipId(result)
|
||||
},
|
||||
@ -180,7 +179,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const res = await UserHelpers.getUserBookmarks(ctx.user, args.max_id, args.since_id, args.min_id, args.limit);
|
||||
const bookmarks = await NoteConverter.encodeMany(res.data, ctx.user, ctx.cache);
|
||||
const bookmarks = await NoteConverter.encodeMany(res.data, ctx.user, ctx);
|
||||
ctx.body = bookmarks.map(s => convertStatusIds(s));
|
||||
ctx.pagination = res.pagination;
|
||||
}
|
||||
@ -190,7 +189,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const res = await UserHelpers.getUserFavorites(ctx.user, args.max_id, args.since_id, args.min_id, args.limit);
|
||||
const favorites = await NoteConverter.encodeMany(res.data, ctx.user, ctx.cache);
|
||||
const favorites = await NoteConverter.encodeMany(res.data, ctx.user, ctx);
|
||||
ctx.body = favorites.map(s => convertStatusIds(s));
|
||||
ctx.pagination = res.pagination;
|
||||
}
|
||||
@ -199,7 +198,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
auth(true, ["read:mutes"]),
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const res = await UserHelpers.getUserMutes(ctx.user, args.max_id, args.since_id, args.min_id, args.limit, ctx.cache);
|
||||
const res = await UserHelpers.getUserMutes(ctx.user, args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
ctx.body = res.data.map(m => convertAccountId(m));
|
||||
ctx.pagination = res.pagination;
|
||||
}
|
||||
@ -209,7 +208,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const res = await UserHelpers.getUserBlocks(ctx.user, args.max_id, args.since_id, args.min_id, args.limit);
|
||||
const blocks = await UserConverter.encodeMany(res.data, ctx.cache);
|
||||
const blocks = await UserConverter.encodeMany(res.data, ctx);
|
||||
ctx.body = blocks.map(b => convertAccountId(b));
|
||||
ctx.pagination = res.pagination;
|
||||
}
|
||||
@ -219,7 +218,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const res = await UserHelpers.getUserFollowRequests(ctx.user, args.max_id, args.since_id, args.min_id, args.limit);
|
||||
const requests = await UserConverter.encodeMany(res.data, ctx.cache);
|
||||
const requests = await UserConverter.encodeMany(res.data, ctx);
|
||||
ctx.body = requests.map(b => convertAccountId(b));
|
||||
ctx.pagination = res.pagination;
|
||||
}
|
||||
@ -228,7 +227,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
"/v1/follow_requests/:id/authorize",
|
||||
auth(true, ["write:follows"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId));
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.acceptFollowRequest(target, ctx.user);
|
||||
ctx.body = convertRelationshipId(result);
|
||||
},
|
||||
@ -237,7 +236,7 @@ export function setupEndpointsAccount(router: Router): void {
|
||||
"/v1/follow_requests/:id/reject",
|
||||
auth(true, ["write:follows"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId));
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.rejectFollowRequest(target, ctx.user);
|
||||
ctx.body = convertRelationshipId(result);
|
||||
},
|
||||
|
@ -4,7 +4,6 @@ import { convertId, IdType } from "../../index.js";
|
||||
import { convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "@/server/api/mastodon/endpoints/timeline.js";
|
||||
import { ListHelpers } from "@/server/api/mastodon/helpers/list.js";
|
||||
import { UserConverter } from "@/server/api/mastodon/converters/user.js";
|
||||
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
|
||||
import { UserLists } from "@/models/index.js";
|
||||
import { getUser } from "@/server/api/common/getters.js";
|
||||
import { toArray } from "@/prelude/array.js";
|
||||
@ -72,7 +71,7 @@ export function setupEndpointsList(router: Router): void {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)));
|
||||
const res = await ListHelpers.getListUsers(ctx.user, id, args.max_id, args.since_id, args.min_id, args.limit);
|
||||
const accounts = await UserConverter.encodeMany(res.data);
|
||||
const accounts = await UserConverter.encodeMany(res.data, ctx);
|
||||
|
||||
ctx.body = accounts.map(account => convertAccountId(account));
|
||||
ctx.pagination = res.pagination;
|
||||
|
@ -16,7 +16,7 @@ export function setupEndpointsMisc(router: Router): void {
|
||||
|
||||
router.get("/v1/instance",
|
||||
async (ctx) => {
|
||||
ctx.body = await MiscHelpers.getInstance();
|
||||
ctx.body = await MiscHelpers.getInstance(ctx);
|
||||
}
|
||||
);
|
||||
|
||||
@ -54,7 +54,7 @@ export function setupEndpointsMisc(router: Router): void {
|
||||
router.get("/v1/trends/statuses",
|
||||
async (ctx) => {
|
||||
const args = limitToInt(ctx.query);
|
||||
ctx.body = await MiscHelpers.getTrendingStatuses(args.limit, args.offset)
|
||||
ctx.body = await MiscHelpers.getTrendingStatuses(args.limit, args.offset, ctx)
|
||||
.then(p => p.map(x => convertStatusIds(x)));
|
||||
}
|
||||
);
|
||||
@ -76,7 +76,7 @@ export function setupEndpointsMisc(router: Router): void {
|
||||
auth(true, ['read']),
|
||||
async (ctx) => {
|
||||
const args = limitToInt(ctx.query);
|
||||
ctx.body = await MiscHelpers.getFollowSuggestions(ctx.user, args.limit)
|
||||
ctx.body = await MiscHelpers.getFollowSuggestions(ctx.user, args.limit, ctx)
|
||||
.then(p => p.map(x => convertSuggestionIds(x)));
|
||||
}
|
||||
);
|
||||
|
@ -2,7 +2,6 @@ import Router from "@koa/router";
|
||||
import { convertId, IdType } from "../../index.js";
|
||||
import { convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "./timeline.js";
|
||||
import { convertNotificationIds } from "../converters.js";
|
||||
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { NotificationHelpers } from "@/server/api/mastodon/helpers/notification.js";
|
||||
import { NotificationConverter } from "@/server/api/mastodon/converters/notification.js";
|
||||
import { auth } from "@/server/api/mastodon/middleware/auth.js";
|
||||
@ -13,7 +12,7 @@ export function setupEndpointsNotifications(router: Router): void {
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)), ['types[]', 'exclude_types[]']);
|
||||
const res = await NotificationHelpers.getNotifications(ctx.user, args.max_id, args.since_id, args.min_id, args.limit, args['types[]'], args['exclude_types[]'], args.account_id);
|
||||
const data = await NotificationConverter.encodeMany(res.data, ctx.user, ctx.cache);
|
||||
const data = await NotificationConverter.encodeMany(res.data, ctx.user, ctx);
|
||||
|
||||
ctx.body = data.map(n => convertNotificationIds(n));
|
||||
ctx.pagination = res.pagination;
|
||||
@ -24,7 +23,7 @@ export function setupEndpointsNotifications(router: Router): void {
|
||||
auth(true, ['read:notifications']),
|
||||
async (ctx) => {
|
||||
const notification = await NotificationHelpers.getNotificationOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx.user);
|
||||
ctx.body = convertNotificationIds(await NotificationConverter.encode(notification, ctx.user));
|
||||
ctx.body = convertNotificationIds(await NotificationConverter.encode(notification, ctx.user, ctx));
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import Router from "@koa/router";
|
||||
import { argsToBools, convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "./timeline.js";
|
||||
import { convertSearchIds } from "../converters.js";
|
||||
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { SearchHelpers } from "@/server/api/mastodon/helpers/search.js";
|
||||
import { auth } from "@/server/api/mastodon/middleware/auth.js";
|
||||
|
||||
@ -10,7 +9,7 @@ export function setupEndpointsSearch(router: Router): void {
|
||||
auth(true, ['read:search']),
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(argsToBools(limitToInt(ctx.query), ['resolve', 'following', 'exclude_unreviewed'])));
|
||||
const result = await SearchHelpers.search(ctx.user, args.q, args.type, args.resolve, args.following, args.account_id, args['exclude_unreviewed'], args.max_id, args.min_id, args.limit, args.offset, ctx.cache);
|
||||
const result = await SearchHelpers.search(ctx.user, args.q, args.type, args.resolve, args.following, args.account_id, args['exclude_unreviewed'], args.max_id, args.min_id, args.limit, args.offset, ctx);
|
||||
|
||||
ctx.body = convertSearchIds(result);
|
||||
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||
import { NoteHelpers } from "@/server/api/mastodon/helpers/note.js";
|
||||
import { convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "@/server/api/mastodon/endpoints/timeline.js";
|
||||
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
|
||||
import { UserConverter } from "@/server/api/mastodon/converters/user.js";
|
||||
import { PollHelpers } from "@/server/api/mastodon/helpers/poll.js";
|
||||
import { toArray } from "@/prelude/array.js";
|
||||
@ -33,7 +32,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
|
||||
let request = NoteHelpers.normalizeComposeOptions(ctx.request.body);
|
||||
ctx.body = await NoteHelpers.createNote(request, ctx.user)
|
||||
.then(p => NoteConverter.encode(p, ctx.user))
|
||||
.then(p => NoteConverter.encode(p, ctx.user, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
|
||||
if (key !== null) NoteHelpers.postIdempotencyCache.set(key, { status: ctx.body });
|
||||
@ -46,7 +45,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const note = await NoteHelpers.getNoteOr404(noteId, ctx.user);
|
||||
let request = NoteHelpers.normalizeEditOptions(ctx.request.body);
|
||||
ctx.body = await NoteHelpers.editNote(request, note, ctx.user)
|
||||
.then(p => NoteConverter.encode(p, ctx.user))
|
||||
.then(p => NoteConverter.encode(p, ctx.user, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
}
|
||||
);
|
||||
@ -56,7 +55,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const noteId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(noteId, ctx.user);
|
||||
|
||||
const status = await NoteConverter.encode(note, ctx.user);
|
||||
const status = await NoteConverter.encode(note, ctx.user, ctx);
|
||||
ctx.body = convertStatusIds(status);
|
||||
}
|
||||
);
|
||||
@ -65,7 +64,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
async (ctx) => {
|
||||
const noteId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(noteId, ctx.user);
|
||||
ctx.body = await NoteHelpers.deleteNote(note, ctx.user)
|
||||
ctx.body = await NoteHelpers.deleteNote(note, ctx.user, ctx)
|
||||
.then(p => convertStatusIds(p));
|
||||
}
|
||||
);
|
||||
@ -77,10 +76,10 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
const ancestors = await NoteHelpers.getNoteAncestors(note, ctx.user, ctx.user ? 4096 : 60)
|
||||
.then(n => NoteConverter.encodeMany(n, ctx.user, ctx.cache))
|
||||
.then(n => NoteConverter.encodeMany(n, ctx.user, ctx))
|
||||
.then(n => n.map(s => convertStatusIds(s)));
|
||||
const descendants = await NoteHelpers.getNoteDescendants(note, ctx.user, ctx.user ? 4096 : 40, ctx.user ? 4096 : 20)
|
||||
.then(n => NoteConverter.encodeMany(n, ctx.user, ctx.cache))
|
||||
.then(n => NoteConverter.encodeMany(n, ctx.user, ctx))
|
||||
.then(n => n.map(s => convertStatusIds(s)));
|
||||
|
||||
ctx.body = {
|
||||
@ -95,7 +94,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
const res = await NoteHelpers.getNoteEditHistory(note);
|
||||
const res = await NoteHelpers.getNoteEditHistory(note, ctx);
|
||||
ctx.body = res.map(p => convertStatusEditIds(p));
|
||||
}
|
||||
);
|
||||
@ -117,7 +116,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const res = await NoteHelpers.getNoteRebloggedBy(note, ctx.user, args.max_id, args.since_id, args.min_id, args.limit);
|
||||
const users = await UserConverter.encodeMany(res.data, ctx.cache);
|
||||
const users = await UserConverter.encodeMany(res.data, ctx);
|
||||
ctx.body = users.map(m => convertAccountId(m));
|
||||
ctx.pagination = res.pagination;
|
||||
}
|
||||
@ -130,7 +129,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const res = await NoteHelpers.getNoteFavoritedBy(note, args.max_id, args.since_id, args.min_id, args.limit);
|
||||
const users = await UserConverter.encodeMany(res.data, ctx.cache);
|
||||
const users = await UserConverter.encodeMany(res.data, ctx);
|
||||
ctx.body = users.map(m => convertAccountId(m));
|
||||
ctx.pagination = res.pagination;
|
||||
}
|
||||
@ -144,7 +143,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const reaction = await NoteHelpers.getDefaultReaction();
|
||||
|
||||
ctx.body = await NoteHelpers.reactToNote(note, ctx.user, reaction)
|
||||
.then(p => NoteConverter.encode(p, ctx.user))
|
||||
.then(p => NoteConverter.encode(p, ctx.user, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
}
|
||||
);
|
||||
@ -156,7 +155,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
|
||||
ctx.body = await NoteHelpers.removeReactFromNote(note, ctx.user)
|
||||
.then(p => NoteConverter.encode(p, ctx.user))
|
||||
.then(p => NoteConverter.encode(p, ctx.user, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
},
|
||||
);
|
||||
@ -169,7 +168,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
|
||||
ctx.body = await NoteHelpers.reblogNote(note, ctx.user)
|
||||
.then(p => NoteConverter.encode(p, ctx.user))
|
||||
.then(p => NoteConverter.encode(p, ctx.user, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
},
|
||||
);
|
||||
@ -182,7 +181,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
|
||||
ctx.body = await NoteHelpers.unreblogNote(note, ctx.user)
|
||||
.then(p => NoteConverter.encode(p, ctx.user))
|
||||
.then(p => NoteConverter.encode(p, ctx.user, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
},
|
||||
);
|
||||
@ -195,7 +194,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
|
||||
ctx.body = await NoteHelpers.bookmarkNote(note, ctx.user)
|
||||
.then(p => NoteConverter.encode(p, ctx.user))
|
||||
.then(p => NoteConverter.encode(p, ctx.user, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
},
|
||||
);
|
||||
@ -208,7 +207,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
|
||||
ctx.body = await NoteHelpers.unbookmarkNote(note, ctx.user)
|
||||
.then(p => NoteConverter.encode(p, ctx.user))
|
||||
.then(p => NoteConverter.encode(p, ctx.user, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
},
|
||||
);
|
||||
@ -221,7 +220,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
|
||||
ctx.body = await NoteHelpers.pinNote(note, ctx.user)
|
||||
.then(p => NoteConverter.encode(p, ctx.user))
|
||||
.then(p => NoteConverter.encode(p, ctx.user, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
},
|
||||
);
|
||||
@ -234,7 +233,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
|
||||
ctx.body = await NoteHelpers.unpinNote(note, ctx.user)
|
||||
.then(p => NoteConverter.encode(p, ctx.user))
|
||||
.then(p => NoteConverter.encode(p, ctx.user, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
},
|
||||
);
|
||||
@ -247,7 +246,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
|
||||
ctx.body = await NoteHelpers.reactToNote(note, ctx.user, ctx.params.name)
|
||||
.then(p => NoteConverter.encode(p, ctx.user))
|
||||
.then(p => NoteConverter.encode(p, ctx.user, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
},
|
||||
);
|
||||
@ -260,7 +259,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
|
||||
ctx.body = await NoteHelpers.removeReactFromNote(note, ctx.user)
|
||||
.then(p => NoteConverter.encode(p, ctx.user))
|
||||
.then(p => NoteConverter.encode(p, ctx.user, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
},
|
||||
);
|
||||
@ -269,7 +268,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
|
||||
const data = await PollHelpers.getPoll(note, ctx.user, ctx.cache);
|
||||
const data = await PollHelpers.getPoll(note, ctx.user, ctx);
|
||||
ctx.body = convertPollId(data);
|
||||
});
|
||||
router.post<{ Params: { id: string } }>(
|
||||
@ -283,7 +282,7 @@ export function setupEndpointsStatus(router: Router): void {
|
||||
const choices = toArray(body.choices ?? []).map(p => parseInt(p));
|
||||
if (choices.length < 1) throw new MastoApiError(400, "Must vote for at least one option");
|
||||
|
||||
const data = await PollHelpers.voteInPoll(choices, note, ctx.user, ctx.cache);
|
||||
const data = await PollHelpers.voteInPoll(choices, note, ctx.user, ctx);
|
||||
ctx.body = convertPollId(data);
|
||||
},
|
||||
);
|
||||
|
@ -68,7 +68,7 @@ export function setupEndpointsTimeline(router: Router): void {
|
||||
async (ctx, reply) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(argsToBools(limitToInt(ctx.query))));
|
||||
const res = await TimelineHelpers.getPublicTimeline(ctx.user, args.max_id, args.since_id, args.min_id, args.limit, args.only_media, args.local, args.remote);
|
||||
const tl = await NoteConverter.encodeMany(res.data, ctx.user, ctx.cache);
|
||||
const tl = await NoteConverter.encodeMany(res.data, ctx.user, ctx);
|
||||
|
||||
ctx.body = tl.map(s => convertStatusIds(s));
|
||||
ctx.pagination = res.pagination;
|
||||
@ -80,7 +80,7 @@ export function setupEndpointsTimeline(router: Router): void {
|
||||
const tag = (ctx.params.hashtag ?? '').trim();
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(argsToBools(limitToInt(ctx.query))), ['any[]', 'all[]', 'none[]']);
|
||||
const res = await TimelineHelpers.getTagTimeline(ctx.user, tag, args.max_id, args.since_id, args.min_id, args.limit, args['any[]'] ?? [], args['all[]'] ?? [], args['none[]'] ?? [], args.only_media, args.local, args.remote);
|
||||
const tl = await NoteConverter.encodeMany(res.data, ctx.user, ctx.cache);
|
||||
const tl = await NoteConverter.encodeMany(res.data, ctx.user, ctx);
|
||||
|
||||
ctx.body = tl.map(s => convertStatusIds(s));
|
||||
ctx.pagination = res.pagination;
|
||||
@ -91,7 +91,7 @@ export function setupEndpointsTimeline(router: Router): void {
|
||||
async (ctx, reply) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)));
|
||||
const res = await TimelineHelpers.getHomeTimeline(ctx.user, args.max_id, args.since_id, args.min_id, args.limit);
|
||||
const tl = await NoteConverter.encodeMany(res.data, ctx.user, ctx.cache);
|
||||
const tl = await NoteConverter.encodeMany(res.data, ctx.user, ctx);
|
||||
|
||||
ctx.body = tl.map(s => convertStatusIds(s));
|
||||
ctx.pagination = res.pagination;
|
||||
@ -106,7 +106,7 @@ export function setupEndpointsTimeline(router: Router): void {
|
||||
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)));
|
||||
const res = await TimelineHelpers.getListTimeline(ctx.user, list, args.max_id, args.since_id, args.min_id, args.limit);
|
||||
const tl = await NoteConverter.encodeMany(res.data, ctx.user, ctx.cache);
|
||||
const tl = await NoteConverter.encodeMany(res.data, ctx.user, ctx);
|
||||
|
||||
ctx.body = tl.map(s => convertStatusIds(s));
|
||||
ctx.pagination = res.pagination;
|
||||
@ -116,7 +116,7 @@ export function setupEndpointsTimeline(router: Router): void {
|
||||
auth(true, ['read:statuses']),
|
||||
async (ctx, reply) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)));
|
||||
const res = await TimelineHelpers.getConversations(ctx.user, args.max_id, args.since_id, args.min_id, args.limit);
|
||||
const res = await TimelineHelpers.getConversations(ctx.user, args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
|
||||
ctx.body = res.data.map(c => convertConversationIds(c));
|
||||
ctx.pagination = res.pagination;
|
||||
|
@ -19,9 +19,10 @@ import { EmojiConverter } from "@/server/api/mastodon/converters/emoji.js";
|
||||
import { populateEmojis } from "@/misc/populate-emojis.js";
|
||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||
import { VisibilityConverter } from "@/server/api/mastodon/converters/visibility.js";
|
||||
import { MastoContext } from "@/server/api/mastodon/index.js";
|
||||
|
||||
export class MiscHelpers {
|
||||
public static async getInstance(): Promise<MastodonEntity.Instance> {
|
||||
public static async getInstance(ctx: MastoContext): Promise<MastodonEntity.Instance> {
|
||||
const userCount = Users.count({ where: { host: IsNull() } });
|
||||
const noteCount = Notes.count({ where: { userHost: IsNull() } });
|
||||
const instanceCount = Instances.count({ cache: 3600000 });
|
||||
@ -34,7 +35,7 @@ export class MiscHelpers {
|
||||
},
|
||||
order: { id: "ASC" },
|
||||
})
|
||||
.then(p => p ? UserConverter.encode(p) : null)
|
||||
.then(p => p ? UserConverter.encode(p, ctx) : null)
|
||||
.then(p => p ? convertAccountId(p) : null);
|
||||
const meta = await fetchMeta(true);
|
||||
|
||||
@ -133,8 +134,7 @@ export class MiscHelpers {
|
||||
}
|
||||
}
|
||||
|
||||
public static async getFollowSuggestions(user: ILocalUser, limit: number): Promise<MastodonEntity.SuggestedAccount[]> {
|
||||
const cache = UserHelpers.getFreshAccountCache();
|
||||
public static async getFollowSuggestions(user: ILocalUser, limit: number, ctx: MastoContext): Promise<MastodonEntity.SuggestedAccount[]> {
|
||||
const results: Promise<MastodonEntity.SuggestedAccount[]>[] = [];
|
||||
|
||||
const pinned = fetchMeta().then(meta => Promise.all(
|
||||
@ -147,7 +147,7 @@ export class MiscHelpers {
|
||||
}))
|
||||
)
|
||||
.then(p => p.filter(x => !!x) as User[])
|
||||
.then(p => UserConverter.encodeMany(p, cache))
|
||||
.then(p => UserConverter.encodeMany(p, ctx))
|
||||
.then(p => p.map(x => {
|
||||
return { source: "staff", account: x } as MastodonEntity.SuggestedAccount
|
||||
}))
|
||||
@ -167,7 +167,7 @@ export class MiscHelpers {
|
||||
const global = query
|
||||
.take(limit)
|
||||
.getMany()
|
||||
.then(p => UserConverter.encodeMany(p, cache))
|
||||
.then(p => UserConverter.encodeMany(p, ctx))
|
||||
.then(p => p.map(x => {
|
||||
return { source: "global", account: x } as MastodonEntity.SuggestedAccount
|
||||
}));
|
||||
@ -206,7 +206,7 @@ export class MiscHelpers {
|
||||
);
|
||||
}
|
||||
|
||||
public static async getTrendingStatuses(limit: number = 20, offset: number = 0): Promise<MastodonEntity.Status[]> {
|
||||
public static async getTrendingStatuses(limit: number = 20, offset: number = 0, ctx: MastoContext): Promise<MastodonEntity.Status[]> {
|
||||
if (limit > 40) limit = 40;
|
||||
const query = Notes.createQueryBuilder("note")
|
||||
.addSelect("note.score")
|
||||
@ -220,7 +220,7 @@ export class MiscHelpers {
|
||||
.skip(offset)
|
||||
.take(limit)
|
||||
.getMany()
|
||||
.then(result => NoteConverter.encodeMany(result, null));
|
||||
.then(result => NoteConverter.encodeMany(result, null, ctx));
|
||||
}
|
||||
|
||||
public static async getTrendingHashtags(limit: number = 10, offset: number = 0): Promise<MastodonEntity.Tag[]> {
|
||||
|
@ -30,6 +30,7 @@ import { Cache } from "@/misc/cache.js";
|
||||
import AsyncLock from "async-lock";
|
||||
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
||||
import { IsNull } from "typeorm";
|
||||
import { MastoContext } from "@/server/api/mastodon/index.js";
|
||||
|
||||
export class NoteHelpers {
|
||||
public static postIdempotencyCache = new Cache<{ status?: MastodonEntity.Status }>('postIdempotencyCache', 60 * 60);
|
||||
@ -143,9 +144,9 @@ export class NoteHelpers {
|
||||
return note;
|
||||
}
|
||||
|
||||
public static async deleteNote(note: Note, user: ILocalUser): Promise<MastodonEntity.Status> {
|
||||
public static async deleteNote(note: Note, user: ILocalUser, ctx: MastoContext): Promise<MastodonEntity.Status> {
|
||||
if (user.id !== note.userId) throw new MastoApiError(404);
|
||||
const status = await NoteConverter.encode(note, user);
|
||||
const status = await NoteConverter.encode(note, user, ctx);
|
||||
await deleteNote(user, note);
|
||||
status.content = undefined;
|
||||
return status;
|
||||
@ -175,10 +176,9 @@ export class NoteHelpers {
|
||||
});
|
||||
}
|
||||
|
||||
public static async getNoteEditHistory(note: Note): Promise<MastodonEntity.StatusEdit[]> {
|
||||
const cache = UserHelpers.getFreshAccountCache();
|
||||
const account = Promise.resolve(note.user ?? await UserHelpers.getUserCached(note.userId, cache))
|
||||
.then(p => UserConverter.encode(p, cache));
|
||||
public static async getNoteEditHistory(note: Note, ctx: MastoContext): Promise<MastodonEntity.StatusEdit[]> {
|
||||
const account = Promise.resolve(note.user ?? await UserHelpers.getUserCached(note.userId, ctx))
|
||||
.then(p => UserConverter.encode(p, ctx));
|
||||
const edits = await NoteEdits.find({ where: { noteId: note.id }, order: { id: "ASC" } });
|
||||
const history: Promise<MastodonEntity.StatusEdit>[] = [];
|
||||
|
||||
|
@ -13,14 +13,15 @@ import { Not } from "typeorm";
|
||||
import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js";
|
||||
import { populateEmojis } from "@/misc/populate-emojis.js";
|
||||
import { EmojiConverter } from "@/server/api/mastodon/converters/emoji.js";
|
||||
import { AccountCache, UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { MastoContext } from "@/server/api/mastodon/index.js";
|
||||
|
||||
export class PollHelpers {
|
||||
public static async getPoll(note: Note, user: ILocalUser | null, cache: AccountCache = UserHelpers.getFreshAccountCache()): Promise<MastodonEntity.Poll> {
|
||||
public static async getPoll(note: Note, user: ILocalUser | null, ctx: MastoContext): Promise<MastodonEntity.Poll> {
|
||||
if (!await Notes.isVisibleForMe(note, user?.id ?? null))
|
||||
throw new Error('Cannot encode poll not visible for user');
|
||||
|
||||
const noteUser = note.user ?? UserHelpers.getUserCached(note.userId, cache);
|
||||
const noteUser = note.user ?? UserHelpers.getUserCached(note.userId, ctx);
|
||||
const host = Promise.resolve(noteUser).then(noteUser => noteUser.host ?? null);
|
||||
const noteEmoji = await host
|
||||
.then(async host => populateEmojis(note.emojis, host)
|
||||
@ -31,7 +32,7 @@ export class PollHelpers {
|
||||
return populatePoll(note, user?.id ?? null).then(p => PollConverter.encode(p, note.id, noteEmoji));
|
||||
}
|
||||
|
||||
public static async voteInPoll(choices: number[], note: Note, user: ILocalUser, cache: AccountCache = UserHelpers.getFreshAccountCache()): Promise<MastodonEntity.Poll> {
|
||||
public static async voteInPoll(choices: number[], note: Note, user: ILocalUser, ctx: MastoContext): Promise<MastodonEntity.Poll> {
|
||||
if (!note.hasPoll) throw new MastoApiError(404);
|
||||
|
||||
for (const choice of choices) {
|
||||
@ -122,6 +123,6 @@ export class PollHelpers {
|
||||
);
|
||||
}
|
||||
}
|
||||
return this.getPoll(note, user, cache);
|
||||
return this.getPoll(note, user, ctx);
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
|
||||
import { ILocalUser, User } from "@/models/entities/user.js";
|
||||
import { Brackets, In, IsNull } from "typeorm";
|
||||
import { awaitAll } from "@/prelude/await-all.js";
|
||||
import { AccountCache, UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||
import Resolver from "@/remote/activitypub/resolver.js";
|
||||
import { getApId, isActor, isPost } from "@/remote/activitypub/type.js";
|
||||
@ -22,9 +21,10 @@ import { resolveUser } from "@/remote/resolve-user.js";
|
||||
import { createNote } from "@/remote/activitypub/models/note.js";
|
||||
import { getUser } from "@/server/api/common/getters.js";
|
||||
import config from "@/config/index.js";
|
||||
import { MastoContext } from "@/server/api/mastodon/index.js";
|
||||
|
||||
export class SearchHelpers {
|
||||
public static async search(user: ILocalUser, q: string | undefined, type: string | undefined, resolve: boolean = false, following: boolean = false, accountId: string | undefined, excludeUnreviewed: boolean = false, maxId: string | undefined, minId: string | undefined, limit: number = 20, offset: number | undefined, cache: AccountCache = UserHelpers.getFreshAccountCache()): Promise<MastodonEntity.Search> {
|
||||
public static async search(user: ILocalUser, q: string | undefined, type: string | undefined, resolve: boolean = false, following: boolean = false, accountId: string | undefined, excludeUnreviewed: boolean = false, maxId: string | undefined, minId: string | undefined, limit: number = 20, offset: number | undefined, ctx: MastoContext): Promise<MastodonEntity.Search> {
|
||||
if (q === undefined || q.trim().length === 0) throw new Error('Search query cannot be empty');
|
||||
if (limit > 40) limit = 40;
|
||||
const notes = type === 'statuses' || !type ? this.searchNotes(user, q, resolve, following, accountId, maxId, minId, limit, offset) : [];
|
||||
@ -32,8 +32,8 @@ export class SearchHelpers {
|
||||
const tags = type === 'hashtags' || !type ? this.searchTags(q, excludeUnreviewed, limit, offset) : [];
|
||||
|
||||
const result = {
|
||||
statuses: Promise.resolve(notes).then(p => NoteConverter.encodeMany(p, user, cache)),
|
||||
accounts: Promise.resolve(users).then(p => UserConverter.encodeMany(p, cache)),
|
||||
statuses: Promise.resolve(notes).then(p => NoteConverter.encodeMany(p, user, ctx)),
|
||||
accounts: Promise.resolve(users).then(p => UserConverter.encodeMany(p, ctx)),
|
||||
hashtags: Promise.resolve(tags)
|
||||
};
|
||||
|
||||
|
@ -19,6 +19,7 @@ import { awaitAll } from "@/prelude/await-all.js";
|
||||
import { unique } from "@/prelude/array.js";
|
||||
import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js";
|
||||
import { generatePaginationData, LinkPaginationObject } from "@/server/api/mastodon/middleware/pagination.js";
|
||||
import { MastoContext } from "@/server/api/mastodon/index.js";
|
||||
|
||||
export class TimelineHelpers {
|
||||
public static async getHomeTimeline(user: ILocalUser, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 20): Promise<LinkPaginationObject<Note[]>> {
|
||||
@ -163,7 +164,7 @@ export class TimelineHelpers {
|
||||
return PaginationHelpers.execQueryLinkPagination(query, limit, minId !== undefined);
|
||||
}
|
||||
|
||||
public static async getConversations(user: ILocalUser, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 20): Promise<LinkPaginationObject<MastodonEntity.Conversation[]>> {
|
||||
public static async getConversations(user: ILocalUser, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 20, ctx: MastoContext): Promise<LinkPaginationObject<MastodonEntity.Conversation[]>> {
|
||||
if (limit > 40) limit = 40;
|
||||
const sq = Notes.createQueryBuilder("note")
|
||||
.select("COALESCE(note.threadId, note.id)", "conversationId")
|
||||
@ -188,12 +189,11 @@ export class TimelineHelpers {
|
||||
|
||||
return query.take(limit).getMany().then(p => {
|
||||
if (minId !== undefined) p = p.reverse();
|
||||
const cache = UserHelpers.getFreshAccountCache();
|
||||
const conversations = p.map(c => {
|
||||
// Gather all unique IDs except for the local user
|
||||
const userIds = unique([c.userId].concat(c.visibleUserIds).filter(p => p != user.id));
|
||||
const users = userIds.map(id => UserHelpers.getUserCached(id, cache).catch(_ => null));
|
||||
const accounts = Promise.all(users).then(u => UserConverter.encodeMany(u.filter(u => u) as User[], cache));
|
||||
const users = userIds.map(id => UserHelpers.getUserCached(id, ctx).catch(_ => null));
|
||||
const accounts = Promise.all(users).then(u => UserConverter.encodeMany(u.filter(u => u) as User[], ctx));
|
||||
const unread = Notifications.createQueryBuilder('notification')
|
||||
.where("notification.noteId = :noteId")
|
||||
.andWhere("notification.notifieeId = :userId")
|
||||
@ -206,8 +206,8 @@ export class TimelineHelpers {
|
||||
|
||||
return {
|
||||
id: c.threadId ?? c.id,
|
||||
accounts: accounts.then(u => u.length > 0 ? u : UserConverter.encodeMany([user], cache)), // failsafe to prevent apps from crashing case when all participant users have been deleted
|
||||
last_status: NoteConverter.encode(c, user, cache),
|
||||
accounts: accounts.then(u => u.length > 0 ? u : UserConverter.encodeMany([user], ctx)), // failsafe to prevent apps from crashing case when all participant users have been deleted
|
||||
last_status: NoteConverter.encode(c, user, ctx),
|
||||
unread: unread
|
||||
}
|
||||
});
|
||||
|
@ -41,6 +41,7 @@ import { UserProfile } from "@/models/entities/user-profile.js";
|
||||
import { verifyLink } from "@/services/fetch-rel-me.js";
|
||||
import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js";
|
||||
import { generatePaginationData, LinkPaginationObject } from "@/server/api/mastodon/middleware/pagination.js";
|
||||
import { MastoContext } from "@/server/api/mastodon/index.js";
|
||||
|
||||
export type AccountCache = {
|
||||
locks: AsyncLock;
|
||||
@ -147,7 +148,7 @@ export class UserHelpers {
|
||||
return this.getUserRelationshipTo(target.id, localUser.id);
|
||||
}
|
||||
|
||||
public static async updateCredentials(user: ILocalUser, formData: updateCredsData, files: Files | undefined): Promise<MastodonEntity.Account> {
|
||||
public static async updateCredentials(user: ILocalUser, formData: updateCredsData, files: Files | undefined, ctx: MastoContext): Promise<MastodonEntity.Account> {
|
||||
const updates: Partial<User> = {};
|
||||
const profileUpdates: Partial<UserProfile> = {};
|
||||
|
||||
@ -183,11 +184,11 @@ export class UserHelpers {
|
||||
if (Object.keys(updates).length > 0) await Users.update(user.id, updates);
|
||||
if (Object.keys(profileUpdates).length > 0) await UserProfiles.update({ userId: user.id }, profileUpdates);
|
||||
|
||||
return this.verifyCredentials(user);
|
||||
return this.verifyCredentials(user, ctx);
|
||||
}
|
||||
|
||||
public static async verifyCredentials(user: ILocalUser): Promise<MastodonEntity.Account> {
|
||||
const acct = UserConverter.encode(user);
|
||||
public static async verifyCredentials(user: ILocalUser, ctx: MastoContext): Promise<MastodonEntity.Account> {
|
||||
const acct = UserConverter.encode(user, ctx);
|
||||
const profile = UserProfiles.findOneByOrFail({ userId: user.id });
|
||||
const privacy = this.getDefaultNoteVisibility(user);
|
||||
const fields = profile.then(profile => profile.fields.map(field => {
|
||||
@ -224,7 +225,7 @@ export class UserHelpers {
|
||||
});
|
||||
}
|
||||
|
||||
public static async getUserMutes(user: ILocalUser, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 40, cache: AccountCache = UserHelpers.getFreshAccountCache()): Promise<LinkPaginationObject<MastodonEntity.MutedAccount[]>> {
|
||||
public static async getUserMutes(user: ILocalUser, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 40, ctx: MastoContext): Promise<LinkPaginationObject<MastodonEntity.MutedAccount[]>> {
|
||||
if (limit > 80) limit = 80;
|
||||
|
||||
const query = PaginationHelpers.makePaginationQuery(
|
||||
@ -243,7 +244,7 @@ export class UserHelpers {
|
||||
.map(p => p.mutee)
|
||||
.filter(p => p) as User[];
|
||||
|
||||
const result = await UserConverter.encodeMany(users, cache)
|
||||
const result = await UserConverter.encodeMany(users, ctx)
|
||||
.then(res => res.map(m => {
|
||||
const muting = p.find(acc => acc.muteeId === m.id);
|
||||
return {
|
||||
@ -495,7 +496,8 @@ export class UserHelpers {
|
||||
return awaitAll(response);
|
||||
}
|
||||
|
||||
public static async getUserCached(id: string, cache: AccountCache = UserHelpers.getFreshAccountCache()): Promise<User> {
|
||||
public static async getUserCached(id: string, ctx: MastoContext): Promise<User> {
|
||||
const cache = ctx.cache as AccountCache;
|
||||
return cache.locks.acquire(id, async () => {
|
||||
const cacheHit = cache.users.find(p => p.id == id);
|
||||
if (cacheHit) return cacheHit;
|
||||
@ -506,8 +508,8 @@ export class UserHelpers {
|
||||
});
|
||||
}
|
||||
|
||||
public static async getUserCachedOr404(id: string, cache: AccountCache = UserHelpers.getFreshAccountCache()): Promise<User> {
|
||||
return this.getUserCached(id, cache).catch(_ => {
|
||||
public static async getUserCachedOr404(id: string, ctx: MastoContext): Promise<User> {
|
||||
return this.getUserCached(id, ctx).catch(_ => {
|
||||
throw new MastoApiError(404);
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { MastoContext } from "@/server/api/mastodon/index.js";
|
||||
import config from "@/config/index.js";
|
||||
import { convertId, IdType } from "@/misc/convert-id.js";
|
||||
import { ObjectLiteral } from "typeorm";
|
||||
|
||||
type PaginationData = {
|
||||
limit: number;
|
||||
|
Loading…
Reference in New Issue
Block a user