parent
							
								
									72a49f334a
								
							
						
					
					
						commit
						9208825975
					
				
					 6 changed files with 80 additions and 7 deletions
				
			
		|  | @ -13,6 +13,8 @@ | |||
| - ActivityPub: リモートユーザーのDeleteアクティビティに対応 | ||||
| - ActivityPub: add resolver check for blocked instance | ||||
| - ActivityPub: deliverキューのメモリ使用量を削減 | ||||
| - API: 管理者用アカウント削除APIを実装(/admin/accounts/delete) | ||||
| 	- リモートユーザーの削除も可能に | ||||
| - アカウントが凍結された場合に、凍結された旨を表示してからログアウトするように | ||||
| - 凍結されたアカウントにログインしようとしたときに、凍結されている旨を表示するように | ||||
| - リスト、アンテナタイムラインを個別ページとして分割 | ||||
|  |  | |||
|  | @ -173,9 +173,10 @@ export function createImportUserListsJob(user: ThinUser, fileId: DriveFile['id'] | |||
| 	}); | ||||
| } | ||||
| 
 | ||||
| export function createDeleteAccountJob(user: ThinUser) { | ||||
| export function createDeleteAccountJob(user: ThinUser, opts: { soft?: boolean; }) { | ||||
| 	return dbQueue.add('deleteAccount', { | ||||
| 		user: user | ||||
| 		user: user, | ||||
| 		soft: opts.soft | ||||
| 	}, { | ||||
| 		removeOnComplete: true, | ||||
| 		removeOnFail: true | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import * as Bull from 'bull'; | ||||
| import { queueLogger } from '../../logger'; | ||||
| import { DriveFiles, Notes, UserProfiles, Users } from '@/models/index'; | ||||
| import { DbUserJobData } from '@/queue/types'; | ||||
| import { DbUserDeleteJobData } from '@/queue/types'; | ||||
| import { Note } from '@/models/entities/note'; | ||||
| import { DriveFile } from '@/models/entities/drive-file'; | ||||
| import { MoreThan } from 'typeorm'; | ||||
|  | @ -10,7 +10,7 @@ import { sendEmail } from '@/services/send-email'; | |||
| 
 | ||||
| const logger = queueLogger.createSubLogger('delete-account'); | ||||
| 
 | ||||
| export async function deleteAccount(job: Bull.Job<DbUserJobData>): Promise<string | void> { | ||||
| export async function deleteAccount(job: Bull.Job<DbUserDeleteJobData>): Promise<string | void> { | ||||
| 	logger.info(`Deleting account of ${job.data.user.id} ...`); | ||||
| 
 | ||||
| 	const user = await Users.findOne(job.data.user.id); | ||||
|  | @ -83,7 +83,12 @@ export async function deleteAccount(job: Bull.Job<DbUserJobData>): Promise<strin | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	await Users.delete(job.data.user.id); | ||||
| 	// soft指定されている場合は物理削除しない
 | ||||
| 	if (job.data.soft) { | ||||
| 		// nop
 | ||||
| 	} else { | ||||
| 		await Users.delete(job.data.user.id); | ||||
| 	} | ||||
| 
 | ||||
| 	return 'Account deleted'; | ||||
| } | ||||
|  |  | |||
|  | @ -17,12 +17,17 @@ export type InboxJobData = { | |||
| 	signature: httpSignature.IParsedSignature; | ||||
| }; | ||||
| 
 | ||||
| export type DbJobData = DbUserJobData | DbUserImportJobData; | ||||
| export type DbJobData = DbUserJobData | DbUserImportJobData | DbUserDeleteJobData; | ||||
| 
 | ||||
| export type DbUserJobData = { | ||||
| 	user: ThinUser; | ||||
| }; | ||||
| 
 | ||||
| export type DbUserDeleteJobData = { | ||||
| 	user: ThinUser; | ||||
| 	soft?: boolean; | ||||
| }; | ||||
| 
 | ||||
| export type DbUserImportJobData = { | ||||
| 	user: ThinUser; | ||||
| 	fileId: DriveFile['id']; | ||||
|  |  | |||
							
								
								
									
										58
									
								
								src/server/api/endpoints/admin/accounts/delete.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/server/api/endpoints/admin/accounts/delete.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,58 @@ | |||
| import $ from 'cafy'; | ||||
| import define from '../../../define'; | ||||
| import { Users } from '@/models/index'; | ||||
| import { doPostSuspend } from '@/services/suspend-user'; | ||||
| import { publishUserEvent } from '@/services/stream'; | ||||
| import { createDeleteAccountJob } from '@/queue'; | ||||
| import { ID } from '@/misc/cafy-id'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	tags: ['admin'], | ||||
| 
 | ||||
| 	requireCredential: true as const, | ||||
| 	requireModerator: true, | ||||
| 
 | ||||
| 	params: { | ||||
| 		userId: { | ||||
| 			validator: $.type(ID), | ||||
| 		}, | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| export default define(meta, async (ps, me) => { | ||||
| 	const user = await Users.findOne(ps.userId); | ||||
| 
 | ||||
| 	if (user == null) { | ||||
| 		throw new Error('user not found'); | ||||
| 	} | ||||
| 
 | ||||
| 	if (user.isAdmin) { | ||||
| 		throw new Error('cannot suspend admin'); | ||||
| 	} | ||||
| 
 | ||||
| 	if (user.isModerator) { | ||||
| 		throw new Error('cannot suspend moderator'); | ||||
| 	} | ||||
| 
 | ||||
| 	if (Users.isLocalUser(user)) { | ||||
| 		// 物理削除する前にDelete activityを送信する
 | ||||
| 		await doPostSuspend(user).catch(e => {}); | ||||
| 
 | ||||
| 		createDeleteAccountJob(user, { | ||||
| 			soft: false | ||||
| 		}); | ||||
| 	} else { | ||||
| 		createDeleteAccountJob(user, { | ||||
| 			soft: true // リモートユーザーの削除は、完全にDBから物理削除してしまうと再度連合してきてアカウントが復活する可能性があるため、soft指定する
 | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	await Users.update(user.id, { | ||||
| 		isDeleted: true, | ||||
| 	}); | ||||
| 
 | ||||
| 	if (Users.isLocalUser(user)) { | ||||
| 		// Terminate streaming
 | ||||
| 		publishUserEvent(user.id, 'terminate', {}); | ||||
| 	} | ||||
| }); | ||||
|  | @ -35,7 +35,9 @@ export default define(meta, async (ps, user) => { | |||
| 	// 物理削除する前にDelete activityを送信する
 | ||||
| 	await doPostSuspend(user).catch(e => {}); | ||||
| 
 | ||||
| 	createDeleteAccountJob(user); | ||||
| 	createDeleteAccountJob(user, { | ||||
| 		soft: false | ||||
| 	}); | ||||
| 
 | ||||
| 	await Users.update(user.id, { | ||||
| 		isDeleted: true, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue