Merge ebf436d41d
into 00cbf9fe80
This commit is contained in:
commit
64b2c560f8
|
@ -30,6 +30,7 @@
|
|||
- Enhance: 起動前の疎通チェックで、DBとメイン以外のRedisの疎通確認も行うように
|
||||
(Based on https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/588)
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/715)
|
||||
- Enhance: 連合する必要のないプロフィール項目しか更新されなかった場合には連合先にUpdateアクティビティを発行しないように
|
||||
- fix(backend): フォロワーへのメッセージの絵文字をemojisに含めるように
|
||||
- Fix: Nested proxy requestsを検出した際にブロックするように
|
||||
[ghsa-gq5q-c77c-v236](https://github.com/misskey-dev/misskey/security/advisories/ghsa-gq5q-c77c-v236)
|
||||
|
|
|
@ -9,7 +9,8 @@ import { In } from 'typeorm';
|
|||
import * as mfm from 'mfm-js';
|
||||
import { DI } from '@/di-symbols.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 { MiBlocking } from '@/models/Blocking.js';
|
||||
import type { MiRelay } from '@/models/Relay.js';
|
||||
|
@ -251,7 +252,7 @@ export class ApRendererService {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
public renderKey(user: MiLocalUser, key: MiUserKeypair, postfix?: string): IKey {
|
||||
public renderKey(user: { id: MiUser['id'] }, key: MiUserKeypair, postfix?: string): IKey {
|
||||
return {
|
||||
id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`,
|
||||
type: 'Key',
|
||||
|
@ -449,14 +450,25 @@ export class ApRendererService {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
public async renderPerson(user: MiLocalUser) {
|
||||
public async renderPerson(user: MiLocalUserForApPersonRender) {
|
||||
const id = this.userEntityService.genLocalUserUri(user.id);
|
||||
const isSystem = user.username.includes('.');
|
||||
|
||||
/**
|
||||
* 【profile について】
|
||||
*
|
||||
* i/updateで虚無を連合するのを防止するための処理に伴い、
|
||||
* 使用できるプロパティを狭めることで、連合に使用するプロパティを増やした際に
|
||||
* miUserProfileKeysUsedForApPersonRenderを変更するのを
|
||||
* 忘れないようにするためにasを使っている。
|
||||
*
|
||||
* See https://github.com/misskey-dev/misskey/pull/14301
|
||||
*/
|
||||
|
||||
const [avatar, banner, profile] = await Promise.all([
|
||||
user.avatarId ? this.driveFilesRepository.findOneBy({ id: user.avatarId }) : 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 => ({
|
||||
|
|
8
packages/backend/src/misc/prelude/object.ts
Normal file
8
packages/backend/src/misc/prelude/object.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export function getObjKeys<T extends { [x: string]: unknown }>(obj: T): (keyof T)[] {
|
||||
return Object.keys(obj);
|
||||
}
|
|
@ -307,6 +307,24 @@ export type MiPartialRemoteUser = Partial<MiUser> & {
|
|||
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 passwordSchema = { type: 'string', minLength: 1 } as const;
|
||||
export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
|
||||
|
|
|
@ -295,3 +295,12 @@ export class MiUserProfile {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const miUserProfileKeysUsedForApPersonRender = [
|
||||
'fields',
|
||||
'description',
|
||||
'birthday',
|
||||
'location',
|
||||
] as const satisfies (keyof MiUserProfile)[];
|
||||
|
||||
export type MiUserProfileForApPersonRender = Pick<MiUserProfile, typeof miUserProfileKeysUsedForApPersonRender[number]>;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { isDeepStrictEqual } from 'node:util';
|
||||
import RE2 from 're2';
|
||||
import * as mfm from 'mfm-js';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
|
@ -11,6 +12,7 @@ import { JSDOM } from 'jsdom';
|
|||
import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
|
||||
import { extractHashtags } from '@/misc/extract-hashtags.js';
|
||||
import * as Acct from '@/misc/acct.js';
|
||||
import { getObjKeys } from '@/misc/prelude/object.js';
|
||||
import type { UsersRepository, DriveFilesRepository, MiMeta, UserProfilesRepository, PagesRepository } from '@/models/_.js';
|
||||
import type { MiLocalUser, MiUser } from '@/models/User.js';
|
||||
import { birthdaySchema, descriptionSchema, followedMessageSchema, locationSchema, nameSchema } from '@/models/User.js';
|
||||
|
@ -34,6 +36,8 @@ import type { Config } from '@/config.js';
|
|||
import { safeForSql } from '@/misc/safe-for-sql.js';
|
||||
import { AvatarDecorationService } from '@/core/AvatarDecorationService.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 { ApiError } from '../../error.js';
|
||||
|
||||
|
@ -507,15 +511,37 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
this.hashtagService.updateUsertags(user, tags);
|
||||
//#endregion
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await this.usersRepository.update(user.id, updates);
|
||||
//#region 変更されていないプロパティを削除
|
||||
const _updates = getObjKeys(updates).reduce<Partial<MiUser>>((acc, key) => {
|
||||
if (updates[key] !== undefined && !isDeepStrictEqual(updates[key], user[key])) {
|
||||
(acc[key] as MiUser[typeof key]) = updates[key];
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const _profileUpdates = getObjKeys(profileUpdates).reduce<Partial<MiUserProfile>>((acc, key) => {
|
||||
if (profileUpdates[key] !== undefined && !isDeepStrictEqual(profileUpdates[key], profile[key])) {
|
||||
(acc[key] as MiUserProfile[typeof key]) = profileUpdates[key];
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
//#endregion
|
||||
|
||||
if (Object.keys(_updates).length > 0) {
|
||||
await this.usersRepository.update(user.id, _updates);
|
||||
this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id });
|
||||
}
|
||||
|
||||
await this.userProfilesRepository.update(user.id, {
|
||||
...profileUpdates,
|
||||
verifiedLinks: [],
|
||||
});
|
||||
if (
|
||||
Object.keys(_profileUpdates).length > 0 ||
|
||||
profile.fields.filter(x => x.value.startsWith('https://')).length > 0 ||
|
||||
profile.verifiedLinks.length > 0
|
||||
) {
|
||||
await this.userProfilesRepository.update(user.id, {
|
||||
..._profileUpdates,
|
||||
verifiedLinks: [],
|
||||
});
|
||||
}
|
||||
|
||||
const iObj = await this.userEntityService.pack(user.id, user, {
|
||||
schema: 'MeDetailed',
|
||||
|
@ -534,8 +560,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
this.userFollowingService.acceptAllFollowRequests(user);
|
||||
}
|
||||
|
||||
// フォロワーにUpdateを配信
|
||||
this.accountUpdateService.publishToFollowers(user.id);
|
||||
// 連合する必要があるプロパティが変更されている場合はフォロワーにUpdateを配信
|
||||
if (
|
||||
miLocalUserKeysUsedForApPersonRender.some(k => getObjKeys(_updates).includes(k)) ||
|
||||
miUserProfileKeysUsedForApPersonRender.some(k => getObjKeys(_profileUpdates).includes(k))
|
||||
) {
|
||||
this.accountUpdateService.publishToFollowers(user.id);
|
||||
}
|
||||
|
||||
const urls = updatedProfile.fields.filter(x => x.value.startsWith('https://'));
|
||||
for (const url of urls) {
|
||||
|
|
Loading…
Reference in a new issue