From 078399997446b2435335ca0b93730557798942bd Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 2 Jun 2025 17:22:41 -0400 Subject: add copyright header to bubble-timeline.ts --- packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index 17c9b31c90..882e344883 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: Marie and other Sharkey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; import type { NotesRepository, MiMeta } from '@/models/_.js'; -- cgit v1.2.3-freya From 0e16a56086a45263f812dc5c1ad39449f209ccd4 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 2 Jun 2025 17:26:58 -0400 Subject: remove unused DI from bubble-timeline.ts --- packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index 882e344883..25f3bd6c9e 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -5,7 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; -import type { NotesRepository, MiMeta } from '@/models/_.js'; +import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -61,9 +61,6 @@ export const paramDef = { @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.meta) - private serverSettings: MiMeta, - @Inject(DI.notesRepository) private notesRepository: NotesRepository, -- cgit v1.2.3-freya From 9ebb254ce54d909a3bdd7ed4b17e792e01fa3e7f Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 2 Jun 2025 17:27:33 -0400 Subject: avoid unnecessary nextTick callbacks in bubble-timeline.ts --- .../backend/src/server/api/endpoints/notes/bubble-timeline.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index 25f3bd6c9e..281a781922 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -128,11 +128,11 @@ export default class extends Endpoint { // eslint- return true; }); - process.nextTick(() => { - if (me) { + if (me) { + process.nextTick(() => { this.activeUsersChart.read(me); - } - }); + }); + } return await this.noteEntityService.packMany(timeline, me); }); -- cgit v1.2.3-freya From 0a59276457c54573a65398ffe84a3710013423f4 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 2 Jun 2025 17:28:10 -0400 Subject: remove unnecessary call to generateVisibilityQuery in bubble-timeline.ts --- packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index 281a781922..1ef57a14a2 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -91,7 +91,6 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('reply.user', 'replyUser') .leftJoinAndSelect('renote.user', 'renoteUser'); - this.queryService.generateVisibilityQuery(query, me); this.queryService.generateBlockedHostQueryForNote(query); if (me) this.queryService.generateMutedUserQueryForNotes(query, me); if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); -- cgit v1.2.3-freya From 022c49f45f1e00a2b7b04040be28420247ebb6ea Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 2 Jun 2025 17:28:48 -0400 Subject: fix query slowdown caused by unnecessary userHost IS NOT NULL condition in bubble-timeline.ts --- packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index 1ef57a14a2..ef3502de77 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -83,7 +83,6 @@ export default class extends Endpoint { // eslint- ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.channelId IS NULL') - .andWhere('note.userHost IS NOT NULL') .andWhere('userInstance.isBubbled = true') // This comes from generateBlockedHostQueryForNote below .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') -- cgit v1.2.3-freya From d4ea917869e809e4d9f1c8e41dc8ad5b155b5f27 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 2 Jun 2025 17:30:34 -0400 Subject: remove incorrect requireSigninToViewContents check in bubble-timeline.ts --- packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts | 2 -- 1 file changed, 2 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index ef3502de77..1a73f3c7f7 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -83,7 +83,6 @@ export default class extends Endpoint { // eslint- ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.channelId IS NULL') - .andWhere('userInstance.isBubbled = true') // This comes from generateBlockedHostQueryForNote below .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') @@ -94,7 +93,6 @@ export default class extends Endpoint { // eslint- if (me) this.queryService.generateMutedUserQueryForNotes(query, me); if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me); - if (!me) query.andWhere('user.requireSigninToViewContents = false'); if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); -- cgit v1.2.3-freya From 0ccbb8f3cf1e219818037a0c58dab79e430ece1a Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 2 Jun 2025 17:31:09 -0400 Subject: fix hasPoll check in bubble-timeline.ts --- packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index 1a73f3c7f7..ec19fda6a9 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -106,7 +106,7 @@ export default class extends Endpoint { // eslint- .orWhere('note.text IS NOT NULL') .orWhere('note.cw IS NOT NULL') .orWhere('note.replyId IS NOT NULL') - .orWhere('note.hasPoll = false') + .orWhere('note.hasPoll = true') .orWhere('note.fileIds != \'{}\''))); } //#endregion -- cgit v1.2.3-freya From 005b23947e8e9f792b0cd894882bcea1f460765a Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 2 Jun 2025 17:32:06 -0400 Subject: reduce calls to generateMutedUserRenotesQueryForNotes in bubble-timeline.ts --- packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index ec19fda6a9..716330bae9 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -92,7 +92,6 @@ export default class extends Endpoint { // eslint- this.queryService.generateBlockedHostQueryForNote(query); if (me) this.queryService.generateMutedUserQueryForNotes(query, me); if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); - if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me); if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); @@ -108,6 +107,9 @@ export default class extends Endpoint { // eslint- .orWhere('note.replyId IS NOT NULL') .orWhere('note.hasPoll = true') .orWhere('note.fileIds != \'{}\''))); + } else if (me) { + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); + } } //#endregion -- cgit v1.2.3-freya From c49341e7be8ed575625d335e7794e60c4cf9004a Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 2 Jun 2025 17:32:26 -0400 Subject: match if(me) formatting of other endpoints in bubble-timeline.ts --- packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index 716330bae9..8acf1711eb 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -90,8 +90,10 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateBlockedHostQueryForNote(query); - if (me) this.queryService.generateMutedUserQueryForNotes(query, me); - if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); + if (me) { + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); + } if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); -- cgit v1.2.3-freya From 7f0a70561217d30d567a158713a3249ee4f8a981 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 2 Jun 2025 17:34:07 -0400 Subject: use generateMatchingHostQuery in bubble-timeline.ts --- packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index 8acf1711eb..afc571d1f4 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -112,6 +112,11 @@ export default class extends Endpoint { // eslint- } else if (me) { this.queryService.generateMutedUserRenotesQueryForNotes(query, me); } + + if (me) { + this.queryService.generateMatchingHostQueryForNote(query, { isBubbled: true }); + } else { + this.queryService.generateMatchingHostQueryForNote(query, { isBubbled: true, isSilenced: false }); } //#endregion -- cgit v1.2.3-freya From 51d9b1c8c5a6a3f5378203384f1972417b1a34bf Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 2 Jun 2025 17:34:35 -0400 Subject: fetch followings asynchronously in bubble-timeline.ts --- .../server/api/endpoints/notes/bubble-timeline.ts | 25 ++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index afc571d1f4..bbd41ca99f 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -76,7 +76,9 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.btlDisabled); } - const followings = me ? await this.cacheService.userFollowingsCache.fetch(me.id) : undefined; + // Run this asynchronously - we will await it after the query completes. + // Catch-suppression is needed to avoid "unhandled rejection" if the query throws. + const followingsPromise = me ? this.cacheService.userFollowingsCache.fetch(me.id).catch(() => null) : null; //#region Construct query const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), @@ -122,16 +124,17 @@ export default class extends Endpoint { // eslint- let timeline = await query.limit(ps.limit).getMany(); - timeline = timeline.filter(note => { - if (note.user?.isSilenced) { - if (!me) return false; - if (!followings) return false; - if (note.userId !== me.id) { - return followings[note.userId]; - } - } - return true; - }); + const followings = await followingsPromise; + if (me && followings) { + timeline = timeline.filter(note => { + // Allow my own notes + if (note.userId === me.id) return true; + // Allow if not silenced + if (!note.user?.isSilenced) return true; + // Allow if following + return followings[note.userId]; + }); + } if (me) { process.nextTick(() => { -- cgit v1.2.3-freya From 7ab5ce1537feb47480a47e4f3faab16d089820f1 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 2 Jun 2025 22:56:35 -0400 Subject: replace generateBlockedHostQueryForNote allowSilenced parameter with generateSilencedUserQueryForNotes --- packages/backend/src/core/QueryService.ts | 17 +++-------------- .../src/server/api/endpoints/notes/search-by-tag.ts | 3 ++- 2 files changed, 5 insertions(+), 15 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 0f74e7cab1..2e0a368bd7 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -190,9 +190,8 @@ export class QueryService { .setParameters({ meId: me.id }); } - // TODO replace allowSilenced with matchingHostQuery @bindThis - public generateBlockedHostQueryForNote(q: SelectQueryBuilder, excludeAuthor?: boolean, allowSilenced = true): SelectQueryBuilder { + public generateBlockedHostQueryForNote(q: SelectQueryBuilder, excludeAuthor?: boolean): SelectQueryBuilder { const checkFor = (key: 'user' | 'replyUser' | 'renoteUser') => { q.andWhere(new Brackets(qb => { qb.orWhere(`note.${key}Host IS NULL`); // local @@ -207,18 +206,8 @@ export class QueryService { } } - if (allowSilenced) { - // not blocked - this.excludeInstanceWhere(qb, `note.${key}Host`, { - isBlocked: false, - }, 'orWhere'); - } else { - // not blocked or silenced - this.excludeInstanceWhere(qb, `note.${key}Host`, { - isBlocked: false, - isSilenced: false, - }, 'orWhere'); - } + // not blocked + this.excludeInstanceWhere(qb, `note.${key}Host`, { isBlocked: false }, 'orWhere'); })); }; diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index 5c1ab0fb78..01bedd9b1d 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -96,7 +96,8 @@ export default class extends Endpoint { // eslint- if (!this.serverSettings.enableBotTrending) query.andWhere('user.isBot = FALSE'); - this.queryService.generateBlockedHostQueryForNote(query, undefined, false); + this.queryService.generateBlockedHostQueryForNote(query); + this.queryService.generateSilencedUserQueryForNotes(query, me); if (me) this.queryService.generateMutedUserQueryForNotes(query, me); if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me); -- cgit v1.2.3-freya From 2258e439af06dca8093e89c5667c28bf5bc6be74 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 15:14:22 -0400 Subject: replace silenced users check with generateSilencedUserQueryForNotes in bubble-timeline.ts --- .../server/api/endpoints/notes/bubble-timeline.ts | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index bbd41ca99f..214beface5 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -68,7 +68,6 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, private roleService: RoleService, private activeUsersChart: ActiveUsersChart, - private cacheService: CacheService, ) { super(meta, paramDef, async (ps, me) => { const policies = await this.roleService.getUserPolicies(me ? me.id : null); @@ -76,10 +75,6 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.btlDisabled); } - // Run this asynchronously - we will await it after the query completes. - // Catch-suppression is needed to avoid "unhandled rejection" if the query throws. - const followingsPromise = me ? this.cacheService.userFollowingsCache.fetch(me.id).catch(() => null) : null; - //#region Construct query const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) @@ -92,6 +87,7 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateBlockedHostQueryForNote(query); + this.queryService.generateSilencedUserQueryForNotes(query, me); if (me) { this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); @@ -122,19 +118,7 @@ export default class extends Endpoint { // eslint- } //#endregion - let timeline = await query.limit(ps.limit).getMany(); - - const followings = await followingsPromise; - if (me && followings) { - timeline = timeline.filter(note => { - // Allow my own notes - if (note.userId === me.id) return true; - // Allow if not silenced - if (!note.user?.isSilenced) return true; - // Allow if following - return followings[note.userId]; - }); - } + const timeline = await query.limit(ps.limit).getMany(); if (me) { process.nextTick(() => { -- cgit v1.2.3-freya From a392865203cf09ca44053426d3cad2d0f3b48632 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 15:14:37 -0400 Subject: replace renotes check with generateExcludedRenotesQueryForNotes in bubble-timeline.ts --- .../backend/src/server/api/endpoints/notes/bubble-timeline.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index 214beface5..dec5308bc8 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -100,13 +100,7 @@ export default class extends Endpoint { // eslint- if (!ps.withBots) query.andWhere('user.isBot = FALSE'); if (!ps.withRenotes) { - query.andWhere(new Brackets(qb => qb - .orWhere('note.renoteId IS NULL') - .orWhere('note.text IS NOT NULL') - .orWhere('note.cw IS NOT NULL') - .orWhere('note.replyId IS NOT NULL') - .orWhere('note.hasPoll = true') - .orWhere('note.fileIds != \'{}\''))); + this.queryService.generateExcludedRenotesQueryForNotes(query); } else if (me) { this.queryService.generateMutedUserRenotesQueryForNotes(query, me); } -- cgit v1.2.3-freya From 38d4ac91ef6c0c3d76ee53d14d34e1f315cbb65f Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 15:15:04 -0400 Subject: fix bubble timeline query performance --- .../server/api/endpoints/notes/bubble-timeline.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index dec5308bc8..5f16351b20 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -4,7 +4,6 @@ */ import { Inject, Injectable } from '@nestjs/common'; -import { Brackets } from 'typeorm'; import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; @@ -12,7 +11,6 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; -import { CacheService } from '@/core/CacheService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -80,11 +78,21 @@ export default class extends Endpoint { // eslint- ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.channelId IS NULL') + .andWhere('note.userHost IS NOT NULL') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('reply.user', 'replyUser', 'replyUser.id = note.replyUserId') + .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId'); + + // This subquery mess teaches postgres how to use the right indexes. + // Using WHERE or ON conditions causes a fallback to full sequence scan, which times out. + // Important: don't use a query builder here or TypeORM will get confused and stop quoting column names! (known, unfixed bug apparently) + query + .leftJoin('(select "host" from "instance" where "isBubbled" = true)', 'bubbleInstance', '"bubbleInstance"."host" = "note"."userHost"') + .andWhere('"bubbleInstance" IS NOT NULL'); + this.queryService + .leftJoinInstance(query, 'note.userInstance', 'userInstance', '"userInstance"."host" = "bubbleInstance"."host"'); this.queryService.generateBlockedHostQueryForNote(query); this.queryService.generateSilencedUserQueryForNotes(query, me); @@ -104,12 +112,6 @@ export default class extends Endpoint { // eslint- } else if (me) { this.queryService.generateMutedUserRenotesQueryForNotes(query, me); } - - if (me) { - this.queryService.generateMatchingHostQueryForNote(query, { isBubbled: true }); - } else { - this.queryService.generateMatchingHostQueryForNote(query, { isBubbled: true, isSilenced: false }); - } //#endregion const timeline = await query.limit(ps.limit).getMany(); -- cgit v1.2.3-freya From 4b58752a16e01f13950e13adfcc5a5069a05f716 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 15:17:55 -0400 Subject: copy changes to global-timeline.ts --- .../server/api/endpoints/notes/global-timeline.ts | 38 +++++++--------------- 1 file changed, 12 insertions(+), 26 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') 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 e82d9ca7af..6ebb3c1676 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -12,7 +12,6 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; -import { CacheService } from '@/core/CacheService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -68,7 +67,6 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, private roleService: RoleService, private activeUsersChart: ActiveUsersChart, - private cacheService: CacheService, ) { super(meta, paramDef, async (ps, me) => { const policies = await this.roleService.getUserPolicies(me ? me.id : null); @@ -76,8 +74,6 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.gtlDisabled); } - const followings = me ? await this.cacheService.userFollowingsCache.fetch(me.id) : {}; - //#region Construct query const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) @@ -86,15 +82,14 @@ export default class extends Endpoint { // eslint- .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('reply.user', 'replyUser', 'replyUser.id = note.replyUserId') + .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId'); this.queryService.generateBlockedHostQueryForNote(query); - + this.queryService.generateSilencedUserQueryForNotes(query, me); if (me) { this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); - this.queryService.generateMutedUserRenotesQueryForNotes(query, me); } if (ps.withFiles) { @@ -103,29 +98,20 @@ export default class extends Endpoint { // eslint- if (!ps.withBots) query.andWhere('user.isBot = FALSE'); - if (ps.withRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.where('note.renoteId IS NULL'); - qb.orWhere(new Brackets(qb => { - qb.where('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - })); - })); + if (!ps.withRenotes) { + this.queryService.generateExcludedRenotesQueryForNotes(query); + } else if (me) { + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); } //#endregion - let timeline = await query.limit(ps.limit).getMany(); + const timeline = await query.limit(ps.limit).getMany(); - timeline = timeline.filter(note => { - if (note.user?.isSilenced && me && followings && note.userId !== me.id && !followings[note.userId]) return false; - return true; - }); - - process.nextTick(() => { - if (me) { + if (me) { + process.nextTick(() => { this.activeUsersChart.read(me); - } - }); + }); + } return await this.noteEntityService.packMany(timeline, me); }); -- cgit v1.2.3-freya From c3ba1396559147d149b99970d9a26af670d49165 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 15:21:42 -0400 Subject: copy changes to local-timeline.ts --- .../server/api/endpoints/notes/local-timeline.ts | 46 ++++++++++++++-------- 1 file changed, 29 insertions(+), 17 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') 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 f55853f3f3..360528eaed 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -103,13 +103,14 @@ export default class extends Endpoint { // eslint- withFiles: ps.withFiles, withReplies: ps.withReplies, withBots: ps.withBots, + withRenotes: ps.withRenotes, }, me); - process.nextTick(() => { - if (me) { + if (me) { + process.nextTick(() => { this.activeUsersChart.read(me); - } - }); + }); + } return await this.noteEntityService.packMany(timeline, me); } @@ -136,14 +137,15 @@ export default class extends Endpoint { // eslint- withFiles: ps.withFiles, withReplies: ps.withReplies, withBots: ps.withBots, + withRenotes: ps.withRenotes, }, me), }); - process.nextTick(() => { - if (me) { + if (me) { + process.nextTick(() => { this.activeUsersChart.read(me); - } - }); + }); + } return timeline; }); @@ -156,26 +158,38 @@ export default class extends Endpoint { // eslint- withFiles: boolean, withReplies: boolean, withBots: boolean, + withRenotes: boolean, }, me: MiLocalUser | null) { const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL) AND (note.channelId IS NULL)') + .andWhere('note.visibility = \'public\'') + .andWhere('note.channelId IS NULL') + .andWhere('note.userHost IS NULL') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('reply.user', 'replyUser', 'replyUser.id = note.replyUserId') + .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId'); - this.queryService.generateVisibilityQuery(query, me); this.queryService.generateBlockedHostQueryForNote(query); - if (me) this.queryService.generateMutedUserQueryForNotes(query, me); - if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); - if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me); + this.queryService.generateSilencedUserQueryForNotes(query, me); + if (me) { + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); + } if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } + if (!ps.withBots) query.andWhere('user.isBot = FALSE'); + + if (!ps.withRenotes) { + this.queryService.generateExcludedRenotesQueryForNotes(query); + } else if (me) { + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); + } + if (!ps.withReplies) { query.andWhere(new Brackets(qb => { qb @@ -188,8 +202,6 @@ export default class extends Endpoint { // eslint- })); } - if (!ps.withBots) query.andWhere('user.isBot = FALSE'); - return await query.limit(ps.limit).getMany(); } } -- cgit v1.2.3-freya From ef3b5541f8ca090141bed371fc39480af8ea874b Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 15:26:46 -0400 Subject: copy changes to hybrid-timeline.ts --- .../server/api/endpoints/notes/hybrid-timeline.ts | 57 +++++----------------- packages/misskey-js/src/autogen/types.ts | 6 --- 2 files changed, 12 insertions(+), 51 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') 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 6461a2e33f..083da9090f 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -66,9 +66,6 @@ export const paramDef = { sinceDate: { type: 'integer' }, untilDate: { type: 'integer' }, allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default - includeMyRenotes: { type: 'boolean', default: true }, - includeRenotedMyNotes: { type: 'boolean', default: true }, - includeLocalRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', default: false }, withRenotes: { type: 'boolean', default: true }, withReplies: { type: 'boolean', default: false }, @@ -114,12 +111,10 @@ export default class extends Endpoint { // eslint- untilId, sinceId, limit: ps.limit, - includeMyRenotes: ps.includeMyRenotes, - includeRenotedMyNotes: ps.includeRenotedMyNotes, - includeLocalRenotes: ps.includeLocalRenotes, withFiles: ps.withFiles, withReplies: ps.withReplies, withBots: ps.withBots, + withRenotes: ps.withRenotes, }, me); process.nextTick(() => { @@ -178,12 +173,10 @@ export default class extends Endpoint { // eslint- untilId, sinceId, limit, - includeMyRenotes: ps.includeMyRenotes, - includeRenotedMyNotes: ps.includeRenotedMyNotes, - includeLocalRenotes: ps.includeLocalRenotes, withFiles: ps.withFiles, withReplies: ps.withReplies, withBots: ps.withBots, + withRenotes: ps.withRenotes, }, me), }); @@ -199,12 +192,10 @@ export default class extends Endpoint { // eslint- untilId: string | null, sinceId: string | null, limit: number, - includeMyRenotes: boolean, - includeRenotedMyNotes: boolean, - includeLocalRenotes: boolean, withFiles: boolean, withReplies: boolean, withBots: boolean, + withRenotes: boolean, }, me: MiLocalUser) { const followees = await this.userFollowingService.getFollowees(me.id); const followingChannels = await this.channelFollowingsRepository.find({ @@ -227,8 +218,8 @@ export default class extends Endpoint { // eslint- .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('reply.user', 'replyUser', 'replyUser.id = note.replyUserId') + .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId'); if (followingChannels.length > 0) { const followingChannelIds = followingChannels.map(x => x.followeeId); @@ -255,45 +246,21 @@ export default class extends Endpoint { // eslint- this.queryService.generateVisibilityQuery(query, me); this.queryService.generateBlockedHostQueryForNote(query); + this.queryService.generateSilencedUserQueryForNotes(query, me); this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); - this.queryService.generateMutedUserRenotesQueryForNotes(query, me); - - if (ps.includeMyRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :meId', { meId: me.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.includeRenotedMyNotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserId != :meId', { meId: me.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.includeLocalRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserHost IS NOT NULL'); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } if (!ps.withBots) query.andWhere('user.isBot = FALSE'); + + if (!ps.withRenotes) { + this.queryService.generateExcludedRenotesQueryForNotes(query); + } else { + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); + } //#endregion return await query.limit(ps.limit).getMany(); diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index c0c0fd7895..68e150c9b7 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -27230,12 +27230,6 @@ export type operations = { untilDate?: number; /** @default false */ allowPartial?: boolean; - /** @default true */ - includeMyRenotes?: boolean; - /** @default true */ - includeRenotedMyNotes?: boolean; - /** @default true */ - includeLocalRenotes?: boolean; /** @default false */ withFiles?: boolean; /** @default true */ -- cgit v1.2.3-freya From 66572d16a881018575d2bd6740235854eb6cfbdd Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 15:31:15 -0400 Subject: copy changes to timeline.ts --- .../src/server/api/endpoints/notes/timeline.ts | 56 ++++------------------ packages/misskey-js/src/autogen/types.ts | 6 --- 2 files changed, 9 insertions(+), 53 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index a2dfa7fdac..a23a61373e 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -49,9 +49,6 @@ export const paramDef = { sinceDate: { type: 'integer' }, untilDate: { type: 'integer' }, allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default - includeMyRenotes: { type: 'boolean', default: true }, - includeRenotedMyNotes: { type: 'boolean', default: true }, - includeLocalRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', default: false }, withRenotes: { type: 'boolean', default: true }, withBots: { type: 'boolean', default: true }, @@ -88,9 +85,6 @@ export default class extends Endpoint { // eslint- untilId, sinceId, limit: ps.limit, - includeMyRenotes: ps.includeMyRenotes, - includeRenotedMyNotes: ps.includeRenotedMyNotes, - includeLocalRenotes: ps.includeLocalRenotes, withFiles: ps.withFiles, withRenotes: ps.withRenotes, withBots: ps.withBots, @@ -131,9 +125,6 @@ export default class extends Endpoint { // eslint- untilId, sinceId, limit, - includeMyRenotes: ps.includeMyRenotes, - includeRenotedMyNotes: ps.includeRenotedMyNotes, - includeLocalRenotes: ps.includeLocalRenotes, withFiles: ps.withFiles, withRenotes: ps.withRenotes, withBots: ps.withBots, @@ -148,7 +139,7 @@ export default class extends Endpoint { // eslint- }); } - private async getFromDb(ps: { untilId: string | null; sinceId: string | null; limit: number; includeMyRenotes: boolean; includeRenotedMyNotes: boolean; includeLocalRenotes: boolean; withFiles: boolean; withRenotes: boolean; withBots: boolean; }, me: MiLocalUser) { + private async getFromDb(ps: { untilId: string | null; sinceId: string | null; limit: number; withFiles: boolean; withRenotes: boolean; withBots: boolean; }, me: MiLocalUser) { const followees = await this.userFollowingService.getFollowees(me.id); const followingChannels = await this.channelFollowingsRepository.find({ where: { @@ -161,8 +152,8 @@ export default class extends Endpoint { // eslint- .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('reply.user', 'replyUser', 'replyUser.id = note.replyUserId') + .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId'); if (followees.length > 0 && followingChannels.length > 0) { // ユーザー・チャンネルともにフォローあり @@ -212,47 +203,18 @@ export default class extends Endpoint { // eslint- this.queryService.generateBlockedHostQueryForNote(query); this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); - this.queryService.generateMutedUserRenotesQueryForNotes(query, me); - - if (ps.includeMyRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :meId', { meId: me.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.includeRenotedMyNotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserId != :meId', { meId: me.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.includeLocalRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserHost IS NOT NULL'); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } - if (ps.withRenotes === false) { - query.andWhere('note.renoteId IS NULL'); - } - if (!ps.withBots) query.andWhere('user.isBot = FALSE'); + + if (!ps.withRenotes) { + this.queryService.generateExcludedRenotesQueryForNotes(query); + } else if (me) { + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); + } //#endregion return await query.limit(ps.limit).getMany(); diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 68e150c9b7..b85f1601de 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -28645,12 +28645,6 @@ export type operations = { untilDate?: number; /** @default false */ allowPartial?: boolean; - /** @default true */ - includeMyRenotes?: boolean; - /** @default true */ - includeRenotedMyNotes?: boolean; - /** @default true */ - includeLocalRenotes?: boolean; /** @default false */ withFiles?: boolean; /** @default true */ -- cgit v1.2.3-freya From 023924fe9afa91518fbec970bdeeede2f6ef4424 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 15:36:56 -0400 Subject: copy changes to antennas/notes.ts --- .../backend/src/server/api/endpoints/antennas/notes.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 7e79f0dccc..1abeee53d2 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -14,6 +14,7 @@ import { IdService } from '@/core/IdService.js'; import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { trackPromise } from '@/misc/promise-tracker.js'; +import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -75,6 +76,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, private fanoutTimelineService: FanoutTimelineService, private globalEventService: GlobalEventService, + private readonly activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); @@ -106,13 +108,14 @@ export default class extends Endpoint { // eslint- return []; } - const query = this.notesRepository.createQueryBuilder('note') + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), + ps.sinceId, ps.untilId) .where('note.id IN (:...noteIds)', { noteIds: noteIds }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('reply.user', 'replyUser', 'replyUser.id = note.replyUserId') + .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId'); // NOTE: センシティブ除外の設定はこのエンドポイントでは無視する。 // https://github.com/misskey-dev/misskey/pull/15346#discussion_r1929950255 @@ -124,11 +127,10 @@ export default class extends Endpoint { // eslint- this.queryService.generateMutedUserRenotesQueryForNotes(query, me); const notes = await query.getMany(); - if (sinceId != null && untilId == null) { - notes.sort((a, b) => a.id < b.id ? -1 : 1); - } else { - notes.sort((a, b) => a.id > b.id ? -1 : 1); - } + + process.nextTick(() => { + this.activeUsersChart.read(me); + }); return await this.noteEntityService.packMany(notes, me); }); -- cgit v1.2.3-freya From 6896bcea9dde208f8a7939d0f7949b82cf62eeb4 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 15:44:50 -0400 Subject: copy changes to mentions.ts --- .../src/server/api/endpoints/notes/mentions.ts | 30 ++++++++++++---------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 269b57366c..7c54bc69ab 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -10,6 +10,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; +import ActiveUsersChart from '@/core/chart/charts/active-users.js'; export const meta = { tags: ['notes'], @@ -57,43 +58,44 @@ export default class extends Endpoint { // eslint- private noteEntityService: NoteEntityService, private queryService: QueryService, + private readonly activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const followingQuery = this.followingsRepository.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: me.id }); - - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere(new Brackets(qb => { - qb // このmeIdAsListパラメータはqueryServiceのgenerateVisibilityQueryでセットされる - .where(':meIdAsList <@ note.mentions') - .orWhere(':meIdAsList <@ note.visibleUserIds'); - })) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), + ps.sinceId, ps.untilId) + .andWhere(new Brackets(qb => qb + .orWhere(':meId = ANY (note.mentions)') + .orWhere(':meId = ANY (note.visibleUserIds)'))) + .setParameters({ meId: me.id }) // Avoid scanning primary key index .orderBy('CONCAT(note.id)', 'DESC') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('reply.user', 'replyUser', 'replyUser.id = note.replyUserId') + .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId'); this.queryService.generateVisibilityQuery(query, me); this.queryService.generateBlockedHostQueryForNote(query); this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateMutedNoteThreadQuery(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); if (ps.visibility) { query.andWhere('note.visibility = :visibility', { visibility: ps.visibility }); } if (ps.following) { - query.andWhere(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: me.id }); - query.setParameters(followingQuery.getParameters()); + this.queryService.andFollowingUser(query, ':meId', 'note.userId'); } const mentions = await query.limit(ps.limit).getMany(); + process.nextTick(() => { + this.activeUsersChart.read(me); + }); + return await this.noteEntityService.packMany(mentions, me); }); } -- cgit v1.2.3-freya From 285a4748c3ba7c65c798d3c44041047191862620 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 15:47:55 -0400 Subject: copy changes to user-list-timeline.ts --- .../api/endpoints/notes/user-list-timeline.ts | 70 +++++----------------- packages/misskey-js/src/autogen/types.ts | 6 -- 2 files changed, 14 insertions(+), 62 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') 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 60f18a09b0..8872672b67 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 @@ -57,9 +57,6 @@ export const paramDef = { sinceDate: { type: 'integer' }, untilDate: { type: 'integer' }, allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default - includeMyRenotes: { type: 'boolean', default: true }, - includeRenotedMyNotes: { type: 'boolean', default: true }, - includeLocalRenotes: { type: 'boolean', default: true }, withRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', @@ -109,14 +106,13 @@ export default class extends Endpoint { // eslint- untilId, sinceId, limit: ps.limit, - includeMyRenotes: ps.includeMyRenotes, - includeRenotedMyNotes: ps.includeRenotedMyNotes, - includeLocalRenotes: ps.includeLocalRenotes, withFiles: ps.withFiles, withRenotes: ps.withRenotes, }, me); - this.activeUsersChart.read(me); + process.nextTick(() => { + this.activeUsersChart.read(me); + }); return await this.noteEntityService.packMany(timeline, me); } @@ -135,15 +131,14 @@ export default class extends Endpoint { // eslint- untilId, sinceId, limit, - includeMyRenotes: ps.includeMyRenotes, - includeRenotedMyNotes: ps.includeRenotedMyNotes, - includeLocalRenotes: ps.includeLocalRenotes, withFiles: ps.withFiles, withRenotes: ps.withRenotes, }, me), }); - this.activeUsersChart.read(me); + process.nextTick(() => { + this.activeUsersChart.read(me); + }); return timeline; }); @@ -153,9 +148,6 @@ export default class extends Endpoint { // eslint- untilId: string | null, sinceId: string | null, limit: number, - includeMyRenotes: boolean, - includeRenotedMyNotes: boolean, - includeLocalRenotes: boolean, withFiles: boolean, withRenotes: boolean, }, me: MiLocalUser) { @@ -165,8 +157,8 @@ export default class extends Endpoint { // eslint- .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('reply.user', 'replyUser', 'replyUser.id = note.replyUserId') + .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId') .andWhere('userListMemberships.userListId = :userListId', { userListId: list.id }) .andWhere('note.channelId IS NULL') // チャンネルノートではない .andWhere(new Brackets(qb => { @@ -193,51 +185,17 @@ export default class extends Endpoint { // eslint- this.queryService.generateBlockedHostQueryForNote(query); this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); - this.queryService.generateMutedUserRenotesQueryForNotes(query, me); - - if (ps.includeMyRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :meId', { meId: me.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.includeRenotedMyNotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserId != :meId', { meId: me.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - if (ps.includeLocalRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserHost IS NOT NULL'); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); } - if (ps.withRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere(new Brackets(qb => { - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - })); - })); + if (!ps.withRenotes) { + this.queryService.generateExcludedRenotesQueryForNotes(query); + } else if (me) { + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); } - if (ps.withFiles) { - query.andWhere('note.fileIds != \'{}\''); - } //#endregion return await query.limit(ps.limit).getMany(); diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index b85f1601de..d5712cdbed 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -28846,12 +28846,6 @@ export type operations = { /** @default false */ allowPartial?: boolean; /** @default true */ - includeMyRenotes?: boolean; - /** @default true */ - includeRenotedMyNotes?: boolean; - /** @default true */ - includeLocalRenotes?: boolean; - /** @default true */ withRenotes?: boolean; /** * @description Only show notes that have attached files. -- cgit v1.2.3-freya From 6b8766616c0c28277c38ee550511087d5ef938a4 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 15:50:19 -0400 Subject: copy changes to channels/timeline.ts --- .../src/server/api/endpoints/channels/timeline.ts | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 99ae1c2211..19f1921e0a 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -96,7 +96,11 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchChannel); } - if (me) this.activeUsersChart.read(me); + if (me) { + process.nextTick(() => { + this.activeUsersChart.read(me); + }); + } if (!this.serverSettings.enableFanoutTimeline) { return await this.noteEntityService.packMany(await this.getFromDb({ untilId, sinceId, limit: ps.limit, channelId: channel.id, withFiles: ps.withFiles, withRenotes: ps.withRenotes }, me), me); @@ -134,30 +138,25 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('note.channel', 'channel'); + .leftJoinAndSelect('reply.user', 'replyUser', 'replyUser.id = note.replyUserId') + .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId'); this.queryService.generateBlockedHostQueryForNote(query); this.queryService.generateVisibilityQuery(query, me); if (me) { this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); - this.queryService.generateMutedUserRenotesQueryForNotes(query, me); - } - - if (ps.withRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere(new Brackets(qb => { - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - })); - })); } if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } + + if (!ps.withRenotes) { + this.queryService.generateExcludedRenotesQueryForNotes(query); + } else if (me) { + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); + } //#endregion return await query.limit(ps.limit).getMany(); -- cgit v1.2.3-freya From b6b44758d1b8ceb61714dd181083250d79ebe5dc Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 15:52:31 -0400 Subject: copy changes to roles/notes.ts --- packages/backend/src/server/api/endpoints/roles/notes.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts index 536384a381..4752561ad5 100644 --- a/packages/backend/src/server/api/endpoints/roles/notes.ts +++ b/packages/backend/src/server/api/endpoints/roles/notes.ts @@ -12,6 +12,7 @@ import { DI } from '@/di-symbols.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { IdService } from '@/core/IdService.js'; import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; +import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -74,6 +75,7 @@ export default class extends Endpoint { // eslint- private noteEntityService: NoteEntityService, private queryService: QueryService, private fanoutTimelineService: FanoutTimelineService, + private readonly activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); @@ -101,11 +103,12 @@ export default class extends Endpoint { // eslint- const query = this.notesRepository.createQueryBuilder('note') .where('note.id IN (:...noteIds)', { noteIds: noteIds }) .andWhere('(note.visibility = \'public\')') + .orderBy('note.id', 'DESC') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('reply.user', 'replyUser', 'replyUser.id = note.replyUserId') + .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId'); this.queryService.generateBlockedHostQueryForNote(query); this.queryService.generateMutedUserQueryForNotes(query, me); @@ -113,7 +116,10 @@ export default class extends Endpoint { // eslint- this.queryService.generateMutedUserRenotesQueryForNotes(query, me); const notes = await query.getMany(); - notes.sort((a, b) => a.id > b.id ? -1 : 1); + + process.nextTick(() => { + this.activeUsersChart.read(me); + }); return await this.noteEntityService.packMany(notes, me); }); -- cgit v1.2.3-freya From 8f466e8ac5728fe11da4d5cd0290db6be3d008c7 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 15:56:20 -0400 Subject: copy changes to notes/following.ts --- .../backend/src/server/api/endpoints/notes/following.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/following.ts b/packages/backend/src/server/api/endpoints/notes/following.ts index 088b172ba4..ac26dbbbc8 100644 --- a/packages/backend/src/server/api/endpoints/notes/following.ts +++ b/packages/backend/src/server/api/endpoints/notes/following.ts @@ -12,6 +12,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; import { QueryService } from '@/core/QueryService.js'; import { ApiError } from '@/server/api/error.js'; +import ActiveUsersChart from '@/core/chart/charts/active-users.js'; export const meta = { tags: ['notes'], @@ -76,8 +77,9 @@ export default class extends Endpoint { // eslint- @Inject(DI.notesRepository) private notesRepository: NotesRepository, - private noteEntityService: NoteEntityService, - private queryService: QueryService, + private readonly noteEntityService: NoteEntityService, + private readonly queryService: QueryService, + private readonly activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { if (ps.includeReplies && ps.filesOnly) throw new ApiError(meta.errors.bothWithRepliesAndWithFiles); @@ -128,8 +130,8 @@ export default class extends Endpoint { // eslint- .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('reply.user', 'replyUser', 'replyUser.id = note.replyUserId') + .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId') // Exclude channel notes .andWhere({ channelId: IsNull() }) @@ -157,11 +159,15 @@ export default class extends Endpoint { // eslint- // Support pagination this.queryService .makePaginationQuery(query, ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .orderBy('note.id', 'DESC') .take(ps.limit); // Query and return the next page const notes = await query.getMany(); + + process.nextTick(() => { + this.activeUsersChart.read(me); + }); + return await this.noteEntityService.packMany(notes, me, { skipHide: true }); }); } -- cgit v1.2.3-freya From 97f8e054ce121a2c2f36202ae625d8177db2a975 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 3 Jun 2025 20:20:57 -0400 Subject: restore join to note.channel in channel/timeline.ts --- packages/backend/src/server/api/endpoints/channels/timeline.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 19f1921e0a..b7152130d5 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -137,9 +137,9 @@ export default class extends Endpoint { // eslint- .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') .leftJoinAndSelect('reply.user', 'replyUser', 'replyUser.id = note.replyUserId') - .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId'); + .leftJoinAndSelect('renote.user', 'renoteUser', 'renoteUser.id = note.renoteUserId') + .leftJoinAndSelect('note.channel', 'channel'); this.queryService.generateBlockedHostQueryForNote(query); this.queryService.generateVisibilityQuery(query, me); -- cgit v1.2.3-freya