wip
This commit is contained in:
parent
dde1230235
commit
271ca3f5c9
|
@ -10058,6 +10058,10 @@ export interface Locale extends ILocale {
|
||||||
* ギャラリーの投稿を削除
|
* ギャラリーの投稿を削除
|
||||||
*/
|
*/
|
||||||
"deleteGalleryPost": string;
|
"deleteGalleryPost": string;
|
||||||
|
/**
|
||||||
|
* プロキシアカウントの説明を更新
|
||||||
|
*/
|
||||||
|
"updateProxyAccountDescription": string;
|
||||||
};
|
};
|
||||||
"_fileViewer": {
|
"_fileViewer": {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2664,6 +2664,7 @@ _moderationLogTypes:
|
||||||
deletePage: "ページを削除"
|
deletePage: "ページを削除"
|
||||||
deleteFlash: "Playを削除"
|
deleteFlash: "Playを削除"
|
||||||
deleteGalleryPost: "ギャラリーの投稿を削除"
|
deleteGalleryPost: "ギャラリーの投稿を削除"
|
||||||
|
updateProxyAccountDescription: "プロキシアカウントの説明を更新"
|
||||||
|
|
||||||
_fileViewer:
|
_fileViewer:
|
||||||
title: "ファイルの詳細"
|
title: "ファイルの詳細"
|
||||||
|
|
|
@ -133,7 +133,7 @@ const $meta: Provider = {
|
||||||
for (const key in body.after) {
|
for (const key in body.after) {
|
||||||
(meta as any)[key] = (body.after as any)[key];
|
(meta as any)[key] = (body.after as any)[key];
|
||||||
}
|
}
|
||||||
meta.proxyAccount = null; // joinなカラムは通常取ってこないので
|
meta.rootUser = null; // joinなカラムは通常取ってこないので
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -20,10 +20,10 @@ import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
|
||||||
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
|
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
|
||||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { ProxyAccountService } from '@/core/ProxyAccountService.js';
|
|
||||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||||
import InstanceChart from '@/core/chart/charts/instance.js';
|
import InstanceChart from '@/core/chart/charts/instance.js';
|
||||||
import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
|
import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
|
||||||
|
import { SystemAccountService } from '@/core/SystemAccountService.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AccountMoveService {
|
export class AccountMoveService {
|
||||||
|
@ -55,12 +55,12 @@ export class AccountMoveService {
|
||||||
private apRendererService: ApRendererService,
|
private apRendererService: ApRendererService,
|
||||||
private apDeliverManagerService: ApDeliverManagerService,
|
private apDeliverManagerService: ApDeliverManagerService,
|
||||||
private globalEventService: GlobalEventService,
|
private globalEventService: GlobalEventService,
|
||||||
private proxyAccountService: ProxyAccountService,
|
|
||||||
private perUserFollowingChart: PerUserFollowingChart,
|
private perUserFollowingChart: PerUserFollowingChart,
|
||||||
private federatedInstanceService: FederatedInstanceService,
|
private federatedInstanceService: FederatedInstanceService,
|
||||||
private instanceChart: InstanceChart,
|
private instanceChart: InstanceChart,
|
||||||
private relayService: RelayService,
|
private relayService: RelayService,
|
||||||
private queueService: QueueService,
|
private queueService: QueueService,
|
||||||
|
private systemAccountService: SystemAccountService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,11 +126,11 @@ export class AccountMoveService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// follow the new account
|
// follow the new account
|
||||||
const proxy = await this.proxyAccountService.fetch();
|
const proxy = await this.systemAccountService.fetch('proxy');
|
||||||
const followings = await this.followingsRepository.findBy({
|
const followings = await this.followingsRepository.findBy({
|
||||||
followeeId: src.id,
|
followeeId: src.id,
|
||||||
followerHost: IsNull(), // follower is local
|
followerHost: IsNull(), // follower is local
|
||||||
followerId: proxy ? Not(proxy.id) : undefined,
|
followerId: Not(proxy.id),
|
||||||
});
|
});
|
||||||
const followJobs = followings.map(following => ({
|
const followJobs = followings.map(following => ({
|
||||||
from: { id: following.followerId },
|
from: { id: following.followerId },
|
||||||
|
@ -250,10 +250,8 @@ export class AccountMoveService {
|
||||||
|
|
||||||
// Have the proxy account follow the new account in the same way as UserListService.push
|
// Have the proxy account follow the new account in the same way as UserListService.push
|
||||||
if (this.userEntityService.isRemoteUser(dst)) {
|
if (this.userEntityService.isRemoteUser(dst)) {
|
||||||
const proxy = await this.proxyAccountService.fetch();
|
const proxy = await this.systemAccountService.fetch('proxy');
|
||||||
if (proxy) {
|
this.queueService.createFollowJob([{ from: { id: proxy.id }, to: { id: dst.id } }]);
|
||||||
this.queueService.createFollowJob([{ from: { id: proxy.id }, to: { id: dst.id } }]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ export class MetaService implements OnApplicationShutdown {
|
||||||
case 'metaUpdated': {
|
case 'metaUpdated': {
|
||||||
this.cache = { // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい
|
this.cache = { // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい
|
||||||
...(body.after),
|
...(body.after),
|
||||||
proxyAccount: null, // joinなカラムは通常取ってこないので
|
rootUser: null, // joinなカラムは通常取ってこないので
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { DataSource, IsNull } from 'typeorm';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { MiLocalUser, MiUser } from '@/models/User.js';
|
import { MiLocalUser, MiUser } from '@/models/User.js';
|
||||||
import { MiSystemAccount, MiUsedUsername, MiUserKeypair, MiUserProfile, type UsersRepository, type SystemAccountsRepository } from '@/models/_.js';
|
import { MiSystemAccount, MiUsedUsername, MiUserKeypair, MiUserProfile, type UsersRepository, type SystemAccountsRepository } from '@/models/_.js';
|
||||||
import type { UserProfilesRepository } from '@/models/_.js';
|
import type { MiMeta, UserProfilesRepository } from '@/models/_.js';
|
||||||
import { MemoryKVCache } from '@/misc/cache.js';
|
import { MemoryKVCache } from '@/misc/cache.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@ -27,6 +27,9 @@ export class SystemAccountService {
|
||||||
@Inject(DI.db)
|
@Inject(DI.db)
|
||||||
private db: DataSource,
|
private db: DataSource,
|
||||||
|
|
||||||
|
@Inject(DI.meta)
|
||||||
|
private meta: MiMeta,
|
||||||
|
|
||||||
@Inject(DI.systemAccountsRepository)
|
@Inject(DI.systemAccountsRepository)
|
||||||
private systemAccountsRepository: SystemAccountsRepository,
|
private systemAccountsRepository: SystemAccountsRepository,
|
||||||
|
|
||||||
|
@ -64,6 +67,7 @@ export class SystemAccountService {
|
||||||
} else {
|
} else {
|
||||||
const created = await this.createCorrespondingUser(type, {
|
const created = await this.createCorrespondingUser(type, {
|
||||||
username: `system.${type}`,
|
username: `system.${type}`,
|
||||||
|
name: this.meta.name,
|
||||||
});
|
});
|
||||||
this.cache.set(type, created);
|
this.cache.set(type, created);
|
||||||
return created;
|
return created;
|
||||||
|
@ -72,8 +76,8 @@ export class SystemAccountService {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async createCorrespondingUser(type: typeof SYSTEM_ACCOUNT_TYPES[number], extra: {
|
private async createCorrespondingUser(type: typeof SYSTEM_ACCOUNT_TYPES[number], extra: {
|
||||||
username: string;
|
username: MiUser['username'];
|
||||||
name?: string;
|
name?: MiUser['name'];
|
||||||
}): Promise<MiLocalUser> {
|
}): Promise<MiLocalUser> {
|
||||||
const password = randomUUID();
|
const password = randomUUID();
|
||||||
|
|
||||||
|
@ -139,7 +143,7 @@ export class SystemAccountService {
|
||||||
@bindThis
|
@bindThis
|
||||||
public async updateCorrespondingUserProfile(type: typeof SYSTEM_ACCOUNT_TYPES[number], extra: {
|
public async updateCorrespondingUserProfile(type: typeof SYSTEM_ACCOUNT_TYPES[number], extra: {
|
||||||
name?: string;
|
name?: string;
|
||||||
description?: string;
|
description?: MiUserProfile['description'];
|
||||||
}): Promise<MiLocalUser> {
|
}): Promise<MiLocalUser> {
|
||||||
const user = await this.fetch(type);
|
const user = await this.fetch(type);
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,11 @@ import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { ProxyAccountService } from '@/core/ProxyAccountService.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import { RedisKVCache } from '@/misc/cache.js';
|
import { RedisKVCache } from '@/misc/cache.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
|
import { SystemAccountService } from '@/core/SystemAccountService.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserListService implements OnApplicationShutdown, OnModuleInit {
|
export class UserListService implements OnApplicationShutdown, OnModuleInit {
|
||||||
|
@ -43,8 +43,8 @@ export class UserListService implements OnApplicationShutdown, OnModuleInit {
|
||||||
private userEntityService: UserEntityService,
|
private userEntityService: UserEntityService,
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private globalEventService: GlobalEventService,
|
private globalEventService: GlobalEventService,
|
||||||
private proxyAccountService: ProxyAccountService,
|
|
||||||
private queueService: QueueService,
|
private queueService: QueueService,
|
||||||
|
private systemAccountService: SystemAccountService,
|
||||||
) {
|
) {
|
||||||
this.membersCache = new RedisKVCache<Set<string>>(this.redisClient, 'userListMembers', {
|
this.membersCache = new RedisKVCache<Set<string>>(this.redisClient, 'userListMembers', {
|
||||||
lifetime: 1000 * 60 * 30, // 30m
|
lifetime: 1000 * 60 * 30, // 30m
|
||||||
|
@ -111,10 +111,8 @@ export class UserListService implements OnApplicationShutdown, OnModuleInit {
|
||||||
|
|
||||||
// このインスタンス内にこのリモートユーザーをフォローしているユーザーがいなくても投稿を受け取るためにダミーのユーザーがフォローしたということにする
|
// このインスタンス内にこのリモートユーザーをフォローしているユーザーがいなくても投稿を受け取るためにダミーのユーザーがフォローしたということにする
|
||||||
if (this.userEntityService.isRemoteUser(target)) {
|
if (this.userEntityService.isRemoteUser(target)) {
|
||||||
const proxy = await this.proxyAccountService.fetch();
|
const proxy = await this.systemAccountService.fetch('proxy');
|
||||||
if (proxy) {
|
this.queueService.createFollowJob([{ from: { id: proxy.id }, to: { id: target.id } }]);
|
||||||
this.queueService.createFollowJob([{ from: { id: proxy.id }, to: { id: target.id } }]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,18 +3,11 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import type {
|
|
||||||
UsersRepository,
|
|
||||||
} from '@/models/_.js';
|
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
|
||||||
import {
|
import {
|
||||||
descriptionSchema,
|
descriptionSchema,
|
||||||
nameSchema,
|
|
||||||
} from '@/models/User.js';
|
} from '@/models/User.js';
|
||||||
import { ApiError } from '@/server/api/error.js';
|
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||||
import { SystemAccountService } from '@/core/SystemAccountService.js';
|
import { SystemAccountService } from '@/core/SystemAccountService.js';
|
||||||
|
@ -24,52 +17,30 @@ export const meta = {
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireModerator: true,
|
requireModerator: true,
|
||||||
kind: 'write:admin:update-proxy-account',
|
kind: 'write:admin:account',
|
||||||
|
|
||||||
errors: {
|
|
||||||
accessDenied: {
|
|
||||||
message: 'Only administrators can edit members of the role.',
|
|
||||||
code: 'ACCESS_DENIED',
|
|
||||||
id: '101f9105-27cb-489c-842a-69b6d2092c03',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
ref: 'UserDetailed',
|
ref: 'UserDetailed',
|
||||||
},
|
},
|
||||||
|
|
||||||
required: [],
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
name: { ...nameSchema, nullable: true },
|
|
||||||
description: { ...descriptionSchema, nullable: true },
|
description: { ...descriptionSchema, nullable: true },
|
||||||
avatarId: { type: 'string', format: 'misskey:id', nullable: true },
|
|
||||||
bannerId: { type: 'string', format: 'misskey:id', nullable: true },
|
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.usersRepository)
|
|
||||||
private usersRepository: UsersRepository,
|
|
||||||
|
|
||||||
private roleService: RoleService,
|
|
||||||
private userEntityService: UserEntityService,
|
private userEntityService: UserEntityService,
|
||||||
private moderationLogService: ModerationLogService,
|
private moderationLogService: ModerationLogService,
|
||||||
private systemAccountService: SystemAccountService,
|
private systemAccountService: SystemAccountService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const _me = await this.usersRepository.findOneByOrFail({ id: me.id });
|
|
||||||
if (!await this.roleService.isModerator(_me)) {
|
|
||||||
throw new ApiError(meta.errors.accessDenied);
|
|
||||||
}
|
|
||||||
|
|
||||||
const proxy = await this.systemAccountService.updateCorrespondingUserProfile('proxy', {
|
const proxy = await this.systemAccountService.updateCorrespondingUserProfile('proxy', {
|
||||||
description: ps.description,
|
description: ps.description,
|
||||||
});
|
});
|
||||||
|
@ -78,11 +49,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
schema: 'MeDetailed',
|
schema: 'MeDetailed',
|
||||||
});
|
});
|
||||||
|
|
||||||
this.moderationLogService.log(me, 'updateUser', {
|
if (ps.description !== undefined) {
|
||||||
userId: proxy.id,
|
this.moderationLogService.log(me, 'updateProxyAccountDescription', {
|
||||||
userUsername: proxy.username,
|
before: null, //TODO
|
||||||
userHost: proxy.host,
|
after: ps.description,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return updated;
|
return updated;
|
||||||
});
|
});
|
||||||
|
|
|
@ -122,6 +122,7 @@ export const moderationLogTypes = [
|
||||||
'deletePage',
|
'deletePage',
|
||||||
'deleteFlash',
|
'deleteFlash',
|
||||||
'deleteGalleryPost',
|
'deleteGalleryPost',
|
||||||
|
'updateProxyAccountDescription',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type ModerationLogPayloads = {
|
export type ModerationLogPayloads = {
|
||||||
|
@ -374,25 +375,29 @@ export type ModerationLogPayloads = {
|
||||||
postUserUsername: string;
|
postUserUsername: string;
|
||||||
post: any;
|
post: any;
|
||||||
};
|
};
|
||||||
|
updateProxyAccountDescription: {
|
||||||
|
before: string | null;
|
||||||
|
after: string | null;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Serialized<T> = {
|
export type Serialized<T> = {
|
||||||
[K in keyof T]:
|
[K in keyof T]:
|
||||||
T[K] extends Date
|
T[K] extends Date
|
||||||
? string
|
? string
|
||||||
: T[K] extends (Date | null)
|
: T[K] extends (Date | null)
|
||||||
? (string | null)
|
? (string | null)
|
||||||
: T[K] extends Record<string, any>
|
: T[K] extends Record<string, any>
|
||||||
? Serialized<T[K]>
|
? Serialized<T[K]>
|
||||||
: T[K] extends (Record<string, any> | null)
|
: T[K] extends (Record<string, any> | null)
|
||||||
? (Serialized<T[K]> | null)
|
? (Serialized<T[K]> | null)
|
||||||
: T[K] extends (Record<string, any> | undefined)
|
: T[K] extends (Record<string, any> | undefined)
|
||||||
? (Serialized<T[K]> | undefined)
|
? (Serialized<T[K]> | undefined)
|
||||||
: T[K];
|
: T[K];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FilterUnionByProperty<
|
export type FilterUnionByProperty<
|
||||||
Union,
|
Union,
|
||||||
Property extends string | number | symbol,
|
Property extends string | number | symbol,
|
||||||
Condition
|
Condition,
|
||||||
> = Union extends Record<Property, Condition> ? Union : never;
|
> = Union extends Record<Property, Condition> ? Union : never;
|
||||||
|
|
|
@ -170,6 +170,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<CodeDiff :context="5" :hideHeader="true" :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/>
|
<CodeDiff :context="5" :hideHeader="true" :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="log.type === 'updateProxyAccountDescription'">
|
||||||
|
<div :class="$style.diff">
|
||||||
|
<CodeDiff :context="5" :hideHeader="true" :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>raw</summary>
|
<summary>raw</summary>
|
||||||
|
|
|
@ -8282,8 +8282,6 @@ export type operations = {
|
||||||
sensitiveMediaDetectionSensitivity: string;
|
sensitiveMediaDetectionSensitivity: string;
|
||||||
setSensitiveFlagAutomatically: boolean;
|
setSensitiveFlagAutomatically: boolean;
|
||||||
enableSensitiveMediaDetectionForVideos: boolean;
|
enableSensitiveMediaDetectionForVideos: boolean;
|
||||||
/** Format: id */
|
|
||||||
proxyAccountId: string | null;
|
|
||||||
email: string | null;
|
email: string | null;
|
||||||
smtpSecure: boolean;
|
smtpSecure: boolean;
|
||||||
smtpHost: string | null;
|
smtpHost: string | null;
|
||||||
|
@ -10623,8 +10621,6 @@ export type operations = {
|
||||||
sensitiveMediaDetectionSensitivity?: 'medium' | 'low' | 'high' | 'veryLow' | 'veryHigh';
|
sensitiveMediaDetectionSensitivity?: 'medium' | 'low' | 'high' | 'veryLow' | 'veryHigh';
|
||||||
setSensitiveFlagAutomatically?: boolean;
|
setSensitiveFlagAutomatically?: boolean;
|
||||||
enableSensitiveMediaDetectionForVideos?: boolean;
|
enableSensitiveMediaDetectionForVideos?: boolean;
|
||||||
/** Format: misskey:id */
|
|
||||||
proxyAccountId?: string | null;
|
|
||||||
maintainerName?: string | null;
|
maintainerName?: string | null;
|
||||||
maintainerEmail?: string | null;
|
maintainerEmail?: string | null;
|
||||||
langs?: string[];
|
langs?: string[];
|
||||||
|
|
Loading…
Reference in New Issue