diff --git a/packages/backend/src/db/scylla.ts b/packages/backend/src/db/scylla.ts index a658e54035..08c08a0430 100644 --- a/packages/backend/src/db/scylla.ts +++ b/packages/backend/src/db/scylla.ts @@ -9,6 +9,7 @@ import { ChannelFollowingsCache, InstanceMutingsCache, LocalFollowingsCache, + RenoteMutingsCache, UserBlockedCache, UserMutingsCache, userWordMuteCache, @@ -16,7 +17,7 @@ import { import { getTimestamp } from "@/misc/gen-id.js"; import Logger from "@/services/logger.js"; import { UserProfiles } from "@/models/index.js"; -import { getWordHardMute } from "@/misc/check-word-mute"; +import { getWordHardMute } from "@/misc/check-word-mute.js"; function newClient(): Client | null { if (!config.scylla) { @@ -107,8 +108,8 @@ export const prepared = { (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, select: { byDate: `SELECT * FROM note WHERE "createdAtDate" = ?`, - byUri: `SELECT * FROM note WHERE "uri" IN ?`, - byUrl: `SELECT * FROM note WHERE "url" IN ?`, + byUri: `SELECT * FROM note WHERE "uri" = ?`, + byUrl: `SELECT * FROM note WHERE "url" = ?`, byId: `SELECT * FROM note_by_id WHERE "id" IN ?`, byUserId: `SELECT * FROM note_by_userid WHERE "userId" IN ?`, }, @@ -482,3 +483,15 @@ export async function filterBlockedUser( !(note.renoteUserId && blockerIds.includes(note.renoteUserId)), ); } + +export async function filterMutedRenotes( + notes: ScyllaNote[], + user: { id: User["id"] }, +): Promise { + const cache = await RenoteMutingsCache.init(user.id); + const muteeIds = await cache.getAll(); + + return notes.filter( + (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 b8cf15d8d3..b97cc13b3e 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -7,6 +7,7 @@ import { Followings, MutedNotes, Mutings, + RenoteMutings, UserProfiles, } from "@/models/index.js"; import { IsNull } from "typeorm"; @@ -73,7 +74,7 @@ export class Cache { public async delete(...keys: (string | null)[]): Promise { if (keys.length > 0) { - const _keys = keys.map(this.prefixedKey); + const _keys = keys.map((key) => this.prefixedKey(key)); await redisClient.del(_keys); } } @@ -233,7 +234,9 @@ class HashCache { } public async delete(...fields: string[]) { - await redisClient.hdel(this.key, ...fields); + if (fields.length > 0) { + await redisClient.hdel(this.key, ...fields); + } } public async clear() { @@ -401,3 +404,22 @@ export class UserBlockedCache extends SetCache { } export const userWordMuteCache = new Cache("mutedWord", 60 * 30); + +export class RenoteMutingsCache extends SetCache { + private constructor(userId: string) { + const fetcher = () => + RenoteMutings.find({ + select: ["muteeId"], + where: { muterId: userId }, + }).then((mutes) => mutes.map(({ muteeId }) => muteeId)); + + super("renoteMute", userId, fetcher); + } + + public static async init(userId: string): Promise { + const cache = new RenoteMutingsCache(userId); + await cache.fetch(); + + return cache; + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index ecdcaa3e5c..581dc4aa21 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -21,6 +21,7 @@ import { filterMutedUser, filterMutedNote, filterBlockedUser, + filterMutedRenotes, } from "@/db/scylla.js"; import { ChannelFollowingsCache, LocalFollowingsCache } from "@/misc/cache.js"; @@ -92,6 +93,7 @@ export default define(meta, paramDef, async (ps, user) => { filtered = await filterMutedUser(filtered, user); filtered = await filterMutedNote(filtered, user); filtered = await filterBlockedUser(filtered, user); + filtered = await filterMutedRenotes(filtered, user); return filtered; }; diff --git a/packages/backend/src/server/api/endpoints/renote-mute/create.ts b/packages/backend/src/server/api/endpoints/renote-mute/create.ts index f09f197c01..d9a1c365e4 100644 --- a/packages/backend/src/server/api/endpoints/renote-mute/create.ts +++ b/packages/backend/src/server/api/endpoints/renote-mute/create.ts @@ -4,6 +4,7 @@ import { RenoteMuting } from "@/models/entities/renote-muting.js"; import define from "../../define.js"; import { ApiError } from "../../error.js"; import { getUser } from "../../common/getters.js"; +import { RenoteMutingsCache } from "@/misc/cache.js"; export const meta = { tags: ["account"], @@ -47,12 +48,8 @@ export default define(meta, paramDef, async (ps, user) => { }); // Check if already muting - const exist = await RenoteMutings.exist({ - where: { - muterId: muter.id, - muteeId: mutee.id, - }, - }); + const cache = await RenoteMutingsCache.init(muter.id); + const exist = await cache.has(mutee.id); if (exist) { throw new ApiError(meta.errors.alreadyMuting); @@ -66,5 +63,7 @@ export default define(meta, paramDef, async (ps, user) => { muteeId: mutee.id, } as RenoteMuting); + await cache.add(mutee.id); + // publishUserEvent(user.id, "mute", mutee); }); diff --git a/packages/backend/src/server/api/endpoints/renote-mute/delete.ts b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts index 7a898141c3..5921c47102 100644 --- a/packages/backend/src/server/api/endpoints/renote-mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts @@ -2,6 +2,7 @@ import { RenoteMutings } from "@/models/index.js"; import define from "../../define.js"; import { ApiError } from "../../error.js"; import { getUser } from "../../common/getters.js"; +import { RenoteMutingsCache } from "@/misc/cache.js"; export const meta = { tags: ["account"], @@ -45,19 +46,19 @@ export default define(meta, paramDef, async (ps, user) => { }); // Check not muting - const muting = await RenoteMutings.findOneBy({ - muterId: muter.id, - muteeId: mutee.id, - }); + const cache = await RenoteMutingsCache.init(muter.id); + const muting = await cache.has(mutee.id); - if (muting == null) { + if (!muting) { throw new ApiError(meta.errors.notMuting); } // Delete mute await RenoteMutings.delete({ - id: muting.id, + muterId: muter.id, + muteeId: mutee.id, }); + await cache.delete(mutee.id); // publishUserEvent(user.id, "unmute", mutee); }); diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index c48544a444..ba433e9450 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -711,7 +711,7 @@ async function insertNote( emojis: string[], mentionedUsers: MinimumUser[], ) { - if (data.createdAt === null || data.createdAt === undefined) { + if (!data.createdAt || isNaN(data.createdAt.getTime())) { data.createdAt = new Date(); } const insert = new Note({