enhance: improve moderation log

This commit is contained in:
syuilo 2023-09-29 13:26:11 +09:00
parent 424bb78387
commit 9771f1c435
9 changed files with 91 additions and 2 deletions

3
locales/index.d.ts vendored
View File

@ -2283,6 +2283,9 @@ export interface Locale {
"unmarkSensitiveDriveFile": string; "unmarkSensitiveDriveFile": string;
"resolveAbuseReport": string; "resolveAbuseReport": string;
"createInvitation": string; "createInvitation": string;
"createAd": string;
"deleteAd": string;
"updateAd": string;
}; };
} }
declare const locales: { declare const locales: {

View File

@ -2196,3 +2196,6 @@ _moderationLogTypes:
unmarkSensitiveDriveFile: "ファイルをセンシティブ解除" unmarkSensitiveDriveFile: "ファイルをセンシティブ解除"
resolveAbuseReport: "通報を解決" resolveAbuseReport: "通報を解決"
createInvitation: "招待コードを作成" createInvitation: "招待コードを作成"
createAd: "広告を作成"
deleteAd: "広告を削除"
updateAd: "広告を更新"

View File

@ -8,6 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import type { AdsRepository } from '@/models/_.js'; import type { AdsRepository } from '@/models/_.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
export const meta = { export const meta = {
tags: ['admin'], tags: ['admin'],
@ -39,6 +40,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private adsRepository: AdsRepository, private adsRepository: AdsRepository,
private idService: IdService, private idService: IdService,
private moderationLogService: ModerationLogService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const ad = await this.adsRepository.insert({ const ad = await this.adsRepository.insert({
@ -54,6 +56,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
place: ps.place, place: ps.place,
memo: ps.memo, memo: ps.memo,
}).then(r => this.adsRepository.findOneByOrFail({ id: r.identifiers[0].id })); }).then(r => this.adsRepository.findOneByOrFail({ id: r.identifiers[0].id }));
this.moderationLogService.log(me, 'createAd', {
adId: ad.id,
ad: ad,
});
return ad; return ad;
}); });
} }

View File

@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.js';
import type { AdsRepository } from '@/models/_.js'; import type { AdsRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { ApiError } from '../../../error.js'; import { ApiError } from '../../../error.js';
export const meta = { export const meta = {
@ -37,6 +38,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor( constructor(
@Inject(DI.adsRepository) @Inject(DI.adsRepository)
private adsRepository: AdsRepository, private adsRepository: AdsRepository,
private moderationLogService: ModerationLogService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const ad = await this.adsRepository.findOneBy({ id: ps.id }); const ad = await this.adsRepository.findOneBy({ id: ps.id });
@ -44,6 +47,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (ad == null) throw new ApiError(meta.errors.noSuchAd); if (ad == null) throw new ApiError(meta.errors.noSuchAd);
await this.adsRepository.delete(ad.id); await this.adsRepository.delete(ad.id);
this.moderationLogService.log(me, 'deleteAd', {
adId: ad.id,
ad: ad,
});
}); });
} }
} }

View File

@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.js';
import type { AdsRepository } from '@/models/_.js'; import type { AdsRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { ApiError } from '../../../error.js'; import { ApiError } from '../../../error.js';
export const meta = { export const meta = {
@ -46,6 +47,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor( constructor(
@Inject(DI.adsRepository) @Inject(DI.adsRepository)
private adsRepository: AdsRepository, private adsRepository: AdsRepository,
private moderationLogService: ModerationLogService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const ad = await this.adsRepository.findOneBy({ id: ps.id }); const ad = await this.adsRepository.findOneBy({ id: ps.id });
@ -63,6 +66,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
startsAt: new Date(ps.startsAt), startsAt: new Date(ps.startsAt),
dayOfWeek: ps.dayOfWeek, dayOfWeek: ps.dayOfWeek,
}); });
const updatedAd = await this.adsRepository.findOneByOrFail({ id: ad.id });
this.moderationLogService.log(me, 'updateAd', {
adId: ad.id,
before: ad,
after: updatedAd,
});
}); });
} }
} }

View File

@ -57,6 +57,9 @@ export const moderationLogTypes = [
'unmarkSensitiveDriveFile', 'unmarkSensitiveDriveFile',
'resolveAbuseReport', 'resolveAbuseReport',
'createInvitation', 'createInvitation',
'createAd',
'updateAd',
'deleteAd',
] as const; ] as const;
export type ModerationLogPayloads = { export type ModerationLogPayloads = {
@ -202,6 +205,19 @@ export type ModerationLogPayloads = {
createInvitation: { createInvitation: {
invitations: any[]; invitations: any[];
}; };
createAd: {
adId: string;
ad: any;
};
updateAd: {
adId: string;
before: any;
after: any;
};
deleteAd: {
adId: string;
ad: any;
};
}; };
export type Serialized<T> = { export type Serialized<T> = {

View File

@ -6,7 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<MkFolder> <MkFolder>
<template #label> <template #label>
<b>{{ i18n.ts._moderationLogTypes[log.type] }}</b> <b
:class="{
[$style.logGreen]: ['createRole', 'addCustomEmoji', 'createGlobalAnnouncement', 'createUserAnnouncement', 'createAd', 'createInvitation'].includes(log.type),
[$style.logYellow]: ['markSensitiveDriveFile', 'resetPassword'].includes(log.type),
[$style.logRed]: ['suspend', 'deleteRole', 'suspendRemoteInstance', 'deleteGlobalAnnouncement', 'deleteUserAnnouncement', 'deleteCustomEmoji', 'deleteNote', 'deleteDriveFile', 'deleteAd'].includes(log.type)
}"
>{{ i18n.ts._moderationLogTypes[log.type] }}</b>
<span v-if="log.type === 'updateUserNote'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span> <span v-if="log.type === 'updateUserNote'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
<span v-else-if="log.type === 'suspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span> <span v-else-if="log.type === 'suspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
<span v-else-if="log.type === 'unsuspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span> <span v-else-if="log.type === 'unsuspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
@ -18,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-else-if="log.type === 'deleteRole'">: {{ log.info.role.name }}</span> <span v-else-if="log.type === 'deleteRole'">: {{ log.info.role.name }}</span>
<span v-else-if="log.type === 'addCustomEmoji'">: {{ log.info.emoji.name }}</span> <span v-else-if="log.type === 'addCustomEmoji'">: {{ log.info.emoji.name }}</span>
<span v-else-if="log.type === 'updateCustomEmoji'">: {{ log.info.before.name }}</span> <span v-else-if="log.type === 'updateCustomEmoji'">: {{ log.info.before.name }}</span>
<span v-else-if="log.type === 'deleteCustomEmoji'">: {{ log.info.emoji.name }}</span>
<span v-else-if="log.type === 'markSensitiveDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span> <span v-else-if="log.type === 'markSensitiveDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span>
<span v-else-if="log.type === 'unmarkSensitiveDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span> <span v-else-if="log.type === 'unmarkSensitiveDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span>
<span v-else-if="log.type === 'suspendRemoteInstance'">: {{ log.info.host }}</span> <span v-else-if="log.type === 'suspendRemoteInstance'">: {{ log.info.host }}</span>
@ -76,6 +83,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/> <CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
</div> </div>
</template> </template>
<template v-else-if="log.type === 'updateAd'">
<div :class="$style.diff">
<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
</div>
</template>
<details> <details>
<summary>raw</summary> <summary>raw</summary>
@ -114,4 +126,16 @@ const props = defineProps<{
border-radius: 6px; border-radius: 6px;
overflow: clip; overflow: clip;
} }
.logYellow {
color: var(--warning);
}
.logRed {
color: var(--error);
}
.logGreen {
color: var(--success);
}
</style> </style>

View File

@ -2625,7 +2625,7 @@ type ModerationLog = {
}); });
// @public (undocumented) // @public (undocumented)
export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation"]; export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd"];
// @public (undocumented) // @public (undocumented)
export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"];

View File

@ -75,6 +75,9 @@ export const moderationLogTypes = [
'unmarkSensitiveDriveFile', 'unmarkSensitiveDriveFile',
'resolveAbuseReport', 'resolveAbuseReport',
'createInvitation', 'createInvitation',
'createAd',
'updateAd',
'deleteAd',
] as const; ] as const;
export type ModerationLogPayloads = { export type ModerationLogPayloads = {
@ -220,4 +223,17 @@ export type ModerationLogPayloads = {
createInvitation: { createInvitation: {
invitations: any[]; invitations: any[];
}; };
createAd: {
adId: string;
ad: any;
};
updateAd: {
adId: string;
before: any;
after: any;
};
deleteAd: {
adId: string;
ad: any;
};
}; };