How to send an email using AWS in Node project - node.js

My Angular app sends some data to a node server (app.js) via a POST request, the request body is then returned in the response.
I am now trying to send an email that contains this data sent in the request body.
Currently, I can read a HTML file to populate the email body, & then send the email but I need to replace that HTML file with the data sent in my req.body.
Here is what I have so far in app.js:
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
app.post('/postData', bodyParser.json(), (req, res) => {
res.json(req.body)
readFile();
sendEmail();
})
app.listen(3000, () => console.log('Example app listening on port 3000!'))
var AWS = require('aws-sdk');
const fs = require('fs');
var params;
var htmlFileName = '/Users/myName/Desktop/test.html'
AWS.config.loadFromPath('config-aig.json');
const fromEmail = 'myName';
const toEmail = 'myName'
const subject = 'Test Email' + Date()
function sendEmail() {
// Create the promise and SES service object
var sendPromise = new AWS.SES({ apiVersion: '2010-12-01'}).sendEmail(params).promise();
sendPromise.then(
function (data) {
console.log('send email success');
}).catch(
function (err) {
console.error('error --> ', err, err.stack);
});
}
function readFile(callback) {
return new Promise(
function (resolve, reject) {
fs.readFile(htmlFileName, 'utf8',
function read(err, data) {
if (err) {
return reject(err)
}
else {
console.log('file read success');
return resolve(data);
}
})
}
)
}
readFile()
.then((res) => {
// Create sendEmail params
params = {
Destination: { /* required */
ToAddresses: [
toEmail,
]
},
Message: { /* required */
Body: { /* required */
Html: {
Charset: "UTF-8",
Data: res
}
},
Subject: {
Charset: 'UTF-8',
Data: subject
}
},
Source: fromEmail, /* required */
}
sendEmail();
})
.catch((err) => {
console.log('File Read Error : ', err)
}
)
Can someone please show me how I can replace my htmlFileName with the req.body?

I use ejs to template my email here is a code i frenquently use to send email !
If you have questions i would be glade to answer
const ejs = require('ejs');
const AWS = require('aws-sdk');
const mailcomposer = require('mailcomposer');
const config_aws = {
accessKeyId: '',
secretAccessKey: '',
region: 'eu-west-1',
expeditor: '',
limitExpeditor: 50
};
AWS.config.update(config_aws);
const ses = new AWS.SES();
async function sendAnEmail(
expeditor,
subject,
destinator,
body,
destinator_name = null,
bcc = null,
callback
) {
ejs.renderFile(
`${__dirname}/templates/template.ejs`,
{
destinator,
destinator_name,
subject,
body
},
(err, html) => {
if (err) return console.error(err);
const sesEmailProps = {
Source: config_aws.expeditor,
Destination: {
ToAddresses: [`${destinator}`]
},
Message: {
Body: {
Html: {
Charset: 'UTF-8',
Data: html
},
Text: {
Charset: 'UTF-8',
Data: html ? html.replace(/<(?:.|\n)*?>/gm, '') : ''
}
},
Subject: {
Charset: 'UTF-8',
Data: subject
}
}
};
if (bcc) {
sesEmailProps.Destination = {
ToAddresses: [`${destinator}`],
BccAddresses: bcc // ARRAY LIMIT OF 49
};
}
ses.sendEmail(sesEmailProps, (error, data) => {
if (error) console.error(error);
callback(error, data);
});
}
);
}

Related

Fetch api not posting data to backend server

I am using cognitive service by azure which I am using Face API, In frontend the user will take picture then will call the API to check if face detected or not after that face id will be added using Add Face under FaceList as in azure documentation, after that I want to update column in database if face added successfully, here i am calling a function called senddata() which will use fetch API to send data to backend server then in server the database column will be updated, the problem is after face added successfully the senddata() function is not posting any data to backend server:
here is the code of taking picture:
const takePicture = async () => {
if (camera) {
const data = await camera.current.takePictureAsync({ quality: 0.25, base64: true });
const selfie_ab = base64ToArrayBuffer.decode(data.base64);
setTakingPic(true)
try {
const facedetect_instance_options = { ...base_instance_options };
facedetect_instance_options.headers['Content-Type'] = 'application/octet-stream';
const facedetect_instance = axios.create(facedetect_instance_options);
const facedetect_res = await facedetect_instance.post(
`/detect?returnFaceId=true&detectionModel=detection_02`,
selfie_ab
);
console.log("face detect res: ", facedetect_res.data);
if (facedetect_res.data.length) {
const add_face_instance_options = { ...base_instance_options };
add_face_instance_options.headers['Content-Type'] = 'application/octet-stream';
const add_face_instance = axios.create(add_face_instance_options);
const addface_res = await add_face_instance.post(
`/facelists/${facelist_id}/persistedFaces`, selfie_ab
);
if (addface_res.data.persistedFaceId.length) {
const status = "on hold";
const faceid = addface_res.data.persistedFaceId;
senddata(status, faceid)
console.log("Face add and send for approval: ", addface_res.data.persistedFaceId);
} else {
Alert.alert("error", "something went wrong");
}
} else {
Alert.alert("error", "Detection failure. Please make sure there is sufficient light when taking a selfie");
}
} catch (err) {
console.log("err: ", err);
}
}
};
here the senddata() function:
const senddata = (status, faceid) => {
console.log(status)
console.log(faceid)
fetch('http://*********/users/updateRegStatus', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
status: status,
faceid: faceid
})
})
.then((response) => response.json())
.then((res) => {
if (res.success === true) {
alert(res.message);
navigation.navigate('dashboard')
}
else {
alert(res.message);
}
})
}
and the following code form the backend which for updating the database:
router.post('/updateRegStatus', function (req, res, next) {
var status = req.body.status;
var faceid = req.body.faceid;
connection.query("INSERT INTO face_status (status,faceid) VALUES (?,?) ", [status, faceid], function (err, row) {
if (err) {
console.log(err);
} else {
res.send({ 'success': true, 'message': 'Your face details sent for approval' });
}
});
});
pleaser help me

AWS NodeJS Lambda email function not working correctly

I have a AWS Lambda that first gets data from an external API endpoint and then will loop the json data and will send email to each of the records. Here is the code:
var aws = require("aws-sdk");
var ses = new aws.SES({ region: "us-east-1" });
const https = require('https');
exports.handler = async function(event, context) {
return httprequest().then((data) => {
const response = {
statusCode: 200,
body: JSON.stringify(data),
};
data.forEach(function(item) {
// send email
sendEmailAsync(item);
});
return response;
});
};
function sendEmailAsync(item) {
return new Promise((resolve, reject) => {
console.log(`Sending email to: ${item.fullName} `);
let emailFrom = process.env.emailFrom;
const htmlBody = `
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>Hi ${item.fullName},</p>
<p>...</p>
</body>
</html>
`;
const textBody = `
Hi ${item.fullName},
...
`;
var params = {
Destination: {
ToAddresses: [item.email],
},
Message: {
Body: {
Html: {
Charset: "UTF-8",
Data: htmlBody
},
Text: {
Charset: "UTF-8",
Data: textBody
}
},
Subject: {
Charset: "UTF-8",
Data: "Test Email"
}
},
Source: emailFrom,
};
const sendPromise = ses
.sendEmail(params)
.promise();
sendPromise.then(data => {
console.log(data.MessageId);
context.done(null, "Success");
resolve(data.MessageId);
})
.catch(err => {
console.error(err, err.stack);
context.done(null, "Failed");
reject(err.message);
});
});
}
function httprequest() {
let host = process.env.endPointHost;
let path = process.env.endPointPath;
let apiKey = process.env.apiKey;
return new Promise((resolve, reject) => {
const options = {
host: host,
path: path + '?APIKey=' + apiKey,
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
};
const req = https.request(options, (res) => {
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(new Error('statusCode=' + res.statusCode));
}
var body = [];
res.on('data', function(chunk) {
body.push(chunk);
});
res.on('end', function() {
try {
body = JSON.parse(Buffer.concat(body).toString());
}
catch (e) {
reject(e);
}
resolve(body);
});
});
req.on('error', (e) => {
reject(e.message);
});
// send the request
req.end();
});
}
Everything works fine except that when the sendEmailAsync(item); function is being called I get the console log but never get any result about the email being sent or not. Of course, I don't get any email but I'm pretty sure that the problem is that I have to await maybe the function inside the foreach statement but have tried some code and didn't work.
Any clue?
As the forEach won't give the expected result with the await function, we shall use Promise.all or for..of way of looping.
Let me give you the example by taking only the loop from your snippet.
Promise.all
await Promise.all(data.map(async (item) => {
try {
let res = await sendEmailAsync(item);
console.log(res)
} catch (error) {
console.log('Error: '+ error);
}
}))
for..of
for(let item of data) {
try {
let res = await sendEmailAsync(item);
console.log(res);
} catch (error) {
console.log('Error: ' + error);
}
}

How to save a text to speech audio file client side?

desired behaviour
allow user to download text to speech audio file by clicking a button, like this official demo:
https://text-to-speech-starter-kit.ng.bluemix.net
what i've tried
i am using:
https://github.com/watson-developer-cloud/node-sdk
i can generate an audio file server side but can't figure out how to send that file back to the client for them to save - so i am trying to generate it client side instead.
attempt 01: generate audio file server side
server.js (works)
const fs = require('fs');
const TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
const textToSpeech = new TextToSpeechV1({
iam_apikey: '{apikey}',
});
const synthesizeParams = {
text: 'Hello world',
accept: 'audio/wav',
voice: 'en-US_AllisonVoice',
};
textToSpeech.synthesize(synthesizeParams)
.then(audio => {
audio.pipe(fs.createWriteStream('hello_world.wav'));
})
.catch(err => {
console.log('error:', err);
});
for reference, according to the docs, the .synthesize() method response type is:
NodeJS.ReadableStream|FileObject|Buffer
attempt 02: generate audio file client side
server.js - required to get token (works)
var AuthorizationV1 = require('ibm-watson/authorization/v1');
var iam_apikey = local_settings.TEXT_TO_SPEECH_IAM_APIKEY;
var url = local_settings.TEXT_TO_SPEECH_URL;
var authorization = new AuthorizationV1({
iam_apikey: iam_apikey,
url: url
});
const api_tts_token_get = async (req, res) => {
authorization.getToken(function(err, token) {
if (!token) {
console.log('error:', err);
} else {
res.json({ token: token, url: url });
}
});
}
app.route("/api/:api_version/text-to-speech/token")
.get(api_tts_token_get);
client.js (doesn’t work)
var TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
const get_token = (parameters) => {
$.ajax({
url: "/api/v1/text-to-speech/token",
data: parameters,
dataType: 'json',
cache: false,
headers: headers,
success: function(results) {
var token = results.token;
var url = results.url;
var textToSpeech = new TextToSpeechV1({ token: token, url: url });
var synthesizeParams = {
text: 'hello world!',
accept: 'audio/wav',
voice: 'en-US_AllisonV3Voice'
};
textToSpeech.synthesize(synthesizeParams, function(err, result) {
if (err) {
return console.log(err);
}
console.log(result);
});
},
statusCode: {
500: function() {
console.log("that didn't work");
}
}
});
}
webpack.config.js
added per instructions at:
https://github.com/watson-developer-cloud/node-sdk/tree/master/examples/webpack#important-notes
node: {
// see http://webpack.github.io/docs/configuration.html#node
// and https://webpack.js.org/configuration/node/
fs: 'empty',
net: 'empty',
tls: 'empty'
},
chrome dev tools errors:
xhr.js:108 Refused to set unsafe header "User-Agent"
The provided value 'stream' is not a valid enum value of type XMLHttpRequestResponseType.
Access to XMLHttpRequest at 'https://***.watsonplatform.net/text-to-speech/api/v1/synthesize?voice=en-US_AllisonV3Voice'
from origin 'http://localhost:3000' has been blocked by CORS policy:
Request header field x-ibmcloud-sdk-analytics is not allowed by
Access-Control-Allow-Headers in preflight response.
Error: Response not received. Body of error is HTTP ClientRequest object
at RequestWrapper.formatError (requestwrapper.js:218)
at eval (requestwrapper.js:206)
Here is one solution i have figured out.
It generates the audio file server side and sends it back via res.download().
The only caveat is that you can't use $.ajax() but rather something like:
window.open("/api/v1/audio?file_id=12345");
server.js
var TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
const api_audio_get = async (req, res) => {
var query_parameters = req.query;
var file_id = query_parameters.file_id;
var textToSpeech = new TextToSpeechV1({
iam_apikey: local_settings.TEXT_TO_SPEECH_IAM_APIKEY,
url: local_settings.TEXT_TO_SPEECH_URL
});
const synthesizeParams = {
text: 'here is test voice',
accept: 'audio/wav',
voice: 'en-US_AllisonV3Voice',
};
textToSpeech.synthesize(
synthesizeParams,
function(err, audio) {
if (err) {
console.log(err);
return;
}
// see: https://stackoverflow.com/a/46413467
// this allows you to create temp file on server, send it, then delete it
var filename = file_id + ".wav";
var absPath = path.join(__dirname, "/my_files/", filename);
var relPath = path.join("./my_files", filename); // path relative to server root
// see: https://nodejs.org/en/knowledge/advanced/streams/how-to-use-fs-create-write-stream/
var write_stream = fs.createWriteStream(relPath);
// audio is written to the writestream
audio.pipe(write_stream);
// see: https://stackoverflow.com/questions/19829379/detecting-the-end-of-a-writestream-in-node
write_stream.on('finish', function() {
res.download(absPath, (err) => {
if (err) {
console.log(err);
}
fs.unlink(relPath, (err) => {
if (err) {
console.log(err);
}
console.log("FILE [" + filename + "] REMOVED!");
});
});
});
}
);
}
// route handler
app.route("/api/:api_version/audio")
.get(api_audio_get);
client.js
$(document).on("click", ".download_audio", function() {
window.open("/api/v1/audio?file_id=12345");
});

Node JS serverless rest API lambda function that first performs GET request and then POST if condition is met

I'm new to NodeJS and I'm supposed to write a serverless rest API for a online store (school project). The team I'm in is responsible of the orders customers place. To be able to place the order there has to be enough quantity in inventory (another API), so we need to check quantity in inventory using GET before we store the order in a database using POST. How should we go about this? This is what I have tried, but I end up getting timeout. The code below is based on this example: aws-node-rest-api-with-dynamodb for me to get the hang of NodeJS and serverless.
.yml file
functions:
create:
handler: todos/test.f
events:
- http:
path: todos
method: post
cors: true
test.js
const create = require("./create.js");
exports.f = function() {
const https = require('https');
https.get('url goes here', (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
console.log(data);
var str = String(data);
console.log("Check: " + (str.trim() == "OK"))
create.c(); //also tried create.create();
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
}
create.js
'use strict';
const uuid = require('uuid');
const dynamodb = require('./dynamodb');
exports.c = function (){
console.log("Fire!");
}
module.exports.create = (event, context, callback) => {
const timestamp = new Date().getTime();
const data = JSON.parse(event.body);
if (typeof data.text !== 'string') {
console.error('Validation Failed');
callback(null, {
statusCode: 400,
headers: { 'Content-Type': 'text/plain' },
body: 'Couldn\'t create the todo item.',
});
return;
}
const params = {
TableName: 'todos',
Item: {
id: uuid.v1(),
text: data.text,
checked: false,
createdAt: timestamp,
updatedAt: timestamp,
},
};
// write the todo to the database
dynamodb.put(params, (error) => {
// handle potential errors
if (error) {
console.error(error);
callback(null, {
statusCode: error.statusCode || 501,
headers: { 'Content-Type': 'text/plain' },
body: 'Couldn\'t create the todo item.',
});
return;
}
// create a response
const response = {
statusCode: 200,
body: JSON.stringify(params.Item),
};
callback(null, response);
});
};
Any thoughts on how to get this to work?

Axios POSTed FormData has empty body on serverside

This is the client side code. Data is not empty, file is getting uploaded correctly.
export function addGame(data) {
return dispatch => {
const formData = new FormData();
formData.append("game.cover", data.gameCover[0]);
formData.append("game.title", data.gameTitle);
formData.append("game.price", data.gamePrice);
formData.append("game.description", data.description);
return axios.post(apiUrl + "/games/add", formData).then(res => {
dispatch({ type: ADD_GAME, payload: res.data.game });
});
};
}
and this is the serverside
router.post("/add", auth, async (req, res) => {
const body = await req.body;
console.log(body);
let formErrors = [];
if (!body.gameTitle) formErrors.push("Game title is required.");
if (!body.description) formErrors.push("Description is required.");
if (!body.gamePrice) formErrors.push("Price is required.");
if (formErrors.length) res.status(400).send({ success: false, formErrors });
else {
let gameCoverFileName;
if (!fileUpload(req, gameCoverFileName))
formErrors.push("Failed to upload file");
const result = await gameModel.create({
title: body.gameTitle,
cover: gameCoverFileName,
price: body.gamePrice,
description: body.description
});
if (result)
res.status(201).send({
success: true,
game: {
gameTitle: result.title,
gameCover: gameCoverFileName,
gamePrice: result.price,
description: result.description
}
});
} });
And I'm getting empty body
You need to additionally process the multipart form-data. For example with multiparty:
const multiparty = require("multiparty");
router.post("/add", auth, async (req, res) => {
try {
const parse = function (req) {
return new Promise(function(resolve, reject) {
const form = new multiparty.Form()
form.parse(req, function(err, fields, files) {
!err ? resolve([fields, files]) : reject(err)
})
})
}
const [body] = await parse(req)
console.log(body)
} catch (err) {
console.log(err)
}
res.json("ok")
})

Resources