Nodemailer application sends email on page refresh - javascript

Building a simple form to allow users to send me an email. The form properly sends the email on submit. However, I also receive an email when the page is refreshed on my browser. What gives? I'm not sure if the issue is in my server.js file or the index.ejs. Is the submit button being triggered somehow on the page load?
Here's the server.js file:
var express = require('express'),
path = require('path'),
nodeMailer = require('nodemailer'),
bodyParser = require('body-parser');
var app = express();
app.set('view engine', 'ejs');
app.use(express.static(__dirname));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
var port = 3000;
app.get('/', function (req, res) {
res.render('index');
});
app.post('/contact', function (req, res) {
let transporter = nodeMailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
user: '****#gmail.com',
pass: '****'
}
});
let mailOptions = {
from: req.body.name + ' <' + req.body.email + '>', // sender address
to: '****#gmail.com', // list of receivers
subject: 'New message test', // Subject line
text: `${req.body.name} (${req.body.email}) says: ${req.body.message}`, // plain text body
html: '<b>Email Test</b>' // html body
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('Message %s sent: %s', info.messageId, info.response);
res.render('index');
});
});
app.listen(port, function(){
console.log('Server is running at port: ',port);
});
And the index.ejs:
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
<form action="/contact" id="contact-form" method="post" role="form">
<fieldset>
<label for="name">Name *</label>
<input id="name" name="name" type="text" placeholder="Your name" required="required">
<label for="email">Email *</label>
<input id="email" name="email" type="text" placeholder="Your email" required="required">
<label for="message">Message *</label>
<textarea id="message" name="message" placeholder="Enter your message here" rows="3" required="required"></textarea>
<button type="submit">Submit</button>
</fieldset>
</form>
</body>
</html>

If you open the route / it send the email straight away?

This is property of your browser, after submitting <FORM> for first time, every time you will refresh same request will be send.
Why? As your next page is result of what you did in previous page, check your url on browser being changed to localhost:3000/contact, this is not a GET method, but a post, this has occurred as you have rendered a page on POST call. Even though if a different ejs would have rendered, on refresh same call to POST: contact would have been send.
Only solution is to use single-page application.
Example
server.js:
```
var express = require('express'),
path = require('path'),
nodeMailer = require('nodemailer'),
bodyParser = require('body-parser');
var app = express();
app.set('view engine', 'ejs');
app.use(express.static(__dirname));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
var port = 3000;
app.get('/', function (req, res) {
res.render('index');
});
app.post('/contact', function (req, res) {
let transporter = nodeMailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
user: '****#gmail.com',
pass: '****'
}
});
let mailOptions = {
from: req.body.name + ' <' + req.body.email + '>', // sender address
to: '****#gmail.com', // list of receivers
subject: 'New message test', // Subject line
text: `${req.body.name} (${req.body.email}) says: ${req.body.message}`, // plain text body
html: '<b>Email Test</b>' // html body
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
// Send Response back to caller
res.status(400).send(error);
}
console.log('Message %s sent: %s', info.messageId, info.response);
res.status(200).send('Success');
});
});
app.listen(port, function(){
console.log('Server is running at port: ',port);
});
`
HTML/EJS:
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="/css/app.css" />
<link rel="icon" href="logo.jpg" />
<title>Send Mail</title>
</head>
<body>
<div class="col-md-6 offset-md-3">
<!-- No need for `form` as will use JavaScript for Single Page Application -->
<div id="sendMail" class="hide">
<div class="col-md-12 form-group">
<label for="email">Email:</label>
<input id="email" class="form-control" type="email">
<span id="emailError" class="hide error">Valid Email Required</span>
</div>
<div class="col-md-12 form-group">
<label for="name">First Name:</label>
<input id="name" class="form-control" type="text">
<span id="nameError" class="hide error">Valid First Name Required</span>
</div>
<div class="col-md-12 form-group">
<label for="message">Message :</label>
<textarea id="message" class="form-control" type="text"></textarea>
<span id="meggageError" class="hide error">Valid Last Name Required</span>
</div>
<div class="col-md-12 form-group">
<p class="btn btn-primary form-control" onclick="sendMail()">Submit</p>
</div>
</div>
</div>
<script type="text/javascript" src="/js/jquery.min.js"></script>
<!-- `login.js` is only used in `login.ejs` -->
<script type="text/javascript" src="/js/sendMail.js"></script>
<script type="text/javascript" src="/js/service.js"></script>
</body>
</html>
sendMail.js:
"use strict";
function sendMail() {
// JavaScript uses `id` to fetch value
let email = $("#email").val(),
name = $("#name").val(),
message = $("#addLname").val();
// Show error `span` when email is invalid
if ( validateEmail(email) ) {
$("#emailError").addClass("hide");
} else {
$("#emailError").removeClass("hide");
return;
}
// Show error `span` when First Name is invalid
if ( validateFname(fName) ) {
$("#nameError").addClass("hide");
} else {
$("#nameError").removeClass("hide");
return;
}
// Show error `span` when Last Name is invalid
if ( message ) {
$("#messageError").addClass("hide");
} else {
$("#messageError").removeClass("hide");
return;
}
// Calling local API to set authentication
// Everything in public is visible for hackers
// Thus to hide auth calling local backend
$.ajax({
"url": "/contact",
"method": "POST",
"data": {email, name, message}
})
.then( result => {
// On success empty all the input fields.
$("#email").val('');
$("#name").val('');
$("#message").val('');
// Message to notify success submition
alert("Successfully Send Mail.");
return;
})
.catch( err => {
// Notify in case some error occured
alert("An error occured.");
return;
});
}
service.js:
"use strict";
/**
* Common services used by all the other scripts
* All these are generic functions
*
*/
// Validate Email based upon pattern
function validateEmail (email) {
if ( email && email.match(/^([A-Za-z0-9]{2,})([#]{1})([A-z]{1,})([.]{1})([A-z.]{1,})*$/) ) {
return true;
}
return false;
}
// Validate First Name based upon pattern
function validateName (name) {
if ( name && name.match(/^([A-Za-z]{2,})*$/) ) {
return true;
}
return false;
}

Related

Send data from HTML form to Express API

I'm trying to build a login page for users and need to send the login info to my express server. I'm not too sure about how to call the api for my server and send the information across though.
Right now I get a 405 error method not allowed when I try to submit the form by clicking on the button.
index.html:
<!DOCTYPE html>
<html land="en">
<head>
<meta charset="UTF-8">
<title>Home page</title>
<script src="../app.js"></script>
</head>
<body>
To login
</body>
</html>
login.html:
<!DOCTYPE html>
<html land="en">
<head>
<meta charset="UTF-8">
<title>Login page</title><br><br>
<script src="../app.js"></script>
</head>
<body>
<form id="login-form" method="post" action="/login">
<input type="text" name="username" id="username-field" placeholder="Username" required><br><br>
<!-- TODO: Change type to "password" -->
<input type="text" name="password" id="password-field" placeholder="Password" required><br><br>
<input type="submit" value="Login" id="login-form-submit">
</form>
</body>
</html>
app.js:
const express = require('express')
var bodyParser = require('body-parser')
var port = process.env.PORT || 3000;
var app = express();
app.use(express.json());
app.use(express.urlencoded({"extended": true}));
app.get('/', function (req, res) {
res.render(__dirname + '/views/index.html');
});
app.post('/login', function (req, res) {
res.statusCode = 200;
res.send({ 'body': req.body });
});
// Listen on port 3000, IP defaults to 127.0.0.1
app.listen(port, () => {
console.log('Server listening at http://127.0.0.1:' + port + '/');
});
You can do it using javascript. Add this to your login.html file
<script type="text/javascript" >
async function login(){
// Get values from FORM
let username = document.getElementById("username-field")
let password = document.getElementById("password-field")
// Body payload
let body = { username, password }
// Fetch options
let option = {
method: 'POST',
body: JSON.stringify(body),
headers: {
'Content-Type': 'application/json'
}
}
// Call /login as POST and wait for reply
const response = await fetch(`/login`, options)
const json = await response.json()
// Use json response
alert(json)
}
document.getElementById("login-form-submit").addEventListener("click", login);
</script>
In order for the server to be properly up, you need to make sure you run it as node app.js instead of adding it to the html file.
UPDATE:
Check out this repo for a working solution : https://github.com/rui-costa/stackoverflow-68238492

pop-up and css won't work on localhost or server?

Please educate me about how am I going to connect the CSS to the server. I've followed two tutorials on youtube, one is using node.js and nodemailer. With this, I use a localhost to run my website but the CSS and js I made from the second tutorial (pop-up when button is clicked) won't work on the localhost but when I clicked the html file itself.
Is this because the tutorials are for different kinds of website? like static and dynamic?
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewpoint" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link href="style.css" rel="stylesheet" type="text/css" />
<title>Document</title>
</head>
<body>
<h1>Welcome to my App</h1>
<form>
<div>
<label for="email">Sender's Email: </label>
<input type="email" id="email" placeholder="Your Email"> <br>
</div>
<div>
<label for="classNum">To whom: R</label>
<input type="number" id="classNum" placeholder="class#" min="1" max="31"> <br>
</div>
<div>
<label for="subject">Subject: </label>
<input type="text" id="subject" placeholder="Subject"> <br>
</div>
<div>
<label for="text">Letter: </label> <br>
<textarea name="text" id="text" cols="30" rows="10"></textarea> <br>
</div>
<input type="submit" value="Submit" class="modal-button" data-modal-target="#modal">
<div class="modal" id="modal">
<div class="modal-header">
<div class="title">Letter Sent!</div>
</div>
<div class="modal-body">
Pressing this button will refresh the page.
<div><button data-close-button class="refresh-button">Send another letter</button></div>
</div>
</div>
<div id="overlay"></div>
</form>
<script src="script.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$('form').on('submit', (e) => {
e.preventDefault();
const email = $('#email').val().trim();
const subject = $('#subject').val().trim();
const text = $('#text').val().trim();
const classNum = $('#classNum').val().trim();
const data = {
email,
subject,
text,
classNum
};
$.post('/email', data, function(){
console.log('Server received our data')
});
});;
</script>
</body>
</html>
This is the server.js
const express = require('express');
const sendMail = require('./mail')
const log = console.log;
const app = express();
const path = require('path');
const PORT = 8080;
app.use(express.urlencoded({
extended: false
}));
app.use(express.json());
app.post('/email', (req, res) => {
const { subject, email, text, classNum} = req.body;
console.log('Data: ', req.body);
sendMail(email, subject, text, classNum, function(err, data){
if (err){
res.status(500).json({ message: 'Internal Error'});
}
else{
res.json({ message: 'Email sent!' });
}
});
// res.json({ message: 'Message received!' })
});
app.get('/', (req, res) =>{
res.sendFile(path.join(__dirname, 'views', 'index.html'));
});
app.listen(PORT, () => log('Server is starting on PORT: ', 8080));
And this one is for the pop-up, script.js
const openModalButtons = document.querySelectorAll('[data-modal-target]');
const closeModalButtons = document.querySelectorAll('[data-close-button]');
const overlay = document.getElementById('overlay');
var path = require('path') //from stackoverflow
app.use(express.static(path.join(__dirname, 'public')));
openModalButtons.forEach(button => {
button.addEventListener('click', () => {
const modal = document.querySelector(button.dataset.modalTarget)
openModal(modal)
})
})
closeModalButtons.forEach(button => {
button.addEventListener('click', () => {
const modal = button.closest('.modal')
closeModal(modal)
})
})
function openModal(modal) {
if (modal == null) return
modal.classList.add('active')
overlay.classList.add('active')
}
function closeModal(modal) {
if (modal == null) return
window.open("https://www.w3schools.com");
}
Please tell me if I need to include the CSS and the mail.js .
If you want to allow users or browsers to get files from your server, you need to add them to your server-side code. For example, you've added a stylesheet reference to your index.html, so the browser will try to get that file (/style.css) from the server. You haven't put any reference to this on the server side, so the server will respond with 404 Not Found or another error.
In order to make the server respond to a request for "/style.css", you need to add the following to your server-side index.js:
app.get("/style.css" /*name of file in index.html*/, (req, res) => {
res.sendFile(path.join(__dirname, 'views', 'style.css')); //CHANGE THIS TO THE NAME OF THE FILE ON THE SERVER
});
The same needs to happen for your browser script, script.js:
app.get("/script.js" /*name of file in index.html*/, (req, res) => {
res.sendFile(path.join(__dirname, 'views', 'script.js'));
});
app.get tells express to respond to a GET request to the first parameter: in the example, that's "/style.css". If you wanted to respond to a GET request to "/foobar", then you would write app.get("/foobar", (req, res) => {/*SOME CODE HERE*/}); . The reason why it wasn't working was becuase when the browser tried to find style.css and script.js, the server didn't know what to do because you hadn't included app.get for those files, and therefore responded with an error.
This might be confusing due to the architecture of how this works. Look at this diagram:
==== HOW A WEBSITE SENDS A FILE ====
______ ____ ______
/ . . \ GET /file [____] READ / |
| j | ======> [____] ======> | file |
\__===_/ <====== [____] <====== | |
user RESPONSE server |_______|

Nodemailer - redirecting to success page

Trying to get my page to redirect from a node.js and express.js page after sending an email. The message gets sent and I am getting the console.log() in my terminal (using morgan for logging) but it's not redirecting me to the success page and I'm not getting a console.log error in the browser. It just stalls and then i get a localhost didn't send any data error. Never used nodemailer before but I managed to get the message sent, I'm just having a problem redirecting to a new page.
Thanks!!
//Success html code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<h1>Success</h1>
</body>
</html>
// Form Box
<form action="/contact" id="contact-form" method="post" role="form">
<fieldset>
<label for="name">Name *</label>
<input id="name" name="name" type="text" placeholder="Your name" required="required">
<label for="email">Email *</label>
<input id="email" name="email" type="text" placeholder="Your email" required="required">
<label for="message">Message *</label>
<textarea id="message" name="message" placeholder="Enter your message here" rows="3" required="required"></textarea>
<button type="submit">Submit</button>
</fieldset>
</form>
// Success HTML route
app.get('/success', function(req,res){
res.sendFile(__dirname + '/views' + '/success.html');
});
//nodemailer
// POST route from contact form
app.post('/contact', function (req, res) {
let mailOpts, smtpTrans;
smtpTrans = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
user: process.env.GMAIL_USER,
pass: process.env.GMAIL_PASS
}
});
mailOpts = {
from: req.body.name + ' <' + req.body.email + '>',
to: process.env.GMAIL_USER,
subject: 'New message from Portfolio site',
text: `${req.body.name} (${req.body.email}) says: ${req.body.message}`
};
smtpTrans.sendMail(mailOpts, function (error, res) {
if (error) {
return console.log(error);
}
else {
console.log('success');
res.redirect('/success');
}
});
});
wondering if you still stuck on the issue but in case, below worked for me.
app.post('/contact', function (req, res) {
let mailOpts, smtpTrans;
smtpTrans = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
user: process.env.GMAIL_USER,
pass: process.env.GMAIL_PASS
}
});
mailOpts = {
from: req.body.name + ' <' + req.body.email + '>',
to: process.env.GMAIL_USER,
subject: 'New message from Portfolio site',
text: `${req.body.name} (${req.body.email}) says: ${req.body.message}`
};
smtpTrans.sendMail(mailOpts, function (error, res) {
if (error) {
return console.log(error);
}
else {
console.log('success');
// NOT HERE res.redirect('/success');
}
});
res.redirect('/success'); // I MOVED THE REDIRECTING CODE HERE AND WORKED
});

Node.JS + mySQL -> How to do the ajax call and access the data on server.js?

So this is my server.js file, where the db is being connected to :
var app = require('express')();
var mysql = require('mysql');
var db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'user_data',
port: 3306
});
db.connect();
app.get('/', function (req, res) {
res.sendFile(__dirname + "/start.html");
});
app.listen(3000);
So I want to make something like a registration form, where you input your username and password. Then, after you click the submit button, the username and password are supposed to go straight to the mySQL database. So apparently, I need an ajax call which sends the data to that server, but I don't know how to access it on the server.js.
$("button#submit").click(function () {
$.ajax({
'url': 'http://localhost:3000',
'type': 'post',
'data': {
'username': $("#usr").val(),
'password': $("#pwd").val()
},
'success': function (data) {
alert('Data: ' + data);
}
});
})
After I get that data on the server.js, I will obviously want to make a query (to insert the values) but how do I get the data?
This is how I did it for one of my projects:
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Contact Form</title>
</head>
<body>
<div id="contact">
<h1>Send an email</h1>
<form action="/myaction" method="post">
<fieldset>
<label for="name">Name:</label>
<input type="text" id="name" name="name" placeholder="Enter your full name" />
<label for="location">Location:</label>
<input type="text" id="location" name="location" placeholder="Enter your location" />
<input type="submit" value="Send message" />
</fieldset>
</form>
</div>
</body>
</html>
Now when user clicks submit /myaction url will hit.
db.connect(function(err,connection){
app.post('/myaction',function(req,res){
console.log(req.body);
var employee={name:req.body.name,location:req.body.location}
db.query('INSERT into employees SET ?',employee,function(err,res){
if(err){
throw err;
}
else{
console.log(res);
}
})
res.send(JSON.stringify(req.body));
})
})
Make sure to include these in your server.js
var express=require('express');
var bodyParser=require('body-parser');
app=express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
So you can see i have defined app.post and I am hitting my url. Then we simply write a sql query to store data.

Sending Email with Node Mailer and Sendgrid in Angular MEAN stack using angular-fullstack from Yeoman

I am trying to understand where to locate the logic to send an email via a contact form in my Angular App (using angular-fullstack MEAN stack from Yeoman).
I can add the logic to send an email in the app.js file on the server side using nodemailer and sendgrid and everything works and an email is sent every time I refresh the server, however I am a little fuzzy on where to place the logic so that it only gets sent once the form is submitted and it hits the server side.
This is what the create action looks like on the Express JS side...
exports.create = function(req, res) {
Contact.create(req.body, function(err, contact) {
if(err) { return handleError(res, err); }
return res.json(201, contact);
});
};
Here is the code in app.js that is working, but obviously not in the right place...
var nodemailer = require('nodemailer');
var sgTransport = require('nodemailer-sendgrid-transport');
var options = {
auth: {
api_user: 'username', // 'SENDGRID_USERNAME' - Recommended to store as evn variables
api_key: 'password', // 'SENDGRID_PASSWORD'
}
};
var mailer = nodemailer.createTransport(sgTransport(options));
var email = {
to: 'sendto#email.com',
from: 'sendfrom#email.com',
subject: 'Test Email',
text: 'Awesome Email',
html: '<b>Bold and Awesome Email</b>'
};
mailer.sendMail(email, function(err, res) {
if (err) {
console.log(err)
}
console.log(res);
});
Coming from a rails background my initial thought is to stick the logic in the create action so that if the object is created successfully the email gets sent. Is this a correct way of thinking about it in this scenario...I am new to the MEAN stack.
Thanks for any help...
You need to create a route on the server that you can post form values to from Angular using $http.post.
The following lets you enter details in an Angular form. The form is then posted to Node where the req.body values are extracted and added email object. The email is then send by SendGrid.
SERVER.JS
var express = require('express');
var http = require('http');
var bodyParser = require('body-parser');
var dotenv = require('dotenv');
dotenv.load(); //load environment variables from .env into ENV (process.env).
var sendgrid_username = process.env.SENDGRID_USERNAME;
var sendgrid_password = process.env.SENDGRID_PASSWORD;
var sendgrid = require('sendgrid')(sendgrid_username, sendgrid_password);
var email = new sendgrid.Email();
var app = express();
app.use(bodyParser.json()); //needed for req.body
app.set('port', process.env.PORT || 3000);
app.use(express.static(__dirname + '/public'));
app.post('/email', function(req, res) {
email.addTo(req.body.to);
email.setFrom(req.body.from);
email.setSubject(req.body.subject);
email.setText(req.body.text);
email.addHeader('X-Sent-Using', 'SendGrid-API');
email.addHeader('X-Transport', 'web');
sendgrid.send(email, function(err, json) {
if (err) {
return res.send("Problem Sending Email!!!!");
}
console.log(json);
res.send("Email Sent OK!!!!");
});
});
var server = http.createServer(app);
server.listen(app.get('port'), function() {
console.log('Express server listening on port ' + app.get('port') ) ;
});
INDEX.HTML
<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="utf-8">
<title></title>
<!-- CSS -->
</head>
<body ng-controller="MainCtrl">
<form name="emailForm">
<div class="group">
<input type="email" name="to" ng-model="email.to" ng-required="true">
<label>To</label>
</div>
<div>
<input type="email" name="from" ng-model="email.from" ng-required="true">
<label>From</label>
</div>
<div>
<input type="text" name="subject" ng-model="email.subject" ng-required="true">
<label>Subject</label>
</div>
<div>
<textarea ng-model="email.text" name="text" placeholder="Enter Text Here.."></textarea>
</div>
<button id="emailSubmitBn" type="submit" ng-click="submitEmail()">
Submit
</button>
</form>
<!-- JS -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</body>
</html>
CLIENT SIDE APP.JS
angular.module('myApp', [])
.controller('MainCtrl', function($scope, $http) {
$scope.submitEmail = function() {
console.log("TEST");
//Request
$http.post('/email', $scope.email)
.success(function(data, status) {
console.log("Sent ok");
})
.error(function(data, status) {
console.log("Error");
})
};
});

Categories

Resources