From 53415e9ba4bbee35b337afd97940e23eb4523d2c Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 12 Apr 2018 04:22:06 +0900
Subject: [PATCH] wip

---
 src/models/drive-file.ts   | 67 ++++++++++++++++++++++++++++++++------
 src/models/drive-folder.ts | 49 +++++++++++++++++++++++++---
 src/models/user.ts         | 10 ++++++
 3 files changed, 112 insertions(+), 14 deletions(-)

diff --git a/src/models/drive-file.ts b/src/models/drive-file.ts
index c86570f0f7..ff31ba05dd 100644
--- a/src/models/drive-file.ts
+++ b/src/models/drive-file.ts
@@ -1,8 +1,11 @@
-import * as mongodb from 'mongodb';
+import * as mongo from 'mongodb';
 import deepcopy = require('deepcopy');
 import { pack as packFolder } from './drive-folder';
 import config from '../config';
 import monkDb, { nativeDbConn } from '../db/mongodb';
+import Note, { deleteNote } from './note';
+import MessagingMessage, { deleteMessagingMessage } from './messaging-message';
+import User from './user';
 
 const DriveFile = monkDb.get<IDriveFile>('driveFiles.files');
 
@@ -10,9 +13,9 @@ DriveFile.createIndex('metadata.uri', { sparse: true, unique: true });
 
 export default DriveFile;
 
-const getGridFSBucket = async (): Promise<mongodb.GridFSBucket> => {
+const getGridFSBucket = async (): Promise<mongo.GridFSBucket> => {
 	const db = await nativeDbConn();
-	const bucket = new mongodb.GridFSBucket(db, {
+	const bucket = new mongo.GridFSBucket(db, {
 		bucketName: 'driveFiles'
 	});
 	return bucket;
@@ -22,14 +25,14 @@ export { getGridFSBucket };
 
 export type IMetadata = {
 	properties: any;
-	userId: mongodb.ObjectID;
-	folderId: mongodb.ObjectID;
+	userId: mongo.ObjectID;
+	folderId: mongo.ObjectID;
 	comment: string;
 	uri: string;
 };
 
 export type IDriveFile = {
-	_id: mongodb.ObjectID;
+	_id: mongo.ObjectID;
 	uploadDate: Date;
 	md5: string;
 	filename: string;
@@ -47,12 +50,56 @@ export function validateFileName(name: string): boolean {
 	);
 }
 
+/**
+ * DriveFileを物理削除します
+ */
+export async function deleteDriveFile(driveFile: string | mongo.ObjectID | IDriveFile) {
+	let d: IDriveFile;
+
+	// Populate
+	if (mongo.ObjectID.prototype.isPrototypeOf(driveFile)) {
+		d = await DriveFile.findOne({
+			_id: driveFile
+		});
+	} else if (typeof driveFile === 'string') {
+		d = await DriveFile.findOne({
+			_id: new mongo.ObjectID(driveFile)
+		});
+	} else {
+		d = driveFile as IDriveFile;
+	}
+
+	if (d == null) return;
+
+	// このDriveFileを添付しているNoteをすべて削除
+	await Promise.all((
+		await Note.find({ mediaIds: d._id })
+	).map(x => deleteNote(x)));
+
+	// このDriveFileを添付しているMessagingMessageをすべて削除
+	await Promise.all((
+		await MessagingMessage.find({ fileId: d._id })
+	).map(x => deleteMessagingMessage(x)));
+
+	// このDriveFileがアバターやバナーに使われていたらそれらのプロパティをnullにする
+	const u = await User.findOne({ _id: d.metadata.userId });
+	if (u) {
+		if (u.avatarId.equals(d._id)) {
+			await User.update({ _id: u._id }, { $set: { avatarId: null } });
+		}
+		if (u.bannerId.equals(d._id)) {
+			await User.update({ _id: u._id }, { $set: { bannerId: null } });
+		}
+	}
+
+	// このDriveFileを削除
+	await DriveFile.remove({
+		_id: d._id
+	});
+}
+
 /**
  * Pack a drive file for API response
- *
- * @param {any} file
- * @param {any} options?
- * @return {Promise<any>}
  */
 export const pack = (
 	file: any,
diff --git a/src/models/drive-folder.ts b/src/models/drive-folder.ts
index 45cc9c9649..e7961936aa 100644
--- a/src/models/drive-folder.ts
+++ b/src/models/drive-folder.ts
@@ -21,12 +21,53 @@ export function isValidFolderName(name: string): boolean {
 	);
 }
 
+/**
+ * DriveFolderを物理削除します
+ */
+export async function deleteDriveFolder(driveFolder: string | mongo.ObjectID | IDriveFolder) {
+	let d: IDriveFolder;
+
+	// Populate
+	if (mongo.ObjectID.prototype.isPrototypeOf(driveFolder)) {
+		d = await DriveFolder.findOne({
+			_id: driveFolder
+		});
+	} else if (typeof driveFolder === 'string') {
+		d = await DriveFolder.findOne({
+			_id: new mongo.ObjectID(driveFolder)
+		});
+	} else {
+		d = driveFolder as IDriveFolder;
+	}
+
+	if (d == null) return;
+
+	// このDriveFolderに格納されているDriveFileがあればすべてルートに移動
+	await DriveFile.update({
+		'metadata.folderId': d._id
+	}, {
+		$set: {
+			'metadata.folderId': null
+		}
+	});
+
+	// このDriveFolderに格納されているDriveFolderがあればすべてルートに移動
+	await DriveFolder.update({
+		parentId: d._id
+	}, {
+		$set: {
+			parentId: null
+		}
+	});
+
+	// このDriveFolderを削除
+	await DriveFolder.remove({
+		_id: d._id
+	});
+}
+
 /**
  * Pack a drive folder for API response
- *
- * @param {any} folder
- * @param {any} options?
- * @return {Promise<any>}
  */
 export const pack = (
 	folder: any,
diff --git a/src/models/user.ts b/src/models/user.ts
index 6155324be8..b56cf03ef8 100644
--- a/src/models/user.ts
+++ b/src/models/user.ts
@@ -13,6 +13,8 @@ import Favorite, { deleteFavorite } from './favorite';
 import NoteReaction, { deleteNoteReaction } from './note-reaction';
 import MessagingMessage, { deleteMessagingMessage } from './messaging-message';
 import MessagingHistory, { deleteMessagingHistory } from './messaging-history';
+import DriveFile, { deleteDriveFile } from './drive-file';
+import DriveFolder, { deleteDriveFolder } from './drive-folder';
 
 const User = db.get<IUser>('users');
 
@@ -190,6 +192,14 @@ export async function deleteUser(user: string | mongo.ObjectID | IUser) {
 	).map(x => deleteMessagingHistory(x)));
 
 	// このユーザーのDriveFileをすべて削除
+	await Promise.all((
+		await DriveFile.find({ 'metadata.userId': u._id })
+	).map(x => deleteDriveFile(x)));
+
+	// このユーザーのDriveFolderをすべて削除
+	await Promise.all((
+		await DriveFolder.find({ userId: u._id })
+	).map(x => deleteDriveFolder(x)));
 
 	// このユーザーのFollowingをすべて削除