summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoranatawa12 <anatawa12@icloud.com>2025-04-03 19:22:49 +0900
committerGitHub <noreply@github.com>2025-04-03 19:22:49 +0900
commitcab82452ecb9aa6f89da22a1177cbf0e01f0462b (patch)
treee0cf3599ec319a2ffeb8cc7233bf834c387e304f
parentrefactor(frontend): refactor MkDateSeparatedList (diff)
downloadsharkey-cab82452ecb9aa6f89da22a1177cbf0e01f0462b.tar.gz
sharkey-cab82452ecb9aa6f89da22a1177cbf0e01f0462b.tar.bz2
sharkey-cab82452ecb9aa6f89da22a1177cbf0e01f0462b.zip
Copy role on move (#15745)
* feat(backend): copyOnMoveAccount * feat(endpoints): copyOnMoveAccount * feat(frontend): copyOnMoveAccount * docs(changelog): アカウントのマイグレーション時に古いアカウントからロールをコピーできるようになりました。 * fix: spdx header for migration * Update locales/ja-JP.yml * copyOnMoveAccount -> preserveAssignmentOnMoveAccount * fix: check for preserveAssignmentOnMoveAccount --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
-rw-r--r--CHANGELOG.md2
-rw-r--r--locales/index.d.ts8
-rw-r--r--locales/ja-JP.yml2
-rw-r--r--packages/backend/migration/1743558299182-RoleCopyOnMoveAccount.js16
-rw-r--r--packages/backend/src/core/AccountMoveService.ts29
-rw-r--r--packages/backend/src/core/RoleService.ts1
-rw-r--r--packages/backend/src/core/entities/RoleEntityService.ts4
-rw-r--r--packages/backend/src/models/Role.ts5
-rw-r--r--packages/backend/src/models/json-schema/role.ts5
-rw-r--r--packages/backend/src/server/api/endpoints/admin/roles/create.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/admin/roles/update.ts2
-rw-r--r--packages/frontend/src/pages/admin/roles.editor.vue5
-rw-r--r--packages/misskey-js/src/autogen/types.ts4
13 files changed, 83 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9da5e9bb2e..434a18442a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,8 @@
- 過去自分が送ったメッセージ・自分に送られたメッセージの検索が可能です
- 参加中のルームをミュートして通知が来ないように設定可能です
- メッセージにはリアクションも可能です
+- Feat: アカウントのマイグレーション時に古いアカウントからロールをコピーできるようになりました。
+ - 管理者がロールの設定でマイグレーション時にコピーするかを指定できるようになります。
- Enhance: セキュリティを強化するため、ジョブキューのダッシュボード(bull-board)統合が削除されました。
- Misskeyネイティブでダッシュボードを実装予定です
- Enhance: フロントエンドのエラートラッキングができるように
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 9ef4886cb5..afaa2d975d 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -7362,6 +7362,14 @@ export interface Locale extends ILocale {
*/
"descriptionOfDisplayOrder": string;
/**
+ * アサイン状態を移行先アカウントにも引き継ぐ
+ */
+ "preserveAssignmentOnMoveAccount": string;
+ /**
+ * オンにすると、このロールが付与されたアカウントが移行された際に、移行先アカウントにもこのロールが引き継がれるようになります。
+ */
+ "preserveAssignmentOnMoveAccount_description": string;
+ /**
* モデレーターのメンバー編集を許可
*/
"canEditMembersByModerator": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 947b7b4fa5..323dc3a38a 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1907,6 +1907,8 @@ _role:
descriptionOfIsExplorable: "オンにすると、「みつける」でメンバー一覧が公開されるほか、ロールのタイムラインが利用可能になります。"
displayOrder: "表示順"
descriptionOfDisplayOrder: "数値が大きいほどUI上で先頭に表示されます。"
+ preserveAssignmentOnMoveAccount: "アサイン状態を移行先アカウントにも引き継ぐ"
+ preserveAssignmentOnMoveAccount_description: "オンにすると、このロールが付与されたアカウントが移行された際に、移行先アカウントにもこのロールが引き継がれるようになります。"
canEditMembersByModerator: "モデレーターのメンバー編集を許可"
descriptionOfCanEditMembersByModerator: "オンにすると、管理者に加えてモデレーターもこのロールへユーザーをアサイン/アサイン解除できるようになります。オフにすると管理者のみが行えます。"
priority: "優先度"
diff --git a/packages/backend/migration/1743558299182-RoleCopyOnMoveAccount.js b/packages/backend/migration/1743558299182-RoleCopyOnMoveAccount.js
new file mode 100644
index 0000000000..ff4f7a051b
--- /dev/null
+++ b/packages/backend/migration/1743558299182-RoleCopyOnMoveAccount.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class RoleCopyOnMoveAccount1743558299182 {
+ name = 'RoleCopyOnMoveAccount1743558299182'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "role" ADD "preserveAssignmentOnMoveAccount" boolean NOT NULL DEFAULT false`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "preserveAssignmentOnMoveAccount"`);
+ }
+}
diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts
index 0fbb9bcd80..406563bee8 100644
--- a/packages/backend/src/core/AccountMoveService.ts
+++ b/packages/backend/src/core/AccountMoveService.ts
@@ -24,6 +24,7 @@ import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import InstanceChart from '@/core/chart/charts/instance.js';
import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
import { SystemAccountService } from '@/core/SystemAccountService.js';
+import { RoleService } from '@/core/RoleService.js';
@Injectable()
export class AccountMoveService {
@@ -61,6 +62,7 @@ export class AccountMoveService {
private relayService: RelayService,
private queueService: QueueService,
private systemAccountService: SystemAccountService,
+ private roleService: RoleService,
) {
}
@@ -119,6 +121,7 @@ export class AccountMoveService {
await Promise.all([
this.copyBlocking(src, dst),
this.copyMutings(src, dst),
+ this.copyRoles(src, dst),
this.updateLists(src, dst),
]);
} catch {
@@ -201,6 +204,32 @@ export class AccountMoveService {
await this.mutingsRepository.insert(arrayToInsert);
}
+ @bindThis
+ public async copyRoles(src: ThinUser, dst: ThinUser): Promise<void> {
+ // Insert new roles with the same values except userId
+ // role service may have cache for roles so retrieve roles from service
+ const [oldRoleAssignments, roles] = await Promise.all([
+ this.roleService.getUserAssigns(src.id),
+ this.roleService.getRoles(),
+ ]);
+
+ if (oldRoleAssignments.length === 0) return;
+
+ // No promise all since the only async operation is writing to the database
+ for (const oldRoleAssignment of oldRoleAssignments) {
+ const role = roles.find(x => x.id === oldRoleAssignment.roleId);
+ if (role == null) continue; // Very unlikely however removing role may cause this case
+ if (!role.preserveAssignmentOnMoveAccount) continue;
+
+ try {
+ await this.roleService.assign(dst.id, role.id, oldRoleAssignment.expiresAt);
+ } catch (e) {
+ if (e instanceof RoleService.AlreadyAssignedError) continue;
+ throw e;
+ }
+ }
+ }
+
/**
* Update lists while moving accounts.
* - No removal of the old account from the lists
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 86f8a5caa1..0a2659ee32 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -630,6 +630,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
isModerator: values.isModerator,
isExplorable: values.isExplorable,
asBadge: values.asBadge,
+ preserveAssignmentOnMoveAccount: values.preserveAssignmentOnMoveAccount,
canEditMembersByModerator: values.canEditMembersByModerator,
displayOrder: values.displayOrder,
policies: values.policies,
diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts
index 2a7dc37bce..3fa38c9521 100644
--- a/packages/backend/src/core/entities/RoleEntityService.ts
+++ b/packages/backend/src/core/entities/RoleEntityService.ts
@@ -13,6 +13,7 @@ import type { MiRole } from '@/models/Role.js';
import { bindThis } from '@/decorators.js';
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
import { IdService } from '@/core/IdService.js';
+import { Packed } from '@/misc/json-schema.js';
@Injectable()
export class RoleEntityService {
@@ -31,7 +32,7 @@ export class RoleEntityService {
public async pack(
src: MiRole['id'] | MiRole,
me?: { id: MiUser['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')
@@ -67,6 +68,7 @@ export class RoleEntityService {
isModerator: role.isModerator,
isExplorable: role.isExplorable,
asBadge: role.asBadge,
+ preserveAssignmentOnMoveAccount: role.preserveAssignmentOnMoveAccount,
canEditMembersByModerator: role.canEditMembersByModerator,
displayOrder: role.displayOrder,
policies: policies,
diff --git a/packages/backend/src/models/Role.ts b/packages/backend/src/models/Role.ts
index a173971b2c..4c7da252bd 100644
--- a/packages/backend/src/models/Role.ts
+++ b/packages/backend/src/models/Role.ts
@@ -251,6 +251,11 @@ export class MiRole {
@Column('boolean', {
default: false,
})
+ public preserveAssignmentOnMoveAccount: boolean;
+
+ @Column('boolean', {
+ default: false,
+ })
public canEditMembersByModerator: boolean;
// UIに表示する際の並び順用(大きいほど先頭)
diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts
index 1685a806c9..6f63dcef2e 100644
--- a/packages/backend/src/models/json-schema/role.ts
+++ b/packages/backend/src/models/json-schema/role.ts
@@ -389,6 +389,11 @@ export const packedRoleSchema = {
optional: false, nullable: false,
example: false,
},
+ preserveAssignmentOnMoveAccount: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ example: false,
+ },
canEditMembersByModerator: {
type: 'boolean',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/create.ts b/packages/backend/src/server/api/endpoints/admin/roles/create.ts
index e0c02f7a5d..f92f7ebaeb 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts
@@ -36,6 +36,7 @@ export const paramDef = {
isAdministrator: { type: 'boolean' },
isExplorable: { type: 'boolean', default: false }, // optional for backward compatibility
asBadge: { type: 'boolean' },
+ preserveAssignmentOnMoveAccount: { type: 'boolean' },
canEditMembersByModerator: { type: 'boolean' },
displayOrder: { type: 'number' },
policies: {
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
index 465ad7aaaf..175adcb63f 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
@@ -41,6 +41,7 @@ export const paramDef = {
isAdministrator: { type: 'boolean' },
isExplorable: { type: 'boolean' },
asBadge: { type: 'boolean' },
+ preserveAssignmentOnMoveAccount: { type: 'boolean' },
canEditMembersByModerator: { type: 'boolean' },
displayOrder: { type: 'number' },
policies: {
@@ -78,6 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
isAdministrator: ps.isAdministrator,
isExplorable: ps.isExplorable,
asBadge: ps.asBadge,
+ preserveAssignmentOnMoveAccount: ps.preserveAssignmentOnMoveAccount,
canEditMembersByModerator: ps.canEditMembersByModerator,
displayOrder: ps.displayOrder,
policies: ps.policies,
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index d1e823215a..73119940c1 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -52,6 +52,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
+ <MkSwitch v-model="role.preserveAssignmentOnMoveAccount" :readonly="readonly">
+ <template #label>{{ i18n.ts._role.preserveAssignmentOnMoveAccount }}</template>
+ <template #caption>{{ i18n.ts._role.preserveAssignmentOnMoveAccount_description }}</template>
+ </MkSwitch>
+
<MkSwitch v-model="role.canEditMembersByModerator" :readonly="readonly">
<template #label>{{ i18n.ts._role.canEditMembersByModerator }}</template>
<template #caption>{{ i18n.ts._role.descriptionOfCanEditMembersByModerator }}</template>
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 0d54c2d4ab..6bbf1e3319 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -5135,6 +5135,8 @@ export type components = {
/** @example false */
asBadge: boolean;
/** @example false */
+ preserveAssignmentOnMoveAccount: boolean;
+ /** @example false */
canEditMembersByModerator: boolean;
policies: {
[key: string]: {
@@ -9392,6 +9394,7 @@ export type operations = {
/** @default false */
isExplorable?: boolean;
asBadge: boolean;
+ preserveAssignmentOnMoveAccount?: boolean;
canEditMembersByModerator: boolean;
displayOrder: number;
policies: Record<string, never>;
@@ -9667,6 +9670,7 @@ export type operations = {
isAdministrator?: boolean;
isExplorable?: boolean;
asBadge?: boolean;
+ preserveAssignmentOnMoveAccount?: boolean;
canEditMembersByModerator?: boolean;
displayOrder?: number;
policies?: Record<string, never>;