parent
							
								
									bc3ae901cc
								
							
						
					
					
						commit
						329f055a97
					
				
					 7 changed files with 90 additions and 18 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue