diff --git a/README.md b/README.md index e65f81d..17e1129 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,9 @@ the email forwarding mapping from original destinations to new destination. - For Role, choose "Basic Execution Role" under Create New Role. In the popup, give the role a name (e.g., LambdaSesForwarder). Configure the role policy to - the following: + the following: +( Note: Action `s3:DeleteObject` is only needed when using the +`emailCleanupOnS3` option.) ``` { "Version": "2012-10-17", @@ -88,7 +90,8 @@ the email forwarding mapping from original destinations to new destination. "Effect": "Allow", "Action": [ "s3:GetObject", - "s3:PutObject" + "s3:PutObject", + "s3:DeleteObject" ], "Resource": "arn:aws:s3:::S3-BUCKET-NAME/*" } @@ -162,7 +165,9 @@ likely need to adjust the bucket policy statement with one like this: } ``` -8. Optionally set the S3 lifecycle for this bucket to delete/expire objects +8. Optionally, to clean up saved email files on the AWS S3 Bucket, you can either: +- Set the `emailCleanupOnS3` option in the script configuration to true (this will delete files on a succesful process). +- Set the S3 lifecycle for this bucket to delete/expire objects after a few days to clean up the saved emails. ## Extending diff --git a/example/index.js b/example/index.js index 98acf98..dfc115d 100644 --- a/example/index.js +++ b/example/index.js @@ -16,7 +16,8 @@ exports.handler = function(event, context, callback) { "abuse@example.com": [ "example.jim@example.com" ] - } + }, + emailCleanupS3: false } }; LambdaForwarder.handler(event, context, callback, overrides); diff --git a/index.js b/index.js index ead0495..73f2552 100644 --- a/index.js +++ b/index.js @@ -27,6 +27,8 @@ console.log("AWS Lambda SES Forwarder // @arithmetric // Version 4.2.0"); // // To match a mailbox name on all domains, use a key without the "at" symbol // and domain part of an email address (i.e. `info`). +// +// - emailCleanupOnS3: true to delete email from S3 bucket var defaultConfig = { fromEmail: "noreply@example.com", subjectPrefix: "", @@ -46,7 +48,8 @@ var defaultConfig = { "info": [ "info@example.com" ] - } + }, + emailCleanupOnS3: false }; /** @@ -280,6 +283,37 @@ exports.sendMessage = function(data) { }); }; +/** + * Clean up (delete) the S3 email object if `data.config.emailCleanupOnS3` equals true + * + * @param {object} data - Data bundle with context, email, etc. + * + * @return {object} - Promise resolved with data. + */ +exports.emailCleanupOnS3 = function(data) { + if (!data.config.emailCleanupOnS3) { + return Promise.resolve(data); + } + data.log({level: "info", message: "Deleting email at s3://" + + data.config.emailBucket + '/' + data.config.emailKeyPrefix + + data.email.messageId}); + return new Promise(function(resolve, reject) { + data.s3.deleteObject({ + Bucket: data.config.emailBucket, + Key: data.config.emailKeyPrefix + data.email.messageId + }, function(err, result) { + if (err) { + data.log({level: "error", message: "deleteObject() returned error:", + error: err, stack: err.stack}); + return reject(new Error('Error: Email cleanup on S3 failed.')); + } + data.log({level: "info", message: "emailCleanupOnS3() successful.", + result: result}); + resolve(data); + }); + }); +}; + /** * Handler function to be invoked by AWS Lambda with an inbound SES email as * the event. @@ -297,7 +331,8 @@ exports.handler = function(event, context, callback, overrides) { exports.transformRecipients, exports.fetchMessage, exports.processMessage, - exports.sendMessage + exports.sendMessage, + exports.emailCleanupOnS3 ]; var data = { event: event, diff --git a/test/emailCleanupOnS3.js b/test/emailCleanupOnS3.js new file mode 100644 index 0000000..8de942c --- /dev/null +++ b/test/emailCleanupOnS3.js @@ -0,0 +1,76 @@ + +/* global describe, it */ + +var assert = require("assert"); + +var index = require("../index"); + +describe('index.js', function() { + describe('#emailCleanupOnS3()', function() { + it('should simply return data when emailCleanupOnS3 is false', + function(done) { + var data = { + config: { + emailCleanupOnS3: false + } + }; + index.emailCleanupOnS3(data) + .then(function() { + assert.ok(true, "emailCleanupOnS3 returned successfully"); + done(); + }); + }); + + it('should invoke the AWS S3 SDK to delete the email object', + function(done) { + var data = { + config: { + emailBucket: "bucket", + emailKeyPrefix: "prefix/", + emailCleanupOnS3: true + }, + context: {}, + email: { + messageId: "abc" + }, + log: console.log, + s3: { + deleteObject: function(options, callback) { + callback(null); + } + } + }; + index.emailCleanupOnS3(data) + .then(function() { + assert.ok(true, "emailCleanupOnS3 returned successfully"); + done(); + }); + }); + + it('should result in failure if the AWS S3 SDK cannot delete the object', + function(done) { + var data = { + config: { + emailBucket: "bucket", + emailKeyPrefix: "prefix/", + emailCleanupOnS3: true + }, + context: {}, + email: { + messageId: "abc" + }, + log: console.log, + s3: { + deleteObject: function(options, callback) { + callback(true); + } + } + }; + index.emailCleanupOnS3(data) + .catch(function(err) { + assert.ok(err, "emailCleanupOnS3 aborted operation"); + done(); + }); + }); + }); +}); diff --git a/test/handler.js b/test/handler.js index 167c644..4b71f8f 100644 --- a/test/handler.js +++ b/test/handler.js @@ -21,6 +21,9 @@ describe('index.js', function() { }, getObject: function(options, callback) { callback(null, {Body: "email data"}); + }, + deleteObject: function(options, callback) { + callback(null, {}); } }, ses: { @@ -35,7 +38,8 @@ describe('index.js', function() { "info@example.com": [ "jim@example.com" ] - } + }, + emailCleanupOnS3: false } }; index.handler(event, context, callback, overrides);