summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-08-21 12:41:56 +0900
committerGitHub <noreply@github.com>2021-08-21 12:41:56 +0900
commitfd1ef4a62d670aab5f0c0089ab3806639c779813 (patch)
tree0a13e5827e0a45dcc9aa592ea09a968369ff0058 /src
parentfix bug (diff)
downloadmisskey-fd1ef4a62d670aab5f0c0089ab3806639c779813.tar.gz
misskey-fd1ef4a62d670aab5f0c0089ab3806639c779813.tar.bz2
misskey-fd1ef4a62d670aab5f0c0089ab3806639c779813.zip
enhance(server): Use job queue for account delete (#7668)
* enhance(server): Use job queue for account delete Fix #5336 * ジョブをひとつに * remove done call * clean up * add User.isDeleted * コミット忘れ * Update 1629512953000-user-is-deleted.ts * show dialog * lint * Update 1629512953000-user-is-deleted.ts
Diffstat (limited to 'src')
-rw-r--r--src/client/account.ts1
-rw-r--r--src/client/init.ts7
-rw-r--r--src/models/entities/user.ts7
-rw-r--r--src/models/repositories/user.ts1
-rw-r--r--src/queue/index.ts9
-rw-r--r--src/queue/processors/db/delete-account.ts79
-rw-r--r--src/queue/processors/db/index.ts4
-rw-r--r--src/server/api/endpoints/i/delete-account.ts13
8 files changed, 118 insertions, 3 deletions
diff --git a/src/client/account.ts b/src/client/account.ts
index 7cd3d8cb88..ee1d845493 100644
--- a/src/client/account.ts
+++ b/src/client/account.ts
@@ -11,6 +11,7 @@ type Account = {
token: string;
isModerator: boolean;
isAdmin: boolean;
+ isDeleted: boolean;
};
const data = localStorage.getItem('account');
diff --git a/src/client/init.ts b/src/client/init.ts
index 0313af4374..194ece886b 100644
--- a/src/client/init.ts
+++ b/src/client/init.ts
@@ -310,6 +310,13 @@ for (const plugin of ColdDeviceStorage.get('plugins').filter(p => p.active)) {
}
if ($i) {
+ if ($i.isDeleted) {
+ dialog({
+ type: 'warning',
+ text: i18n.locale.accountDeletionInProgress,
+ });
+ }
+
if ('Notification' in window) {
// 許可を得ていなかったらリクエスト
if (Notification.permission === 'default') {
diff --git a/src/models/entities/user.ts b/src/models/entities/user.ts
index 060ec06b9a..65aebd2d1a 100644
--- a/src/models/entities/user.ts
+++ b/src/models/entities/user.ts
@@ -175,6 +175,13 @@ export class User {
})
public isExplorable: boolean;
+ // アカウントが削除されたかどうかのフラグだが、完全に削除される際は物理削除なので実質削除されるまでの「削除が進行しているかどうか」のフラグ
+ @Column('boolean', {
+ default: false,
+ comment: 'Whether the User is deleted.'
+ })
+ public isDeleted: boolean;
+
@Column('varchar', {
length: 128, array: true, default: '{}'
})
diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts
index f56090bb82..d4bb995ce2 100644
--- a/src/models/repositories/user.ts
+++ b/src/models/repositories/user.ts
@@ -252,6 +252,7 @@ export class UserRepository extends Repository<User> {
autoAcceptFollowed: profile!.autoAcceptFollowed,
noCrawle: profile!.noCrawle,
isExplorable: user.isExplorable,
+ isDeleted: user.isDeleted,
hideOnlineStatus: user.hideOnlineStatus,
hasUnreadSpecifiedNotes: NoteUnreads.count({
where: { userId: user.id, isSpecified: true },
diff --git a/src/queue/index.ts b/src/queue/index.ts
index ff96c0fb15..4ca7998e61 100644
--- a/src/queue/index.ts
+++ b/src/queue/index.ts
@@ -171,6 +171,15 @@ export function createImportUserListsJob(user: ThinUser, fileId: DriveFile['id']
});
}
+export function createDeleteAccountJob(user: ThinUser) {
+ return dbQueue.add('deleteAccount', {
+ user: user
+ }, {
+ removeOnComplete: true,
+ removeOnFail: true
+ });
+}
+
export function createDeleteObjectStorageFileJob(key: string) {
return objectStorageQueue.add('deleteFile', {
key: key
diff --git a/src/queue/processors/db/delete-account.ts b/src/queue/processors/db/delete-account.ts
new file mode 100644
index 0000000000..95614b61aa
--- /dev/null
+++ b/src/queue/processors/db/delete-account.ts
@@ -0,0 +1,79 @@
+import * as Bull from 'bull';
+import { queueLogger } from '../../logger';
+import { DriveFiles, Notes, Users } from '@/models/index';
+import { DbUserJobData } from '@/queue/types';
+import { Note } from '@/models/entities/note';
+import { DriveFile } from '@/models/entities/drive-file';
+import { MoreThan } from 'typeorm';
+import { deleteFileSync } from '@/services/drive/delete-file';
+
+const logger = queueLogger.createSubLogger('delete-account');
+
+export async function deleteAccount(job: Bull.Job<DbUserJobData>): Promise<string | void> {
+ logger.info(`Deleting account of ${job.data.user.id} ...`);
+
+ const user = await Users.findOne(job.data.user.id);
+ if (user == null) {
+ return;
+ }
+
+ { // Delete notes
+ let cursor: Note['id'] | null = null;
+
+ while (true) {
+ const notes = await Notes.find({
+ where: {
+ userId: user.id,
+ ...(cursor ? { id: MoreThan(cursor) } : {})
+ },
+ take: 100,
+ order: {
+ id: 1
+ }
+ });
+
+ if (notes.length === 0) {
+ break;
+ }
+
+ cursor = notes[notes.length - 1].id;
+
+ await Notes.delete(notes.map(note => note.id));
+ }
+
+ logger.succ(`All of notes deleted`);
+ }
+
+ { // Delete files
+ let cursor: DriveFile['id'] | null = null;
+
+ while (true) {
+ const files = await DriveFiles.find({
+ where: {
+ userId: user.id,
+ ...(cursor ? { id: MoreThan(cursor) } : {})
+ },
+ take: 10,
+ order: {
+ id: 1
+ }
+ });
+
+ if (files.length === 0) {
+ break;
+ }
+
+ cursor = files[files.length - 1].id;
+
+ for (const file of files) {
+ await deleteFileSync(file);
+ }
+ }
+
+ logger.succ(`All of files deleted`);
+ }
+
+ await Users.delete(job.data.user.id);
+
+ return 'Account deleted';
+}
diff --git a/src/queue/processors/db/index.ts b/src/queue/processors/db/index.ts
index b56b7bfa2c..b051a28e0b 100644
--- a/src/queue/processors/db/index.ts
+++ b/src/queue/processors/db/index.ts
@@ -8,6 +8,7 @@ import { exportBlocking } from './export-blocking';
import { exportUserLists } from './export-user-lists';
import { importFollowing } from './import-following';
import { importUserLists } from './import-user-lists';
+import { deleteAccount } from './delete-account';
const jobs = {
deleteDriveFiles,
@@ -17,7 +18,8 @@ const jobs = {
exportBlocking,
exportUserLists,
importFollowing,
- importUserLists
+ importUserLists,
+ deleteAccount,
} as Record<string, Bull.ProcessCallbackFunction<DbJobData> | Bull.ProcessPromiseFunction<DbJobData>>;
export default function(dbQueue: Bull.Queue<DbJobData>) {
diff --git a/src/server/api/endpoints/i/delete-account.ts b/src/server/api/endpoints/i/delete-account.ts
index f761e5cc34..77f11925cd 100644
--- a/src/server/api/endpoints/i/delete-account.ts
+++ b/src/server/api/endpoints/i/delete-account.ts
@@ -1,9 +1,10 @@
import $ from 'cafy';
import * as bcrypt from 'bcryptjs';
import define from '../../define';
-import { Users, UserProfiles } from '@/models/index';
+import { UserProfiles, Users } from '@/models/index';
import { doPostSuspend } from '@/services/suspend-user';
import { publishUserEvent } from '@/services/stream';
+import { createDeleteAccountJob } from '@/queue';
export const meta = {
requireCredential: true as const,
@@ -19,6 +20,10 @@ export const meta = {
export default define(meta, async (ps, user) => {
const profile = await UserProfiles.findOneOrFail(user.id);
+ const userDetailed = await Users.findOneOrFail(user.id);
+ if (userDetailed.isDeleted) {
+ return;
+ }
// Compare password
const same = await bcrypt.compare(ps.password, profile.password!);
@@ -30,7 +35,11 @@ export default define(meta, async (ps, user) => {
// 物理削除する前にDelete activityを送信する
await doPostSuspend(user).catch(e => {});
- await Users.delete(user.id);
+ createDeleteAccountJob(user);
+
+ await Users.update(user.id, {
+ isDeleted: true,
+ });
// Terminate streaming
publishUserEvent(user.id, 'terminate', {});