From 6542ad4a12f45096c4cf69921304ef3198d5b2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=82=8F=E3=82=8F=E3=82=8F=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Wed, 25 Dec 2024 09:42:59 +0900 Subject: [PATCH] =?UTF-8?q?enhance(role):=20=E3=83=AD=E3=83=BC=E3=83=AB?= =?UTF-8?q?=E3=81=AE=E5=89=B2=E3=82=8A=E5=BD=93=E3=81=A6=E6=99=82=E3=83=A1?= =?UTF-8?q?=E3=83=A2=E3=82=92=E6=AE=8B=E3=81=9B=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=20(MisskeyIO#842)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration/1735078824104-RoleAssignment-Memo.js | 13 +++++++++++++ packages/backend/src/core/RoleService.ts | 8 ++++++-- packages/backend/src/models/RoleAssignment.ts | 7 +++++++ .../src/server/api/endpoints/admin/roles/assign.ts | 3 ++- .../src/server/api/endpoints/admin/roles/users.ts | 2 ++ .../src/server/api/endpoints/admin/show-user.ts | 5 +++++ packages/backend/src/types.ts | 2 ++ packages/backend/test/unit/RoleService.ts | 2 +- packages/frontend/src/pages/admin-user.vue | 10 +++++++++- packages/frontend/src/pages/admin/modlog.ModLog.vue | 3 +++ packages/frontend/src/pages/admin/roles.role.vue | 10 +++++++++- packages/frontend/src/scripts/get-user-menu.ts | 9 ++++++++- packages/misskey-js/src/autogen/types.ts | 3 +++ packages/misskey-js/src/consts.ts | 2 ++ 14 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 packages/backend/migration/1735078824104-RoleAssignment-Memo.js diff --git a/packages/backend/migration/1735078824104-RoleAssignment-Memo.js b/packages/backend/migration/1735078824104-RoleAssignment-Memo.js new file mode 100644 index 000000000..87599f09b --- /dev/null +++ b/packages/backend/migration/1735078824104-RoleAssignment-Memo.js @@ -0,0 +1,13 @@ +export class RoleAssignmentMemo1735078824104 { + name = 'RoleAssignmentMemo1735078824104' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "role_assignment" ADD "memo" character varying(256)`); + await queryRunner.query(`COMMENT ON COLUMN "role_assignment"."memo" IS 'memo for the role assignment'`); + } + + async down(queryRunner) { + await queryRunner.query(`COMMENT ON COLUMN "role_assignment"."memo" IS 'memo for the role assignment'`); + await queryRunner.query(`ALTER TABLE "role_assignment" DROP COLUMN "memo"`); + } +} diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 9089480f9..1c205dca0 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -488,7 +488,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { } @bindThis - public async assign(userId: MiUser['id'], roleId: MiRole['id'], expiresAt: Date | null = null, moderator?: MiUser): Promise { + public async assign(userId: MiUser['id'], roleId: MiRole['id'], memo: string | null = null, expiresAt: Date | null = null, moderator?: MiUser): Promise { const now = Date.now(); const role = await this.rolesRepository.findOneByOrFail({ id: roleId }); @@ -512,6 +512,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { expiresAt: expiresAt, roleId: roleId, userId: userId, + memo: memo, }).then(x => this.roleAssignmentsRepository.findOneByOrFail(x.identifiers[0])); this.globalEventService.publishInternalEvent('userRoleAssigned', created); @@ -521,9 +522,10 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { roleId: roleId, }); } - } else if (existing.expiresAt !== expiresAt) { + } else if (existing.expiresAt !== expiresAt || existing.memo !== memo) { await this.roleAssignmentsRepository.update(existing.id, { expiresAt: expiresAt, + memo: memo, }); } else { throw new IdentifiableError('67d8689c-25c6-435f-8ced-631e4b81fce1', 'User is already assigned to this role.'); @@ -542,6 +544,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { userUsername: user.username, userHost: user.host, expiresAt: expiresAt ? expiresAt.toISOString() : null, + memo: memo, }); } } @@ -582,6 +585,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { userId: userId, userUsername: user.username, userHost: user.host, + memo: existing.memo, }); } } diff --git a/packages/backend/src/models/RoleAssignment.ts b/packages/backend/src/models/RoleAssignment.ts index b74fd90b9..67792725d 100644 --- a/packages/backend/src/models/RoleAssignment.ts +++ b/packages/backend/src/models/RoleAssignment.ts @@ -52,4 +52,11 @@ export class MiRoleAssignment { nullable: true, }) public expiresAt: Date | null; + + @Column('varchar', { + comment: 'memo for the role assignment', + length: 256, + nullable: true, + }) + public memo: string | null; } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/assign.ts b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts index df5955efd..b1f8a2ec6 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/assign.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts @@ -49,6 +49,7 @@ export const paramDef = { properties: { roleId: { type: 'string', format: 'misskey:id' }, userId: { type: 'string', format: 'misskey:id' }, + memo: { type: 'string' }, expiresAt: { type: 'integer', nullable: true, @@ -90,7 +91,7 @@ export default class extends Endpoint { // eslint- return; } - await this.roleService.assign(user.id, role.id, ps.expiresAt ? new Date(ps.expiresAt) : null, me); + await this.roleService.assign(user.id, role.id, ps.memo, ps.expiresAt ? new Date(ps.expiresAt) : null, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts index 45758d4f5..f126e5652 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts @@ -36,6 +36,7 @@ export const meta = { id: { type: 'string', format: 'misskey:id' }, createdAt: { type: 'string', format: 'date-time' }, user: { ref: 'UserDetailed' }, + memo: { type: 'string', nullable: true }, expiresAt: { type: 'string', format: 'date-time', nullable: true }, }, required: ['id', 'createdAt', 'user'], @@ -93,6 +94,7 @@ export default class extends Endpoint { // eslint- id: assign.id, createdAt: this.idService.parse(assign.id).date.toISOString(), user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }), + memo: assign.memo, expiresAt: assign.expiresAt?.toISOString() ?? null, }))); }); diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index f1818086a..f2c40541f 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -177,6 +177,10 @@ export const meta = { type: 'string', optional: false, nullable: false, }, + memo: { + type: 'string', + optional: false, nullable: true, + } }, }, }, @@ -262,6 +266,7 @@ export default class extends Endpoint { // eslint- createdAt: this.idService.parse(a.id).date.toISOString(), expiresAt: a.expiresAt ? a.expiresAt.toISOString() : null, roleId: a.roleId, + memo: a.memo, })), }; }); diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 9e595e83e..e125b074f 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -150,6 +150,7 @@ export type ModerationLogPayloads = { roleId: string; roleName: string; expiresAt: string | null; + memo: string | null; }; unassignRole: { userId: string; @@ -157,6 +158,7 @@ export type ModerationLogPayloads = { userHost: string | null; roleId: string; roleName: string; + memo: string | null; }; createRole: { roleId: string; diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index e0b7621b2..d27211302 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -239,7 +239,7 @@ describe('RoleService', () => { }, }, }); - await roleService.assign(user.id, role.id, new Date(Date.now() + (1000 * 60 * 60 * 24))); + await roleService.assign(user.id, role.id, 'test', new Date(Date.now() + (1000 * 60 * 60 * 24))); metaService.fetch.mockResolvedValue({ policies: { canManageCustomEmojis: false, diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index b4e58d99f..09a51efdc 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -133,6 +133,7 @@ SPDX-License-Identifier: AGPL-3.0-only
Assigned:
+
Memo: {{ info.roleAssigns.find(a => a.roleId === role.id).memo }}
Period: {{ new Date(info.roleAssigns.find(a => a.roleId === role.id).expiresAt).toLocaleString() }}
Period: {{ i18n.ts.indefinitely }}
@@ -502,8 +503,15 @@ async function assignRole() { : period === 'oneMonth' ? Date.now() + (1000 * 60 * 60 * 24 * 30) : null; + const { canceled: canceled3, result: memo } = await os.inputText({ + title: i18n.ts.addMemo, + type: 'textarea', + placeholder: i18n.ts.memo, + }); + if (canceled3) return; + await os.apiWithDialog('admin/roles/assign', { - roleId, userId: user.value.id, expiresAt, + roleId, userId: user.value.id, memo: memo ?? undefined, expiresAt, }).then(refreshUser); } diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue index 7cfb31506..849bbc6e7 100644 --- a/packages/frontend/src/pages/admin/modlog.ModLog.vue +++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue @@ -90,10 +90,13 @@ SPDX-License-Identifier: AGPL-3.0-only