From 8e0b0c7cb9bc54b61a31f0f482889182507a2245 Mon Sep 17 00:00:00 2001 From: Namekuji Date: Wed, 20 Sep 2023 01:57:02 -0400 Subject: [PATCH] refactor: cache suspended users --- packages/backend/src/db/scylla.ts | 148 +++++------------- packages/backend/src/misc/cache.ts | 19 +++ .../backend/src/server/api/common/getters.ts | 9 +- .../api/endpoints/admin/suspend-user.ts | 2 + .../api/endpoints/admin/unsuspend-user.ts | 2 + .../server/api/endpoints/antennas/notes.ts | 19 ++- .../server/api/endpoints/channels/timeline.ts | 13 +- .../src/server/api/endpoints/clips/notes.ts | 24 +-- .../server/api/endpoints/i/notifications.ts | 6 +- .../backend/src/server/api/endpoints/notes.ts | 2 +- .../server/api/endpoints/notes/children.ts | 26 +-- .../server/api/endpoints/notes/featured.ts | 19 ++- .../api/endpoints/notes/global-timeline.ts | 24 +-- .../api/endpoints/notes/hybrid-timeline.ts | 21 +-- .../api/endpoints/notes/local-timeline.ts | 28 ++-- .../endpoints/notes/recommended-timeline.ts | 26 +-- .../src/server/api/endpoints/notes/renotes.ts | 20 ++- .../server/api/endpoints/notes/timeline.ts | 21 +-- .../api/endpoints/notes/user-list-timeline.ts | 4 +- .../src/server/api/endpoints/users/notes.ts | 22 +-- .../server/api/endpoints/users/reactions.ts | 4 +- 21 files changed, 234 insertions(+), 225 deletions(-) diff --git a/packages/backend/src/db/scylla.ts b/packages/backend/src/db/scylla.ts index fa2dbc8287..59cabd8271 100644 --- a/packages/backend/src/db/scylla.ts +++ b/packages/backend/src/db/scylla.ts @@ -404,7 +404,7 @@ export async function execPaginationQuery( userIds?: string[]; }, filter?: { - note?: (_: ScyllaNote[]) => Promise; + note?: (_: ScyllaNote[]) => ScyllaNote[]; reaction?: (_: ScyllaNoteReaction[]) => Promise; notification?: (_: ScyllaNotification[]) => Promise; }, @@ -492,7 +492,7 @@ export async function execPaginationQuery( } else { const notes = result.rows.map(parseScyllaNote); (found as ScyllaNote[]).push( - ...(filter?.note ? await filter.note(notes) : notes), + ...(filter?.note ? filter.note(notes) : notes), ); untilDate = notes[notes.length - 1].createdAt; } @@ -517,11 +517,11 @@ export async function execPaginationQuery( return found as ScyllaNote[]; } -export async function filterVisibility( +export function filterVisibility( notes: ScyllaNote[], user: { id: User["id"] } | null, - followingIds?: User["id"][], -): Promise { + followingIds: User["id"][], +): ScyllaNote[] { let filtered = notes; if (!user) { @@ -529,15 +529,6 @@ export async function filterVisibility( ["public", "home"].includes(note.visibility), ); } else { - let ids: User["id"][]; - if (followingIds) { - ids = followingIds; - } else { - ids = await LocalFollowingsCache.init(user.id).then((cache) => - cache.getAll(), - ); - } - filtered = filtered.filter( (note) => ["public", "home"].includes(note.visibility) || @@ -545,18 +536,18 @@ export async function filterVisibility( note.visibleUserIds.includes(user.id) || note.mentions.includes(user.id) || (note.visibility === "followers" && - (ids.includes(note.userId) || note.replyUserId === user.id)), + (followingIds.includes(note.userId) || note.replyUserId === user.id)), ); } return filtered; } -export async function filterChannel( +export function filterChannel( notes: ScyllaNote[], user: { id: User["id"] } | null, - followingIds?: Channel["id"][], -): Promise { + followingIds: Channel["id"][], +): ScyllaNote[] { let filtered = notes; if (!user) { @@ -564,16 +555,8 @@ export async function filterChannel( } else { const channelNotes = filtered.filter((note) => !!note.channelId); if (channelNotes.length > 0) { - let followings: Channel["id"][]; - if (followingIds) { - followings = followingIds; - } else { - followings = await ChannelFollowingsCache.init(user.id).then((cache) => - cache.getAll(), - ); - } filtered = filtered.filter( - (note) => !note.channelId || followings.includes(note.channelId), + (note) => !note.channelId || followingIds.includes(note.channelId), ); } } @@ -581,11 +564,11 @@ export async function filterChannel( return filtered; } -export async function filterReply( +export function filterReply( notes: ScyllaNote[], withReplies: boolean, user: { id: User["id"] } | null, -): Promise { +): ScyllaNote[] { let filtered = notes; if (!user) { @@ -605,113 +588,60 @@ export async function filterReply( return filtered; } -export async function filterMutedUser( +export function filterMutedUser( notes: ScyllaNote[], - user: { id: User["id"] }, - mutedIds?: User["id"][], - mutedInstances?: UserProfile["mutedInstances"], + mutedIds: User["id"][], + mutedInstances: UserProfile["mutedInstances"], exclude?: User, -): Promise { - let ids: User["id"][]; - let instances: UserProfile["mutedInstances"]; - - if (mutedIds) { - ids = mutedIds; - } else { - ids = await UserMutingsCache.init(user.id).then((cache) => cache.getAll()); - } - - if (mutedInstances) { - instances = mutedInstances; - } else { - instances = await InstanceMutingsCache.init(user.id).then((cache) => - cache.getAll(), - ); - } +): ScyllaNote[] { + let userIds: User["id"][] = mutedIds; if (exclude) { - ids = ids.filter((id) => id !== exclude.id); + userIds = mutedIds.filter((id) => id !== exclude.id); } return notes.filter( (note) => - !ids.includes(note.userId) && - !(note.replyUserId && ids.includes(note.replyUserId)) && - !(note.renoteUserId && ids.includes(note.renoteUserId)) && - !(note.userHost && instances.includes(note.userHost)) && - !(note.replyUserHost && instances.includes(note.replyUserHost)) && - !(note.renoteUserHost && instances.includes(note.renoteUserHost)), + !userIds.includes(note.userId) && + !(note.replyUserId && userIds.includes(note.replyUserId)) && + !(note.renoteUserId && userIds.includes(note.renoteUserId)) && + !(note.userHost && mutedInstances.includes(note.userHost)) && + !(note.replyUserHost && mutedInstances.includes(note.replyUserHost)) && + !(note.renoteUserHost && mutedInstances.includes(note.renoteUserHost)), ); } -export async function filterMutedNote( +export function filterMutedNote( notes: ScyllaNote[], user: { id: User["id"] }, - mutedWords?: string[][], -): Promise { - let words = mutedWords; - - if (!words) { - words = await userWordMuteCache.fetchMaybe(user.id, () => - UserProfiles.findOne({ - select: ["mutedWords"], - where: { userId: user.id }, - }).then((profile) => profile?.mutedWords), - ); - } - - if (words && words.length > 0) { + mutedWords: string[][], +): ScyllaNote[] { + if (mutedWords.length > 0) { return notes.filter( - (note) => !getWordHardMute(note, user, words as string[][]), + (note) => !getWordHardMute(note, user, mutedWords), ); } return notes; } -export async function filterBlockUser( +export function filterBlockUser( notes: ScyllaNote[], - user: { id: User["id"] }, - blockIds?: User["id"][], -): Promise { - let ids: User["id"][]; - - if (blockIds) { - ids = blockIds; - } else { - const blocked = await UserBlockedCache.init(user.id).then((cache) => - cache.getAll(), - ); - const blocking = await UserBlockingCache.init(user.id).then((cache) => - cache.getAll(), - ); - ids = [...blocked, ...blocking]; - } - + blockIds: User["id"][], +): ScyllaNote[] { return notes.filter( (note) => - !ids.includes(note.userId) && - !(note.replyUserId && ids.includes(note.replyUserId)) && - !(note.renoteUserId && ids.includes(note.renoteUserId)), + !blockIds.includes(note.userId) && + !(note.replyUserId && blockIds.includes(note.replyUserId)) && + !(note.renoteUserId && blockIds.includes(note.renoteUserId)), ); } -export async function filterMutedRenotes( +export function filterMutedRenotes( notes: ScyllaNote[], - user: { id: User["id"] }, - muteeIds?: User["id"][], -): Promise { - let ids: User["id"][]; - - if (muteeIds) { - ids = muteeIds; - } else { - ids = await RenoteMutingsCache.init(user.id).then((cache) => - cache.getAll(), - ); - } - + muteeIds: User["id"][], +): ScyllaNote[] { return notes.filter( - (note) => note.text || !note.renoteId || !ids.includes(note.userId), + (note) => note.text || !note.renoteId || !muteeIds.includes(note.userId), ); } diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index 349c83d410..48d0abaf96 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -8,6 +8,7 @@ import { Mutings, RenoteMutings, UserProfiles, + Users, } from "@/models/index.js"; import { IsNull } from "typeorm"; import config from "@/config/index.js"; @@ -479,3 +480,21 @@ export class RenoteMutingsCache extends SetCache { return cache; } } + +export class SuspendedUsersCache extends SetCache { + private constructor() { + const fetcher = () => Users.find({ + select: ["id"], + where: { isSuspended: true } + }).then((users) => users.map(({ id }) => id)); + + super("suspendedUsers", "system", fetcher); + } + + public static async init(): Promise { + const cache = new SuspendedUsersCache(); + await cache.fetch(); + + return cache; + } +} diff --git a/packages/backend/src/server/api/common/getters.ts b/packages/backend/src/server/api/common/getters.ts index 2dcd6916a0..2e1000a949 100644 --- a/packages/backend/src/server/api/common/getters.ts +++ b/packages/backend/src/server/api/common/getters.ts @@ -10,6 +10,7 @@ import { scyllaClient, } from "@/db/scylla.js"; import { userByIdCache } from "@/services/user-cache.js"; +import { LocalFollowingsCache } from "@/misc/cache.js"; /** * Get note for API processing, taking into account visibility. @@ -28,7 +29,13 @@ export async function getNote( ); if (result.rowLength > 0) { const candidate = parseScyllaNote(result.first()); - const filtered = await filterVisibility([candidate], me, followingIds); + let ids: string[] = []; + if (followingIds) { + ids = followingIds + } else if (me) { + ids = await LocalFollowingsCache.init(me.id).then((cache) => cache.getAll()); + } + const filtered = filterVisibility([candidate], me, ids); if (filtered.length > 0) { note = filtered[0]; } diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index 502d6f583c..681bd4a49b 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -6,6 +6,7 @@ import { insertModerationLog } from "@/services/insert-moderation-log.js"; import { doPostSuspend } from "@/services/suspend-user.js"; import { publishUserEvent } from "@/services/stream.js"; import { scyllaClient } from "@/db/scylla.js"; +import { SuspendedUsersCache } from "@/misc/cache.js"; export const meta = { tags: ["admin"], @@ -37,6 +38,7 @@ export default define(meta, paramDef, async (ps, me) => { throw new Error("cannot suspend moderator"); } + await SuspendedUsersCache.init().then((cache) => cache.add(user.id)); await Users.update(user.id, { isSuspended: true, }); diff --git a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts index e51d5851c2..bc50d49a4f 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts @@ -1,3 +1,4 @@ +import { SuspendedUsersCache } from "@/misc/cache.js"; import define from "../../define.js"; import { Users } from "@/models/index.js"; import { insertModerationLog } from "@/services/insert-moderation-log.js"; @@ -25,6 +26,7 @@ export default define(meta, paramDef, async (ps, me) => { throw new Error("user not found"); } + await SuspendedUsersCache.init().then((cache) => cache.delete(user.id)); await Users.update(user.id, { isSuspended: false, }); diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index ad39d664f6..ff887542ad 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -23,6 +23,7 @@ import { InstanceMutingsCache, LocalFollowingsCache, RenoteMutingsCache, + SuspendedUsersCache, UserBlockedCache, UserBlockingCache, UserMutingsCache, @@ -93,6 +94,7 @@ export default define(meta, paramDef, async (ps, user) => { blockerIds, blockingIds, renoteMutedIds, + suspendedUsers, ] = await Promise.all([ LocalFollowingsCache.init(user.id).then((cache) => cache.getAll()), UserMutingsCache.init(user.id).then((cache) => cache.getAll()), @@ -108,6 +110,7 @@ export default define(meta, paramDef, async (ps, user) => { UserBlockedCache.init(user.id).then((cache) => cache.getAll()), UserBlockingCache.init(user.id).then((cache) => cache.getAll()), RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()), + SuspendedUsersCache.init().then((cache) => cache.getAll()), ]); const userIds: string[] = []; @@ -139,21 +142,21 @@ export default define(meta, paramDef, async (ps, user) => { .map((xs) => xs.filter((x) => x !== "")) .filter((xs) => xs.length > 0); - const filter = async (notes: ScyllaNote[]) => { - let filtered = await filterVisibility(notes, user, followingUserIds); - filtered = await filterReply(filtered, antenna.withReplies, user); - filtered = await filterMutedUser( + const filter = (notes: ScyllaNote[]) => { + let filtered = filterVisibility(notes, user, followingUserIds); + filtered = filterReply(filtered, antenna.withReplies, user); + filtered = filterMutedUser( filtered, - user, mutedUserIds, mutedInstances, ); - filtered = await filterMutedNote(filtered, user, mutedWords); - filtered = await filterBlockUser(filtered, user, [ + filtered = filterMutedNote(filtered, user, mutedWords); + filtered = filterBlockUser(filtered, [ ...blockerIds, ...blockingIds, + ...suspendedUsers, ]); - filtered = await filterMutedRenotes(filtered, user, renoteMutedIds); + filtered = filterMutedRenotes(filtered, renoteMutedIds); if (antenna.withFile) { filtered = filtered.filter((n) => n.files.length > 0); } diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index caa070e04d..aab3ceb3f0 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -12,6 +12,7 @@ import { scyllaClient, } from "@/db/scylla.js"; import { + SuspendedUsersCache, UserBlockedCache, UserBlockingCache, UserMutingsCache, @@ -87,15 +88,17 @@ export default define(meta, paramDef, async (ps, user) => { UserBlockingCache.init(user.id).then((cache) => cache.getAll()), ]); } + const suspendedUsers = await SuspendedUsersCache.init().then((cache) => cache.getAll()); - const filter = async (notes: ScyllaNote[]) => { - if (!user) return notes; - let filtered = await filterMutedUser(notes, user, mutedUserIds, []); - filtered = await filterMutedNote(filtered, user, mutedWords); - filtered = await filterBlockUser(filtered, user, [ + const filter = (notes: ScyllaNote[]) => { + let filtered = filterBlockUser(notes, [ ...blockerIds, ...blockingIds, + ...suspendedUsers ]); + if (!user) return filtered; + filtered = filterMutedUser(notes, mutedUserIds, []); + filtered = filterMutedNote(filtered, user, mutedWords); return filtered; }; diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index bff93a89ec..b97f02cd4f 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -18,6 +18,7 @@ import { import { InstanceMutingsCache, LocalFollowingsCache, + SuspendedUsersCache, UserBlockedCache, UserBlockingCache, UserMutingsCache, @@ -91,6 +92,7 @@ export default define(meta, paramDef, async (ps, user) => { } const noteIds = await ClipNotes.find({ + select: ["noteId"], where: whereOpt, order: { noteId: "DESC" }, take: Math.min(ps.limit * 2, config.scylla?.queryLimit ?? 100), @@ -108,6 +110,8 @@ export default define(meta, paramDef, async (ps, user) => { blockingIds, ]: string[][] = []; let mutedWords: string[][]; + blockerIds = []; + blockingIds = []; if (user) { [ followingUserIds, @@ -132,22 +136,22 @@ export default define(meta, paramDef, async (ps, user) => { UserBlockingCache.init(user.id).then((cache) => cache.getAll()), ]); } + const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll()); - const filter = async (notes: ScyllaNote[]) => { - let filtered = notes; + const filter = (notes: ScyllaNote[]) => { + let filtered = filterBlockUser(notes, [ + ...blockerIds, + ...blockingIds, + ...suspendedUserIds, + ]); if (user) { - filtered = await filterVisibility(filtered, user, followingUserIds); - filtered = await filterMutedUser( + filtered = filterVisibility(filtered, user, followingUserIds); + filtered = filterMutedUser( filtered, - user, mutedUserIds, mutedInstances, ); - filtered = await filterMutedNote(filtered, user, mutedWords); - filtered = await filterBlockUser(filtered, user, [ - ...blockerIds, - ...blockingIds, - ]); + filtered = filterMutedNote(filtered, user, mutedWords); } return filtered; }; diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 68b0e51a06..9c478b9e6e 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -21,6 +21,7 @@ import { import { InstanceMutingsCache, LocalFollowingsCache, + SuspendedUsersCache, UserBlockedCache, UserBlockingCache, UserMutingsCache, @@ -98,12 +99,14 @@ export default define(meta, paramDef, async (ps, user) => { mutedInstances, blockerIds, blockingIds, + suspendedUsers, ] = await Promise.all([ LocalFollowingsCache.init(user.id).then((cache) => cache.getAll()), UserMutingsCache.init(user.id).then((cache) => cache.getAll()), InstanceMutingsCache.init(user.id).then((cache) => cache.getAll()), UserBlockedCache.init(user.id).then((cache) => cache.getAll()), UserBlockingCache.init(user.id).then((cache) => cache.getAll()), + SuspendedUsersCache.init().then((cache) => cache.getAll()), ]); const validUserIds = [user.id, ...followingUserIds]; @@ -130,7 +133,8 @@ export default define(meta, paramDef, async (ps, user) => { n.notifierId && (mutedUserIds.includes(n.notifierId) || blockingIds.includes(n.notifierId) || - blockerIds.includes(n.notifierId)) + blockerIds.includes(n.notifierId) || + suspendedUsers.includes(n.notifierId)) ), ); if (ps.directOnly) { diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 4dd6b7f0e1..8cd00803cf 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -42,7 +42,7 @@ export const paramDef = { export default define(meta, paramDef, async (ps) => { if (scyllaClient) { - const filter = async (notes: ScyllaNote[]) => { + const filter = (notes: ScyllaNote[]) => { let filtered = notes.filter((note) => !note.localOnly); if (ps.reply === undefined) { filtered = filtered.filter( diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index e0e349334f..6fe945ae64 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -19,6 +19,7 @@ import type { Note } from "@/models/entities/note.js"; import { InstanceMutingsCache, LocalFollowingsCache, + SuspendedUsersCache, UserBlockedCache, UserBlockingCache, UserMutingsCache, @@ -67,6 +68,8 @@ export default define(meta, paramDef, async (ps, user) => { blockingIds, ]: string[][] = []; let mutedWords: string[][] = []; + blockerIds = []; + blockingIds = []; if (user) { [ followingUserIds, @@ -91,6 +94,7 @@ export default define(meta, paramDef, async (ps, user) => { UserBlockingCache.init(user.id).then((cache) => cache.getAll()), ]); } + const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll()); const root = await getNote(ps.noteId, user, followingUserIds).catch( () => null, @@ -99,20 +103,20 @@ export default define(meta, paramDef, async (ps, user) => { return await Notes.packMany([]); } - const filter = async (notes: ScyllaNote[]) => { - let filtered = await filterVisibility(notes, user, followingUserIds); + const filter = (notes: ScyllaNote[]) => { + let filtered = filterVisibility(notes, user, followingUserIds); + filtered = filterBlockUser(filtered, [ + ...blockerIds, + ...blockingIds, + ...suspendedUserIds, + ]); if (user) { - filtered = await filterMutedUser( + filtered = filterMutedUser( filtered, - user, mutedUserIds, mutedInstances, ); - filtered = await filterMutedNote(filtered, user, mutedWords); - filtered = await filterBlockUser(filtered, user, [ - ...blockerIds, - ...blockingIds, - ]); + filtered = filterMutedNote(filtered, user, mutedWords); } return filtered; }; @@ -123,7 +127,7 @@ export default define(meta, paramDef, async (ps, user) => { [root.id], { prepare: true }, ); - const foundNotes = await filter( + const foundNotes = filter( renoteResult.rows.map(parseScyllaNote).filter((note) => !!note.text), ); @@ -140,7 +144,7 @@ export default define(meta, paramDef, async (ps, user) => { [note.id], { prepare: true }, ); - const replies = await filter(replyResult.rows.map(parseScyllaNote)); + const replies = filter(replyResult.rows.map(parseScyllaNote)); if (replies.length > 0) { foundNotes.push(...replies); queue.push(...replies); diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index ec26bc3acd..ff9eb1ff17 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -13,6 +13,7 @@ import { } from "@/db/scylla.js"; import { InstanceMutingsCache, + SuspendedUsersCache, UserBlockedCache, UserBlockingCache, UserMutingsCache, @@ -63,6 +64,9 @@ export default define(meta, paramDef, async (ps, user) => { let [mutedUserIds, mutedInstances, blockerIds, blockingIds]: string[][] = []; let mutedWords: string[][] = []; + blockerIds = []; + blockingIds = []; + const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll()); const foundNotes: ScyllaNote[] = []; let searchedDays = 0; @@ -101,18 +105,19 @@ export default define(meta, paramDef, async (ps, user) => { break; } + notes = filterBlockUser(notes, [ + ...blockerIds, + ...blockingIds, + ...suspendedUserIds, + ]); + if (user) { - notes = await filterMutedUser( + notes = filterMutedUser( notes, - user, mutedUserIds, mutedInstances, ); - notes = await filterMutedNote(notes, user, mutedWords); - notes = await filterBlockUser(notes, user, [ - ...blockerIds, - ...blockingIds, - ]); + notes = filterMutedNote(notes, user, mutedWords); } foundNotes.push(...notes); diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index f400e0ff91..e8467a44a0 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -22,6 +22,7 @@ import { import { InstanceMutingsCache, RenoteMutingsCache, + SuspendedUsersCache, UserBlockedCache, UserBlockingCache, UserMutingsCache, @@ -103,6 +104,9 @@ export default define(meta, paramDef, async (ps, user) => { renoteMutedIds, ]: string[][] = []; let mutedWords: string[][]; + blockerIds = []; + blockingIds = []; + const suspendedUsers = await SuspendedUsersCache.init().then((cache) => cache.getAll()); if (user) { [ mutedUserIds, @@ -128,24 +132,24 @@ export default define(meta, paramDef, async (ps, user) => { ]); } - const filter = async (notes: ScyllaNote[]) => { + const filter = (notes: ScyllaNote[]) => { let filtered = notes.filter( (n) => n.visibility === "public" && !n.channelId, ); - filtered = await filterReply(filtered, ps.withReplies, user); + filtered = filterReply(filtered, ps.withReplies, user); + filtered = filterBlockUser(filtered, [ + ...blockerIds, + ...blockingIds, + ...suspendedUsers, + ]); if (user) { - filtered = await filterMutedUser( + filtered = filterMutedUser( filtered, - user, mutedUserIds, mutedInstances, ); - filtered = await filterMutedNote(filtered, user, mutedWords); - filtered = await filterBlockUser(filtered, user, [ - ...blockerIds, - ...blockingIds, - ]); - filtered = await filterMutedRenotes(filtered, user, renoteMutedIds); + filtered = filterMutedNote(filtered, user, mutedWords); + filtered = filterMutedRenotes(filtered, renoteMutedIds); } if (ps.withFiles) { filtered = filtered.filter((n) => n.files.length > 0); diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 80d2cddea2..7fced64927 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -29,6 +29,7 @@ import { InstanceMutingsCache, LocalFollowingsCache, RenoteMutingsCache, + SuspendedUsersCache, UserBlockedCache, UserBlockingCache, UserMutingsCache, @@ -111,6 +112,7 @@ export default define(meta, paramDef, async (ps, user) => { blockerIds, blockingIds, renoteMutedIds, + suspendedUserIds, ] = await Promise.all([ ChannelFollowingsCache.init(user.id).then((cache) => cache.getAll()), LocalFollowingsCache.init(user.id).then((cache) => cache.getAll()), @@ -127,6 +129,7 @@ export default define(meta, paramDef, async (ps, user) => { UserBlockedCache.init(user.id).then((cache) => cache.getAll()), UserBlockingCache.init(user.id).then((cache) => cache.getAll()), RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()), + SuspendedUsersCache.init().then((cache) => cache.getAll()), ]); const homeUserIds = [user.id, ...followingUserIds]; const optFilter = (n: ScyllaNote) => @@ -137,22 +140,22 @@ export default define(meta, paramDef, async (ps, user) => { const localFilter = (notes: ScyllaNote[]) => notes.filter((n) => !homeUserIds.includes(n.userId)); - const commonFilter = async (notes: ScyllaNote[]) => { - let filtered = await filterChannel(notes, user, followingChannelIds); - filtered = await filterReply(filtered, ps.withReplies, user); - filtered = await filterVisibility(filtered, user, followingUserIds); - filtered = await filterMutedUser( + const commonFilter = (notes: ScyllaNote[]) => { + let filtered = filterChannel(notes, user, followingChannelIds); + filtered = filterReply(filtered, ps.withReplies, user); + filtered = filterVisibility(filtered, user, followingUserIds); + filtered = filterMutedUser( filtered, - user, mutedUserIds, mutedInstances, ); - filtered = await filterMutedNote(filtered, user, mutedWords); - filtered = await filterBlockUser(filtered, user, [ + filtered = filterMutedNote(filtered, user, mutedWords); + filtered = filterBlockUser(filtered, [ ...blockerIds, ...blockingIds, + ...suspendedUserIds, ]); - filtered = await filterMutedRenotes(filtered, user, renoteMutedIds); + filtered = filterMutedRenotes(filtered, renoteMutedIds); if (!ps.includeMyRenotes) { filtered = filtered.filter((n) => n.userId !== user.id || optFilter(n)); } diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index dbab2f019b..8997fc4ea2 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -29,6 +29,7 @@ import { InstanceMutingsCache, LocalFollowingsCache, RenoteMutingsCache, + SuspendedUsersCache, UserBlockedCache, UserBlockingCache, UserMutingsCache, @@ -120,6 +121,8 @@ export default define(meta, paramDef, async (ps, user) => { renoteMutedIds, ]: string[][] = []; let mutedWords: string[][]; + blockerIds = []; + blockingIds = []; if (user) { [ followingChannelIds, @@ -148,24 +151,25 @@ export default define(meta, paramDef, async (ps, user) => { RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()), ]); } + const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll()); - const filter = async (notes: ScyllaNote[]) => { - let filtered = await filterChannel(notes, user, followingChannelIds); - filtered = await filterReply(filtered, ps.withReplies, user); - filtered = await filterVisibility(filtered, user, followingUserIds); + const filter = (notes: ScyllaNote[]) => { + let filtered = filterChannel(notes, user, followingChannelIds); + filtered = filterReply(filtered, ps.withReplies, user); + filtered = filterVisibility(filtered, user, followingUserIds); + filtered = filterBlockUser(filtered, [ + ...blockerIds, + ...blockingIds, + ...suspendedUserIds, + ]); if (user) { - filtered = await filterMutedUser( + filtered = filterMutedUser( filtered, - user, mutedUserIds, mutedInstances, ); - filtered = await filterMutedNote(filtered, user, mutedWords); - filtered = await filterBlockUser(filtered, user, [ - ...blockerIds, - ...blockingIds, - ]); - filtered = await filterMutedRenotes(filtered, user, renoteMutedIds); + filtered = filterMutedNote(filtered, user, mutedWords); + filtered = filterMutedRenotes(filtered, renoteMutedIds); } if (ps.withFiles) { filtered = filtered.filter((n) => n.files.length > 0); diff --git a/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts b/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts index 3cd8127c9a..11e6b06731 100644 --- a/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts @@ -27,6 +27,7 @@ import { InstanceMutingsCache, LocalFollowingsCache, RenoteMutingsCache, + SuspendedUsersCache, UserBlockedCache, UserBlockingCache, UserMutingsCache, @@ -116,6 +117,8 @@ export default define(meta, paramDef, async (ps, user) => { renoteMutedIds, ]: string[][] = []; let mutedWords: string[][]; + blockerIds = []; + blockingIds = []; if (user) { [ followingUserIds, @@ -142,8 +145,9 @@ export default define(meta, paramDef, async (ps, user) => { RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()), ]); } + const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll()); - const filter = async (notes: ScyllaNote[]) => { + const filter = (notes: ScyllaNote[]) => { let filtered = notes.filter( (n) => n.visibility === "public" && @@ -151,21 +155,21 @@ export default define(meta, paramDef, async (ps, user) => { m.recommendedInstances.includes(n.userHost) && !n.channelId, ); - filtered = await filterReply(filtered, ps.withReplies, user); - filtered = await filterVisibility(filtered, user, followingUserIds); + filtered = filterReply(filtered, ps.withReplies, user); + filtered = filterVisibility(filtered, user, followingUserIds); + filtered = filterBlockUser(filtered, [ + ...blockerIds, + ...blockingIds, + ...suspendedUserIds, + ]); if (user) { - filtered = await filterMutedUser( + filtered = filterMutedUser( filtered, - user, mutedUserIds, mutedInstances, ); - filtered = await filterMutedNote(filtered, user, mutedWords); - filtered = await filterBlockUser(filtered, user, [ - ...blockerIds, - ...blockingIds, - ]); - filtered = await filterMutedRenotes(filtered, user, renoteMutedIds); + filtered = filterMutedNote(filtered, user, mutedWords); + filtered = filterMutedRenotes(filtered, renoteMutedIds); } if (ps.withFiles) { filtered = filtered.filter((n) => n.files.length > 0); diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index b5385d4e9b..bd7d0e926d 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -17,6 +17,7 @@ import { import { InstanceMutingsCache, LocalFollowingsCache, + SuspendedUsersCache, UserBlockedCache, UserBlockingCache, UserMutingsCache, @@ -78,6 +79,8 @@ export default define(meta, paramDef, async (ps, user) => { if (scyllaClient) { let [mutedUserIds, mutedInstances, blockerIds, blockingIds]: string[][] = []; + blockerIds = []; + blockingIds = []; if (user) { [mutedUserIds, mutedInstances, blockerIds, blockingIds] = await Promise.all([ @@ -87,24 +90,25 @@ export default define(meta, paramDef, async (ps, user) => { UserBlockingCache.init(user.id).then((cache) => cache.getAll()), ]); } + const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll()); - const filter = async (notes: ScyllaNote[]) => { + const filter = (notes: ScyllaNote[]) => { let filtered = notes; if (ps.userId) { filtered = filtered.filter((n) => n.userId === ps.userId); } - filtered = await filterVisibility(filtered, user, followingUserIds); + filtered = filterVisibility(filtered, user, followingUserIds); + filtered = filterBlockUser(filtered, [ + ...blockerIds, + ...blockingIds, + ...suspendedUserIds, + ]); if (user) { - filtered = await filterMutedUser( + filtered = filterMutedUser( filtered, - user, mutedUserIds, mutedInstances, ); - filtered = await filterBlockUser(filtered, user, [ - ...blockerIds, - ...blockingIds, - ]); } return filtered; }; diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 2722736530..e98e89a030 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -28,6 +28,7 @@ import { InstanceMutingsCache, LocalFollowingsCache, RenoteMutingsCache, + SuspendedUsersCache, UserBlockedCache, UserBlockingCache, UserMutingsCache, @@ -102,6 +103,7 @@ export default define(meta, paramDef, async (ps, user) => { blockerIds, blockingIds, renoteMutedIds, + suspendedUserIds, ] = await Promise.all([ ChannelFollowingsCache.init(user.id).then((cache) => cache.getAll()), followingsCache.getAll(), @@ -118,28 +120,29 @@ export default define(meta, paramDef, async (ps, user) => { UserBlockedCache.init(user.id).then((cache) => cache.getAll()), UserBlockingCache.init(user.id).then((cache) => cache.getAll()), RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()), + SuspendedUsersCache.init().then((cache) => cache.getAll()), ]); const validUserIds = [user.id, ...followingUserIds]; const optFilter = (n: ScyllaNote) => !n.renoteId || !!n.text || n.files.length > 0 || n.hasPoll; - const filter = async (notes: ScyllaNote[]) => { + const filter = (notes: ScyllaNote[]) => { let filtered = notes.filter((n) => validUserIds.includes(n.userId)); - filtered = await filterChannel(filtered, user, followingChannelIds); - filtered = await filterReply(filtered, ps.withReplies, user); - filtered = await filterVisibility(filtered, user, followingUserIds); - filtered = await filterMutedUser( + filtered = filterChannel(filtered, user, followingChannelIds); + filtered = filterReply(filtered, ps.withReplies, user); + filtered = filterVisibility(filtered, user, followingUserIds); + filtered = filterMutedUser( filtered, - user, mutedUserIds, mutedInstances, ); - filtered = await filterMutedNote(filtered, user, mutedWords); - filtered = await filterBlockUser(filtered, user, [ + filtered = filterMutedNote(filtered, user, mutedWords); + filtered = filterBlockUser(filtered, [ ...blockerIds, ...blockingIds, + ...suspendedUserIds, ]); - filtered = await filterMutedRenotes(filtered, user, renoteMutedIds); + filtered = filterMutedRenotes(filtered, renoteMutedIds); if (!ps.includeMyRenotes) { filtered = filtered.filter((n) => n.userId !== user.id || optFilter(n)); } diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 73734a8cf4..3f5996f67c 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -91,8 +91,8 @@ export default define(meta, paramDef, async (ps, user) => { ); const optFilter = (n: ScyllaNote) => !n.renoteId || !!n.text || n.files.length > 0 || n.hasPoll; - const filter = async (notes: ScyllaNote[]) => { - let filtered = await filterVisibility(notes, user, followingUserIds); + const filter = (notes: ScyllaNote[]) => { + let filtered = filterVisibility(notes, user, followingUserIds); if (!ps.includeMyRenotes) { filtered = filtered.filter((n) => n.userId !== user.id || optFilter(n)); } diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 6050e86305..ba02f6ab75 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -21,6 +21,7 @@ import { InstanceMutingsCache, LocalFollowingsCache, RenoteMutingsCache, + SuspendedUsersCache, UserBlockedCache, UserBlockingCache, UserMutingsCache, @@ -94,6 +95,8 @@ export default define(meta, paramDef, async (ps, me) => { blockingIds, ]: string[][] = []; let mutedWords: string[][]; + blockerIds = []; + blockingIds = []; if (me) { [ followingUserIds, @@ -118,6 +121,7 @@ export default define(meta, paramDef, async (ps, me) => { UserBlockingCache.init(me.id).then((cache) => cache.getAll()), ]); } + const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll()); if ( me && @@ -128,21 +132,21 @@ export default define(meta, paramDef, async (ps, me) => { return Notes.packMany([]); } - const filter = async (notes: ScyllaNote[]) => { + const filter = (notes: ScyllaNote[]) => { let filtered = notes.filter((n) => n.userId === ps.userId); - filtered = await filterVisibility(filtered, me, followingUserIds); + filtered = filterVisibility(filtered, me, followingUserIds); + filtered = filterBlockUser(filtered, [ + ...blockerIds, + ...blockingIds, + ...suspendedUserIds, + ]); if (me) { - filtered = await filterMutedUser( + filtered = filterMutedUser( filtered, - me, mutedUserIds, mutedInstances, ); - filtered = await filterMutedNote(filtered, me, mutedWords); - filtered = await filterBlockUser(filtered, me, [ - ...blockerIds, - ...blockingIds, - ]); + filtered = filterMutedNote(filtered, me, mutedWords); } if (ps.withFiles) { filtered = filtered.filter((n) => n.files.length > 0); diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index 722cf0126f..fed4dbd999 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -79,11 +79,11 @@ export default define(meta, paramDef, async (ps, me) => { const notes = await client .execute(prepared.note.select.byIds, [noteIds], { prepare: true }) .then((result) => result.rows.map(parseScyllaNote)); - const filteredNoteIds = await filterVisibility( + const filteredNoteIds = filterVisibility( notes, me, followingUserIds, - ).then((notes) => notes.map(({ id }) => id)); + ).map(({ id }) => id); noteIds = noteIds.filter((id) => filteredNoteIds.includes(id)); }