parent
bc3ae901cc
commit
329f055a97
|
@ -16,6 +16,7 @@ You should also include the user name that made the change.
|
||||||
- Server: Add rate limit to i/notifications @tamaina
|
- Server: Add rate limit to i/notifications @tamaina
|
||||||
- Client: Improve control panel @syuilo
|
- Client: Improve control panel @syuilo
|
||||||
- Client: Show warning in control panel when there is an unresolved abuse report @syuilo
|
- Client: Show warning in control panel when there is an unresolved abuse report @syuilo
|
||||||
|
- Make possible to delete an account by admin @syuilo
|
||||||
- Improve player detection in URL preview @mei23
|
- Improve player detection in URL preview @mei23
|
||||||
- Add Badge Image to Push Notification #8012 @tamaina
|
- Add Badge Image to Push Notification #8012 @tamaina
|
||||||
- Client: Removing entries from a clip @futchitwo
|
- Client: Removing entries from a clip @futchitwo
|
||||||
|
|
|
@ -855,6 +855,8 @@ thereIsUnresolvedAbuseReportWarning: "未対応の通報があります。"
|
||||||
recommended: "推奨"
|
recommended: "推奨"
|
||||||
check: "チェック"
|
check: "チェック"
|
||||||
isSystemAccount: "システムにより自動で作成・管理されているアカウントです。"
|
isSystemAccount: "システムにより自動で作成・管理されているアカウントです。"
|
||||||
|
typeToConfirm: "この操作を行うには {x} と入力してください"
|
||||||
|
deleteAccount: "アカウント削除"
|
||||||
|
|
||||||
_emailUnavailable:
|
_emailUnavailable:
|
||||||
used: "既に使用されています"
|
used: "既に使用されています"
|
||||||
|
|
|
@ -59,6 +59,7 @@ import * as ep___admin_unsilenceUser from './endpoints/admin/unsilence-user.js';
|
||||||
import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js';
|
import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js';
|
||||||
import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js';
|
import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js';
|
||||||
import * as ep___admin_vacuum from './endpoints/admin/vacuum.js';
|
import * as ep___admin_vacuum from './endpoints/admin/vacuum.js';
|
||||||
|
import * as ep___admin_deleteAccount from './endpoints/admin/delete-account.js';
|
||||||
import * as ep___announcements from './endpoints/announcements.js';
|
import * as ep___announcements from './endpoints/announcements.js';
|
||||||
import * as ep___antennas_create from './endpoints/antennas/create.js';
|
import * as ep___antennas_create from './endpoints/antennas/create.js';
|
||||||
import * as ep___antennas_delete from './endpoints/antennas/delete.js';
|
import * as ep___antennas_delete from './endpoints/antennas/delete.js';
|
||||||
|
@ -370,6 +371,7 @@ const eps = [
|
||||||
['admin/unsuspend-user', ep___admin_unsuspendUser],
|
['admin/unsuspend-user', ep___admin_unsuspendUser],
|
||||||
['admin/update-meta', ep___admin_updateMeta],
|
['admin/update-meta', ep___admin_updateMeta],
|
||||||
['admin/vacuum', ep___admin_vacuum],
|
['admin/vacuum', ep___admin_vacuum],
|
||||||
|
['admin/delete-account', ep___admin_deleteAccount],
|
||||||
['announcements', ep___announcements],
|
['announcements', ep___announcements],
|
||||||
['antennas/create', ep___antennas_create],
|
['antennas/create', ep___antennas_create],
|
||||||
['antennas/delete', ep___antennas_delete],
|
['antennas/delete', ep___antennas_delete],
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { Users } from '@/models/index.js';
|
||||||
|
import { deleteAccount } from '@/services/delete-account.js';
|
||||||
|
import define from '../../define.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['admin'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
requireAdmin: true,
|
||||||
|
|
||||||
|
res: {
|
||||||
|
},
|
||||||
|
} 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
|
||||||
|
export default define(meta, paramDef, async (ps) => {
|
||||||
|
const user = await Users.findOneByOrFail({ id: ps.userId });
|
||||||
|
if (user.isDeleted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await deleteAccount(user);
|
||||||
|
});
|
|
@ -1,9 +1,7 @@
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import define from '../../define.js';
|
|
||||||
import { UserProfiles, Users } from '@/models/index.js';
|
import { UserProfiles, Users } from '@/models/index.js';
|
||||||
import { doPostSuspend } from '@/services/suspend-user.js';
|
import { deleteAccount } from '@/services/delete-account.js';
|
||||||
import { publishUserEvent } from '@/services/stream.js';
|
import define from '../../define.js';
|
||||||
import { createDeleteAccountJob } from '@/queue/index.js';
|
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
@ -34,17 +32,5 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
throw new Error('incorrect password');
|
throw new Error('incorrect password');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 物理削除する前にDelete activityを送信する
|
await deleteAccount(user);
|
||||||
await doPostSuspend(user).catch(e => {});
|
|
||||||
|
|
||||||
createDeleteAccountJob(user, {
|
|
||||||
soft: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
await Users.update(user.id, {
|
|
||||||
isDeleted: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Terminate streaming
|
|
||||||
publishUserEvent(user.id, 'terminate', {});
|
|
||||||
});
|
});
|
||||||
|
|
23
packages/backend/src/services/delete-account.ts
Normal file
23
packages/backend/src/services/delete-account.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { Users } from '@/models/index.js';
|
||||||
|
import { createDeleteAccountJob } from '@/queue/index.js';
|
||||||
|
import { publishUserEvent } from './stream.js';
|
||||||
|
import { doPostSuspend } from './suspend-user.js';
|
||||||
|
|
||||||
|
export async function deleteAccount(user: {
|
||||||
|
id: string;
|
||||||
|
host: string | null;
|
||||||
|
}): Promise<void> {
|
||||||
|
// 物理削除する前にDelete activityを送信する
|
||||||
|
await doPostSuspend(user).catch(e => {});
|
||||||
|
|
||||||
|
createDeleteAccountJob(user, {
|
||||||
|
soft: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await Users.update(user.id, {
|
||||||
|
isDeleted: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Terminate streaming
|
||||||
|
publishUserEvent(user.id, 'terminate', {});
|
||||||
|
}
|
|
@ -35,7 +35,10 @@
|
||||||
<FormSwitch v-model="silenced" class="_formBlock" @update:modelValue="toggleSilence">{{ $ts.silence }}</FormSwitch>
|
<FormSwitch v-model="silenced" class="_formBlock" @update:modelValue="toggleSilence">{{ $ts.silence }}</FormSwitch>
|
||||||
<FormSwitch v-model="suspended" class="_formBlock" @update:modelValue="toggleSuspend">{{ $ts.suspend }}</FormSwitch>
|
<FormSwitch v-model="suspended" class="_formBlock" @update:modelValue="toggleSuspend">{{ $ts.suspend }}</FormSwitch>
|
||||||
{{ $ts.reflectMayTakeTime }}
|
{{ $ts.reflectMayTakeTime }}
|
||||||
<FormButton v-if="user.host == null && iAmModerator" class="_formBlock" @click="resetPassword"><i class="fas fa-key"></i> {{ $ts.resetPassword }}</FormButton>
|
<div class="_formBlock">
|
||||||
|
<FormButton v-if="user.host == null && iAmModerator" inline style="margin-right: 8px;" @click="resetPassword"><i class="fas fa-key"></i> {{ $ts.resetPassword }}</FormButton>
|
||||||
|
<FormButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ $ts.deleteAccount }}</FormButton>
|
||||||
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
|
@ -233,6 +236,30 @@ async function deleteAllFiles() {
|
||||||
await refreshUser();
|
await refreshUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function deleteAccount() {
|
||||||
|
const confirm = await os.confirm({
|
||||||
|
type: 'warning',
|
||||||
|
text: i18n.ts.deleteAccountConfirm,
|
||||||
|
});
|
||||||
|
if (confirm.canceled) return;
|
||||||
|
|
||||||
|
const typed = await os.inputText({
|
||||||
|
text: i18n.t('typeToConfirm', { x: user?.username }),
|
||||||
|
});
|
||||||
|
if (typed.canceled) return;
|
||||||
|
|
||||||
|
if (typed.result === user?.username) {
|
||||||
|
await os.apiWithDialog('admin/delete-account', {
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
os.alert({
|
||||||
|
type: 'error',
|
||||||
|
text: 'input not match',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(() => props.userId, () => {
|
watch(() => props.userId, () => {
|
||||||
init = createFetcher();
|
init = createFetcher();
|
||||||
}, {
|
}, {
|
||||||
|
|
Loading…
Reference in a new issue