Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node.js 20 support #149

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 78 additions & 72 deletions index.js
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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') ||
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
};

/**
Expand All @@ -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] : '';
Expand Down Expand Up @@ -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({
Expand All @@ -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;
};

/**
Expand All @@ -336,24 +343,23 @@ 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,
callback: callback,
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) {
Expand Down