This commit is contained in:
syuilo 2023-11-19 10:18:57 +09:00
parent 2f7d10bf23
commit b65fd34981
13 changed files with 98 additions and 36 deletions

View file

@ -564,10 +564,10 @@ output: "Output"
script: "Script" script: "Script"
disablePagesScript: "Disable AiScript on Pages" disablePagesScript: "Disable AiScript on Pages"
updateRemoteUser: "Update remote user information" updateRemoteUser: "Update remote user information"
deleteUserAvatar: "Delete user icon" unsetUserAvatar: "Delete user icon"
deleteUserAvatarConfirm: "Are you sure that you want to delete this user's icon?" unsetUserAvatarConfirm: "Are you sure that you want to delete this user's icon?"
deleteUserBanner: "Delete user banner" unsetUserBanner: "Delete user banner"
deleteUserBannerConfirm: "Are you sure that you want to delete this user's banner?" unsetUserBannerConfirm: "Are you sure that you want to delete this user's banner?"
deleteAllFiles: "Delete all files" deleteAllFiles: "Delete all files"
deleteAllFilesConfirm: "Are you sure that you want to delete all files?" deleteAllFilesConfirm: "Are you sure that you want to delete all files?"
removeAllFollowing: "Unfollow all followed users" removeAllFollowing: "Unfollow all followed users"

10
locales/index.d.ts vendored
View file

@ -567,10 +567,10 @@ export interface Locale {
"script": string; "script": string;
"disablePagesScript": string; "disablePagesScript": string;
"updateRemoteUser": string; "updateRemoteUser": string;
"deleteUserAvatar": string; "unsetUserAvatar": string;
"deleteUserAvatarConfirm": string; "unsetUserAvatarConfirm": string;
"deleteUserBanner": string; "unsetUserBanner": string;
"deleteUserBannerConfirm": string; "unsetUserBannerConfirm": string;
"deleteAllFiles": string; "deleteAllFiles": string;
"deleteAllFilesConfirm": string; "deleteAllFilesConfirm": string;
"removeAllFollowing": string; "removeAllFollowing": string;
@ -2417,6 +2417,8 @@ export interface Locale {
"createAvatarDecoration": string; "createAvatarDecoration": string;
"updateAvatarDecoration": string; "updateAvatarDecoration": string;
"deleteAvatarDecoration": string; "deleteAvatarDecoration": string;
"unsetUserAvatar": string;
"unsetUserBanner": string;
}; };
"_fileViewer": { "_fileViewer": {
"title": string; "title": string;

View file

@ -564,10 +564,10 @@ output: "出力"
script: "スクリプト" script: "スクリプト"
disablePagesScript: "Pagesのスクリプトを無効にする" disablePagesScript: "Pagesのスクリプトを無効にする"
updateRemoteUser: "リモートユーザー情報の更新" updateRemoteUser: "リモートユーザー情報の更新"
deleteUserAvatar: "アイコンを削除" unsetUserAvatar: "アイコンを解除"
deleteUserAvatarConfirm: "アイコンを削除しますか?" unsetUserAvatarConfirm: "アイコンを解除しますか?"
deleteUserBanner: "バナーを削除" unsetUserBanner: "バナーを解除"
deleteUserBannerConfirm: "バナーを削除しますか?" unsetUserBannerConfirm: "バナーを解除しますか?"
deleteAllFiles: "すべてのファイルを削除" deleteAllFiles: "すべてのファイルを削除"
deleteAllFilesConfirm: "すべてのファイルを削除しますか?" deleteAllFilesConfirm: "すべてのファイルを削除しますか?"
removeAllFollowing: "フォローを全解除" removeAllFollowing: "フォローを全解除"
@ -2318,6 +2318,8 @@ _moderationLogTypes:
createAvatarDecoration: "アイコンデコレーションを作成" createAvatarDecoration: "アイコンデコレーションを作成"
updateAvatarDecoration: "アイコンデコレーションを更新" updateAvatarDecoration: "アイコンデコレーションを更新"
deleteAvatarDecoration: "アイコンデコレーションを削除" deleteAvatarDecoration: "アイコンデコレーションを削除"
unsetUserAvatar: "ユーザーのアイコンを解除"
unsetUserBanner: "ユーザーのバナーを解除"
_fileViewer: _fileViewer:
title: "ファイルの詳細" title: "ファイルの詳細"

View file

@ -24,8 +24,8 @@ import * as ep___admin_avatarDecorations_delete from './endpoints/admin/avatar-d
import * as ep___admin_avatarDecorations_list from './endpoints/admin/avatar-decorations/list.js'; import * as ep___admin_avatarDecorations_list from './endpoints/admin/avatar-decorations/list.js';
import * as ep___admin_avatarDecorations_update from './endpoints/admin/avatar-decorations/update.js'; import * as ep___admin_avatarDecorations_update from './endpoints/admin/avatar-decorations/update.js';
import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js'; import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js';
import * as ep___admin_deleteUserAvatar from './endpoints/admin/delete-user-avatar.js'; import * as ep___admin_unsetUserAvatar from './endpoints/admin/unset-user-avatar.js';
import * as ep___admin_deleteUserBanner from './endpoints/admin/delete-user-banner.js'; import * as ep___admin_unsetUserBanner from './endpoints/admin/unset-user-banner.js';
import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js'; import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js';
import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js'; import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js';
import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; import * as ep___admin_drive_files from './endpoints/admin/drive/files.js';
@ -385,8 +385,8 @@ const $admin_avatarDecorations_delete: Provider = { provide: 'ep:admin/avatar-de
const $admin_avatarDecorations_list: Provider = { provide: 'ep:admin/avatar-decorations/list', useClass: ep___admin_avatarDecorations_list.default }; const $admin_avatarDecorations_list: Provider = { provide: 'ep:admin/avatar-decorations/list', useClass: ep___admin_avatarDecorations_list.default };
const $admin_avatarDecorations_update: Provider = { provide: 'ep:admin/avatar-decorations/update', useClass: ep___admin_avatarDecorations_update.default }; const $admin_avatarDecorations_update: Provider = { provide: 'ep:admin/avatar-decorations/update', useClass: ep___admin_avatarDecorations_update.default };
const $admin_deleteAllFilesOfAUser: Provider = { provide: 'ep:admin/delete-all-files-of-a-user', useClass: ep___admin_deleteAllFilesOfAUser.default }; const $admin_deleteAllFilesOfAUser: Provider = { provide: 'ep:admin/delete-all-files-of-a-user', useClass: ep___admin_deleteAllFilesOfAUser.default };
const $admin_deleteUserAvatar: Provider = { provide: 'ep:admin/delete-user-avatar', useClass: ep___admin_deleteUserAvatar.default }; const $admin_unsetUserAvatar: Provider = { provide: 'ep:admin/unset-user-avatar', useClass: ep___admin_unsetUserAvatar.default };
const $admin_deleteUserBanner: Provider = { provide: 'ep:admin/delete-user-banner', useClass: ep___admin_deleteUserBanner.default }; const $admin_unsetUserBanner: Provider = { provide: 'ep:admin/unset-user-banner', useClass: ep___admin_unsetUserBanner.default };
const $admin_drive_cleanRemoteFiles: Provider = { provide: 'ep:admin/drive/clean-remote-files', useClass: ep___admin_drive_cleanRemoteFiles.default }; const $admin_drive_cleanRemoteFiles: Provider = { provide: 'ep:admin/drive/clean-remote-files', useClass: ep___admin_drive_cleanRemoteFiles.default };
const $admin_drive_cleanup: Provider = { provide: 'ep:admin/drive/cleanup', useClass: ep___admin_drive_cleanup.default }; const $admin_drive_cleanup: Provider = { provide: 'ep:admin/drive/cleanup', useClass: ep___admin_drive_cleanup.default };
const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default }; const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default };
@ -750,8 +750,8 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$admin_avatarDecorations_list, $admin_avatarDecorations_list,
$admin_avatarDecorations_update, $admin_avatarDecorations_update,
$admin_deleteAllFilesOfAUser, $admin_deleteAllFilesOfAUser,
$admin_deleteUserAvatar, $admin_unsetUserAvatar,
$admin_deleteUserBanner, $admin_unsetUserBanner,
$admin_drive_cleanRemoteFiles, $admin_drive_cleanRemoteFiles,
$admin_drive_cleanup, $admin_drive_cleanup,
$admin_drive_files, $admin_drive_files,
@ -1109,8 +1109,8 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$admin_avatarDecorations_list, $admin_avatarDecorations_list,
$admin_avatarDecorations_update, $admin_avatarDecorations_update,
$admin_deleteAllFilesOfAUser, $admin_deleteAllFilesOfAUser,
$admin_deleteUserAvatar, $admin_unsetUserAvatar,
$admin_deleteUserBanner, $admin_unsetUserBanner,
$admin_drive_cleanRemoteFiles, $admin_drive_cleanRemoteFiles,
$admin_drive_cleanup, $admin_drive_cleanup,
$admin_drive_files, $admin_drive_files,

View file

@ -24,8 +24,8 @@ import * as ep___admin_avatarDecorations_delete from './endpoints/admin/avatar-d
import * as ep___admin_avatarDecorations_list from './endpoints/admin/avatar-decorations/list.js'; import * as ep___admin_avatarDecorations_list from './endpoints/admin/avatar-decorations/list.js';
import * as ep___admin_avatarDecorations_update from './endpoints/admin/avatar-decorations/update.js'; import * as ep___admin_avatarDecorations_update from './endpoints/admin/avatar-decorations/update.js';
import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js'; import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js';
import * as ep___admin_deleteUserAvatar from './endpoints/admin/delete-user-avatar.js'; import * as ep___admin_unsetUserAvatar from './endpoints/admin/unset-user-avatar.js';
import * as ep___admin_deleteUserBanner from './endpoints/admin/delete-user-banner.js'; import * as ep___admin_unsetUserBanner from './endpoints/admin/unset-user-banner.js';
import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js'; import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js';
import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js'; import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js';
import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; import * as ep___admin_drive_files from './endpoints/admin/drive/files.js';
@ -383,8 +383,8 @@ const eps = [
['admin/avatar-decorations/list', ep___admin_avatarDecorations_list], ['admin/avatar-decorations/list', ep___admin_avatarDecorations_list],
['admin/avatar-decorations/update', ep___admin_avatarDecorations_update], ['admin/avatar-decorations/update', ep___admin_avatarDecorations_update],
['admin/delete-all-files-of-a-user', ep___admin_deleteAllFilesOfAUser], ['admin/delete-all-files-of-a-user', ep___admin_deleteAllFilesOfAUser],
['admin/delete-user-avatar', ep___admin_deleteUserAvatar], ['admin/unset-user-avatar', ep___admin_unsetUserAvatar],
['admin/delete-user-banner', ep___admin_deleteUserBanner], ['admin/unset-user-banner', ep___admin_unsetUserBanner],
['admin/drive/clean-remote-files', ep___admin_drive_cleanRemoteFiles], ['admin/drive/clean-remote-files', ep___admin_drive_cleanRemoteFiles],
['admin/drive/cleanup', ep___admin_drive_cleanup], ['admin/drive/cleanup', ep___admin_drive_cleanup],
['admin/drive/files', ep___admin_drive_files], ['admin/drive/files', ep___admin_drive_files],

View file

@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import type { UsersRepository } from '@/models/_.js'; import type { UsersRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.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'],
@ -29,6 +30,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
constructor( constructor(
@Inject(DI.usersRepository) @Inject(DI.usersRepository)
private usersRepository: UsersRepository, private usersRepository: UsersRepository,
private moderationLogService: ModerationLogService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const user = await this.usersRepository.findOneBy({ id: ps.userId }); const user = await this.usersRepository.findOneBy({ id: ps.userId });
@ -36,6 +39,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
if (user == null) { if (user == null) {
throw new Error('user not found'); throw new Error('user not found');
} }
if (user.avatarId == null) return;
await this.usersRepository.update(user.id, { await this.usersRepository.update(user.id, {
avatar: null, avatar: null,
@ -43,6 +48,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
avatarUrl: null, avatarUrl: null,
avatarBlurhash: null, avatarBlurhash: null,
}); });
this.moderationLogService.log(me, 'unsetUserAvatar', {
userId: user.id,
userUsername: user.username,
userHost: user.host,
fileId: user.avatarId,
});
}); });
} }
} }

View file

@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import type { UsersRepository } from '@/models/_.js'; import type { UsersRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.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'],
@ -29,6 +30,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
constructor( constructor(
@Inject(DI.usersRepository) @Inject(DI.usersRepository)
private usersRepository: UsersRepository, private usersRepository: UsersRepository,
private moderationLogService: ModerationLogService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const user = await this.usersRepository.findOneBy({ id: ps.userId }); const user = await this.usersRepository.findOneBy({ id: ps.userId });
@ -37,12 +40,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
throw new Error('user not found'); throw new Error('user not found');
} }
if (user.bannerId == null) return;
await this.usersRepository.update(user.id, { await this.usersRepository.update(user.id, {
banner: null, banner: null,
bannerId: null, bannerId: null,
bannerUrl: null, bannerUrl: null,
bannerBlurhash: null, bannerBlurhash: null,
}); });
this.moderationLogService.log(me, 'unsetUserBanner', {
userId: user.id,
userUsername: user.username,
userHost: user.host,
fileId: user.bannerId,
});
}); });
} }
} }

View file

@ -63,6 +63,8 @@ export const moderationLogTypes = [
'createAvatarDecoration', 'createAvatarDecoration',
'updateAvatarDecoration', 'updateAvatarDecoration',
'deleteAvatarDecoration', 'deleteAvatarDecoration',
'unsetUserAvatar',
'unsetUserBanner',
] as const; ] as const;
export type ModerationLogPayloads = { export type ModerationLogPayloads = {
@ -237,6 +239,18 @@ export type ModerationLogPayloads = {
avatarDecorationId: string; avatarDecorationId: string;
avatarDecoration: any; avatarDecoration: any;
}; };
unsetUserAvatar: {
userId: string;
userUsername: string;
userHost: string | null;
fileId: string;
};
unsetUserBanner: {
userId: string;
userUsername: string;
userHost: string | null;
fileId: string;
};
}; };
export type Serialized<T> = { export type Serialized<T> = {

View file

@ -123,8 +123,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkFolder> </MkFolder>
<div> <div>
<MkButton v-if="iAmModerator" inline danger style="margin-right: 8px;" @click="deleteUserAvatar"><i class="ti ti-user-circle"></i> {{ i18n.ts.deleteUserAvatar }}</MkButton> <MkButton v-if="iAmModerator" inline danger style="margin-right: 8px;" @click="unsetUserAvatar"><i class="ti ti-user-circle"></i> {{ i18n.ts.unsetUserAvatar }}</MkButton>
<MkButton v-if="iAmModerator" inline danger @click="deleteUserBanner"><i class="ti ti-photo"></i> {{ i18n.ts.deleteUserBanner }}</MkButton> <MkButton v-if="iAmModerator" inline danger @click="unsetUserBanner"><i class="ti ti-photo"></i> {{ i18n.ts.unsetUserBanner }}</MkButton>
</div> </div>
<MkButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</MkButton> <MkButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</MkButton>
</div> </div>
@ -324,14 +324,14 @@ async function toggleSuspend(v) {
} }
} }
async function deleteUserAvatar() { async function unsetUserAvatar() {
const confirm = await os.confirm({ const confirm = await os.confirm({
type: 'warning', type: 'warning',
text: i18n.ts.deleteUserAvatarConfirm, text: i18n.ts.unsetUserAvatarConfirm,
}); });
if (confirm.canceled) return; if (confirm.canceled) return;
const process = async () => { const process = async () => {
await os.api('admin/delete-user-avatar', { userId: user.id }); await os.api('admin/unset-user-avatar', { userId: user.id });
os.success(); os.success();
}; };
await process().catch(err => { await process().catch(err => {
@ -343,14 +343,14 @@ async function deleteUserAvatar() {
refreshUser(); refreshUser();
} }
async function deleteUserBanner() { async function unsetUserBanner() {
const confirm = await os.confirm({ const confirm = await os.confirm({
type: 'warning', type: 'warning',
text: i18n.ts.deleteUserBannerConfirm, text: i18n.ts.unsetUserBannerConfirm,
}); });
if (confirm.canceled) return; if (confirm.canceled) return;
const process = async () => { const process = async () => {
await os.api('admin/delete-user-banner', { userId: user.id }); await os.api('admin/unset-user-banner', { userId: user.id });
os.success(); os.success();
}; };
await process().catch(err => { await process().catch(err => {

View file

@ -345,13 +345,13 @@ export type Endpoints = {
}; };
res: null; res: null;
}; };
'admin/delete-user-avatar': { 'admin/unset-user-avatar': {
req: { req: {
userId: User['id']; userId: User['id'];
}; };
res: null; res: null;
}; };
'admin/delete-user-banner': { 'admin/unset-user-banner': {
req: { req: {
userId: User['id']; userId: User['id'];
}; };

View file

@ -15,8 +15,8 @@ export type Endpoints = {
// admin // admin
'admin/abuse-user-reports': { req: TODO; res: TODO; }; 'admin/abuse-user-reports': { req: TODO; res: TODO; };
'admin/delete-all-files-of-a-user': { req: { userId: User['id']; }; res: null; }; 'admin/delete-all-files-of-a-user': { req: { userId: User['id']; }; res: null; };
'admin/delete-user-avatar': { req: { userId: User['id']; }; res: null; }; 'admin/unset-user-avatar': { req: { userId: User['id']; }; res: null; };
'admin/delete-user-banner': { req: { userId: User['id']; }; res: null; }; 'admin/unset-user-banner': { req: { userId: User['id']; }; res: null; };
'admin/delete-logs': { req: NoParams; res: null; }; 'admin/delete-logs': { req: NoParams; res: null; };
'admin/get-index-stats': { req: TODO; res: TODO; }; 'admin/get-index-stats': { req: TODO; res: TODO; };
'admin/get-table-stats': { req: TODO; res: TODO; }; 'admin/get-table-stats': { req: TODO; res: TODO; };

View file

@ -81,6 +81,8 @@ export const moderationLogTypes = [
'createAvatarDecoration', 'createAvatarDecoration',
'updateAvatarDecoration', 'updateAvatarDecoration',
'deleteAvatarDecoration', 'deleteAvatarDecoration',
'unsetUserAvatar',
'unsetUserBanner',
] as const; ] as const;
export type ModerationLogPayloads = { export type ModerationLogPayloads = {
@ -255,4 +257,16 @@ export type ModerationLogPayloads = {
avatarDecorationId: string; avatarDecorationId: string;
avatarDecoration: any; avatarDecoration: any;
}; };
unsetUserAvatar: {
userId: string;
userUsername: string;
userHost: string | null;
fileId: string;
};
unsetUserBanner: {
userId: string;
userUsername: string;
userHost: string | null;
fileId: string;
};
}; };

View file

@ -727,4 +727,10 @@ export type ModerationLog = {
} | { } | {
type: 'resolveAbuseReport'; type: 'resolveAbuseReport';
info: ModerationLogPayloads['resolveAbuseReport']; info: ModerationLogPayloads['resolveAbuseReport'];
} | {
type: 'unsetUserAvatar';
info: ModerationLogPayloads['unsetUserAvatar'];
} | {
type: 'unsetUserBanner';
info: ModerationLogPayloads['unsetUserBanner'];
}); });