summaryrefslogtreecommitdiff
path: root/packages/backend/src/core
diff options
context:
space:
mode:
authoranatawa12 <anatawa12@icloud.com>2025-05-01 17:56:24 +0900
committerGitHub <noreply@github.com>2025-05-01 17:56:24 +0900
commit2fcb50273d69a798c1368b05bd2de46fae2227bf (patch)
treebc25b73919b1ee62154e1ba0919d3f5534dcf32c /packages/backend/src/core
parent[skip ci] Update CHANGELOG.md (prepend template) (diff)
downloadmisskey-2fcb50273d69a798c1368b05bd2de46fae2227bf.tar.gz
misskey-2fcb50273d69a798c1368b05bd2de46fae2227bf.tar.bz2
misskey-2fcb50273d69a798c1368b05bd2de46fae2227bf.zip
Exclude suspended users note from most timelines (#15775)
* feat: exclude notes by suspended user from FTT timeline endpoint * feat: exclude notes by suspended user from DB based timelines * chore: fix types * chore: fix types * chore: fix non-reply / renote * chore: fix non-reply / renote * test: update test * docs(changelog): 凍結されたユーザのノートが各種タイムラインで表示されないように * Exclude suspended users note from featured * fix: join user * Update CHANGELOG.md --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Diffstat (limited to 'packages/backend/src/core')
-rw-r--r--packages/backend/src/core/FanoutTimelineEndpointService.ts18
-rw-r--r--packages/backend/src/core/QueryService.ts22
-rw-r--r--packages/backend/src/core/SearchService.ts9
3 files changed, 48 insertions, 1 deletions
diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts
index 1ffeb4b3a4..6253f792ed 100644
--- a/packages/backend/src/core/FanoutTimelineEndpointService.ts
+++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts
@@ -36,6 +36,7 @@ type TimelineOptions = {
excludeNoFiles?: boolean;
excludeReplies?: boolean;
excludePureRenotes: boolean;
+ ignoreAuthorFromUserSuspension?: boolean;
dbFallback: (untilId: string | null, sinceId: string | null, limit: number) => Promise<MiNote[]>,
};
@@ -139,6 +140,23 @@ export class FanoutTimelineEndpointService {
};
}
+ {
+ const parentFilter = filter;
+ filter = (note) => {
+ const noteJoined = note as MiNote & {
+ renoteUser: MiUser | null;
+ replyUser: MiUser | null;
+ };
+ if (!ps.ignoreAuthorFromUserSuspension) {
+ if (note.user!.isSuspended) return false;
+ }
+ if (note.userId !== note.renoteUserId && noteJoined.renoteUser?.isSuspended) return false;
+ if (note.userId !== note.replyUserId && noteJoined.replyUser?.isSuspended) return false;
+
+ return parentFilter(note);
+ };
+ }
+
const redisTimeline: MiNote[] = [];
let readFromRedis = 0;
let lastSuccessfulRate = 1; // rateをキャッシュする?
diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts
index 119eb49c02..e219efaf3d 100644
--- a/packages/backend/src/core/QueryService.ts
+++ b/packages/backend/src/core/QueryService.ts
@@ -287,4 +287,26 @@ export class QueryService {
.andWhere(instanceSuspension('renoteUser'));
}
}
+
+ // Requirements: user replyUser renoteUser must be joined
+ @bindThis
+ public generateSuspendedUserQueryForNote(q: SelectQueryBuilder<any>, excludeAuthor?: boolean): void {
+ if (excludeAuthor) {
+ const brakets = (user: string) => new Brackets(qb => qb
+ .where(`note.${user}Id IS NULL`)
+ .orWhere(`user.id = ${user}.id`)
+ .orWhere(`${user}.isSuspended = FALSE`));
+ q
+ .andWhere(brakets('replyUser'))
+ .andWhere(brakets('renoteUser'));
+ } else {
+ const brakets = (user: string) => new Brackets(qb => qb
+ .where(`note.${user}Id IS NULL`)
+ .orWhere(`${user}.isSuspended = FALSE`));
+ q
+ .andWhere('user.isSuspended = FALSE')
+ .andWhere(brakets('replyUser'))
+ .andWhere(brakets('renoteUser'));
+ }
+ }
}
diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts
index d94281920e..20a776ded8 100644
--- a/packages/backend/src/core/SearchService.ts
+++ b/packages/backend/src/core/SearchService.ts
@@ -235,6 +235,7 @@ export class SearchService {
this.queryService.generateVisibilityQuery(query, me);
this.queryService.generateBlockedHostQueryForNote(query);
+ this.queryService.generateSuspendedUserQueryForNote(query);
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
@@ -297,11 +298,17 @@ export class SearchService {
])
: [new Set<string>(), new Set<string>()];
- const query = this.notesRepository.createQueryBuilder('note');
+ const query = this.notesRepository.createQueryBuilder('note')
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
query.where('note.id IN (:...noteIds)', { noteIds: res.hits.map(x => x.id) });
this.queryService.generateBlockedHostQueryForNote(query);
+ this.queryService.generateSuspendedUserQueryForNote(query);
const notes = (await query.getMany()).filter(note => {
if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false;