feat: 凍結されたユーザーのコンテンツを見えないようにする (MisskeyIO#134)

ついでにEntityServiceの型定義、meのoptionalをやめる
This commit is contained in:
まっちゃとーにゅ
2023-08-08 20:13:05 +09:00
committed by GitHub
parent 3b73874196
commit 7f0acd3ea4
70 changed files with 742 additions and 325 deletions

View File

@@ -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<Packed<'AbuseUserReport'>> {
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<Packed<'AbuseUserReport'>[]> {
return (await Promise.allSettled(reports.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'AbuseUserReport'>>).value);
}
}

View File

@@ -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,

View File

@@ -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 });

View File

@@ -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<Packed<'Blocking'>> {
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<Packed<'Blocking'>[]> {
return (await Promise.allSettled(blockings.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'Blocking'>>).value);
}
}

View File

@@ -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<Packed<'Channel'>> {
const channel = typeof src === 'object' ? src : await this.channelsRepository.findOneByOrFail({ id: src });

View File

@@ -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<Packed<'Clip'>> {
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<Packed<'Clip'>[]> {
return (await Promise.allSettled(clips.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'Clip'>>).value);
}
}

View File

@@ -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<Packed<'DriveFile'>> {
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<Packed<'DriveFile'> | 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<Packed<'DriveFile'>[]> {
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<Packed<'DriveFile'>>).value);
}
@bindThis
public async packManyByIdsMap(
fileIds: DriveFile['id'][],
me: { id: User['id'] } | null | undefined,
options?: PackOptions,
): Promise<Map<Packed<'DriveFile'>['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<Packed<'DriveFile'>['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<Packed<'DriveFile'>[]> {
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);
}
}

View File

@@ -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';

View File

@@ -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<Packed<'EmojiSimple'>[]> {
return (await Promise.allSettled(emojis.map(x => this.packSimple(x))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'EmojiSimple'>>).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<Packed<'EmojiDetailed'>[]> {
return (await Promise.allSettled(emojis.map(x => this.packDetailed(x))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'EmojiDetailed'>>).value);
}
}

View File

@@ -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<Packed<'Flash'>> {
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<Packed<'Flash'>[]> {
return (await Promise.allSettled(flashs.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'Flash'>>).value);
}
}

View File

@@ -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<Packed<'FlashLike'>> {
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<Packed<'FlashLike'>[]> {
return (await Promise.allSettled(likes.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'FlashLike'>>).value);
}
}

View File

@@ -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<Packed<'FollowRequest'>> {
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<Packed<'FollowRequest'>[]> {
return (await Promise.allSettled(requests.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'FollowRequest'>>).value);
}
}

View File

@@ -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<Packed<'Following'>[]> {
return (await Promise.allSettled(followings.map(x => this.pack(x, me, opts))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'Following'>>).value);
}
}

View File

@@ -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<Packed<'GalleryLike'>> {
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<Packed<'GalleryLike'>[]> {
return (await Promise.allSettled(likes.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'GalleryLike'>>).value);
}
}

View File

@@ -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<Packed<'GalleryPost'>> {
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<Packed<'GalleryPost'>[]> {
return (await Promise.allSettled(posts.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'GalleryPost'>>).value);
}
}

View File

@@ -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<Packed<'Hashtag'>[]> {
return (await Promise.allSettled(hashtags.map(x => this.pack(x))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'Hashtag'>>).value);
}
}

View File

@@ -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<Packed<'FederationInstance'>[]> {
return (await Promise.allSettled(instances.map(x => this.pack(x))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'FederationInstance'>>).value);
}
}

View File

@@ -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<Packed<'InviteCode'>> {
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<Packed<'InviteCode'>[]> {
return (await Promise.allSettled(targets.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'InviteCode'>>).value);
}
}

View File

@@ -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<Packed<'ModerationLog'>> {
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<Packed<'ModerationLog'>[]> {
return (await Promise.allSettled(reports.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'ModerationLog'>>).value);
}
}

View File

@@ -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<Packed<'Muting'>> {
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<Packed<'Muting'>[]> {
return (await Promise.allSettled(mutings.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'Muting'>>).value);
}
}

View File

@@ -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<Note['fileIds'][number], Packed<'DriveFile'> | null>): Promise<Packed<'DriveFile'>[]> {
public async packAttachedFiles(
fileIds: Note['fileIds'],
packedFiles: Map<Note['fileIds'][number], Packed<'DriveFile'> | null>,
me: { id: User['id'] } | null | undefined,
): Promise<Packed<'DriveFile'>[]> {
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<Packed<'Note'>[]> {
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<Packed<'Note'>>).value);
}
@bindThis

View File

@@ -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<Packed<'NoteFavorite'>> {
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<Packed<'NoteFavorite'>[]> {
return (await Promise.allSettled(favorites.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'NoteFavorite'>>).value);
}
}

View File

@@ -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;
},

View File

@@ -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<Packed<'Notification'>[]> {
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<Packed<'Notification'>>).value);
}
}

View File

@@ -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<Packed<'Page'>> {
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<Packed<'Page'>[]> {
return (await Promise.allSettled(pages.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'Page'>>).value);
}
}

View File

@@ -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<Packed<'PageLike'>> {
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<Packed<'PageLike'>[]> {
return (await Promise.allSettled(likes.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'PageLike'>>).value);
}
}

View File

@@ -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<Packed<'RenoteMuting'>> {
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<Packed<'RenoteMuting'>[]> {
return (await Promise.allSettled(mutings.map(u => this.pack(u, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'RenoteMuting'>>).value);
}
}

View File

@@ -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<Packed<'Role'>> {
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<Packed<'Role'>[]> {
return (await Promise.allSettled(roles.map(x => this.pack(x, me))))
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<Packed<'Role'>>).value);
}
}

View File

@@ -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';

View File

@@ -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<ExpectsMe extends boolean | null = null, D extends boolean = false>(
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<D extends boolean = false>(
public async packMany<D extends boolean = false>(
users: (User['id'] | User)[],
me?: { id: User['id'] } | null | undefined,
me: { id: User['id'] } | null | undefined,
options?: {
detail?: D,
includeSecrets?: boolean,
},
): Promise<IsUserDetailed<D>[]> {
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<IsUserDetailed<D>>).value);
}
}

View File

@@ -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';