diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 355e5e8c0d..b2bfb72b89 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -624,7 +624,7 @@ export class DriveService { this.registerLogger.succ(`drive file has been created ${file.id}`); if (user) { - this.driveFileEntityService.pack(file, { self: true }).then(packedFile => { + this.driveFileEntityService.pack(file, user, { self: true }).then(packedFile => { // Publish driveFileCreated event this.globalEventService.publishMainStream(user.id, 'driveFileCreated', packedFile); this.globalEventService.publishDriveStream(user.id, 'fileCreated', packedFile); diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 648ff76483..bf831f86fa 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -54,7 +54,7 @@ const mutedWordsCache = new MemorySingleCache<{ userId: UserProfile['userId']; m type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; class NotificationManager { - private notifier: { id: User['id']; }; + private notifier: { id: User['id'] }; private note: Note; private queue: { target: LocalUser['id']; @@ -64,7 +64,7 @@ class NotificationManager { constructor( private mutingsRepository: MutingsRepository, private notificationService: NotificationService, - notifier: { id: User['id']; }, + notifier: { id: User['id'] }, note: Note, ) { this.notifier = notifier; @@ -547,7 +547,7 @@ export class NoteCreateService implements OnApplicationShutdown { } // Pack the note - const noteObj = await this.noteEntityService.pack(note); + const noteObj = await this.noteEntityService.pack(note, user); this.globalEventService.publishNotesStream(noteObj); @@ -767,7 +767,7 @@ export class NoteCreateService implements OnApplicationShutdown { } @bindThis - private incNotesCountOfUser(user: { id: User['id']; }) { + private incNotesCountOfUser(user: { id: User['id'] }) { this.usersRepository.createQueryBuilder().update() .set({ updatedAt: new Date(), diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 435d5d2389..f4aa48d5d5 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -104,7 +104,7 @@ export class QueryService { } @bindThis - public generateChannelQuery(q: SelectQueryBuilder, me?: { id: User['id'] } | null): void { + public generateChannelQuery(q: SelectQueryBuilder, me: { id: User['id'] } | null): void { if (me == null) { q.andWhere('note.channelId IS NULL'); } else { @@ -208,7 +208,7 @@ export class QueryService { } @bindThis - public generateRepliesQuery(q: SelectQueryBuilder, withReplies: boolean, me?: Pick | null): void { + public generateRepliesQuery(q: SelectQueryBuilder, withReplies: boolean, me: Pick | null): void { if (me == null) { q.andWhere(new Brackets(qb => { qb .where('note.replyId IS NULL') // 返信ではない @@ -234,7 +234,7 @@ export class QueryService { } @bindThis - public generateVisibilityQuery(q: SelectQueryBuilder, me?: { id: User['id'] } | null): void { + public generateVisibilityQuery(q: SelectQueryBuilder, me: { id: User['id'] } | null): void { // This code must always be synchronized with the checks in Notes.isVisibleForMe. if (me == null) { q.andWhere(new Brackets(qb => { qb diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index 08cc907ebf..5199c3c8cb 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -50,7 +50,7 @@ export class UserListService { userListId: list.id, } as UserListJoining); - this.globalEventService.publishUserListStream(list.id, 'userAdded', await this.userEntityService.pack(target)); + this.globalEventService.publishUserListStream(list.id, 'userAdded', await this.userEntityService.pack(target, me)); // このインスタンス内にこのリモートユーザーをフォローしているユーザーがいなくても投稿を受け取るためにダミーのユーザーがフォローしたということにする if (this.userEntityService.isRemoteUser(target)) { diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts index 94ae1856b9..4441774906 100644 --- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts @@ -4,6 +4,8 @@ import type { AbuseUserReportsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; import { bindThis } from '@/decorators.js'; +import { Packed } from '@/misc/json-schema.js'; +import type { User } from '@/models/entities/User.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -19,7 +21,8 @@ export class AbuseUserReportEntityService { @bindThis public async pack( src: AbuseUserReport['id'] | AbuseUserReport, - ) { + me: { id: User['id'] } | null | undefined, + ) : Promise> { const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src }); return await awaitAll({ @@ -30,13 +33,13 @@ export class AbuseUserReportEntityService { reporterId: report.reporterId, targetUserId: report.targetUserId, assigneeId: report.assigneeId, - reporter: this.userEntityService.pack(report.reporter ?? report.reporterId, null, { + reporter: this.userEntityService.pack(report.reporter ?? report.reporterId, me, { detail: true, }), - targetUser: this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, { + targetUser: this.userEntityService.pack(report.targetUser ?? report.targetUserId, me, { detail: true, }), - assignee: report.assigneeId ? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, { + assignee: report.assigneeId ? this.userEntityService.pack(report.assignee ?? report.assigneeId, me, { detail: true, }) : null, forwarded: report.forwarded, @@ -44,9 +47,12 @@ export class AbuseUserReportEntityService { } @bindThis - public packMany( - reports: any[], - ) { - return Promise.all(reports.map(x => this.pack(x))); + public async packMany( + reports: (AbuseUserReport['id'] | AbuseUserReport)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(reports.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } diff --git a/packages/backend/src/core/entities/AppEntityService.ts b/packages/backend/src/core/entities/AppEntityService.ts index 0b4c3935c7..99d4a85a35 100644 --- a/packages/backend/src/core/entities/AppEntityService.ts +++ b/packages/backend/src/core/entities/AppEntityService.ts @@ -20,7 +20,7 @@ export class AppEntityService { @bindThis public async pack( src: App['id'] | App, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, options?: { detail?: boolean, includeSecret?: boolean, diff --git a/packages/backend/src/core/entities/AuthSessionEntityService.ts b/packages/backend/src/core/entities/AuthSessionEntityService.ts index a1874c63ab..a096ed5eca 100644 --- a/packages/backend/src/core/entities/AuthSessionEntityService.ts +++ b/packages/backend/src/core/entities/AuthSessionEntityService.ts @@ -20,7 +20,7 @@ export class AuthSessionEntityService { @bindThis public async pack( src: AuthSession['id'] | AuthSession, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, ) { const session = typeof src === 'object' ? src : await this.authSessionsRepository.findOneByOrFail({ id: src }); diff --git a/packages/backend/src/core/entities/BlockingEntityService.ts b/packages/backend/src/core/entities/BlockingEntityService.ts index e169c7e90a..5529f98c62 100644 --- a/packages/backend/src/core/entities/BlockingEntityService.ts +++ b/packages/backend/src/core/entities/BlockingEntityService.ts @@ -21,7 +21,7 @@ export class BlockingEntityService { @bindThis public async pack( src: Blocking['id'] | Blocking, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, ): Promise> { const blocking = typeof src === 'object' ? src : await this.blockingsRepository.findOneByOrFail({ id: src }); @@ -36,10 +36,12 @@ export class BlockingEntityService { } @bindThis - public packMany( - blockings: any[], - me: { id: User['id'] }, - ) { - return Promise.all(blockings.map(x => this.pack(x, me))); + public async packMany( + blockings: (Blocking['id'] | Blocking)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(blockings.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } diff --git a/packages/backend/src/core/entities/ChannelEntityService.ts b/packages/backend/src/core/entities/ChannelEntityService.ts index de99ce72c4..f62e264c58 100644 --- a/packages/backend/src/core/entities/ChannelEntityService.ts +++ b/packages/backend/src/core/entities/ChannelEntityService.ts @@ -1,14 +1,20 @@ import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { ChannelFavoritesRepository, ChannelFollowingsRepository, ChannelsRepository, DriveFilesRepository, NoteUnreadsRepository, NotesRepository } from '@/models/index.js'; +import type { + ChannelFavoritesRepository, + ChannelFollowingsRepository, + ChannelsRepository, + DriveFilesRepository, + NotesRepository, + NoteUnreadsRepository, +} from '@/models/index.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { Channel } from '@/models/entities/Channel.js'; import { bindThis } from '@/decorators.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; import { NoteEntityService } from './NoteEntityService.js'; -import { In } from 'typeorm'; @Injectable() export class ChannelEntityService { @@ -39,7 +45,7 @@ export class ChannelEntityService { @bindThis public async pack( src: Channel['id'] | Channel, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, detailed?: boolean, ): Promise> { const channel = typeof src === 'object' ? src : await this.channelsRepository.findOneByOrFail({ id: src }); diff --git a/packages/backend/src/core/entities/ClipEntityService.ts b/packages/backend/src/core/entities/ClipEntityService.ts index f558cbc33d..336d8793d0 100644 --- a/packages/backend/src/core/entities/ClipEntityService.ts +++ b/packages/backend/src/core/entities/ClipEntityService.ts @@ -3,7 +3,6 @@ import { DI } from '@/di-symbols.js'; import type { ClipFavoritesRepository, ClipsRepository, User } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; import type { Clip } from '@/models/entities/Clip.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; @@ -24,7 +23,7 @@ export class ClipEntityService { @bindThis public async pack( src: Clip['id'] | Clip, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, ): Promise> { const meId = me ? me.id : null; const clip = typeof src === 'object' ? src : await this.clipsRepository.findOneByOrFail({ id: src }); @@ -34,7 +33,7 @@ export class ClipEntityService { createdAt: clip.createdAt.toISOString(), lastClippedAt: clip.lastClippedAt ? clip.lastClippedAt.toISOString() : null, userId: clip.userId, - user: this.userEntityService.pack(clip.user ?? clip.userId), + user: this.userEntityService.pack(clip.user ?? clip.userId, me), name: clip.name, description: clip.description, isPublic: clip.isPublic, @@ -44,11 +43,13 @@ export class ClipEntityService { } @bindThis - public packMany( - clips: Clip[], - me?: { id: User['id'] } | null | undefined, - ) { - return Promise.all(clips.map(x => this.pack(x, me))); + public async packMany( + clips: (Clip['id'] | Clip)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(clips.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index 80442af09b..4c472ee5cf 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -1,7 +1,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { NotesRepository, DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository, NotesRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import type { Packed } from '@/misc/json-schema.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; @@ -9,6 +9,9 @@ import type { User } from '@/models/entities/User.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { appendQuery, query } from '@/misc/prelude/url.js'; import { deepClone } from '@/misc/clone.js'; +import { bindThis } from '@/decorators.js'; +import { isMimeImage } from '@/misc/is-mime-image.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { UtilityService } from '../UtilityService.js'; import { VideoProcessingService } from '../VideoProcessingService.js'; import { UserEntityService } from './UserEntityService.js'; @@ -19,9 +22,6 @@ type PackOptions = { self?: boolean, withUser?: boolean, }; -import { bindThis } from '@/decorators.js'; -import { isMimeImage } from '@/misc/is-mime-image.js'; -import { isNotNull } from '@/misc/is-not-null.js'; @Injectable() export class DriveFileEntityService { @@ -186,6 +186,7 @@ export class DriveFileEntityService { @bindThis public async pack( src: DriveFile['id'] | DriveFile, + me: { id: User['id'] } | null | undefined, options?: PackOptions, ): Promise> { const opts = Object.assign({ @@ -213,13 +214,14 @@ export class DriveFileEntityService { detail: true, }) : null, userId: opts.withUser ? file.userId : null, - user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId) : null, + user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId, me) : null, }); } @bindThis public async packNullable( src: DriveFile['id'] | DriveFile, + me: { id: User['id'] } | null | undefined, options?: PackOptions, ): Promise | null> { const opts = Object.assign({ @@ -248,27 +250,30 @@ export class DriveFileEntityService { detail: true, }) : null, userId: opts.withUser ? file.userId : null, - user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId) : null, + user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId, me) : null, }); } @bindThis public async packMany( files: DriveFile[], + me: { id: User['id'] } | null | undefined, options?: PackOptions, ): Promise[]> { - const items = await Promise.all(files.map(f => this.packNullable(f, options))); - return items.filter((x): x is Packed<'DriveFile'> => x != null); + return (await Promise.allSettled(files.map(f => this.packNullable(f, me, options)))) + .filter(result => result.status === 'fulfilled' && result.value != null) + .map(result => (result as PromiseFulfilledResult>).value); } @bindThis public async packManyByIdsMap( fileIds: DriveFile['id'][], + me: { id: User['id'] } | null | undefined, options?: PackOptions, ): Promise['id'], Packed<'DriveFile'> | null>> { if (fileIds.length === 0) return new Map(); const files = await this.driveFilesRepository.findBy({ id: In(fileIds) }); - const packedFiles = await this.packMany(files, options); + const packedFiles = await this.packMany(files, me, options); const map = new Map['id'], Packed<'DriveFile'> | null>(packedFiles.map(f => [f.id, f])); for (const id of fileIds) { if (!map.has(id)) map.set(id, null); @@ -279,10 +284,11 @@ export class DriveFileEntityService { @bindThis public async packManyByIds( fileIds: DriveFile['id'][], + me: { id: User['id'] } | null | undefined, options?: PackOptions, ): Promise[]> { if (fileIds.length === 0) return []; - const filesMap = await this.packManyByIdsMap(fileIds, options); + const filesMap = await this.packManyByIdsMap(fileIds, me, options); return fileIds.map(id => filesMap.get(id)).filter(isNotNull); } } diff --git a/packages/backend/src/core/entities/DriveFolderEntityService.ts b/packages/backend/src/core/entities/DriveFolderEntityService.ts index 13929b145f..bb675cea38 100644 --- a/packages/backend/src/core/entities/DriveFolderEntityService.ts +++ b/packages/backend/src/core/entities/DriveFolderEntityService.ts @@ -3,7 +3,6 @@ import { DI } from '@/di-symbols.js'; import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; import type { DriveFolder } from '@/models/entities/DriveFolder.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index 74740c70de..e98d302dd7 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -2,7 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { EmojisRepository } from '@/models/index.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; import type { Emoji } from '@/models/entities/Emoji.js'; import { bindThis } from '@/decorators.js'; @@ -33,10 +32,12 @@ export class EmojiEntityService { } @bindThis - public packSimpleMany( - emojis: any[], - ) { - return Promise.all(emojis.map(x => this.packSimple(x))); + public async packSimpleMany( + emojis: (Emoji['id'] | Emoji)[], + ) : Promise[]> { + return (await Promise.allSettled(emojis.map(x => this.packSimple(x)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } @bindThis @@ -62,10 +63,11 @@ export class EmojiEntityService { } @bindThis - public packDetailedMany( - emojis: any[], - ) { - return Promise.all(emojis.map(x => this.packDetailed(x))); + public async packDetailedMany( + emojis: (Emoji['id'] | Emoji)[], + ) : Promise[]> { + return (await Promise.allSettled(emojis.map(x => this.packDetailed(x)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } - diff --git a/packages/backend/src/core/entities/FlashEntityService.ts b/packages/backend/src/core/entities/FlashEntityService.ts index 92345457c9..2bd0b95aaa 100644 --- a/packages/backend/src/core/entities/FlashEntityService.ts +++ b/packages/backend/src/core/entities/FlashEntityService.ts @@ -1,9 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { FlashsRepository, FlashLikesRepository } from '@/models/index.js'; +import type { FlashLikesRepository, FlashsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { Flash } from '@/models/entities/Flash.js'; import { bindThis } from '@/decorators.js'; @@ -25,7 +24,7 @@ export class FlashEntityService { @bindThis public async pack( src: Flash['id'] | Flash, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, ): Promise> { const meId = me ? me.id : null; const flash = typeof src === 'object' ? src : await this.flashsRepository.findOneByOrFail({ id: src }); @@ -45,11 +44,12 @@ export class FlashEntityService { } @bindThis - public packMany( - flashs: Flash[], - me?: { id: User['id'] } | null | undefined, - ) { - return Promise.all(flashs.map(x => this.pack(x, me))); + public async packMany( + flashs: (Flash['id'] | Flash)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(flashs.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } - diff --git a/packages/backend/src/core/entities/FlashLikeEntityService.ts b/packages/backend/src/core/entities/FlashLikeEntityService.ts index 0351ec3014..8bc3ca0925 100644 --- a/packages/backend/src/core/entities/FlashLikeEntityService.ts +++ b/packages/backend/src/core/entities/FlashLikeEntityService.ts @@ -1,10 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { FlashLikesRepository } from '@/models/index.js'; -import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { FlashLike } from '@/models/entities/FlashLike.js'; import { bindThis } from '@/decorators.js'; +import { Packed } from '@/misc/json-schema.js'; import { FlashEntityService } from './FlashEntityService.js'; @Injectable() @@ -20,8 +20,8 @@ export class FlashLikeEntityService { @bindThis public async pack( src: FlashLike['id'] | FlashLike, - me?: { id: User['id'] } | null | undefined, - ) { + me: { id: User['id'] } | null | undefined, + ) : Promise> { const like = typeof src === 'object' ? src : await this.flashLikesRepository.findOneByOrFail({ id: src }); return { @@ -31,11 +31,12 @@ export class FlashLikeEntityService { } @bindThis - public packMany( - likes: any[], - me: { id: User['id'] }, - ) { - return Promise.all(likes.map(x => this.pack(x, me))); + public async packMany( + likes: (FlashLike['id'] | FlashLike)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(likes.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } - diff --git a/packages/backend/src/core/entities/FollowRequestEntityService.ts b/packages/backend/src/core/entities/FollowRequestEntityService.ts index 6f6f4be412..269b6fcb62 100644 --- a/packages/backend/src/core/entities/FollowRequestEntityService.ts +++ b/packages/backend/src/core/entities/FollowRequestEntityService.ts @@ -1,10 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { FollowRequestsRepository } from '@/models/index.js'; -import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { FollowRequest } from '@/models/entities/FollowRequest.js'; import { bindThis } from '@/decorators.js'; +import { Packed } from '@/misc/json-schema.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -20,8 +20,8 @@ export class FollowRequestEntityService { @bindThis public async pack( src: FollowRequest['id'] | FollowRequest, - me?: { id: User['id'] } | null | undefined, - ) { + me: { id: User['id'] } | null | undefined, + ) : Promise> { const request = typeof src === 'object' ? src : await this.followRequestsRepository.findOneByOrFail({ id: src }); return { @@ -30,5 +30,14 @@ export class FollowRequestEntityService { followee: await this.userEntityService.pack(request.followeeId, me), }; } -} + @bindThis + public async packMany( + requests: (FollowRequest['id'] | FollowRequest)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(requests.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); + } +} diff --git a/packages/backend/src/core/entities/FollowingEntityService.ts b/packages/backend/src/core/entities/FollowingEntityService.ts index 55ba4e67ad..3377023358 100644 --- a/packages/backend/src/core/entities/FollowingEntityService.ts +++ b/packages/backend/src/core/entities/FollowingEntityService.ts @@ -3,7 +3,6 @@ import { DI } from '@/di-symbols.js'; import type { FollowingsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { Following } from '@/models/entities/Following.js'; import { bindThis } from '@/decorators.js'; @@ -66,7 +65,7 @@ export class FollowingEntityService { @bindThis public async pack( src: Following['id'] | Following, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, opts?: { populateFollowee?: boolean; populateFollower?: boolean; @@ -91,15 +90,16 @@ export class FollowingEntityService { } @bindThis - public packMany( - followings: any[], - me?: { id: User['id'] } | null | undefined, + public async packMany( + followings: (Following['id'] | Following)[], + me: { id: User['id'] } | null | undefined, opts?: { populateFollowee?: boolean; populateFollower?: boolean; }, - ) { - return Promise.all(followings.map(x => this.pack(x, me, opts))); + ) : Promise[]> { + return (await Promise.allSettled(followings.map(x => this.pack(x, me, opts)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } - diff --git a/packages/backend/src/core/entities/GalleryLikeEntityService.ts b/packages/backend/src/core/entities/GalleryLikeEntityService.ts index 73c264da94..57243539dd 100644 --- a/packages/backend/src/core/entities/GalleryLikeEntityService.ts +++ b/packages/backend/src/core/entities/GalleryLikeEntityService.ts @@ -1,9 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { GalleryLikesRepository } from '@/models/index.js'; -import type { } from '@/models/entities/Blocking.js'; import type { GalleryLike } from '@/models/entities/GalleryLike.js'; import { bindThis } from '@/decorators.js'; +import { Packed } from '@/misc/json-schema.js'; +import type { User } from '@/models/entities/User.js'; import { GalleryPostEntityService } from './GalleryPostEntityService.js'; @Injectable() @@ -19,8 +20,8 @@ export class GalleryLikeEntityService { @bindThis public async pack( src: GalleryLike['id'] | GalleryLike, - me?: any, - ) { + me: { id: User['id'] } | null | undefined, + ) : Promise> { const like = typeof src === 'object' ? src : await this.galleryLikesRepository.findOneByOrFail({ id: src }); return { @@ -30,11 +31,13 @@ export class GalleryLikeEntityService { } @bindThis - public packMany( - likes: any[], - me: any, - ) { - return Promise.all(likes.map(x => this.pack(x, me))); + public async packMany( + likes: (GalleryLike['id'] | GalleryLike)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(likes.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } diff --git a/packages/backend/src/core/entities/GalleryPostEntityService.ts b/packages/backend/src/core/entities/GalleryPostEntityService.ts index c44a5df118..1e0e6e8ec1 100644 --- a/packages/backend/src/core/entities/GalleryPostEntityService.ts +++ b/packages/backend/src/core/entities/GalleryPostEntityService.ts @@ -3,7 +3,6 @@ import { DI } from '@/di-symbols.js'; import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { GalleryPost } from '@/models/entities/GalleryPost.js'; import { bindThis } from '@/decorators.js'; @@ -27,7 +26,7 @@ export class GalleryPostEntityService { @bindThis public async pack( src: GalleryPost['id'] | GalleryPost, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, ): Promise> { const meId = me ? me.id : null; const post = typeof src === 'object' ? src : await this.galleryPostsRepository.findOneByOrFail({ id: src }); @@ -42,7 +41,7 @@ export class GalleryPostEntityService { description: post.description, fileIds: post.fileIds, // TODO: packMany causes N+1 queries - files: this.driveFileEntityService.packManyByIds(post.fileIds), + files: this.driveFileEntityService.packManyByIds(post.fileIds, me), tags: post.tags.length > 0 ? post.tags : undefined, isSensitive: post.isSensitive, likedCount: post.likedCount, @@ -51,11 +50,12 @@ export class GalleryPostEntityService { } @bindThis - public packMany( - posts: GalleryPost[], - me?: { id: User['id'] } | null | undefined, - ) { - return Promise.all(posts.map(x => this.pack(x, me))); + public async packMany( + posts: (GalleryPost['id'] | GalleryPost)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(posts.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } - diff --git a/packages/backend/src/core/entities/HashtagEntityService.ts b/packages/backend/src/core/entities/HashtagEntityService.ts index 2cd79b8f8c..f896d8882b 100644 --- a/packages/backend/src/core/entities/HashtagEntityService.ts +++ b/packages/backend/src/core/entities/HashtagEntityService.ts @@ -2,7 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { HashtagsRepository } from '@/models/index.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; import type { Hashtag } from '@/models/entities/Hashtag.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; @@ -33,10 +32,11 @@ export class HashtagEntityService { } @bindThis - public packMany( + public async packMany( hashtags: Hashtag[], - ) { - return Promise.all(hashtags.map(x => this.pack(x))); + ) : Promise[]> { + return (await Promise.allSettled(hashtags.map(x => this.pack(x)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } - diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index 3bf84ed375..4998bc00b6 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -2,7 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { InstancesRepository } from '@/models/index.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; import type { Instance } from '@/models/entities/Instance.js'; import { MetaService } from '@/core/MetaService.js'; import { bindThis } from '@/decorators.js'; @@ -51,10 +50,11 @@ export class InstanceEntityService { } @bindThis - public packMany( + public async packMany( instances: Instance[], - ) { - return Promise.all(instances.map(x => this.pack(x))); + ) : Promise[]> { + return (await Promise.allSettled(instances.map(x => this.pack(x)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } - diff --git a/packages/backend/src/core/entities/InviteCodeEntityService.ts b/packages/backend/src/core/entities/InviteCodeEntityService.ts index 2d8e7a4681..09244b27e6 100644 --- a/packages/backend/src/core/entities/InviteCodeEntityService.ts +++ b/packages/backend/src/core/entities/InviteCodeEntityService.ts @@ -21,7 +21,7 @@ export class InviteCodeEntityService { @bindThis public async pack( src: RegistrationTicket['id'] | RegistrationTicket, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, ): Promise> { const target = typeof src === 'object' ? src : await this.registrationTicketsRepository.findOneOrFail({ where: { @@ -43,10 +43,12 @@ export class InviteCodeEntityService { } @bindThis - public packMany( - targets: any[], - me: { id: User['id'] }, - ) { - return Promise.all(targets.map(x => this.pack(x, me))); + public async packMany( + targets: (RegistrationTicket['id'] | RegistrationTicket)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(targets.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } diff --git a/packages/backend/src/core/entities/ModerationLogEntityService.ts b/packages/backend/src/core/entities/ModerationLogEntityService.ts index 59815d2639..b82cea82b0 100644 --- a/packages/backend/src/core/entities/ModerationLogEntityService.ts +++ b/packages/backend/src/core/entities/ModerationLogEntityService.ts @@ -2,9 +2,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { ModerationLogsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; import type { ModerationLog } from '@/models/entities/ModerationLog.js'; import { bindThis } from '@/decorators.js'; +import { Packed } from '@/misc/json-schema.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -20,7 +21,8 @@ export class ModerationLogEntityService { @bindThis public async pack( src: ModerationLog['id'] | ModerationLog, - ) { + me: { id: User['id'] } | null | undefined, + ) : Promise> { const log = typeof src === 'object' ? src : await this.moderationLogsRepository.findOneByOrFail({ id: src }); return await awaitAll({ @@ -29,17 +31,20 @@ export class ModerationLogEntityService { type: log.type, info: log.info, userId: log.userId, - user: this.userEntityService.pack(log.user ?? log.userId, null, { + user: this.userEntityService.pack(log.user ?? log.userId, me, { detail: true, }), }); } @bindThis - public packMany( - reports: any[], - ) { - return Promise.all(reports.map(x => this.pack(x))); + public async packMany( + reports: (ModerationLog['id'] | ModerationLog)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(reports.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } diff --git a/packages/backend/src/core/entities/MutingEntityService.ts b/packages/backend/src/core/entities/MutingEntityService.ts index 561d53292e..e942fabcfa 100644 --- a/packages/backend/src/core/entities/MutingEntityService.ts +++ b/packages/backend/src/core/entities/MutingEntityService.ts @@ -3,7 +3,6 @@ import { DI } from '@/di-symbols.js'; import type { MutingsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { Muting } from '@/models/entities/Muting.js'; import { bindThis } from '@/decorators.js'; @@ -22,7 +21,7 @@ export class MutingEntityService { @bindThis public async pack( src: Muting['id'] | Muting, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, ): Promise> { const muting = typeof src === 'object' ? src : await this.mutingsRepository.findOneByOrFail({ id: src }); @@ -38,11 +37,12 @@ export class MutingEntityService { } @bindThis - public packMany( - mutings: any[], - me: { id: User['id'] }, - ) { - return Promise.all(mutings.map(x => this.pack(x, me))); + public async packMany( + mutings: (Muting['id'] | Muting)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(mutings.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } - diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 546e5f56d2..afa0dc27b3 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -9,7 +9,16 @@ import { awaitAll } from '@/misc/prelude/await-all.js'; import type { User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; -import type { UsersRepository, NotesRepository, FollowingsRepository, PollsRepository, PollVotesRepository, NoteReactionsRepository, ChannelsRepository, DriveFilesRepository } from '@/models/index.js'; +import type { + ChannelsRepository, + DriveFilesRepository, + FollowingsRepository, + NoteReactionsRepository, + NotesRepository, + PollsRepository, + PollVotesRepository, + UsersRepository, +} from '@/models/index.js'; import { bindThis } from '@/decorators.js'; import { isNotNull } from '@/misc/is-not-null.js'; import type { OnModuleInit } from '@nestjs/common'; @@ -253,13 +262,17 @@ export class NoteEntityService implements OnModuleInit { } @bindThis - public async packAttachedFiles(fileIds: Note['fileIds'], packedFiles: Map | null>): Promise[]> { + public async packAttachedFiles( + fileIds: Note['fileIds'], + packedFiles: Map | null>, + me: { id: User['id'] } | null | undefined, + ): Promise[]> { const missingIds = []; for (const id of fileIds) { if (!packedFiles.has(id)) missingIds.push(id); } if (missingIds.length) { - const additionalMap = await this.driveFileEntityService.packManyByIdsMap(missingIds); + const additionalMap = await this.driveFileEntityService.packManyByIdsMap(missingIds, me); for (const [k, v] of additionalMap) { packedFiles.set(k, v); } @@ -270,7 +283,7 @@ export class NoteEntityService implements OnModuleInit { @bindThis public async pack( src: Note['id'] | Note, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, options?: { detail?: boolean; skipHide?: boolean; @@ -326,7 +339,7 @@ export class NoteEntityService implements OnModuleInit { emojis: host != null ? this.customEmojiService.populateEmojis(note.emojis, host) : undefined, tags: note.tags.length > 0 ? note.tags : undefined, fileIds: note.fileIds, - files: packedFiles != null ? this.packAttachedFiles(note.fileIds, packedFiles) : this.driveFileEntityService.packManyByIds(note.fileIds), + files: packedFiles != null ? this.packAttachedFiles(note.fileIds, packedFiles, me) : this.driveFileEntityService.packManyByIds(note.fileIds, me), replyId: note.replyId, renoteId: note.renoteId, channelId: note.channelId ?? undefined, @@ -387,12 +400,12 @@ export class NoteEntityService implements OnModuleInit { @bindThis public async packMany( notes: Note[], - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, options?: { detail?: boolean; skipHide?: boolean; }, - ) { + ) : Promise[]> { if (notes.length === 0) return []; const meId = me ? me.id : null; @@ -414,15 +427,17 @@ export class NoteEntityService implements OnModuleInit { await this.customEmojiService.prefetchEmojis(this.aggregateNoteEmojis(notes)); // TODO: 本当は renote とか reply がないのに renoteId とか replyId があったらここで解決しておく const fileIds = notes.map(n => [n.fileIds, n.renote?.fileIds, n.reply?.fileIds]).flat(2).filter(isNotNull); - const packedFiles = fileIds.length > 0 ? await this.driveFileEntityService.packManyByIdsMap(fileIds) : new Map(); + const packedFiles = fileIds.length > 0 ? await this.driveFileEntityService.packManyByIdsMap(fileIds, me) : new Map(); - return await Promise.all(notes.map(n => this.pack(n, me, { + return (await Promise.allSettled(notes.map(n => this.pack(n, me, { ...options, _hint_: { myReactions: myReactionsMap, packedFiles, }, - }))); + })))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } @bindThis diff --git a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts index badb8fb816..408de50d98 100644 --- a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts +++ b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts @@ -1,10 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { NoteFavoritesRepository } from '@/models/index.js'; -import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { NoteFavorite } from '@/models/entities/NoteFavorite.js'; import { bindThis } from '@/decorators.js'; +import { Packed } from '@/misc/json-schema.js'; import { NoteEntityService } from './NoteEntityService.js'; @Injectable() @@ -20,8 +20,8 @@ export class NoteFavoriteEntityService { @bindThis public async pack( src: NoteFavorite['id'] | NoteFavorite, - me?: { id: User['id'] } | null | undefined, - ) { + me: { id: User['id'] } | null | undefined, + ) : Promise> { const favorite = typeof src === 'object' ? src : await this.noteFavoritesRepository.findOneByOrFail({ id: src }); return { @@ -33,10 +33,12 @@ export class NoteFavoriteEntityService { } @bindThis - public packMany( - favorites: any[], - me: { id: User['id'] }, - ) { - return Promise.all(favorites.map(x => this.pack(x, me))); + public async packMany( + favorites: (NoteFavorite['id'] | NoteFavorite)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(favorites.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts index d454ddb70a..ab31aefd54 100644 --- a/packages/backend/src/core/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts @@ -1,16 +1,15 @@ import { Inject, Injectable } from '@nestjs/common'; +import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; import type { NoteReactionsRepository } from '@/models/index.js'; import type { Packed } from '@/misc/json-schema.js'; import { bindThis } from '@/decorators.js'; -import type { OnModuleInit } from '@nestjs/common'; -import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; +import type { OnModuleInit } from '@nestjs/common'; import type { ReactionService } from '../ReactionService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; -import { ModuleRef } from '@nestjs/core'; @Injectable() export class NoteReactionEntityService implements OnModuleInit { @@ -39,7 +38,7 @@ export class NoteReactionEntityService implements OnModuleInit { @bindThis public async pack( src: NoteReaction['id'] | NoteReaction, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, options?: { withNote: boolean; }, diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index 02c6982847..b50fb159a9 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -2,7 +2,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { AccessTokensRepository, FollowRequestsRepository, NoteReactionsRepository, NotesRepository, User, UsersRepository } from '@/models/index.js'; +import type { + AccessTokensRepository, + FollowRequestsRepository, + NoteReactionsRepository, + NotesRepository, + UsersRepository, +} from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Notification } from '@/models/entities/Notification.js'; import type { Note } from '@/models/entities/Note.js'; @@ -10,6 +16,7 @@ import type { Packed } from '@/misc/json-schema.js'; import { bindThis } from '@/decorators.js'; import { isNotNull } from '@/misc/is-not-null.js'; import { notificationTypes } from '@/types.js'; +import type { User } from '@/models/entities/User.js'; import type { OnModuleInit } from '@nestjs/common'; import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { UserEntityService } from './UserEntityService.js'; @@ -108,7 +115,7 @@ export class NotificationEntityService implements OnModuleInit { public async packMany( notifications: Notification[], meId: User['id'], - ) { + ): Promise[]> { if (notifications.length === 0) return []; let validNotifications = notifications; @@ -143,9 +150,8 @@ export class NotificationEntityService implements OnModuleInit { validNotifications = validNotifications.filter(x => (x.type !== 'receiveFollowRequest') || reqs.some(r => r.followerId === x.notifierId)); } - return await Promise.all(validNotifications.map(x => this.pack(x, meId, {}, { - packedNotes, - packedUsers, - }))); + return (await Promise.allSettled(validNotifications.map(x => this.pack(x, meId, {}, { packedNotes, packedUsers })))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts index 94b26a5017..31b11d70a1 100644 --- a/packages/backend/src/core/entities/PageEntityService.ts +++ b/packages/backend/src/core/entities/PageEntityService.ts @@ -1,9 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { DriveFilesRepository, PagesRepository, PageLikesRepository } from '@/models/index.js'; +import type { DriveFilesRepository, PageLikesRepository, PagesRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { Page } from '@/models/entities/Page.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; @@ -31,7 +30,7 @@ export class PageEntityService { @bindThis public async pack( src: Page['id'] | Page, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, ): Promise> { const meId = me ? me.id : null; const page = typeof src === 'object' ? src : await this.pagesRepository.findOneByOrFail({ id: src }); @@ -94,19 +93,20 @@ export class PageEntityService { font: page.font, script: page.script, eyeCatchingImageId: page.eyeCatchingImageId, - eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId) : null, - attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is DriveFile => x != null)), + eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId, me) : null, + attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is DriveFile => x != null), me), likedCount: page.likedCount, isLiked: meId ? await this.pageLikesRepository.exist({ where: { pageId: page.id, userId: meId } }) : undefined, }); } @bindThis - public packMany( - pages: Page[], - me?: { id: User['id'] } | null | undefined, - ) { - return Promise.all(pages.map(x => this.pack(x, me))); + public async packMany( + pages: (Page['id'] | Page)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(pages.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } - diff --git a/packages/backend/src/core/entities/PageLikeEntityService.ts b/packages/backend/src/core/entities/PageLikeEntityService.ts index 99465d0ea3..6a4538a1af 100644 --- a/packages/backend/src/core/entities/PageLikeEntityService.ts +++ b/packages/backend/src/core/entities/PageLikeEntityService.ts @@ -1,10 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { PageLikesRepository } from '@/models/index.js'; -import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { PageLike } from '@/models/entities/PageLike.js'; import { bindThis } from '@/decorators.js'; +import { Packed } from '@/misc/json-schema.js'; import { PageEntityService } from './PageEntityService.js'; @Injectable() @@ -20,8 +20,8 @@ export class PageLikeEntityService { @bindThis public async pack( src: PageLike['id'] | PageLike, - me?: { id: User['id'] } | null | undefined, - ) { + me: { id: User['id'] } | null | undefined, + ) : Promise> { const like = typeof src === 'object' ? src : await this.pageLikesRepository.findOneByOrFail({ id: src }); return { @@ -31,11 +31,12 @@ export class PageLikeEntityService { } @bindThis - public packMany( - likes: any[], - me: { id: User['id'] }, - ) { - return Promise.all(likes.map(x => this.pack(x, me))); + public async packMany( + likes: (PageLike['id'] | PageLike)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(likes.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } - diff --git a/packages/backend/src/core/entities/RenoteMutingEntityService.ts b/packages/backend/src/core/entities/RenoteMutingEntityService.ts index f8871e0495..23a85e189f 100644 --- a/packages/backend/src/core/entities/RenoteMutingEntityService.ts +++ b/packages/backend/src/core/entities/RenoteMutingEntityService.ts @@ -3,7 +3,6 @@ import { DI } from '@/di-symbols.js'; import type { RenoteMutingsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { RenoteMuting } from '@/models/entities/RenoteMuting.js'; import { bindThis } from '@/decorators.js'; @@ -22,7 +21,7 @@ export class RenoteMutingEntityService { @bindThis public async pack( src: RenoteMuting['id'] | RenoteMuting, - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, ): Promise> { const muting = typeof src === 'object' ? src : await this.renoteMutingsRepository.findOneByOrFail({ id: src }); @@ -37,11 +36,12 @@ export class RenoteMutingEntityService { } @bindThis - public packMany( - mutings: any[], - me: { id: User['id'] }, - ) { - return Promise.all(mutings.map(x => this.pack(x, me))); + public async packMany( + mutings: (RenoteMuting['id'] | RenoteMuting)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(mutings.map(u => this.pack(u, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } - diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts index 54818782dd..3a26f150b1 100644 --- a/packages/backend/src/core/entities/RoleEntityService.ts +++ b/packages/backend/src/core/entities/RoleEntityService.ts @@ -7,6 +7,7 @@ import type { User } from '@/models/entities/User.js'; import type { Role } from '@/models/entities/Role.js'; import { bindThis } from '@/decorators.js'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; +import { Packed } from '@/misc/json-schema.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -25,8 +26,8 @@ export class RoleEntityService { @bindThis public async pack( src: Role['id'] | Role, - me?: { id: User['id'] } | null | undefined, - ) { + me: { id: User['id'] } | null | undefined, + ) : Promise> { const role = typeof src === 'object' ? src : await this.rolesRepository.findOneByOrFail({ id: src }); const assignedCount = await this.roleAssignmentsRepository.createQueryBuilder('assign') @@ -69,11 +70,12 @@ export class RoleEntityService { } @bindThis - public packMany( - roles: any[], - me: { id: User['id'] }, - ) { - return Promise.all(roles.map(x => this.pack(x, me))); + public async packMany( + roles: (Role['id'] | Role)[], + me: { id: User['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(roles.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } - diff --git a/packages/backend/src/core/entities/SigninEntityService.ts b/packages/backend/src/core/entities/SigninEntityService.ts index a6c4407ce8..0362e99424 100644 --- a/packages/backend/src/core/entities/SigninEntityService.ts +++ b/packages/backend/src/core/entities/SigninEntityService.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { SigninsRepository } from '@/models/index.js'; -import type { } from '@/models/entities/Blocking.js'; import type { Signin } from '@/models/entities/Signin.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index e86eef0042..f230c3b2b8 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -10,12 +10,39 @@ import type { Promiseable } from '@/misc/prelude/await-all.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js'; import type { LocalUser, PartialLocalUser, PartialRemoteUser, RemoteUser, User } from '@/models/entities/User.js'; -import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js'; -import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, AnnouncementsRepository, PagesRepository, UserProfile, RenoteMutingsRepository, UserMemoRepository } from '@/models/index.js'; +import { + birthdaySchema, + descriptionSchema, + localUsernameSchema, + locationSchema, + nameSchema, + passwordSchema, +} from '@/models/entities/User.js'; +import type { + AnnouncementReadsRepository, + AnnouncementsRepository, + BlockingsRepository, + ChannelFollowingsRepository, + DriveFilesRepository, + FollowingsRepository, + FollowRequestsRepository, + InstancesRepository, + MutingsRepository, + NoteUnreadsRepository, + PagesRepository, + RenoteMutingsRepository, + UserMemoRepository, + UserNotePiningsRepository, + UserProfile, + UserProfilesRepository, + UserSecurityKeysRepository, + UsersRepository, +} from '@/models/index.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { OnModuleInit } from '@nestjs/common'; import type { AntennaService } from '../AntennaService.js'; import type { CustomEmojiService } from '../CustomEmojiService.js'; @@ -297,7 +324,7 @@ export class UserEntityService implements OnModuleInit { public async pack( src: User['id'] | User, - me?: { id: User['id']; } | null | undefined, + me: { id: User['id'] } | null | undefined, options?: { detail?: D, includeSecrets?: boolean, @@ -332,6 +359,7 @@ export class UserEntityService implements OnModuleInit { const meId = me ? me.id : null; const isMe = meId === user.id; const iAmModerator = me ? await this.roleService.isModerator(me as User) : false; + if (user.isSuspended && !iAmModerator) throw new IdentifiableError('8ca4f428-b32e-4f83-ac43-406ed7cd0452', 'This user is suspended.'); const relation = meId && !isMe && opts.detail ? await this.getRelation(meId, user.id) : null; const pins = opts.detail ? await this.userNotePiningsRepository.createQueryBuilder('pin') @@ -511,14 +539,16 @@ export class UserEntityService implements OnModuleInit { return await awaitAll(packed); } - public packMany( + public async packMany( users: (User['id'] | User)[], - me?: { id: User['id'] } | null | undefined, + me: { id: User['id'] } | null | undefined, options?: { detail?: D, includeSecrets?: boolean, }, ): Promise[]> { - return Promise.all(users.map(u => this.pack(u, me, options))); + return (await Promise.allSettled(users.map(u => this.pack(u, me, options)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); } } diff --git a/packages/backend/src/core/entities/UserListEntityService.ts b/packages/backend/src/core/entities/UserListEntityService.ts index 8628819278..b9719180a5 100644 --- a/packages/backend/src/core/entities/UserListEntityService.ts +++ b/packages/backend/src/core/entities/UserListEntityService.ts @@ -2,7 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/entities/Blocking.js'; import type { UserList } from '@/models/entities/UserList.js'; import { bindThis } from '@/decorators.js'; import { UserEntityService } from './UserEntityService.js'; diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index ec6bc4a5fb..22903fe4ee 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -7,29 +7,32 @@ import { packedUserDetailedSchema, packedUserSchema, } from '@/models/json-schema/user.js'; -import { packedNoteSchema } from '@/models/json-schema/note.js'; -import { packedUserListSchema } from '@/models/json-schema/user-list.js'; +import { packedAbuseUserReportSchema } from '@/models/json-schema/abuse-user-report.js'; +import { packedAntennaSchema } from '@/models/json-schema/antenna.js'; import { packedAppSchema } from '@/models/json-schema/app.js'; -import { packedNotificationSchema } from '@/models/json-schema/notification.js'; +import { packedBlockingSchema } from '@/models/json-schema/blocking.js'; +import { packedChannelSchema } from '@/models/json-schema/channel.js'; +import { packedClipSchema } from '@/models/json-schema/clip.js'; import { packedDriveFileSchema } from '@/models/json-schema/drive-file.js'; import { packedDriveFolderSchema } from '@/models/json-schema/drive-folder.js'; -import { packedFollowingSchema } from '@/models/json-schema/following.js'; -import { packedMutingSchema } from '@/models/json-schema/muting.js'; -import { packedRenoteMutingSchema } from '@/models/json-schema/renote-muting.js'; -import { packedBlockingSchema } from '@/models/json-schema/blocking.js'; -import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js'; +import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/json-schema/emoji.js'; +import { packedFederationInstanceSchema } from '@/models/json-schema/federation-instance.js'; +import { packedFlashLikeSchema, packedFlashSchema } from '@/models/json-schema/flash.js'; +import { packedFollowRequestSchema, packedFollowingSchema } from '@/models/json-schema/following.js'; +import { packedGalleryLikeSchema, packedGalleryPostSchema } from '@/models/json-schema/gallery.js'; import { packedHashtagSchema } from '@/models/json-schema/hashtag.js'; import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js'; -import { packedPageSchema } from '@/models/json-schema/page.js'; +import { packedModerationLogSchema } from '@/models/json-schema/moderation-log.js'; +import { packedMutingSchema } from '@/models/json-schema/muting.js'; import { packedNoteFavoriteSchema } from '@/models/json-schema/note-favorite.js'; -import { packedChannelSchema } from '@/models/json-schema/channel.js'; -import { packedAntennaSchema } from '@/models/json-schema/antenna.js'; -import { packedClipSchema } from '@/models/json-schema/clip.js'; -import { packedFederationInstanceSchema } from '@/models/json-schema/federation-instance.js'; +import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js'; +import { packedNoteSchema } from '@/models/json-schema/note.js'; +import { packedNotificationSchema } from '@/models/json-schema/notification.js'; +import { packedPageLikeSchema, packedPageSchema } from '@/models/json-schema/page.js'; import { packedQueueCountSchema } from '@/models/json-schema/queue.js'; -import { packedGalleryPostSchema } from '@/models/json-schema/gallery-post.js'; -import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/json-schema/emoji.js'; -import { packedFlashSchema } from '@/models/json-schema/flash.js'; +import { packedRenoteMutingSchema } from '@/models/json-schema/renote-muting.js'; +import { packedRoleSchema } from '@/models/json-schema/role.js'; +import { packedUserListSchema } from '@/models/json-schema/user-list.js'; export const refs = { UserLite: packedUserLiteSchema, @@ -49,21 +52,29 @@ export const refs = { DriveFile: packedDriveFileSchema, DriveFolder: packedDriveFolderSchema, Following: packedFollowingSchema, + FollowRequest: packedFollowRequestSchema, Muting: packedMutingSchema, RenoteMuting: packedRenoteMutingSchema, Blocking: packedBlockingSchema, Hashtag: packedHashtagSchema, InviteCode: packedInviteCodeSchema, Page: packedPageSchema, + PageLike: packedPageLikeSchema, Channel: packedChannelSchema, QueueCount: packedQueueCountSchema, Antenna: packedAntennaSchema, Clip: packedClipSchema, FederationInstance: packedFederationInstanceSchema, GalleryPost: packedGalleryPostSchema, + GalleryLike: packedGalleryLikeSchema, EmojiSimple: packedEmojiSimpleSchema, EmojiDetailed: packedEmojiDetailedSchema, Flash: packedFlashSchema, + FlashLike: packedFlashLikeSchema, + + Role: packedRoleSchema, + AbuseUserReport: packedAbuseUserReportSchema, + ModerationLog: packedModerationLogSchema, }; export type Packed = SchemaType; diff --git a/packages/backend/src/models/json-schema/abuse-user-report.ts b/packages/backend/src/models/json-schema/abuse-user-report.ts new file mode 100644 index 0000000000..8cb0ee48c2 --- /dev/null +++ b/packages/backend/src/models/json-schema/abuse-user-report.ts @@ -0,0 +1,58 @@ +export const packedAbuseUserReportSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + comment: { + type: 'string', + optional: false, nullable: false, + }, + resolved: { + type: 'boolean', + optional: false, nullable: false, + }, + reporterId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + reporter: { + type: 'object', + ref: 'UserDetailed', + optional: false, nullable: false, + }, + targetUserId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + targetUser: { + type: 'object', + ref: 'UserDetailed', + optional: false, nullable: false, + }, + assigneeId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + }, + assignee: { + type: 'object', + ref: 'UserDetailed', + optional: true, nullable: true, + }, + forwarded: { + type: 'boolean', + optional: false, nullable: false, + }, + }, +} as const; diff --git a/packages/backend/src/models/json-schema/flash.ts b/packages/backend/src/models/json-schema/flash.ts index 8471a138ec..311a5ad82c 100644 --- a/packages/backend/src/models/json-schema/flash.ts +++ b/packages/backend/src/models/json-schema/flash.ts @@ -49,3 +49,20 @@ export const packedFlashSchema = { }, }, } as const; + +export const packedFlashLikeSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + flash: { + type: 'object', + ref: 'Flash', + optional: false, nullable: false, + }, + }, +} as const; diff --git a/packages/backend/src/models/json-schema/following.ts b/packages/backend/src/models/json-schema/following.ts index 2bcffbfc4d..d5840a70a5 100644 --- a/packages/backend/src/models/json-schema/following.ts +++ b/packages/backend/src/models/json-schema/following.ts @@ -34,3 +34,25 @@ export const packedFollowingSchema = { }, }, } as const; + +export const packedFollowRequestSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + follower: { + type: 'object', + optional: false, nullable: false, + ref: 'UserLite', + }, + followee: { + type: 'object', + optional: false, nullable: false, + ref: 'UserLite', + }, + }, +} as const; diff --git a/packages/backend/src/models/json-schema/gallery-post.ts b/packages/backend/src/models/json-schema/gallery.ts similarity index 81% rename from packages/backend/src/models/json-schema/gallery-post.ts rename to packages/backend/src/models/json-schema/gallery.ts index fc503d4a64..46115a2164 100644 --- a/packages/backend/src/models/json-schema/gallery-post.ts +++ b/packages/backend/src/models/json-schema/gallery.ts @@ -67,3 +67,20 @@ export const packedGalleryPostSchema = { }, }, } as const; + +export const packedGalleryLikeSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + post: { + type: 'object', + ref: 'GalleryPost', + optional: false, nullable: false, + }, + }, +} as const; diff --git a/packages/backend/src/models/json-schema/moderation-log.ts b/packages/backend/src/models/json-schema/moderation-log.ts new file mode 100644 index 0000000000..1dbe478638 --- /dev/null +++ b/packages/backend/src/models/json-schema/moderation-log.ts @@ -0,0 +1,41 @@ +export const packedModerationLogSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + type: { + type: 'string', + optional: false, nullable: false, + }, + info: { + type: 'object', + optional: false, nullable: false, + patternProperties: { + '^': { + type: 'object', + nullable: false, optional: false, + }, + }, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + user: { + type: 'object', + ref: 'UserDetailed', + optional: false, nullable: false, + }, + }, +} as const; diff --git a/packages/backend/src/models/json-schema/page.ts b/packages/backend/src/models/json-schema/page.ts index 55ba3ce7f7..6e402bbd35 100644 --- a/packages/backend/src/models/json-schema/page.ts +++ b/packages/backend/src/models/json-schema/page.ts @@ -49,3 +49,20 @@ export const packedPageSchema = { }, }, } as const; + +export const packedPageLikeSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + page: { + type: 'object', + ref: 'Page', + optional: false, nullable: false, + }, + }, +} as const; diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts new file mode 100644 index 0000000000..1c6619e8a8 --- /dev/null +++ b/packages/backend/src/models/json-schema/role.ts @@ -0,0 +1,103 @@ +export const packedRoleSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + updatedAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + name: { + type: 'string', + optional: false, nullable: false, + }, + description: { + type: 'string', + optional: false, nullable: false, + }, + color: { + type: 'string', + optional: false, nullable: true, + }, + iconUrl: { + type: 'string', + format: 'url', + optional: false, nullable: true, + }, + target: { + type: 'string', + optional: false, nullable: false, + enum: ['manual', 'conditional'], + }, + condFormula: { + type: 'object', + optional: false, nullable: false, + }, + isPublic: { + type: 'boolean', + optional: false, nullable: false, + }, + isAdministrator: { + type: 'boolean', + optional: false, nullable: false, + }, + isModerator: { + type: 'boolean', + optional: false, nullable: false, + }, + isExplorable: { + type: 'boolean', + optional: false, nullable: false, + }, + asBadge: { + type: 'boolean', + optional: false, nullable: false, + }, + canEditMembersByModerator: { + type: 'boolean', + optional: false, nullable: false, + }, + displayOrder: { + type: 'number', + optional: false, nullable: false, + }, + policies: { + type: 'object', + optional: false, nullable: false, + patternProperties: { + '^': { + type: 'object', + nullable: false, optional: false, + properties: { + useDefault: { + type: 'boolean', + nullable: false, optional: false, + }, + priority: { + type: 'number', + nullable: false, optional: false, + }, + value: { + type: 'object', + nullable: false, optional: false, + }, + }, + }, + }, + }, + usersCount: { + type: 'number', + optional: false, nullable: false, + }, + }, +} as const; diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index 13f88e37ce..c018a0e3d4 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -101,7 +101,7 @@ export class ExportNotesProcessorService { if (note.hasPoll) { poll = await this.pollsRepository.findOneByOrFail({ noteId: note.id }); } - const files = await this.driveFileEntityService.packManyByIds(note.fileIds); + const files = await this.driveFileEntityService.packManyByIds(note.fileIds, user); const content = JSON.stringify(serialize(note, poll, files)); const isFirst = exportedNotesCount === 0; await write(isFirst ? content : ',\n' + content); diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 666a91fcee..1b253f83ae 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -71,7 +71,7 @@ export class NodeinfoServerService { const activeHalfyear = null; const activeMonth = null; - const proxyAccount = meta.proxyAccountId ? await this.userEntityService.pack(meta.proxyAccountId).catch(() => null) : null; + const proxyAccount = meta.proxyAccountId ? await this.userEntityService.pack(meta.proxyAccountId, null).catch(() => null) : null; const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies }; diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index 3e8b9fb727..a86b59e0de 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -12,6 +12,7 @@ import { MetaService } from '@/core/MetaService.js'; import { createTemp } from '@/misc/create-temp.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; import { ApiError } from './error.js'; import { RateLimiterService } from './RateLimiterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; @@ -356,7 +357,7 @@ export class ApiCallService implements OnApplicationShutdown { // API invoking return await ep.exec(data, user, token, file, request.ip, request.headers).catch((err: Error) => { - if (err instanceof ApiError || err instanceof AuthenticationError) { + if (err instanceof ApiError || err instanceof IdentifiableError || err instanceof AuthenticationError) { throw err; } else { const errId = randomUUID(); diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index d3b1c7786d..e54a7c1650 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -155,7 +155,7 @@ export class ApiServerService { return { ok: true, token: token.token, - user: await this.userEntityService.pack(token.userId, null, { detail: true }), + user: await this.userEntityService.pack(token.userId, { id: token.userId }, { detail: true }), }; } else { return { diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts index b8ea74b7c5..bd89f0169d 100644 --- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -117,7 +117,7 @@ export default class extends Endpoint { const reports = await query.limit(ps.limit).getMany(); - return await this.abuseUserReportEntityService.packMany(reports); + return await this.abuseUserReportEntityService.packMany(reports, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index 2901fdb774..aa7d2cfc58 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -78,7 +78,7 @@ export default class extends Endpoint { const files = await query.limit(ps.limit).getMany(); - return await this.driveFileEntityService.packMany(files, { detail: true, withUser: true, self: true }); + return await this.driveFileEntityService.packMany(files, me, { detail: true, withUser: true, self: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index 69c95ef19c..581045abb7 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -76,7 +76,7 @@ export default class extends Endpoint { const reports = await query.limit(ps.limit).getMany(); - return await this.moderationLogEntityService.packMany(reports); + return await this.moderationLogEntityService.packMany(reports, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index aaef02d03f..7e62aa4bab 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -61,7 +61,7 @@ export default class extends Endpoint { secret: secret, }).then(x => this.appsRepository.findOneByOrFail(x.identifiers[0])); - return await this.appEntityService.pack(app, null, { + return await this.appEntityService.pack(app, me, { detail: true, includeSecret: true, }); diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts index b1e7bbfded..13a0907555 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -110,7 +110,7 @@ export default class extends Endpoint { return { accessToken: accessToken.token, - user: await this.userEntityService.pack(session.userId, null, { + user: await this.userEntityService.pack(session.userId, me, { detail: true, }), }; diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index f4343248b8..1d38acea86 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -75,7 +75,7 @@ export default class extends Endpoint { const files = await query.limit(ps.limit).getMany(); - return await this.driveFileEntityService.packMany(files, { detail: false, self: true }); + return await this.driveFileEntityService.packMany(files, me, { detail: false, self: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/create.ts b/packages/backend/src/server/api/endpoints/drive/files/create.ts index 72900358ef..129dcab4f9 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts @@ -108,7 +108,7 @@ export default class extends Endpoint { requestIp: instance.enableIpLogging ? ip : null, requestHeaders: instance.enableIpLogging ? headers : null, }); - return await this.driveFileEntityService.pack(driveFile, { self: true }); + return await this.driveFileEntityService.pack(driveFile, me, { self: true }); } catch (err) { if (err instanceof Error || typeof err === 'string') { console.error(err); diff --git a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts index d6d85f4e77..09b0ad1b85 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts @@ -47,7 +47,7 @@ export default class extends Endpoint { userId: me.id, }); - return await this.driveFileEntityService.packMany(files, { self: true }); + return await this.driveFileEntityService.packMany(files, me, { self: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/find.ts b/packages/backend/src/server/api/endpoints/drive/files/find.ts index 858063eb4b..f3c52f9a9e 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts @@ -50,7 +50,7 @@ export default class extends Endpoint { folderId: ps.folderId ?? IsNull(), }); - return await Promise.all(files.map(file => this.driveFileEntityService.pack(file, { self: true }))); + return await Promise.all(files.map(file => this.driveFileEntityService.pack(file, me, { self: true }))); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/show.ts b/packages/backend/src/server/api/endpoints/drive/files/show.ts index 271b33ef4b..136619348e 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/show.ts @@ -84,7 +84,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.accessDenied); } - return await this.driveFileEntityService.pack(file, { + return await this.driveFileEntityService.pack(file, me, { detail: true, withUser: true, self: true, diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index 92a09e6049..3b1087f60a 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -129,7 +129,7 @@ export default class extends Endpoint { isSensitive: file.isSensitive, }); - const fileObj = await this.driveFileEntityService.pack(file, { self: true }); + const fileObj = await this.driveFileEntityService.pack(file, me, { self: true }); // Publish fileUpdated event this.globalEventService.publishDriveStream(me.id, 'fileUpdated', fileObj); diff --git a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts index 0c3b743254..ace2f1cbd5 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts @@ -51,7 +51,7 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, user, _1, _2, _3, ip, headers) => { this.driveService.uploadFromUrl({ url: ps.url, user, folderId: ps.folderId, sensitive: ps.isSensitive, force: ps.force, comment: ps.comment, requestIp: ip, requestHeaders: headers }).then(file => { - this.driveFileEntityService.pack(file, { self: true }).then(packedFile => { + this.driveFileEntityService.pack(file, user, { self: true }).then(packedFile => { this.globalEventService.publishMainStream(user.id, 'urlUploadFinished', { marker: ps.marker, file: packedFile, diff --git a/packages/backend/src/server/api/endpoints/drive/stream.ts b/packages/backend/src/server/api/endpoints/drive/stream.ts index a1c14a8e3f..4996ee6108 100644 --- a/packages/backend/src/server/api/endpoints/drive/stream.ts +++ b/packages/backend/src/server/api/endpoints/drive/stream.ts @@ -58,7 +58,7 @@ export default class extends Endpoint { const files = await query.limit(ps.limit).getMany(); - return await this.driveFileEntityService.packMany(files, { detail: false, self: true }); + return await this.driveFileEntityService.packMany(files, me, { detail: false, self: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/flash/create.ts b/packages/backend/src/server/api/endpoints/flash/create.ts index fc556ad2b7..c8e438ecb2 100644 --- a/packages/backend/src/server/api/endpoints/flash/create.ts +++ b/packages/backend/src/server/api/endpoints/flash/create.ts @@ -60,7 +60,7 @@ export default class extends Endpoint { permissions: ps.permissions, }).then(x => this.flashsRepository.findOneByOrFail(x.identifiers[0])); - return await this.flashEntityService.pack(flash); + return await this.flashEntityService.pack(flash, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/flash/my.ts b/packages/backend/src/server/api/endpoints/flash/my.ts index 45a3b50e08..00f2d37333 100644 --- a/packages/backend/src/server/api/endpoints/flash/my.ts +++ b/packages/backend/src/server/api/endpoints/flash/my.ts @@ -51,7 +51,7 @@ export default class extends Endpoint { .limit(ps.limit) .getMany(); - return await this.flashEntityService.packMany(flashs); + return await this.flashEntityService.packMany(flashs, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/following/requests/list.ts b/packages/backend/src/server/api/endpoints/following/requests/list.ts index 29588e8731..3412df6950 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/list.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/list.ts @@ -67,7 +67,7 @@ export default class extends Endpoint { .limit(ps.limit) .getMany(); - return await Promise.all(requests.map(req => this.followRequestEntityService.pack(req))); + return await this.followRequestEntityService.packMany(requests, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/pages.ts b/packages/backend/src/server/api/endpoints/i/pages.ts index 772486befc..3f4f0a25d8 100644 --- a/packages/backend/src/server/api/endpoints/i/pages.ts +++ b/packages/backend/src/server/api/endpoints/i/pages.ts @@ -51,7 +51,7 @@ export default class extends Endpoint { .limit(ps.limit) .getMany(); - return await this.pageEntityService.packMany(pages); + return await this.pageEntityService.packMany(pages, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index adfa579558..7be4354324 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -341,7 +341,7 @@ export default class extends Endpoint { }; if (ps.detail) { - const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId).catch(() => null) : null; + const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId, null).catch(() => null) : null; response.proxyAccountName = proxyAccount ? proxyAccount.username : null; response.features = { diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 9013b300e7..b8fa2a7319 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -81,7 +81,7 @@ export default class extends Endpoint { const notes = await query.limit(ps.limit).getMany(); - return await this.noteEntityService.packMany(notes); + return await this.noteEntityService.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index 00a07f8fa8..eef8b5ca82 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -117,7 +117,7 @@ export default class extends Endpoint { font: ps.font, })).then(x => this.pagesRepository.findOneByOrFail(x.identifiers[0])); - return await this.pageEntityService.pack(page); + return await this.pageEntityService.pack(page, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts index 40ffec4732..ed6c0ff3c3 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -77,7 +77,7 @@ export default class extends Endpoint { // Pull the user await this.userListJoiningsRepository.delete({ userListId: userList.id, userId: user.id }); - this.globalEventService.publishUserListStream(userList.id, 'userRemoved', await this.userEntityService.pack(user)); + this.globalEventService.publishUserListStream(userList.id, 'userRemoved', await this.userEntityService.pack(user, me)); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index e9d13ba00f..4cc0327141 100644 --- a/packages/backend/src/server/api/endpoints/users/pages.ts +++ b/packages/backend/src/server/api/endpoints/users/pages.ts @@ -51,7 +51,7 @@ export default class extends Endpoint { .limit(ps.limit) .getMany(); - return await this.pageEntityService.packMany(pages); + return await this.pageEntityService.packMany(pages, me); }); } } diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index ec310e0fbc..0c05d0c875 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -1,7 +1,6 @@ -import { randomUUID } from 'node:crypto'; +import { randomUUID, randomBytes } from 'node:crypto'; import { dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { randomBytes } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import { createBullBoard } from '@bull-board/api'; import { BullMQAdapter } from '@bull-board/api/bullMQAdapter.js'; @@ -33,6 +32,7 @@ import { deepClone } from '@/misc/clone.js'; import { bindThis } from '@/decorators.js'; import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; import { RoleService } from '@/core/RoleService.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; import manifest from './manifest.json' assert { type: 'json' }; import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; @@ -201,7 +201,7 @@ export class ClientServerService { const csp = this.config.contentSecurityPolicy ?? 'script-src \'self\' ' + 'https://challenges.cloudflare.com https://hcaptcha.com https://*.hcaptcha.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.recaptcha.net/recaptcha/ {scriptNonce}; ' + - 'worker-src blob: \'self\'; ' + + 'worker-src blob: \'self\'; ' + 'base-uri \'self\'; object-src \'self\'; report-uri /csp-error'; reply.header('Content-Security-Policy-Report-Only', csp.replace('{scriptNonce}', `'nonce-${scriptNonce}'`)); done(); @@ -489,22 +489,29 @@ export class ClientServerService { }); if (note) { - const _note = await this.noteEntityService.pack(note); - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: note.userId }); - const meta = await this.metaService.fetch(); - reply.header('Cache-Control', 'public, max-age=15'); - if (profile.preventAiLearning) { - reply.header('X-Robots-Tag', 'noimageai'); - reply.header('X-Robots-Tag', 'noai'); + try { + const _note = await this.noteEntityService.pack(note, null); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: note.userId }); + const meta = await this.metaService.fetch(); + reply.header('Cache-Control', 'public, max-age=15'); + if (profile.preventAiLearning) { + reply.header('X-Robots-Tag', 'noimageai'); + reply.header('X-Robots-Tag', 'noai'); + } + return await reply.view('note', { + note: _note, + profile, + avatarUrl: _note.user.avatarUrl, + // TODO: Let locale changeable by instance setting + summary: getNoteSummary(_note), + ...this.generateCommonPugData(meta), + }); + } catch (err) { + if ((err as IdentifiableError).id === '8ca4f428-b32e-4f83-ac43-406ed7cd0452') { + return await renderBase(reply); + } + throw err; } - return await reply.view('note', { - note: _note, - profile, - avatarUrl: _note.user.avatarUrl, - // TODO: Let locale changeable by instance setting - summary: getNoteSummary(_note), - ...this.generateCommonPugData(meta), - }); } else { return await renderBase(reply); } @@ -526,24 +533,31 @@ export class ClientServerService { }); if (page) { - const _page = await this.pageEntityService.pack(page); - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: page.userId }); - const meta = await this.metaService.fetch(); - if (['public'].includes(page.visibility)) { - reply.header('Cache-Control', 'public, max-age=15'); - } else { - reply.header('Cache-Control', 'private, max-age=0, must-revalidate'); + try { + const _page = await this.pageEntityService.pack(page, null); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: page.userId }); + const meta = await this.metaService.fetch(); + if (['public'].includes(page.visibility)) { + reply.header('Cache-Control', 'public, max-age=15'); + } else { + reply.header('Cache-Control', 'private, max-age=0, must-revalidate'); + } + if (profile.preventAiLearning) { + reply.header('X-Robots-Tag', 'noimageai'); + reply.header('X-Robots-Tag', 'noai'); + } + return await reply.view('page', { + page: _page, + profile, + avatarUrl: _page.user.avatarUrl, + ...this.generateCommonPugData(meta), + }); + } catch (err) { + if ((err as IdentifiableError).id === '8ca4f428-b32e-4f83-ac43-406ed7cd0452') { + return await renderBase(reply); + } + throw err; } - if (profile.preventAiLearning) { - reply.header('X-Robots-Tag', 'noimageai'); - reply.header('X-Robots-Tag', 'noai'); - } - return await reply.view('page', { - page: _page, - profile, - avatarUrl: _page.user.avatarUrl, - ...this.generateCommonPugData(meta), - }); } else { return await renderBase(reply); } @@ -556,20 +570,27 @@ export class ClientServerService { }); if (flash) { - const _flash = await this.flashEntityService.pack(flash); - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: flash.userId }); - const meta = await this.metaService.fetch(); - reply.header('Cache-Control', 'public, max-age=15'); - if (profile.preventAiLearning) { - reply.header('X-Robots-Tag', 'noimageai'); - reply.header('X-Robots-Tag', 'noai'); + try { + const _flash = await this.flashEntityService.pack(flash, null); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: flash.userId }); + const meta = await this.metaService.fetch(); + reply.header('Cache-Control', 'public, max-age=15'); + if (profile.preventAiLearning) { + reply.header('X-Robots-Tag', 'noimageai'); + reply.header('X-Robots-Tag', 'noai'); + } + return await reply.view('flash', { + flash: _flash, + profile, + avatarUrl: _flash.user.avatarUrl, + ...this.generateCommonPugData(meta), + }); + } catch (err) { + if ((err as IdentifiableError).id === '8ca4f428-b32e-4f83-ac43-406ed7cd0452') { + return await renderBase(reply); + } + throw err; } - return await reply.view('flash', { - flash: _flash, - profile, - avatarUrl: _flash.user.avatarUrl, - ...this.generateCommonPugData(meta), - }); } else { return await renderBase(reply); } @@ -582,20 +603,27 @@ export class ClientServerService { }); if (clip && clip.isPublic) { - const _clip = await this.clipEntityService.pack(clip); - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: clip.userId }); - const meta = await this.metaService.fetch(); - reply.header('Cache-Control', 'public, max-age=15'); - if (profile.preventAiLearning) { - reply.header('X-Robots-Tag', 'noimageai'); - reply.header('X-Robots-Tag', 'noai'); + try { + const _clip = await this.clipEntityService.pack(clip, null); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: clip.userId }); + const meta = await this.metaService.fetch(); + reply.header('Cache-Control', 'public, max-age=15'); + if (profile.preventAiLearning) { + reply.header('X-Robots-Tag', 'noimageai'); + reply.header('X-Robots-Tag', 'noai'); + } + return await reply.view('clip', { + clip: _clip, + profile, + avatarUrl: _clip.user.avatarUrl, + ...this.generateCommonPugData(meta), + }); + } catch (err) { + if ((err as IdentifiableError).id === '8ca4f428-b32e-4f83-ac43-406ed7cd0452') { + return await renderBase(reply); + } + throw err; } - return await reply.view('clip', { - clip: _clip, - profile, - avatarUrl: _clip.user.avatarUrl, - ...this.generateCommonPugData(meta), - }); } else { return await renderBase(reply); } @@ -606,20 +634,27 @@ export class ClientServerService { const post = await this.galleryPostsRepository.findOneBy({ id: request.params.post }); if (post) { - const _post = await this.galleryPostEntityService.pack(post); - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: post.userId }); - const meta = await this.metaService.fetch(); - reply.header('Cache-Control', 'public, max-age=15'); - if (profile.preventAiLearning) { - reply.header('X-Robots-Tag', 'noimageai'); - reply.header('X-Robots-Tag', 'noai'); + try { + const _post = await this.galleryPostEntityService.pack(post, null); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: post.userId }); + const meta = await this.metaService.fetch(); + reply.header('Cache-Control', 'public, max-age=15'); + if (profile.preventAiLearning) { + reply.header('X-Robots-Tag', 'noimageai'); + reply.header('X-Robots-Tag', 'noai'); + } + return await reply.view('gallery-post', { + post: _post, + profile, + avatarUrl: _post.user.avatarUrl, + ...this.generateCommonPugData(meta), + }); + } catch (err) { + if ((err as IdentifiableError).id === '8ca4f428-b32e-4f83-ac43-406ed7cd0452') { + return await renderBase(reply); + } + throw err; } - return await reply.view('gallery-post', { - post: _post, - profile, - avatarUrl: _post.user.avatarUrl, - ...this.generateCommonPugData(meta), - }); } else { return await renderBase(reply); } @@ -632,7 +667,7 @@ export class ClientServerService { }); if (channel) { - const _channel = await this.channelEntityService.pack(channel); + const _channel = await this.channelEntityService.pack(channel, null); const meta = await this.metaService.fetch(); reply.header('Cache-Control', 'public, max-age=15'); return await reply.view('channel', {