enhance(role): ロールの割り当て時メモを残せるように (MisskeyIO#842)
This commit is contained in:
parent
346c848134
commit
6542ad4a12
14 changed files with 72 additions and 7 deletions
|
@ -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"`);
|
||||
}
|
||||
}
|
|
@ -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<void> {
|
||||
public async assign(userId: MiUser['id'], roleId: MiRole['id'], memo: string | null = null, expiresAt: Date | null = null, moderator?: MiUser): Promise<void> {
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<typeof meta, typeof paramDef> { // 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<typeof meta, typeof paramDef> { // 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,
|
||||
})));
|
||||
});
|
||||
|
|
|
@ -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<typeof meta, typeof paramDef> { // eslint-
|
|||
createdAt: this.idService.parse(a.id).date.toISOString(),
|
||||
expiresAt: a.expiresAt ? a.expiresAt.toISOString() : null,
|
||||
roleId: a.roleId,
|
||||
memo: a.memo,
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -133,6 +133,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
<div v-if="expandedRoles.includes(role.id)" :class="$style.roleItemSub">
|
||||
<div>Assigned: <MkTime :time="info.roleAssigns.find(a => a.roleId === role.id).createdAt" mode="detail"/></div>
|
||||
<div v-if="info.roleAssigns.find(a => a.roleId === role.id).memo">Memo: {{ info.roleAssigns.find(a => a.roleId === role.id).memo }}</div>
|
||||
<div v-if="info.roleAssigns.find(a => a.roleId === role.id).expiresAt">Period: {{ new Date(info.roleAssigns.find(a => a.roleId === role.id).expiresAt).toLocaleString() }}</div>
|
||||
<div v-else>Period: {{ i18n.ts.indefinitely }}</div>
|
||||
</div>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -90,10 +90,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template v-else-if="log.type === 'assignRole'">
|
||||
<div>{{ i18n.ts.user }}: {{ log.info.userId }}</div>
|
||||
<div>{{ i18n.ts.role }}: {{ log.info.roleName }} [{{ log.info.roleId }}]</div>
|
||||
<div>{{ i18n.ts.memo }}: {{ log.info.memo }}</div>
|
||||
<div>{{ i18n.ts.expirationDate }}: {{ log.info.expiresAt ?? i18n.ts.indefinitely }}</div>
|
||||
</template>
|
||||
<template v-else-if="log.type === 'unassignRole'">
|
||||
<div>{{ i18n.ts.user }}: {{ log.info.userId }}</div>
|
||||
<div>{{ i18n.ts.role }}: {{ log.info.roleName }} [{{ log.info.roleId }}]</div>
|
||||
<div>{{ i18n.ts.memo }}: {{ log.info.memo }}</div>
|
||||
</template>
|
||||
<template v-else-if="log.type === 'updateCustomEmoji'">
|
||||
<div>{{ i18n.ts.emoji }}: {{ log.info.emojiId }}</div>
|
||||
|
|
|
@ -45,6 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
<div v-if="expandedItems.includes(item.id)" :class="$style.userItemSub">
|
||||
<div>Assigned: <MkTime :time="item.createdAt" mode="detail"/></div>
|
||||
<div v-if="item.memo">Memo: {{ item.memo }}</div>
|
||||
<div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div>
|
||||
<div v-else>Period: {{ i18n.ts.indefinitely }}</div>
|
||||
</div>
|
||||
|
@ -142,7 +143,14 @@ async function assign() {
|
|||
: period === 'oneMonth' ? Date.now() + (1000 * 60 * 60 * 24 * 30)
|
||||
: null;
|
||||
|
||||
await os.apiWithDialog('admin/roles/assign', { roleId: role.id, userId: user.id, expiresAt });
|
||||
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: role.id, userId: user.id, memo: memo ?? undefined, expiresAt });
|
||||
//role.users.push(user);
|
||||
}
|
||||
|
||||
|
|
|
@ -295,7 +295,14 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
|
|||
: period === 'oneMonth' ? Date.now() + (1000 * 60 * 60 * 24 * 30)
|
||||
: null;
|
||||
|
||||
os.apiWithDialog('admin/roles/assign', { roleId: r.id, userId: user.id, expiresAt });
|
||||
const { canceled: canceled3, result: memo } = await os.inputText({
|
||||
title: i18n.ts.addMemo,
|
||||
type: 'textarea',
|
||||
placeholder: i18n.ts.memo,
|
||||
});
|
||||
if (canceled3) return;
|
||||
|
||||
os.apiWithDialog('admin/roles/assign', { roleId: r.id, userId: user.id, memo: memo ?? undefined, expiresAt });
|
||||
},
|
||||
}));
|
||||
},
|
||||
|
|
|
@ -9830,6 +9830,7 @@ export type operations = {
|
|||
createdAt: string;
|
||||
expiresAt: string | null;
|
||||
roleId: string;
|
||||
memo: string | null;
|
||||
})[];
|
||||
};
|
||||
};
|
||||
|
@ -10619,6 +10620,7 @@ export type operations = {
|
|||
roleId: string;
|
||||
/** Format: misskey:id */
|
||||
userId: string;
|
||||
memo?: string;
|
||||
expiresAt?: number | null;
|
||||
};
|
||||
};
|
||||
|
@ -10796,6 +10798,7 @@ export type operations = {
|
|||
/** Format: date-time */
|
||||
createdAt: string;
|
||||
user: components['schemas']['UserDetailed'];
|
||||
memo: string | null;
|
||||
/** Format: date-time */
|
||||
expiresAt: string | null;
|
||||
})[];
|
||||
|
|
|
@ -202,6 +202,7 @@ export type ModerationLogPayloads = {
|
|||
roleId: string;
|
||||
roleName: string;
|
||||
expiresAt: string | null;
|
||||
memo: string | null;
|
||||
};
|
||||
unassignRole: {
|
||||
userId: string;
|
||||
|
@ -209,6 +210,7 @@ export type ModerationLogPayloads = {
|
|||
userHost: string | null;
|
||||
roleId: string;
|
||||
roleName: string;
|
||||
memo: string | null;
|
||||
};
|
||||
createRole: {
|
||||
roleId: string;
|
||||
|
|
Loading…
Reference in a new issue