feat(moderation): モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (MisskeyIO#222)
This commit is contained in:
parent
0ff83829ba
commit
7b53f66541
|
@ -558,6 +558,10 @@ output: "Output"
|
|||
script: "Script"
|
||||
disablePagesScript: "Disable AiScript on Pages"
|
||||
updateRemoteUser: "Update remote user information"
|
||||
deleteUserAvatar: "Delete user icon"
|
||||
deleteUserAvatarConfirm: "Are you sure that you want to delete this user's icon?"
|
||||
deleteUserBanner: "Delete user banner"
|
||||
deleteUserBannerConfirm: "Are you sure that you want to delete this user's banner?"
|
||||
deleteAllFiles: "Delete all files"
|
||||
deleteAllFilesConfirm: "Are you sure that you want to delete all files?"
|
||||
removeAllFollowing: "Unfollow all followed users"
|
||||
|
|
4
locales/index.d.ts
vendored
4
locales/index.d.ts
vendored
|
@ -561,6 +561,10 @@ export interface Locale {
|
|||
"script": string;
|
||||
"disablePagesScript": string;
|
||||
"updateRemoteUser": string;
|
||||
"deleteUserAvatar": string;
|
||||
"deleteUserAvatarConfirm": string;
|
||||
"deleteUserBanner": string;
|
||||
"deleteUserBannerConfirm": string;
|
||||
"deleteAllFiles": string;
|
||||
"deleteAllFilesConfirm": string;
|
||||
"removeAllFollowing": string;
|
||||
|
|
|
@ -558,6 +558,10 @@ output: "出力"
|
|||
script: "スクリプト"
|
||||
disablePagesScript: "Pagesのスクリプトを無効にする"
|
||||
updateRemoteUser: "リモートユーザー情報の更新"
|
||||
deleteUserAvatar: "アイコンを削除"
|
||||
deleteUserAvatarConfirm: "アイコンを削除しますか?"
|
||||
deleteUserBanner: "バナーを削除"
|
||||
deleteUserBannerConfirm: "バナーを削除しますか?"
|
||||
deleteAllFiles: "すべてのファイルを削除"
|
||||
deleteAllFilesConfirm: "すべてのファイルを削除しますか?"
|
||||
removeAllFollowing: "フォローを全解除"
|
||||
|
|
|
@ -23,6 +23,8 @@ import * as ep___admin_abuseReportResolver_update from './endpoints/admin/abuse-
|
|||
import * as ep___admin_abuseReportResolver_delete from './endpoints/admin/abuse-report-resolver/delete.js';
|
||||
import * as ep___admin_abuseReportResolver_list from './endpoints/admin/abuse-report-resolver/list.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_deleteUserBanner from './endpoints/admin/delete-user-banner.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_files from './endpoints/admin/drive/files.js';
|
||||
|
@ -372,6 +374,8 @@ const $admin_abuseReportResolver_update: Provider = { provide: 'ep:admin/abuse-r
|
|||
const $admin_abuseReportResolver_list: Provider = { provide: 'ep:admin/abuse-report-resolver/list', useClass: ep___admin_abuseReportResolver_list.default };
|
||||
const $admin_abuseReportResolver_delete: Provider = { provide: 'ep:admin/abuse-report-resolver/delete', useClass: ep___admin_abuseReportResolver_delete.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_deleteUserBanner: Provider = { provide: 'ep:admin/delete-user-banner', useClass: ep___admin_deleteUserBanner.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_files: Provider = { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default };
|
||||
|
@ -725,6 +729,8 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||
$admin_abuseReportResolver_list,
|
||||
$admin_abuseReportResolver_update,
|
||||
$admin_deleteAllFilesOfAUser,
|
||||
$admin_deleteUserAvatar,
|
||||
$admin_deleteUserBanner,
|
||||
$admin_drive_cleanRemoteFiles,
|
||||
$admin_drive_cleanup,
|
||||
$admin_drive_files,
|
||||
|
@ -1072,6 +1078,8 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||
$admin_abuseReportResolver_list,
|
||||
$admin_abuseReportResolver_update,
|
||||
$admin_deleteAllFilesOfAUser,
|
||||
$admin_deleteUserAvatar,
|
||||
$admin_deleteUserBanner,
|
||||
$admin_drive_cleanRemoteFiles,
|
||||
$admin_drive_cleanup,
|
||||
$admin_drive_files,
|
||||
|
|
|
@ -23,6 +23,8 @@ import * as ep___admin_abuseReportResolver_update from './endpoints/admin/abuse-
|
|||
import * as ep___admin_abuseReportResolver_delete from './endpoints/admin/abuse-report-resolver/delete.js';
|
||||
import * as ep___admin_abuseReportResolver_list from './endpoints/admin/abuse-report-resolver/list.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_deleteUserBanner from './endpoints/admin/delete-user-banner.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_files from './endpoints/admin/drive/files.js';
|
||||
|
@ -370,6 +372,8 @@ const eps = [
|
|||
['admin/abuse-report-resolver/delete', ep___admin_abuseReportResolver_delete],
|
||||
['admin/abuse-report-resolver/update', ep___admin_abuseReportResolver_update],
|
||||
['admin/delete-all-files-of-a-user', ep___admin_deleteAllFilesOfAUser],
|
||||
['admin/delete-user-avatar', ep___admin_deleteUserAvatar],
|
||||
['admin/delete-user-banner', ep___admin_deleteUserBanner],
|
||||
['admin/drive/clean-remote-files', ep___admin_drive_cleanRemoteFiles],
|
||||
['admin/drive/cleanup', ep___admin_drive_cleanup],
|
||||
['admin/drive/files', ep___admin_drive_files],
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { UsersRepository } from '@/models/index.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['userId'],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
constructor(
|
||||
@Inject(DI.usersRepository)
|
||||
private usersRepository: UsersRepository,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const user = await this.usersRepository.findOneBy({ id: ps.userId });
|
||||
|
||||
if (user == null) {
|
||||
throw new Error('user not found');
|
||||
}
|
||||
|
||||
await this.usersRepository.update(user.id, {
|
||||
avatar: null,
|
||||
avatarId: null,
|
||||
avatarUrl: null,
|
||||
avatarBlurhash: null,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { UsersRepository } from '@/models/index.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['userId'],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
constructor(
|
||||
@Inject(DI.usersRepository)
|
||||
private usersRepository: UsersRepository,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const user = await this.usersRepository.findOneBy({ id: ps.userId });
|
||||
|
||||
if (user == null) {
|
||||
throw new Error('user not found');
|
||||
}
|
||||
|
||||
await this.usersRepository.update(user.id, {
|
||||
banner: null,
|
||||
bannerId: null,
|
||||
bannerUrl: null,
|
||||
bannerBlurhash: null,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -100,6 +100,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkButton v-if="user.host == null && iAmModerator" inline style="margin-right: 8px;" @click="resetPassword"><i class="ti ti-key"></i> {{ i18n.ts.resetPassword }}</MkButton>
|
||||
<MkButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</MkButton>
|
||||
</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 @click="deleteUserBanner"><i class="ti ti-photo"></i> {{ i18n.ts.deleteUserBanner }}</MkButton>
|
||||
</div>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-license"></i></template>
|
||||
|
@ -345,6 +349,44 @@ async function toggleSuspend(v) {
|
|||
}
|
||||
}
|
||||
|
||||
async function deleteUserAvatar() {
|
||||
const confirm = await os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.ts.deleteUserAvatarConfirm,
|
||||
});
|
||||
if (confirm.canceled) return;
|
||||
const process = async () => {
|
||||
await os.api('admin/delete-user-avatar', { userId: user.id });
|
||||
os.success();
|
||||
};
|
||||
await process().catch(err => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: err.toString(),
|
||||
});
|
||||
});
|
||||
refreshUser();
|
||||
}
|
||||
|
||||
async function deleteUserBanner() {
|
||||
const confirm = await os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.ts.deleteUserBannerConfirm,
|
||||
});
|
||||
if (confirm.canceled) return;
|
||||
const process = async () => {
|
||||
await os.api('admin/delete-user-banner', { userId: user.id });
|
||||
os.success();
|
||||
};
|
||||
await process().catch(err => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: err.toString(),
|
||||
});
|
||||
});
|
||||
refreshUser();
|
||||
}
|
||||
|
||||
async function deleteAllFiles() {
|
||||
const confirm = await os.confirm({
|
||||
type: 'warning',
|
||||
|
|
|
@ -323,6 +323,18 @@ export type Endpoints = {
|
|||
};
|
||||
res: null;
|
||||
};
|
||||
'admin/delete-user-avatar': {
|
||||
req: {
|
||||
userId: User['id'];
|
||||
};
|
||||
res: null;
|
||||
};
|
||||
'admin/delete-user-banner': {
|
||||
req: {
|
||||
userId: User['id'];
|
||||
};
|
||||
res: null;
|
||||
};
|
||||
'admin/delete-logs': {
|
||||
req: NoParams;
|
||||
res: null;
|
||||
|
@ -2844,8 +2856,8 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u
|
|||
// Warnings were encountered during analysis:
|
||||
//
|
||||
// src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts
|
||||
// src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts
|
||||
// src/api.types.ts:633:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
|
||||
// src/api.types.ts:20:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts
|
||||
// src/api.types.ts:635:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
|
||||
// src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
|
|
|
@ -15,6 +15,8 @@ export type Endpoints = {
|
|||
// admin
|
||||
'admin/abuse-user-reports': { req: TODO; res: TODO; };
|
||||
'admin/delete-all-files-of-a-user': { req: { userId: User['id']; }; res: null; };
|
||||
'admin/delete-user-avatar': { req: { userId: User['id']; }; res: null; };
|
||||
'admin/delete-user-banner': { req: { userId: User['id']; }; res: null; };
|
||||
'admin/delete-logs': { req: NoParams; res: null; };
|
||||
'admin/get-index-stats': { req: TODO; res: TODO; };
|
||||
'admin/get-table-stats': { req: TODO; res: TODO; };
|
||||
|
|
Loading…
Reference in a new issue