Merge branch 'develop' into feature/default-hashtag-timeline
This commit is contained in:
commit
1c075f6068
289 changed files with 3711 additions and 1906 deletions
|
|
@ -96,7 +96,7 @@ export class AccountMoveService {
|
|||
await this.apDeliverManagerService.deliverToFollowers(src, moveAct);
|
||||
|
||||
// Publish meUpdated event
|
||||
const iObj = await this.userEntityService.pack<true, true>(src.id, src, { detail: true, includeSecrets: true });
|
||||
const iObj = await this.userEntityService.pack(src.id, src, { schema: 'MeDetailed', includeSecrets: true });
|
||||
this.globalEventService.publishMainStream(src.id, 'meUpdated', iObj);
|
||||
|
||||
// Unfollow after 24 hours
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ export class EmailService {
|
|||
public async sendEmail(to: string, subject: string, html: string, text: string) {
|
||||
const meta = await this.metaService.fetch(true);
|
||||
|
||||
if (!meta.enableEmail) return;
|
||||
|
||||
const iconUrl = `${this.config.url}/static-assets/mi-white.png`;
|
||||
const emailSettingUrl = `${this.config.url}/settings/email`;
|
||||
|
||||
|
|
|
|||
|
|
@ -54,15 +54,15 @@ export interface MainEventTypes {
|
|||
reply: Packed<'Note'>;
|
||||
renote: Packed<'Note'>;
|
||||
follow: Packed<'UserDetailedNotMe'>;
|
||||
followed: Packed<'User'>;
|
||||
unfollow: Packed<'User'>;
|
||||
meUpdated: Packed<'User'>;
|
||||
followed: Packed<'UserLite'>;
|
||||
unfollow: Packed<'UserDetailedNotMe'>;
|
||||
meUpdated: Packed<'MeDetailed'>;
|
||||
pageEvent: {
|
||||
pageId: MiPage['id'];
|
||||
event: string;
|
||||
var: any;
|
||||
userId: MiUser['id'];
|
||||
user: Packed<'User'>;
|
||||
user: Packed<'UserDetailed'>;
|
||||
};
|
||||
urlUploadFinished: {
|
||||
marker?: string | null;
|
||||
|
|
@ -92,7 +92,7 @@ export interface MainEventTypes {
|
|||
};
|
||||
driveFileCreated: Packed<'DriveFile'>;
|
||||
readAntenna: MiAntenna;
|
||||
receiveFollowRequest: Packed<'User'>;
|
||||
receiveFollowRequest: Packed<'UserLite'>;
|
||||
announcementCreated: {
|
||||
announcement: Packed<'Announcement'>;
|
||||
};
|
||||
|
|
@ -140,8 +140,8 @@ type NoteStreamEventTypes = {
|
|||
};
|
||||
|
||||
export interface UserListEventTypes {
|
||||
userAdded: Packed<'User'>;
|
||||
userRemoved: Packed<'User'>;
|
||||
userAdded: Packed<'UserLite'>;
|
||||
userRemoved: Packed<'UserLite'>;
|
||||
}
|
||||
|
||||
export interface AntennaEventTypes {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { IsNull } from 'typeorm';
|
||||
import { IsNull, Not } from 'typeorm';
|
||||
import type { MiLocalUser } from '@/models/User.js';
|
||||
import type { UsersRepository } from '@/models/_.js';
|
||||
import { MemorySingleCache } from '@/misc/cache.js';
|
||||
|
|
@ -27,6 +27,14 @@ export class InstanceActorService {
|
|||
this.cache = new MemorySingleCache<MiLocalUser>(Infinity);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async realLocalUsersPresent(): Promise<boolean> {
|
||||
return await this.usersRepository.existsBy({
|
||||
host: IsNull(),
|
||||
username: Not(ACTOR_USERNAME),
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getInstanceActor(): Promise<MiLocalUser> {
|
||||
const cached = this.cache.get();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { MiUserKeypair } from '@/models/UserKeypair.js';
|
|||
import { MiUsedUsername } from '@/models/UsedUsername.js';
|
||||
import generateUserToken from '@/misc/generate-native-user-token.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { InstanceActorService } from '@/core/InstanceActorService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import UsersChart from '@/core/chart/charts/users.js';
|
||||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
|
|
@ -37,6 +38,7 @@ export class SignupService {
|
|||
private userEntityService: UserEntityService,
|
||||
private idService: IdService,
|
||||
private metaService: MetaService,
|
||||
private instanceActorService: InstanceActorService,
|
||||
private usersChart: UsersChart,
|
||||
) {
|
||||
}
|
||||
|
|
@ -81,7 +83,7 @@ export class SignupService {
|
|||
throw new Error('USED_USERNAME');
|
||||
}
|
||||
|
||||
const isTheFirstUser = (await this.usersRepository.countBy({ host: IsNull() })) === 0;
|
||||
const isTheFirstUser = !await this.instanceActorService.realLocalUsersPresent();
|
||||
|
||||
if (!opts.ignorePreservedUsernames && !isTheFirstUser) {
|
||||
const instance = await this.metaService.fetch(true);
|
||||
|
|
|
|||
|
|
@ -109,13 +109,13 @@ export class UserBlockingService implements OnModuleInit {
|
|||
|
||||
if (this.userEntityService.isLocalUser(followee)) {
|
||||
this.userEntityService.pack(followee, followee, {
|
||||
detail: true,
|
||||
schema: 'MeDetailed',
|
||||
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
|
||||
}
|
||||
|
||||
if (this.userEntityService.isLocalUser(follower) && !silent) {
|
||||
this.userEntityService.pack(followee, follower, {
|
||||
detail: true,
|
||||
schema: 'UserDetailedNotMe',
|
||||
}).then(async packed => {
|
||||
this.globalEventService.publishMainStream(follower.id, 'unfollow', packed);
|
||||
|
||||
|
|
|
|||
|
|
@ -293,9 +293,9 @@ export class UserFollowingService implements OnModuleInit {
|
|||
if (this.userEntityService.isLocalUser(follower) && !silent) {
|
||||
// Publish follow event
|
||||
this.userEntityService.pack(followee.id, follower, {
|
||||
detail: true,
|
||||
schema: 'UserDetailedNotMe',
|
||||
}).then(async packed => {
|
||||
this.globalEventService.publishMainStream(follower.id, 'follow', packed as Packed<'UserDetailedNotMe'>);
|
||||
this.globalEventService.publishMainStream(follower.id, 'follow', packed);
|
||||
|
||||
const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('follow'));
|
||||
for (const webhook of webhooks) {
|
||||
|
|
@ -360,7 +360,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||
if (!silent && this.userEntityService.isLocalUser(follower)) {
|
||||
// Publish unfollow event
|
||||
this.userEntityService.pack(followee.id, follower, {
|
||||
detail: true,
|
||||
schema: 'UserDetailedNotMe',
|
||||
}).then(async packed => {
|
||||
this.globalEventService.publishMainStream(follower.id, 'unfollow', packed);
|
||||
|
||||
|
|
@ -500,7 +500,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||
this.userEntityService.pack(follower.id, followee).then(packed => this.globalEventService.publishMainStream(followee.id, 'receiveFollowRequest', packed));
|
||||
|
||||
this.userEntityService.pack(followee.id, followee, {
|
||||
detail: true,
|
||||
schema: 'MeDetailed',
|
||||
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
|
||||
|
||||
// 通知を作成
|
||||
|
|
@ -548,7 +548,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||
});
|
||||
|
||||
this.userEntityService.pack(followee.id, followee, {
|
||||
detail: true,
|
||||
schema: 'MeDetailed',
|
||||
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
|
||||
}
|
||||
|
||||
|
|
@ -576,7 +576,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||
}
|
||||
|
||||
this.userEntityService.pack(followee.id, followee, {
|
||||
detail: true,
|
||||
schema: 'MeDetailed',
|
||||
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
|
||||
}
|
||||
|
||||
|
|
@ -696,7 +696,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||
@bindThis
|
||||
private async publishUnfollow(followee: Both, follower: Local): Promise<void> {
|
||||
const packedFollowee = await this.userEntityService.pack(followee.id, follower, {
|
||||
detail: true,
|
||||
schema: 'UserDetailedNotMe',
|
||||
});
|
||||
|
||||
this.globalEventService.publishMainStream(follower.id, 'unfollow', packedFollowee);
|
||||
|
|
|
|||
|
|
@ -225,20 +225,37 @@ export class ApPersonService implements OnModuleInit {
|
|||
return null;
|
||||
}
|
||||
|
||||
private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any): Promise<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>> {
|
||||
private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any): Promise<Partial<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>>> {
|
||||
if (user == null) throw new Error('failed to create user: user is null');
|
||||
|
||||
const [avatar, banner] = await Promise.all([icon, image].map(img => {
|
||||
if (img == null) return null;
|
||||
if (user == null) throw new Error('failed to create user: user is null');
|
||||
// if we have an explicitly missing image, return an
|
||||
// explicitly-null set of values
|
||||
if ((img == null) || (typeof img === 'object' && img.url == null)) {
|
||||
return { id: null, url: null, blurhash: null };
|
||||
}
|
||||
|
||||
return this.apImageService.resolveImage(user, img).catch(() => null);
|
||||
}));
|
||||
|
||||
/*
|
||||
we don't want to return nulls on errors! if the database fields
|
||||
are already null, nothing changes; if the database has old
|
||||
values, we should keep those. The exception is if the remote has
|
||||
actually removed the images: in that case, the block above
|
||||
returns the special {id:null}&c value, and we return those
|
||||
*/
|
||||
return {
|
||||
avatarId: avatar?.id ?? null,
|
||||
bannerId: banner?.id ?? null,
|
||||
avatarUrl: avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null,
|
||||
bannerUrl: banner ? this.driveFileEntityService.getPublicUrl(banner) : null,
|
||||
avatarBlurhash: avatar?.blurhash ?? null,
|
||||
bannerBlurhash: banner?.blurhash ?? null,
|
||||
...( avatar ? {
|
||||
avatarId: avatar.id,
|
||||
avatarUrl: avatar.url ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null,
|
||||
avatarBlurhash: avatar.blurhash,
|
||||
} : {}),
|
||||
...( banner ? {
|
||||
bannerId: banner.id,
|
||||
bannerUrl: banner.url ? this.driveFileEntityService.getPublicUrl(banner) : null,
|
||||
bannerBlurhash: banner.blurhash,
|
||||
} : {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,29 @@ type ToJsonSchema<S> = {
|
|||
};
|
||||
|
||||
export function getJsonSchema<S extends Schema>(schema: S): ToJsonSchema<Unflatten<ChartResult<S>>> {
|
||||
const unflatten = (str: string, parent: Record<string, any>) => {
|
||||
const keys = str.split('.');
|
||||
const key = keys.shift();
|
||||
const nextKey = keys[0];
|
||||
|
||||
if (key == null) return;
|
||||
|
||||
if (parent.properties[key] == null) {
|
||||
parent.properties[key] = nextKey ? {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
required: [],
|
||||
} : {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'number',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (nextKey) unflatten(keys.join('.'), parent.properties[key] as Record<string, any>);
|
||||
};
|
||||
|
||||
const jsonSchema = {
|
||||
type: 'object',
|
||||
properties: {} as Record<string, unknown>,
|
||||
|
|
@ -101,10 +124,7 @@ export function getJsonSchema<S extends Schema>(schema: S): ToJsonSchema<Unflatt
|
|||
};
|
||||
|
||||
for (const k in schema) {
|
||||
jsonSchema.properties[k] = {
|
||||
type: 'array',
|
||||
items: { type: 'number' },
|
||||
};
|
||||
unflatten(k, jsonSchema);
|
||||
}
|
||||
|
||||
return jsonSchema as ToJsonSchema<Unflatten<ChartResult<S>>>;
|
||||
|
|
|
|||
|
|
@ -38,13 +38,13 @@ export class AbuseUserReportEntityService {
|
|||
targetUserId: report.targetUserId,
|
||||
assigneeId: report.assigneeId,
|
||||
reporter: this.userEntityService.pack(report.reporter ?? report.reporterId, null, {
|
||||
detail: true,
|
||||
schema: 'UserDetailedNotMe',
|
||||
}),
|
||||
targetUser: this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, {
|
||||
detail: true,
|
||||
schema: 'UserDetailedNotMe',
|
||||
}),
|
||||
assignee: report.assigneeId ? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, {
|
||||
detail: true,
|
||||
schema: 'UserDetailedNotMe',
|
||||
}) : null,
|
||||
forwarded: report.forwarded,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export class BlockingEntityService {
|
|||
createdAt: this.idService.parse(blocking.id).date.toISOString(),
|
||||
blockeeId: blocking.blockeeId,
|
||||
blockee: this.userEntityService.pack(blocking.blockeeId, me, {
|
||||
detail: true,
|
||||
schema: 'UserDetailedNotMe',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export class FlashEntityService {
|
|||
createdAt: this.idService.parse(flash.id).date.toISOString(),
|
||||
updatedAt: flash.updatedAt.toISOString(),
|
||||
userId: flash.userId,
|
||||
user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { detail: true } すると無限ループするので注意
|
||||
user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
|
||||
title: flash.title,
|
||||
summary: flash.summary,
|
||||
script: flash.script,
|
||||
|
|
|
|||
|
|
@ -89,10 +89,10 @@ export class FollowingEntityService {
|
|||
followeeId: following.followeeId,
|
||||
followerId: following.followerId,
|
||||
followee: opts.populateFollowee ? this.userEntityService.pack(following.followee ?? following.followeeId, me, {
|
||||
detail: true,
|
||||
schema: 'UserDetailedNotMe',
|
||||
}) : undefined,
|
||||
follower: opts.populateFollower ? this.userEntityService.pack(following.follower ?? following.followerId, me, {
|
||||
detail: true,
|
||||
schema: 'UserDetailedNotMe',
|
||||
}) : undefined,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export class ModerationLogEntityService {
|
|||
info: log.info,
|
||||
userId: log.userId,
|
||||
user: this.userEntityService.pack(log.user ?? log.userId, null, {
|
||||
detail: true,
|
||||
schema: 'UserDetailedNotMe',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export class MutingEntityService {
|
|||
expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null,
|
||||
muteeId: muting.muteeId,
|
||||
mutee: this.userEntityService.pack(muting.muteeId, me, {
|
||||
detail: true,
|
||||
schema: 'UserDetailedNotMe',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ export class NoteEntityService implements OnModuleInit {
|
|||
|
||||
return {
|
||||
multiple: poll.multiple,
|
||||
expiresAt: poll.expiresAt,
|
||||
expiresAt: poll.expiresAt?.toISOString() ?? null,
|
||||
choices,
|
||||
};
|
||||
}
|
||||
|
|
@ -324,9 +324,7 @@ export class NoteEntityService implements OnModuleInit {
|
|||
id: note.id,
|
||||
createdAt: this.idService.parse(note.id).date.toISOString(),
|
||||
userId: note.userId,
|
||||
user: this.userEntityService.pack(note.user ?? note.userId, me, {
|
||||
detail: false,
|
||||
}),
|
||||
user: this.userEntityService.pack(note.user ?? note.userId, me),
|
||||
text: text,
|
||||
cw: note.cw,
|
||||
visibility: note.visibility,
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
},
|
||||
hint?: {
|
||||
packedNotes: Map<MiNote['id'], Packed<'Note'>>;
|
||||
packedUsers: Map<MiUser['id'], Packed<'User'>>;
|
||||
packedUsers: Map<MiUser['id'], Packed<'UserLite'>>;
|
||||
},
|
||||
): Promise<Packed<'Notification'>> {
|
||||
const notification = src;
|
||||
|
|
@ -76,9 +76,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
const userIfNeed = 'notifierId' in notification ? (
|
||||
hint?.packedUsers != null
|
||||
? hint.packedUsers.get(notification.notifierId)
|
||||
: this.userEntityService.pack(notification.notifierId, { id: meId }, {
|
||||
detail: false,
|
||||
})
|
||||
: this.userEntityService.pack(notification.notifierId, { id: meId })
|
||||
) : undefined;
|
||||
const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined;
|
||||
|
||||
|
|
@ -131,9 +129,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
const users = userIds.length > 0 ? await this.usersRepository.find({
|
||||
where: { id: In(userIds) },
|
||||
}) : [];
|
||||
const packedUsersArray = await this.userEntityService.packMany(users, { id: meId }, {
|
||||
detail: false,
|
||||
});
|
||||
const packedUsersArray = await this.userEntityService.packMany(users, { id: meId });
|
||||
const packedUsers = new Map(packedUsersArray.map(p => [p.id, p]));
|
||||
|
||||
// 既に解決されたフォローリクエストの通知を除外
|
||||
|
|
@ -161,7 +157,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
},
|
||||
hint?: {
|
||||
packedNotes: Map<MiNote['id'], Packed<'Note'>>;
|
||||
packedUsers: Map<MiUser['id'], Packed<'User'>>;
|
||||
packedUsers: Map<MiUser['id'], Packed<'UserLite'>>;
|
||||
},
|
||||
): Promise<Packed<'Notification'>> {
|
||||
const notification = src;
|
||||
|
|
@ -175,18 +171,14 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
const userIfNeed = 'notifierId' in notification ? (
|
||||
hint?.packedUsers != null
|
||||
? hint.packedUsers.get(notification.notifierId)
|
||||
: this.userEntityService.pack(notification.notifierId, { id: meId }, {
|
||||
detail: false,
|
||||
})
|
||||
: this.userEntityService.pack(notification.notifierId, { id: meId })
|
||||
) : undefined;
|
||||
|
||||
if (notification.type === 'reaction:grouped') {
|
||||
const reactions = await Promise.all(notification.reactions.map(async reaction => {
|
||||
const user = hint?.packedUsers != null
|
||||
? hint.packedUsers.get(reaction.userId)!
|
||||
: await this.userEntityService.pack(reaction.userId, { id: meId }, {
|
||||
detail: false,
|
||||
});
|
||||
: await this.userEntityService.pack(reaction.userId, { id: meId });
|
||||
return {
|
||||
user,
|
||||
reaction: reaction.reaction,
|
||||
|
|
@ -206,9 +198,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
return packedUser;
|
||||
}
|
||||
|
||||
return this.userEntityService.pack(userId, { id: meId }, {
|
||||
detail: false,
|
||||
});
|
||||
return this.userEntityService.pack(userId, { id: meId });
|
||||
}));
|
||||
return await awaitAll({
|
||||
id: notification.id,
|
||||
|
|
@ -275,9 +265,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
const users = userIds.length > 0 ? await this.usersRepository.find({
|
||||
where: { id: In(userIds) },
|
||||
}) : [];
|
||||
const packedUsersArray = await this.userEntityService.packMany(users, { id: meId }, {
|
||||
detail: false,
|
||||
});
|
||||
const packedUsersArray = await this.userEntityService.packMany(users, { id: meId });
|
||||
const packedUsers = new Map(packedUsersArray.map(p => [p.id, p]));
|
||||
|
||||
// 既に解決されたフォローリクエストの通知を除外
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ export class PageEntityService {
|
|||
createdAt: this.idService.parse(page.id).date.toISOString(),
|
||||
updatedAt: page.updatedAt.toISOString(),
|
||||
userId: page.userId,
|
||||
user: this.userEntityService.pack(page.user ?? page.userId, me), // { detail: true } すると無限ループするので注意
|
||||
user: this.userEntityService.pack(page.user ?? page.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
|
||||
content: page.content,
|
||||
variables: page.variables,
|
||||
title: page.title,
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export class RenoteMutingEntityService {
|
|||
createdAt: this.idService.parse(muting.id).date.toISOString(),
|
||||
muteeId: muting.muteeId,
|
||||
mutee: this.userEntityService.pack(muting.muteeId, me, {
|
||||
detail: true,
|
||||
schema: 'UserDetailedNotMe',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,14 +30,6 @@ import type { NoteEntityService } from './NoteEntityService.js';
|
|||
import type { DriveFileEntityService } from './DriveFileEntityService.js';
|
||||
import type { PageEntityService } from './PageEntityService.js';
|
||||
|
||||
type IsUserDetailed<Detailed extends boolean> = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>;
|
||||
type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends boolean> =
|
||||
Detailed extends true ?
|
||||
ExpectsMe extends true ? Packed<'MeDetailed'> :
|
||||
ExpectsMe extends false ? Packed<'UserDetailedNotMe'> :
|
||||
Packed<'UserDetailed'> :
|
||||
Packed<'UserLite'>;
|
||||
|
||||
const Ajv = _Ajv.default;
|
||||
const ajv = new Ajv();
|
||||
|
||||
|
|
@ -303,33 +295,34 @@ export class UserEntityService implements OnModuleInit {
|
|||
return `${this.config.url}/users/${userId}`;
|
||||
}
|
||||
|
||||
public async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>(
|
||||
public async pack<S extends 'MeDetailed' | 'UserDetailedNotMe' | 'UserDetailed' | 'UserLite' = 'UserLite'>(
|
||||
src: MiUser['id'] | MiUser,
|
||||
me?: { id: MiUser['id']; } | null | undefined,
|
||||
options?: {
|
||||
detail?: D,
|
||||
schema?: S,
|
||||
includeSecrets?: boolean,
|
||||
userProfile?: MiUserProfile,
|
||||
},
|
||||
): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> {
|
||||
): Promise<Packed<S>> {
|
||||
const opts = Object.assign({
|
||||
detail: false,
|
||||
schema: 'UserLite',
|
||||
includeSecrets: false,
|
||||
}, options);
|
||||
|
||||
const user = typeof src === 'object' ? src : await this.usersRepository.findOneByOrFail({ id: src });
|
||||
|
||||
const isDetailed = opts.schema !== 'UserLite';
|
||||
const meId = me ? me.id : null;
|
||||
const isMe = meId === user.id;
|
||||
const iAmModerator = me ? await this.roleService.isModerator(me as MiUser) : false;
|
||||
|
||||
const relation = meId && !isMe && opts.detail ? await this.getRelation(meId, user.id) : null;
|
||||
const pins = opts.detail ? await this.userNotePiningsRepository.createQueryBuilder('pin')
|
||||
const relation = meId && !isMe && isDetailed ? await this.getRelation(meId, user.id) : null;
|
||||
const pins = isDetailed ? await this.userNotePiningsRepository.createQueryBuilder('pin')
|
||||
.where('pin.userId = :userId', { userId: user.id })
|
||||
.innerJoinAndSelect('pin.note', 'note')
|
||||
.orderBy('pin.id', 'DESC')
|
||||
.getMany() : [];
|
||||
const profile = opts.detail ? (opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id })) : null;
|
||||
const profile = isDetailed ? (opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id })) : null;
|
||||
|
||||
const followingCount = profile == null ? null :
|
||||
(profile.followingVisibility === 'public') || isMe ? user.followingCount :
|
||||
|
|
@ -341,15 +334,15 @@ export class UserEntityService implements OnModuleInit {
|
|||
(profile.followersVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount :
|
||||
null;
|
||||
|
||||
const isModerator = isMe && opts.detail ? this.roleService.isModerator(user) : null;
|
||||
const isAdmin = isMe && opts.detail ? this.roleService.isAdministrator(user) : null;
|
||||
const unreadAnnouncements = isMe && opts.detail ?
|
||||
const isModerator = isMe && isDetailed ? this.roleService.isModerator(user) : null;
|
||||
const isAdmin = isMe && isDetailed ? this.roleService.isAdministrator(user) : null;
|
||||
const unreadAnnouncements = isMe && isDetailed ?
|
||||
(await this.announcementService.getUnreadAnnouncements(user)).map((announcement) => ({
|
||||
createdAt: this.idService.parse(announcement.id).date.toISOString(),
|
||||
...announcement,
|
||||
})) : null;
|
||||
|
||||
const notificationsInfo = isMe && opts.detail ? await this.getNotificationsInfo(user.id) : null;
|
||||
const notificationsInfo = isMe && isDetailed ? await this.getNotificationsInfo(user.id) : null;
|
||||
|
||||
const packed = {
|
||||
id: user.id,
|
||||
|
|
@ -385,7 +378,7 @@ export class UserEntityService implements OnModuleInit {
|
|||
displayOrder: r.displayOrder,
|
||||
}))) : undefined,
|
||||
|
||||
...(opts.detail ? {
|
||||
...(isDetailed ? {
|
||||
url: profile!.url,
|
||||
uri: user.uri,
|
||||
movedTo: user.movedToUri ? this.apPersonService.resolvePerson(user.movedToUri).then(user => user.id).catch(() => null) : null,
|
||||
|
|
@ -416,7 +409,7 @@ export class UserEntityService implements OnModuleInit {
|
|||
}),
|
||||
pinnedPageId: profile!.pinnedPageId,
|
||||
pinnedPage: profile!.pinnedPageId ? this.pageEntityService.pack(profile!.pinnedPageId, me) : null,
|
||||
publicReactions: profile!.publicReactions,
|
||||
publicReactions: this.isLocalUser(user) ? profile!.publicReactions : false, // https://github.com/misskey-dev/misskey/issues/12964
|
||||
followersVisibility: profile!.followersVisibility,
|
||||
followingVisibility: profile!.followingVisibility,
|
||||
twoFactorEnabled: profile!.twoFactorEnabled,
|
||||
|
|
@ -443,7 +436,7 @@ export class UserEntityService implements OnModuleInit {
|
|||
moderationNote: iAmModerator ? (profile!.moderationNote ?? '') : undefined,
|
||||
} : {}),
|
||||
|
||||
...(opts.detail && isMe ? {
|
||||
...(isDetailed && isMe ? {
|
||||
avatarId: user.avatarId,
|
||||
bannerId: user.bannerId,
|
||||
isModerator: isModerator,
|
||||
|
|
@ -515,19 +508,19 @@ export class UserEntityService implements OnModuleInit {
|
|||
notify: relation.following?.notify ?? 'none',
|
||||
withReplies: relation.following?.withReplies ?? false,
|
||||
} : {}),
|
||||
} as Promiseable<Packed<'User'>> as Promiseable<IsMeAndIsUserDetailed<ExpectsMe, D>>;
|
||||
} as Promiseable<Packed<S>>;
|
||||
|
||||
return await awaitAll(packed);
|
||||
}
|
||||
|
||||
public packMany<D extends boolean = false>(
|
||||
public packMany<S extends 'MeDetailed' | 'UserDetailedNotMe' | 'UserDetailed' | 'UserLite' = 'UserLite'>(
|
||||
users: (MiUser['id'] | MiUser)[],
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
options?: {
|
||||
detail?: D,
|
||||
schema?: S,
|
||||
includeSecrets?: boolean,
|
||||
},
|
||||
): Promise<IsUserDetailed<D>[]> {
|
||||
): Promise<Packed<S>[]> {
|
||||
return Promise.all(users.map(u => this.pack(u, me, options)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue