From 604d3aa1b763fe8fc0558dce74a3fc3b29680480 Mon Sep 17 00:00:00 2001 From: Sparsh Paliwal Date: Fri, 9 Dec 2016 17:36:18 +0530 Subject: [PATCH] delete files in S3 when deleted in editor Fixes #167 --- package.json | 1 + server/controllers/file.controller.js | 11 ++++++- server/controllers/project.controller.js | 29 ++++++++++++++--- server/utils/s3Operations.js | 40 ++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 server/utils/s3Operations.js diff --git a/package.json b/package.json index 8bacbf7b34..7c83283d53 100644 --- a/package.json +++ b/package.json @@ -109,6 +109,7 @@ "redux-thunk": "^2.1.0", "request": "^2.76.0", "request-promise": "^4.1.1", + "s3": "^4.4.0", "s3-policy": "^0.2.0", "shortid": "^2.2.6", "srcdoc-polyfill": "^0.2.0", diff --git a/server/controllers/file.controller.js b/server/controllers/file.controller.js index ebe2643e82..e248823cc4 100644 --- a/server/controllers/file.controller.js +++ b/server/controllers/file.controller.js @@ -1,5 +1,7 @@ import Project from '../models/project'; +import each from 'async/each'; import { resolvePathToFile } from '../utils/filePath'; +import { deleteObjectsFromS3 } from '../utils/s3Operations'; // Bug -> timestamps don't get created, but it seems like this will // be fixed in mongoose soon @@ -38,8 +40,15 @@ function getAllDescendantIds(files, nodeId) { } function deleteMany(files, ids) { - ids.forEach(id => { + let s3ObjectUrlList = []; + each(ids, (id, callback) => { + if (files.id(id).url !== undefined) { + s3ObjectUrlList.push(files.id(id).url); + } files.id(id).remove(); + callback(); + }, (err) => { + deleteObjectsFromS3(s3ObjectUrlList); }); } diff --git a/server/controllers/project.controller.js b/server/controllers/project.controller.js index ff7b86b6a8..f45c2af3c1 100644 --- a/server/controllers/project.controller.js +++ b/server/controllers/project.controller.js @@ -2,6 +2,8 @@ import Project from '../models/project'; import User from '../models/user'; import archiver from 'archiver'; import request from 'request'; +import each from 'async/each'; +import { deleteObjectsFromS3 } from '../utils/s3Operations'; export function createProject(req, res) { @@ -63,12 +65,29 @@ export function getProject(req, res) { }); } -export function deleteProject(req, res) { - Project.remove({ _id: req.params.project_id }, (err) => { - if (err) { - return res.status(404).send({ message: 'Project with that id does not exist' }); +function deleteFilesFromS3(files, fileCallback) { + let s3ObjectUrlList = []; + each(files, (file, callback) => { + if (file.url !== undefined) { + s3ObjectUrlList.push(file.url); } - return res.json({ success: true }); + callback(); + }, (err) => { + fileCallback(); + deleteObjectsFromS3(s3ObjectUrlList); + }); +} + +export function deleteProject(req, res) { + Project.findById(req.params.project_id, (err, project) => { + deleteFilesFromS3(project.files, () => { + Project.remove({ _id: req.params.project_id }, (err) => { + if (err) { + return res.status(404).send({ message: 'Project with that id does not exist' }); + } + return res.json({ success: true }); + }); + }); }); } diff --git a/server/utils/s3Operations.js b/server/utils/s3Operations.js new file mode 100644 index 0000000000..5ce5fdf134 --- /dev/null +++ b/server/utils/s3Operations.js @@ -0,0 +1,40 @@ +import s3 from 's3'; +import each from 'async/each'; + +let client = s3.createClient({ + maxAsyncS3: 20, + s3RetryCount: 3, + s3RetryDelay: 1000, + multipartUploadThreshold: 20971520, // this is the default (20 MB) + multipartUploadSize: 15728640, // this is the default (15 MB) + s3Options: { + accessKeyId: `${process.env.AWS_ACCESS_KEY}`, + secretAccessKey: `${process.env.AWS_SECRET_KEY}`, + }, +}); + +export function deleteObjectsFromS3(urlList, callback) { + if (urlList.length > 0) { + let objectKeyList = []; + each(urlList, (url) => { + let objectKey = url.split("/").pop(); + objectKeyList.push({Key: objectKey}) + }); + let params = { + Bucket: `${process.env.S3_BUCKET}`, + Delete: { + Objects: objectKeyList, + }, + }; + let del = client.deleteObjects(params); + del.on('end', function() { + if(callback) { + callback(); + } + }); + } else { + if(callback) { + callback(); + } + } +}