From 7e92c8ad973d7c4e5ebbafe679c61aa87bf8f226 Mon Sep 17 00:00:00 2001 From: Oscar Saharoy Date: Mon, 18 Dec 2023 22:27:49 +0000 Subject: [PATCH] Node.js 20 support --- index.js | 150 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 78 insertions(+), 72 deletions(-) diff --git a/index.js b/index.js index d0e0ae8..d2a14d1 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,10 @@ "use strict"; -var AWS = require('aws-sdk'); +import { S3Client, CopyObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3"; +import { SESClient, SendRawEmailCommand } from "@aws-sdk/client-ses"; +import { Buffer } from "buffer"; -console.log("AWS Lambda SES Forwarder // @arithmetric // Version 5.1.0"); +console.log("AWS Lambda SES Forwarder // @arithmetric // Version 5.2.0"); // Configure the S3 bucket and key prefix for stored raw emails, and the // mapping of email addresses to forward from and to. @@ -65,7 +67,7 @@ var defaultConfig = { * * @return {object} - Promise resolved with data. */ -exports.parseEvent = function(data) { +const parseEvent = function(data) { // Validate characteristics of a SES event record. if (!data.event || !data.event.hasOwnProperty('Records') || @@ -92,7 +94,7 @@ exports.parseEvent = function(data) { * * @return {object} - Promise resolved with data. */ -exports.transformRecipients = function(data) { +const transformRecipients = function(data) { var newRecipients = []; data.originalRecipients = data.recipients; data.recipients.forEach(function(origEmail) { @@ -152,54 +154,56 @@ exports.transformRecipients = function(data) { * * @return {object} - Promise resolved with data. */ -exports.fetchMessage = function(data) { +const fetchMessage = async function(data) { // Copying email object to ensure read permission data.log({ level: "info", message: "Fetching email at s3://" + data.config.emailBucket + '/' + data.config.emailKeyPrefix + data.email.messageId }); - return new Promise(function(resolve, reject) { - data.s3.copyObject({ - Bucket: data.config.emailBucket, - CopySource: data.config.emailBucket + '/' + data.config.emailKeyPrefix + - data.email.messageId, - Key: data.config.emailKeyPrefix + data.email.messageId, - ACL: 'private', - ContentType: 'text/plain', - StorageClass: 'STANDARD' - }, function(err) { - if (err) { - data.log({ - level: "error", - message: "copyObject() returned error:", - error: err, - stack: err.stack - }); - return reject( - new Error("Error: Could not make readable copy of email.")); - } - // Load the raw email from S3 - data.s3.getObject({ - Bucket: data.config.emailBucket, - Key: data.config.emailKeyPrefix + data.email.messageId - }, function(err, result) { - if (err) { - data.log({ - level: "error", - message: "getObject() returned error:", - error: err, - stack: err.stack - }); - return reject( - new Error("Error: Failed to load message body from S3.")); - } - data.emailData = result.Body.toString(); - return resolve(data); - }); + const copyCommand = new CopyObjectCommand({ + Bucket: data.config.emailBucket, + CopySource: data.config.emailBucket + '/' + data.config.emailKeyPrefix + + data.email.messageId, + Key: data.config.emailKeyPrefix + data.email.messageId, + ACL: 'private', + ContentType: 'text/plain', + StorageClass: 'STANDARD' + }); + + let response; + try { + response = await data.s3.send(copyCommand); + } catch( err ) { + data.log({ + level: "error", + message: "copyObject() returned error:", + error: err, + stack: err.stack }); + throw new Error("Error: Could not make readable copy of email."); + } + + const getCommand = new GetObjectCommand({ + Bucket: data.config.emailBucket, + Key: data.config.emailKeyPrefix + data.email.messageId }); + + try { + response = await data.s3.send(getCommand); + } catch( err ) { + data.log({ + level: "error", + message: "getObject() returned error:", + error: err, + stack: err.stack + }); + throw new Error("Error: Failed to load message body from S3."); + } + + data.emailData = await response.Body.transformToString(); + return data; }; /** @@ -210,7 +214,7 @@ exports.fetchMessage = function(data) { * * @return {object} - Promise resolved with data. */ -exports.processMessage = function(data) { +const processMessage = function(data) { var match = data.emailData.match(/^((?:.+\r?\n)*)(\r?\n(?:.*\s+)*)/m); var header = match && match[1] ? match[1] : data.emailData; var body = match && match[2] ? match[2] : ''; @@ -291,12 +295,12 @@ exports.processMessage = function(data) { * * @return {object} - Promise resolved with data. */ -exports.sendMessage = function(data) { +const sendMessage = async function(data) { var params = { Destinations: data.recipients, Source: data.originalRecipient, RawMessage: { - Data: data.emailData + Data: Buffer.from(data.emailData) } }; data.log({ @@ -305,25 +309,28 @@ exports.sendMessage = function(data) { data.originalRecipients.join(", ") + ". Transformed recipients: " + data.recipients.join(", ") + "." }); - return new Promise(function(resolve, reject) { - data.ses.sendRawEmail(params, function(err, result) { - if (err) { - data.log({ - level: "error", - message: "sendRawEmail() returned error.", - error: err, - stack: err.stack - }); - return reject(new Error('Error: Email sending failed.')); - } - data.log({ - level: "info", - message: "sendRawEmail() successful.", - result: result - }); - resolve(data); + + const sendCommand = new SendRawEmailCommand(params); + + let response; + try { + response = await data.ses.send(sendCommand); + } catch( err ) { + data.log({ + level: "error", + message: "sendRawEmail() returned error.", + error: err, + stack: err.stack, }); + throw new Error('Error: Email sending failed.'); + } + + data.log({ + level: "info", + message: "sendRawEmail() successful.", + result: response, }); + return data; }; /** @@ -336,14 +343,14 @@ exports.sendMessage = function(data) { * @param {object} overrides - Overrides for the default data, including the * configuration, SES object, and S3 object. */ -exports.handler = function(event, context, callback, overrides) { +export const handler = function(event, context, callback, overrides) { var steps = overrides && overrides.steps ? overrides.steps : [ - exports.parseEvent, - exports.transformRecipients, - exports.fetchMessage, - exports.processMessage, - exports.sendMessage + parseEvent, + transformRecipients, + fetchMessage, + processMessage, + sendMessage ]; var data = { event: event, @@ -351,9 +358,8 @@ exports.handler = function(event, context, callback, overrides) { context: context, config: overrides && overrides.config ? overrides.config : defaultConfig, log: overrides && overrides.log ? overrides.log : console.log, - ses: overrides && overrides.ses ? overrides.ses : new AWS.SES(), - s3: overrides && overrides.s3 ? - overrides.s3 : new AWS.S3({signatureVersion: 'v4'}) + ses: overrides && overrides.ses ? overrides.ses : new SESClient(), + s3: overrides && overrides.s3 ? overrides.s3 : new S3Client(), }; Promise.series(steps, data) .then(function(data) {