From ed983a5baf581b28b063edfa25e0d80349e9318e Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 24 Sep 2023 10:46:09 +0900 Subject: [PATCH] improve moderation log --- locales/index.d.ts | 4 + locales/ja-JP.yml | 4 + .../backend/src/core/AnnouncementService.ts | 77 ++++++++++++++++--- .../endpoints/admin/announcements/delete.ts | 5 +- .../endpoints/admin/announcements/update.ts | 7 +- packages/backend/src/types.ts | 22 ++++++ packages/misskey-js/etc/misskey-js.api.md | 26 ++++++- packages/misskey-js/src/consts.ts | 22 ++++++ packages/misskey-js/src/entities.ts | 24 ++++++ 9 files changed, 177 insertions(+), 14 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 1e5396ad6..c2f50dd54 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2262,6 +2262,10 @@ export interface Locale { "deleteNote": string; "createGlobalAnnouncement": string; "createUserAnnouncement": string; + "updateGlobalAnnouncement": string; + "updateUserAnnouncement": string; + "deleteGlobalAnnouncement": string; + "deleteUserAnnouncement": string; "resetPassword": string; "suspendRemoteInstance": string; "unsuspendRemoteInstance": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 47bbb0aa5..43a339426 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2175,6 +2175,10 @@ _moderationLogTypes: deleteNote: "ノートを削除" createGlobalAnnouncement: "全体のお知らせを作成" createUserAnnouncement: "ユーザーへお知らせを作成" + updateGlobalAnnouncement: "全体のお知らせを更新" + updateUserAnnouncement: "ユーザーのお知らせを更新" + deleteGlobalAnnouncement: "全体のお知らせを削除" + deleteUserAnnouncement: "ユーザーのお知らせを削除" resetPassword: "パスワードをリセット" suspendRemoteInstance: "リモートサーバーを停止" unsuspendRemoteInstance: "リモートサーバーを再開" diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts index 31fcb139e..2b4877788 100644 --- a/packages/backend/src/core/AnnouncementService.ts +++ b/packages/backend/src/core/AnnouncementService.ts @@ -60,7 +60,7 @@ export class AnnouncementService { } @bindThis - public async create(values: Partial, moderator: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> { + public async create(values: Partial, moderator?: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> { const announcement = await this.announcementsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), @@ -82,20 +82,24 @@ export class AnnouncementService { announcement: packed, }); - this.moderationLogService.log(moderator, 'createUserAnnouncement', { - announcementId: announcement.id, - announcement: announcement, - userId: values.userId, - }); + if (moderator) { + this.moderationLogService.log(moderator, 'createUserAnnouncement', { + announcementId: announcement.id, + announcement: announcement, + userId: values.userId, + }); + } } else { this.globalEventService.publishBroadcastStream('announcementCreated', { announcement: packed, }); - this.moderationLogService.log(moderator, 'createGlobalAnnouncement', { - announcementId: announcement.id, - announcement: announcement, - }); + if (moderator) { + this.moderationLogService.log(moderator, 'createGlobalAnnouncement', { + announcementId: announcement.id, + announcement: announcement, + }); + } } return { @@ -104,6 +108,59 @@ export class AnnouncementService { }; } + @bindThis + public async update(announcement: MiAnnouncement, values: Partial, moderator?: MiUser): Promise { + await this.announcementsRepository.update(announcement.id, { + updatedAt: new Date(), + title: values.title, + text: values.text, + /* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- 空の文字列の場合、nullを渡すようにするため */ + imageUrl: values.imageUrl || null, + display: values.display, + icon: values.icon, + forExistingUsers: values.forExistingUsers, + needConfirmationToRead: values.needConfirmationToRead, + isActive: values.isActive, + }); + + const after = await this.announcementsRepository.findOneByOrFail({ id: announcement.id }); + + if (moderator) { + if (announcement.userId) { + this.moderationLogService.log(moderator, 'updateUserAnnouncement', { + announcementId: announcement.id, + before: announcement, + after: after, + }); + } else { + this.moderationLogService.log(moderator, 'updateGlobalAnnouncement', { + announcementId: announcement.id, + before: announcement, + after: after, + }); + } + } + } + + @bindThis + public async delete(announcement: MiAnnouncement, moderator?: MiUser): Promise { + await this.announcementsRepository.delete(announcement.id); + + if (moderator) { + if (announcement.userId) { + this.moderationLogService.log(moderator, 'deleteUserAnnouncement', { + announcementId: announcement.id, + announcement: announcement, + }); + } else { + this.moderationLogService.log(moderator, 'deleteGlobalAnnouncement', { + announcementId: announcement.id, + announcement: announcement, + }); + } + } + } + @bindThis public async read(user: MiUser, announcementId: MiAnnouncement['id']): Promise { try { diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts index 80eb6d7a8..80ec28125 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AnnouncementsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; +import { AnnouncementService } from '@/core/AnnouncementService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -37,13 +38,15 @@ export default class extends Endpoint { // eslint- constructor( @Inject(DI.announcementsRepository) private announcementsRepository: AnnouncementsRepository, + + private announcementService: AnnouncementService, ) { super(meta, paramDef, async (ps, me) => { const announcement = await this.announcementsRepository.findOneBy({ id: ps.id }); if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); - await this.announcementsRepository.delete(announcement.id); + await this.announcementService.delete(announcement, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts index 782928048..d36590c26 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AnnouncementsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; +import { AnnouncementService } from '@/core/AnnouncementService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -45,13 +46,15 @@ export default class extends Endpoint { // eslint- constructor( @Inject(DI.announcementsRepository) private announcementsRepository: AnnouncementsRepository, + + private announcementService: AnnouncementService, ) { super(meta, paramDef, async (ps, me) => { const announcement = await this.announcementsRepository.findOneBy({ id: ps.id }); if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); - await this.announcementsRepository.update(announcement.id, { + await this.announcementService.update(announcement, { updatedAt: new Date(), title: ps.title, text: ps.text, @@ -62,7 +65,7 @@ export default class extends Endpoint { // eslint- forExistingUsers: ps.forExistingUsers, needConfirmationToRead: ps.needConfirmationToRead, isActive: ps.isActive, - }); + }, me); }); } } diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index a58bb9585..b85388d6e 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -43,6 +43,10 @@ export const moderationLogTypes = [ 'deleteNote', 'createGlobalAnnouncement', 'createUserAnnouncement', + 'updateGlobalAnnouncement', + 'updateUserAnnouncement', + 'deleteGlobalAnnouncement', + 'deleteUserAnnouncement', 'resetPassword', 'suspendRemoteInstance', 'unsuspendRemoteInstance', @@ -107,6 +111,24 @@ export type ModerationLogPayloads = { announcement: any; userId: string; }; + updateGlobalAnnouncement: { + announcementId: string; + before: any; + after: any; + }; + updateUserAnnouncement: { + announcementId: string; + before: any; + after: any; + }; + deleteGlobalAnnouncement: { + announcementId: string; + announcement: any; + }; + deleteUserAnnouncement: { + announcementId: string; + announcement: any; + }; resetPassword: { targetId: string; }; diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index adedea875..b5d07a394 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2556,6 +2556,30 @@ type ModerationLog = { } | { type: 'promoteQueue'; info: ModerationLogPayloads['promoteQueue']; +} | { + type: 'deleteDriveFile'; + info: ModerationLogPayloads['deleteDriveFile']; +} | { + type: 'deleteNote'; + info: ModerationLogPayloads['deleteNote']; +} | { + type: 'createGlobalAnnouncement'; + info: ModerationLogPayloads['createGlobalAnnouncement']; +} | { + type: 'createUserAnnouncement'; + info: ModerationLogPayloads['createUserAnnouncement']; +} | { + type: 'updateGlobalAnnouncement'; + info: ModerationLogPayloads['updateGlobalAnnouncement']; +} | { + type: 'updateUserAnnouncement'; + info: ModerationLogPayloads['updateUserAnnouncement']; +} | { + type: 'deleteGlobalAnnouncement'; + info: ModerationLogPayloads['deleteGlobalAnnouncement']; +} | { + type: 'deleteUserAnnouncement'; + info: ModerationLogPayloads['deleteUserAnnouncement']; } | { type: 'resetPassword'; info: ModerationLogPayloads['resetPassword']; @@ -2568,7 +2592,7 @@ type ModerationLog = { }); // @public (undocumented) -export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance"]; +export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance"]; // @public (undocumented) export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index a5c8c2ba0..dd4fd2609 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -61,6 +61,10 @@ export const moderationLogTypes = [ 'deleteNote', 'createGlobalAnnouncement', 'createUserAnnouncement', + 'updateGlobalAnnouncement', + 'updateUserAnnouncement', + 'deleteGlobalAnnouncement', + 'deleteUserAnnouncement', 'resetPassword', 'suspendRemoteInstance', 'unsuspendRemoteInstance', @@ -125,6 +129,24 @@ export type ModerationLogPayloads = { announcement: any; userId: string; }; + updateGlobalAnnouncement: { + announcementId: string; + before: any; + after: any; + }; + updateUserAnnouncement: { + announcementId: string; + before: any; + after: any; + }; + deleteGlobalAnnouncement: { + announcementId: string; + announcement: any; + }; + deleteUserAnnouncement: { + announcementId: string; + announcement: any; + }; resetPassword: { targetId: string; }; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index a1fc8befb..b157eb59f 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -607,6 +607,30 @@ export type ModerationLog = { } | { type: 'promoteQueue'; info: ModerationLogPayloads['promoteQueue']; +} | { + type: 'deleteDriveFile'; + info: ModerationLogPayloads['deleteDriveFile']; +} | { + type: 'deleteNote'; + info: ModerationLogPayloads['deleteNote']; +} | { + type: 'createGlobalAnnouncement'; + info: ModerationLogPayloads['createGlobalAnnouncement']; +} | { + type: 'createUserAnnouncement'; + info: ModerationLogPayloads['createUserAnnouncement']; +} | { + type: 'updateGlobalAnnouncement'; + info: ModerationLogPayloads['updateGlobalAnnouncement']; +} | { + type: 'updateUserAnnouncement'; + info: ModerationLogPayloads['updateUserAnnouncement']; +} | { + type: 'deleteGlobalAnnouncement'; + info: ModerationLogPayloads['deleteGlobalAnnouncement']; +} | { + type: 'deleteUserAnnouncement'; + info: ModerationLogPayloads['deleteUserAnnouncement']; } | { type: 'resetPassword'; info: ModerationLogPayloads['resetPassword'];