Trying to separate my SendGrid html email templates on my node server - javascript

I'm running a node server and I'm sending emails with SendGrid. I need to separate my email HTMLs from my js files so I can modify them from a single base. What I have now is this:
const express = require('express')
const config = require('config')
const sgMail = require('#sendgrid/mail')
const sendKey = config.get('SENDGRID_API_KEY')
sgMail.setApiKey(sendKey)
const msg = {
to: "test#test.com",
from: "test#test.com",
subject: 'Welcome To The App',
text: 'Text is here',
html: <strong>HTML HERE</strong>
}
sgMail.send(msg)
I want to call my HTML property outside of my current js file instead of writing HTML inside my msg object.
How can I have a separate welcomeEmail.html file and add it to my msg object in my js file?
I've tried fs module but all I have is
Error: ENOENT: no such file or directory, open './welcomeEmail.html'
I couldn't be able to read my HTML file anyway.
Any idea of what I'm missing?

You can use fs, you probably have read from wrong path.
Use this:
fs.readFile('./welcomeEmail.html', 'utf8', (err, content)=>{//do Something});
Make sure welcomeEmail.html is in the right place in your project.
Please remember readFile is async so you should do the rest of your code in the callback, so your code should be something like this (depends on what is the use case):
const express = require('express')
const config = require('config')
const sgMail = require('#sendgrid/mail')
const sendKey = config.get('SENDGRID_API_KEY')
const fs = require('fs')
sgMail.setApiKey(sendKey)
fs.readFile('./welcomeEmail.html', 'utf8', (err, content)=>{
if(err){
console.log(err);
}
else{
let msg = {
to: "test#test.com",
from: "test#test.com",
subject: 'Welcome To The App',
text: 'Text is here',
html: content
}
sgMail.send(msg)
}
});

Related

Npm run start looking for wrong path

im trying to use google text to speech api . And node js looking for wrong directory.
Error: The file at /home/user/Downloads/service-account-file.json does not exist, or it is not a file. ENOENT: no such file or directory, lstat '/System/Volumes/Data/home/user'
it says your .json not exist in this directory which is correct. But there is nothing that i can provide path for this. Also when i try to move my json to path that error specified. It is still saying same thing.
//and this is my code
const textToSpeech = require('#google-cloud/text-to-speech');
// Import other required libraries
const fs = require('fs');
const util = require('util');
// Creates a client
const client = new textToSpeech.TextToSpeechClient();
async function quickStart() {
// The text to synthesize
const text = 'hello, world!';
// Construct the request
const request = {
input: {text: text},
// Select the language and SSML voice gender (optional)
voice: {languageCode: 'en-US', ssmlGender: 'NEUTRAL'},
// select the type of audio encoding
audioConfig: {audioEncoding: 'MP3'},
};
// Performs the text-to-speech request
const [response] = await client.synthesizeSpeech(request);
// Write the binary audio content to a local file
const writeFile = util.promisify(fs.writeFile);
await writeFile('/Applications/development/gmvoice/output.mp3', response.audioContent, 'binary',function (err) {
if (err) throw err; console.log('Results Received');
});
console.log('Audio content written to file: output.mp3');
}
quickStart();

How to send SMS to a number stored in a JSON file?

I need to send SMS (by Twilio) to defined phone numbers, which are saved in the phone.json file when the submit button will be clicked.
I am using node.js
How am I going to do this?
This is how my phone.json looks:
[
{"Nombre": "Nombre1" ,
"Numero": "+34...."
},
{"Nombre": "Nombre2",
"Numero": "+34..."
}
]
In SMS.js I would create a function like this:
function submit() {
client.messages
.create({
body: "content",
from: "+....",
to: ""}) //this number should come from phone.json
.then(message => console.log(message.sid));
}
This is how my index.js looks:
const http =require('http');
const express = require('express');
const MessagingResponse = require('twilio').twiml.MessagingResponse;
const { fstat } = require('fs');
const { response } = require('express');
const info = require("./database.json");
const app = express();
app.use(express.static("public"));
app.use(express.urlencoded({extended: true}));
app.listen(8000, console.log(`Server running at http://localhost:8000`));
I would be very happy about every suggestion.
I'm assuming you want to do this server-side in Node.js, then you need fs to read the JSON and then you just loop over it in your submit() like this:
import fs from "fs"; // CommonJS syntax: const fs = require("fs");
phone_numbers = JSON.parse(fs.readFileSync("phone.json", "utf8"));
phone_numbers.forEach(function(item, index) {
console.log(item, index);
client.messages
.create({
body: "content",
from: "+....",
to: item["Numero"],
})
.then(message => console.log(message.sid));
});

Missing ; before statement (Jira Addon - oAuth)

I get the error:
[WARNING] File encoding has not been set, using platform encoding windows-1252, i.e. build is platform dependent!
[INFO] Compiling javascript using YUI
[ERROR] missing ; before statement let privateKeyData = fs.readFileSync('location','utf-8');
As shown I have put the ; before let. I don't understand the error. I am creating an add-on for Jira. I started the JS file via cmd and it worked. However when I want to package the project I get that error. Please help.
jQuery(function($) {
var initmyConfluenceMacro = function() {
$(".myConfluenceMacro").each(function() {
const request = require('request');
const fs = require('fs');
let privateKeyData = fs.readFileSync('filelocation', 'utf-8');
const oauth = {
consumer_key: 'mykey',
consumer_secret: privatkey,
token: 'mytoken',
token_secret: 'tokensecret',
signature_method: 'signaturemethod'
};
request.get({
url: 'thelink',
oauth: oauth,
qs: null,
json: true
}, function(e, r, user) {
console.log(user)
});
var html = "output";
$(this).html(html);
});
};
$(document).ready(function() {
initmyConfluenceMacro();
});
});
The problem for the error is
const fs = require('fs');
fs is for (as on their page) security reasons removed from Atlassian and cannot be used. My workaround was to use the velocity template, in order to import the file, and then to parse it to the js file.
I hope this helps. If someone has other ideas please let me know.

How to read file content using nodejs?

I have file from client that i have to read on server side and send back to client for download , How can i acheive that task using nodejs. I tried with fs but i am getting some error.
console.log(data) is coming as empty object
server.js
var multiparty = require('multiparty');
var data = new multiparty.Form();
export function create(req, res) {
data.parse(req, function(err, fields, files) {
console.log(files);
var fileContent = fs.readFileSync(files.file[0].path,'utf8');
res.json(fileContent );
});
}
router.js
var express = require('express');
var controller = require('./fileUpload.controller');
var router = express.Router();
router.post('/fileUpload',controller.create);
module.exports = router;
fileData
{ file:
[ { fieldName: 'file',
originalFilename: 'sco_poc.bpmn',
path: 'C:\\Users\\9u\\AppData\\Local\\Temp\\f4DG8L7nCpNyNvVPYqGPkd44.bpmn',
headers: [Object],
size: 11078 } ] }
I am assuming you are trying download a local file the path from your JSON object 'fileData'. My example below is written in NodeJS
First, you will need to stringify your JSONobject
var jsonString = JSON.stringify({ file:
[ { fieldName: 'file',
originalFilename: 'sco_poc.bpmn',
path: 'C:\\Users\\9u\\AppData\\Local\\Temp\\f4DG8L7nCpNyNvVPYqGPkd44.bpmn',
headers: [Object],
size: 11078 } ] });
//console.log(jsonString)//print jsonString contents
Second, parse it into a JavaScript object
var jsonObj = JSON.parse(jsonString);
//console.log(jsonObj); //print jsonObj contents
Third, get path from jsonObj
var path = jsonObj.file[0].path;
Finally, read the (local) file
fs.readFile(path,function(err,data){
var fileData="";
fileData+=data;
res.writeHead(200, {
'Location': '<if needed>',
'Content-Type':'<expected content-type>'
});
res.end(fileData); //ends response, and sends to client
});
If you look at the very first example on the multiparty NPM page here: https://www.npmjs.com/package/multiparty, you will see that you need to run this for each new request, not just once that you reuse over and over:
var form = new multiparty.Form();
So, for starter move that into your request handler. Then, if you're unsure how to use the results, I'd suggest you add this:
console.log(fields, files);
And, this should show you what data you actually have.
FYI, you can see errors in the parsing with this:
form.on('error', function(err) {
console.log('Error parsing form: ' + err.stack);
});
Also, note this statement from the documentation:
If cb is provided, autoFields and autoFiles are set to true and all
fields and files are collected and passed to the callback, removing
the need to listen to any events on form. This is for convenience when
you want to read everything, but be sure to write cleanup code, as
this will write all uploaded files to the disk, even ones you may not
be interested in.
You will need to cleanup files on disk after each request or they will accumulate.

Pass variable to html template in nodemailer

I want to send email with nodemailer using html template. In that template I need to inject some dynamically some variables and I really can't do that. My code:
var nodemailer = require('nodemailer');
var smtpTransport = require('nodemailer-smtp-transport');
smtpTransport = nodemailer.createTransport(smtpTransport({
host: mailConfig.host,
secure: mailConfig.secure,
port: mailConfig.port,
auth: {
user: mailConfig.auth.user,
pass: mailConfig.auth.pass
}
}));
var mailOptions = {
from: 'my#email.com',
to : 'some#email.com',
subject : 'test subject',
html : { path: 'app/public/pages/emailWithPDF.html' }
};
smtpTransport.sendMail(mailOptions, function (error, response) {
if (error) {
console.log(error);
callback(error);
}
});
Let's say I want in emailWithPDF.html something like this:
Hello {{username}}!
I've found some examples, where was smth like this:
...
html: '<p>Hello {{username}}</p>'
...
but I want it in separate html file. Is it possible?
What you can do is read the HTML file using fs module in node and then replace the elements that you want changed in the html string using handlebars
var nodemailer = require('nodemailer');
var smtpTransport = require('nodemailer-smtp-transport');
var handlebars = require('handlebars');
var fs = require('fs');
var readHTMLFile = function(path, callback) {
fs.readFile(path, {encoding: 'utf-8'}, function (err, html) {
if (err) {
callback(err);
}
else {
callback(null, html);
}
});
};
smtpTransport = nodemailer.createTransport(smtpTransport({
host: mailConfig.host,
secure: mailConfig.secure,
port: mailConfig.port,
auth: {
user: mailConfig.auth.user,
pass: mailConfig.auth.pass
}
}));
readHTMLFile(__dirname + 'app/public/pages/emailWithPDF.html', function(err, html) {
if (err) {
console.log('error reading file', err);
return;
}
var template = handlebars.compile(html);
var replacements = {
username: "John Doe"
};
var htmlToSend = template(replacements);
var mailOptions = {
from: 'my#email.com',
to : 'some#email.com',
subject : 'test subject',
html : htmlToSend
};
smtpTransport.sendMail(mailOptions, function (error, response) {
if (error) {
console.log(error);
}
});
});
I use it in all my projects. more clean and up to date and understandable. callback hell doesn't exist.
sendMail.ts The html file reads with handlebar, puts the relevant variables into the contents, and sends.
import * as nodemailer from 'nodemailer';
import * as handlebars from 'handlebars';
import * as fs from 'fs';
import * as path from 'path';
export async function sendEmail(email: string, subject: string, url: string) {
const __dirname = path.resolve();
const filePath = path.join(__dirname, '../emails/password-reset.html');
const source = fs.readFileSync(filePath, 'utf-8').toString();
const template = handlebars.compile(source);
const replacements = {
username: "Umut YEREBAKMAZ"
};
const htmlToSend = template(replacements);
const transporter = nodemailer.createTransport({
host: "smtp.mailtrap.io",
port: 2525, // 587
secure: false,
auth: {
user: "fg7f6g7g67",
pass: "asds7ds7d6"
}
});
const mailOptions = {
from: '"noreply#yourdomain.com" <noreply#yourdomain.com>',
to: email,
subject: subject,
text: url,
html: htmlToSend
};
const info = await transporter.sendMail(mailOptions);
console.log("Message sent: %s", info.messageId);
console.log("Preview URL: %s", "https://mailtrap.io/inboxes/test/messages/");
}
String replace isn't a good idea because you'll have to restore old strings or create a backup file to be able to change them another time, also it won't be asynchrone and it will cause a problem in every way!
you can do it much easier and more cleaner:
just go to your mail options and add context with your variables:
var mailOptions = {
from: 'nginx-iwnl#gmail.com',
to: 'username#gmail.com',
subject: 'Sending email',
template: 'yourTemplate',
context: { // <=
username: username,
whatever: variable
}
};
next thing to do is openning your html file and call your variables like:
{{username}}
If you're using Nodemailer 2.0.0 or higher, check this documentation:
https://community.nodemailer.com/2-0-0-beta/templating/ There they explain how to make use of external rendering with templates like that:
// external renderer
var EmailTemplate = require('email-templates').EmailTemplate;
var send = transporter.templateSender(new EmailTemplate('template/directory'));
They also give this example:
// create template based sender function
// assumes text.{ext} and html.{ext} in template/directory
var sendPwdReminder = transporter.templateSender(new EmailTemplate('template/directory'), {
from: 'sender#example.com',
});
where you see how to pass variables.
You will need the email-templates module: https://github.com/crocodilejs/node-email-templates and a template engine of your choice.
Also in the documentation of email-templates you'll find how to make your file structure in order that your templates can be found:
html.{{ext}} (required) - for html format of email
text.{{ext}} (optional) - for text format of email style.
{{ext}}(optional) - styles for html format subject.
{{ext}}(optional) - for subject of email
See supported template engines for possible template engine extensions (e.g. .ejs, .jade, .nunjucks) to use for the value of {{ext}} above.
You may prefix any file name with anything you like to help you identify the files more easily in your IDE. The only requirement is that the filename contains html., text., style., and subject. respectively.
Create one file emailTemplates.js there yo can store each template as a function
emailTemplates.js
const newsLetterEmail = (clientName) => `<p>Hi ${clientName}, here you have today news.</p>`
const welcomeEmail = (clientName, username) => `<p>Welcome ${clientName}, your username is ${username}.</p>`
export {newsLetterEmail, welcomeEmail}
Then in the controllers call any templateFunction and store in output varaible
controller.js
import {welcomeEmail} from './emailTeamplates.js'
const registerUser = async(req, res) => {
const {name, usename, email} = req.body
// User register code....
const output = welcomeEmail(name, username)
let mailOptions = {
from: '"Welcome" <welcome#welcome.com>',
to: 'client#gmail.com',
subject: 'Welcome email!',
text: 'Hello World',
html: output,
}
For those using pug as templating engine
Just a quick way to render a template in a separate file using pug's render function:
// function to send an e-mail. Assumes you've got nodemailer and pug templating engine installed.
// transporter object relates to nodemailer, see nodemailer docs for details
const nodemailer = require('nodemailer');
const pug = require('pug');
function send_some_mail(iterable){
var message = {
from: 'from#example.com',
to: 'to#example.com',
subject: 'Message title',
html: pug.renderFile(__dirname + 'path_to_template.pug', {iterable: iterable})
};
transporter.sendMail(message, function(err, info){...})
}
// template.pug
each item in iterable
li
p #{item.name}
See https://pugjs.org/api/getting-started.html for further details. Note that this will cause template re-compilation every time a message is sent. That is fine for occasional e-mail deliveries. If you send tons of e-mails, you can cache the compiled template to work around that. Check out pug docs for that set up if you need it.
You can use a Web Request to build an html template using handlebars or any other engine.
Create a template
First you must create an html template for the email body. In this example I used a handlebars hbs file.
Do your design stuff with html and add the variables that you will need in the message:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Welcome Email Template</title>
</head>
<body>
<p style="font-size: 14px; font-weight: normal;">Hi {{data.name}}</p>
</body>
</html>
Create a template request
You must create the access to this view. Then a request is created where we can send the template name as an url parameter to make the request parameterizable for other templates.
const web = express.Router()
web.post('/template/email/:template', function(req, res) {
res.render(`templates/email/${req.params.template}`, {
data: req.body
})
})
Mail function
Finally you can send the email after making the request to the template. You can use a function like the following:
const nodemailer = require('nodemailer')
const request = require("request")
function sendEmail(toEmail, subject, templateFile) {
var options = {
uri: `http://localhost:3000/template/email/${templateFile}`,
method: 'POST',
json: { name: "Jon Snow" } // All the information that needs to be sent
};
request(options, function (error, response, body) {
if (error) console.log(error)
var transporter = nodemailer.createTransport({
host: mailConfig.host,
port: mailConfig.port,
secure: true,
auth: {
user: mailConfig.account,
pass: mailConfig.password
}
})
var mailOptions = {
from: mailConfig.account,
to: toEmail,
subject: subject,
html: body
}
transporter.sendMail(mailOptions, function(error, info) {
if (error) console.log(error)
})
})
}
This can be done without templates.
Try changing it to this:
`Hello ${username}!`
Make sure that these are not inverted commas but back ticks.
There is one easy way to insert variable inside html in nodemailer.
html:"<p>Your message "+variable1+".Message continueous "+variable 2+"</p>"
You can also use async/await syntax or promises and avoid using callbacks, like I did here, using async/await:
const fs = require("fs").promises;
const path = require("path");
const handlebars = require("handlebars");
const relativeTemplatePath = "../../html/reset-pw-email-template.html";
function sendEmail(){
const templatePath = path.join(__dirname, relativeTemplatePath);
const templateFile = await fs.readFile(templatePath, 'utf-8');
const template = handlebars.compile(templateFile);
const replacements = {
username:""
};
const finalHtml = template(replacements);
const mailOptions = {
from: "",
to: "",
subject: "",
html: finalHtml,
};
}

Categories

Resources