enhance(backend): 連合する必要のないプロフィール項目しか更新されなかった場合にはUpdateアクティビティを発行しないように

This commit is contained in:
kakkokari-gtyih 2024-07-25 16:22:54 +09:00
parent 32651aba67
commit 8af13a6502
5 changed files with 50 additions and 6 deletions

View File

@ -9,7 +9,8 @@ import { In } from 'typeorm';
import * as mfm from 'mfm-js'; import * as mfm from 'mfm-js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import type { MiPartialLocalUser, MiLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js'; import type { MiPartialLocalUser, MiLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser, MiLocalUserForApPersonRender } from '@/models/User.js';
import type { MiUserProfileForApPersonRender } from '@/models/UserProfile.js';
import type { IMentionedRemoteUsers, MiNote } from '@/models/Note.js'; import type { IMentionedRemoteUsers, MiNote } from '@/models/Note.js';
import type { MiBlocking } from '@/models/Blocking.js'; import type { MiBlocking } from '@/models/Blocking.js';
import type { MiRelay } from '@/models/Relay.js'; import type { MiRelay } from '@/models/Relay.js';
@ -251,7 +252,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderKey(user: MiLocalUser, key: MiUserKeypair, postfix?: string): IKey { public renderKey(user: { id: MiUser['id'] }, key: MiUserKeypair, postfix?: string): IKey {
return { return {
id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`, id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`,
type: 'Key', type: 'Key',
@ -449,14 +450,14 @@ export class ApRendererService {
} }
@bindThis @bindThis
public async renderPerson(user: MiLocalUser) { public async renderPerson(user: MiLocalUserForApPersonRender) {
const id = this.userEntityService.genLocalUserUri(user.id); const id = this.userEntityService.genLocalUserUri(user.id);
const isSystem = user.username.includes('.'); const isSystem = user.username.includes('.');
const [avatar, banner, profile] = await Promise.all([ const [avatar, banner, profile] = await Promise.all([
user.avatarId ? this.driveFilesRepository.findOneBy({ id: user.avatarId }) : undefined, user.avatarId ? this.driveFilesRepository.findOneBy({ id: user.avatarId }) : undefined,
user.bannerId ? this.driveFilesRepository.findOneBy({ id: user.bannerId }) : undefined, user.bannerId ? this.driveFilesRepository.findOneBy({ id: user.bannerId }) : undefined,
this.userProfilesRepository.findOneByOrFail({ userId: user.id }), (this.userProfilesRepository.findOneByOrFail({ userId: user.id }) as Promise<MiUserProfileForApPersonRender>),
]); ]);
const attachment = profile.fields.map(field => ({ const attachment = profile.fields.map(field => ({

View File

@ -0,0 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export function keys<T extends { [x: string]: unknown }>(obj: T): (keyof T)[] {
return Object.keys(obj);
}

View File

@ -285,6 +285,24 @@ export type MiPartialRemoteUser = Partial<MiUser> & {
uri: string; uri: string;
} }
export const miLocalUserKeysUsedForApPersonRender = [
'id',
'username',
'avatarId',
'bannerId',
'emojis',
'tags',
'isBot',
'isCat',
'name',
'isLocked',
'isExplorable',
'movedToUri',
'alsoKnownAs',
] as const satisfies (keyof MiLocalUser)[];
export type MiLocalUserForApPersonRender = Pick<MiLocalUser, typeof miLocalUserKeysUsedForApPersonRender[number]>;
export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const; export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const;
export const passwordSchema = { type: 'string', minLength: 1 } as const; export const passwordSchema = { type: 'string', minLength: 1 } as const;
export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;

View File

@ -287,3 +287,12 @@ export class MiUserProfile {
} }
} }
} }
export const miUserProfileKeysUsedForApPersonRender = [
'fields',
'description',
'birthday',
'location',
] as const satisfies (keyof MiUserProfile)[];
export type MiUserProfileForApPersonRender = Pick<MiUserProfile, typeof miUserProfileKeysUsedForApPersonRender[number]>;

View File

@ -11,6 +11,7 @@ import { JSDOM } from 'jsdom';
import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
import { extractHashtags } from '@/misc/extract-hashtags.js'; import { extractHashtags } from '@/misc/extract-hashtags.js';
import * as Acct from '@/misc/acct.js'; import * as Acct from '@/misc/acct.js';
import { keys } from '@/misc/prelude/object.js';
import type { UsersRepository, DriveFilesRepository, UserProfilesRepository, PagesRepository } from '@/models/_.js'; import type { UsersRepository, DriveFilesRepository, UserProfilesRepository, PagesRepository } from '@/models/_.js';
import type { MiLocalUser, MiUser } from '@/models/User.js'; import type { MiLocalUser, MiUser } from '@/models/User.js';
import { birthdaySchema, descriptionSchema, locationSchema, nameSchema } from '@/models/User.js'; import { birthdaySchema, descriptionSchema, locationSchema, nameSchema } from '@/models/User.js';
@ -34,6 +35,8 @@ import type { Config } from '@/config.js';
import { safeForSql } from '@/misc/safe-for-sql.js'; import { safeForSql } from '@/misc/safe-for-sql.js';
import { AvatarDecorationService } from '@/core/AvatarDecorationService.js'; import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
import { notificationRecieveConfig } from '@/models/json-schema/user.js'; import { notificationRecieveConfig } from '@/models/json-schema/user.js';
import { miLocalUserKeysUsedForApPersonRender } from '@/models/User.js';
import { miUserProfileKeysUsedForApPersonRender } from '@/models/UserProfile.js';
import { ApiLoggerService } from '../../ApiLoggerService.js'; import { ApiLoggerService } from '../../ApiLoggerService.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
@ -501,8 +504,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
this.userFollowingService.acceptAllFollowRequests(user); this.userFollowingService.acceptAllFollowRequests(user);
} }
// フォロワーにUpdateを配信 // 連合する必要があるプロパティが変更されている場合はフォロワーにUpdateを配信
if (
miLocalUserKeysUsedForApPersonRender.some(k => keys(updates).includes(k)) ||
miUserProfileKeysUsedForApPersonRender.some(k => keys(profileUpdates).includes(k))
) {
this.accountUpdateService.publishToFollowers(user.id); this.accountUpdateService.publishToFollowers(user.id);
}
const urls = updatedProfile.fields.filter(x => x.value.startsWith('https://')); const urls = updatedProfile.fields.filter(x => x.value.startsWith('https://'));
for (const url of urls) { for (const url of urls) {