From fa67fb42b1baf616d860c54f6cdf9123fc7ed57a Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 8 Apr 2023 13:12:49 +0900
Subject: [PATCH] =?UTF-8?q?enhance(backend):=20Redis=E3=81=AB=E3=83=81?=
 =?UTF-8?q?=E3=83=A3=E3=83=B3=E3=83=8D=E3=83=AB=E6=8A=95=E7=A8=BF=E3=81=8C?=
 =?UTF-8?q?=E3=81=AA=E3=81=84=E5=A0=B4=E5=90=88=E3=81=AFDB=E3=81=8B?=
 =?UTF-8?q?=E3=82=89=E6=8C=81=E3=81=A3=E3=81=A6=E3=81=8F=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fix #10473
---
 .../server/api/endpoints/channels/timeline.ts | 75 ++++++++++++-------
 1 file changed, 47 insertions(+), 28 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts
index dfc2a582dc..2556557b24 100644
--- a/packages/backend/src/server/api/endpoints/channels/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts
@@ -1,7 +1,7 @@
 import { Inject, Injectable } from '@nestjs/common';
 import Redis from 'ioredis';
 import { Endpoint } from '@/server/api/endpoint-base.js';
-import type { ChannelsRepository, NotesRepository } from '@/models/index.js';
+import type { ChannelsRepository, Note, NotesRepository } from '@/models/index.js';
 import { QueryService } from '@/core/QueryService.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import ActiveUsersChart from '@/core/chart/charts/active-users.js';
@@ -73,6 +73,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				throw new ApiError(meta.errors.noSuchChannel);
 			}
 
+			let timeline: Note[] = [];
+
 			const noteIdsRes = await this.redisClient.xrevrange(
 				`channelTimeline:${channel.id}`,
 				ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : '+',
@@ -80,35 +82,52 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				'COUNT', ps.limit + 1); // untilIdに指定したものも含まれるため+1
 
 			if (noteIdsRes.length === 0) {
-				return [];
+				//#region Construct query
+				const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
+					.andWhere('note.channelId = :channelId', { channelId: channel.id })
+					.innerJoinAndSelect('note.user', 'user')
+					.leftJoinAndSelect('note.reply', 'reply')
+					.leftJoinAndSelect('note.renote', 'renote')
+					.leftJoinAndSelect('reply.user', 'replyUser')
+					.leftJoinAndSelect('renote.user', 'renoteUser')
+					.leftJoinAndSelect('note.channel', 'channel');
+
+				if (me) {
+					this.queryService.generateMutedUserQuery(query, me);
+					this.queryService.generateMutedNoteQuery(query, me);
+					this.queryService.generateBlockedUserQuery(query, me);
+				}
+				//#endregion
+
+				timeline = await query.take(ps.limit).getMany();
+			} else {
+				const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId);
+
+				if (noteIds.length === 0) {
+					return [];
+				}
+
+				//#region Construct query
+				const query = this.notesRepository.createQueryBuilder('note')
+					.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('note.channel', 'channel');
+
+				if (me) {
+					this.queryService.generateMutedUserQuery(query, me);
+					this.queryService.generateMutedNoteQuery(query, me);
+					this.queryService.generateBlockedUserQuery(query, me);
+				}
+				//#endregion
+
+				timeline = await query.getMany();
+				timeline.sort((a, b) => a.id > b.id ? -1 : 1);
 			}
 
-			const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId);
-
-			if (noteIds.length === 0) {
-				return [];
-			}
-
-			//#region Construct query
-			const query = this.notesRepository.createQueryBuilder('note')
-				.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('note.channel', 'channel');
-
-			if (me) {
-				this.queryService.generateMutedUserQuery(query, me);
-				this.queryService.generateMutedNoteQuery(query, me);
-				this.queryService.generateBlockedUserQuery(query, me);
-			}
-			//#endregion
-
-			const timeline = await query.getMany();
-			timeline.sort((a, b) => a.id > b.id ? -1 : 1);
-
 			if (me) this.activeUsersChart.read(me);
 
 			return await this.noteEntityService.packMany(timeline, me);