Why is nodemailer sending duplicate emails? - javascript

So this is a very odd question and I don't expect any one to really have an answer to this, but I'm here to try and see if anyone has experienced the same issue.
The issue that I'm noticing is that our application seems to be sending duplicate emails. For example, I can send a report from our application, and it will send that email once, and then it looks like another one gets sent exactly a minute later.
I'm using nodemailer to send the emails from our applications server, and our default email that we use in our office is Outlook v16.0.12130.20272 using IMAP. These emails are being sent by our noreply email which I believe is being hosted through GoDaddy.
I've sent test emails myself and looked in the network tab to see if it might be a timeout issue, but the response completes with a 200 OK status and the timing shows up as completed as well. Also when I console log the response it only occurs once, which makes me believe that it is actually only sending one email. There must be something happening in between when the host sends the email, and when our recipients actually receive them, but I'm not quite sure.
Here is the server.js file. This is where the smtp request is being made.
var nodemailer = require("nodemailer");
const path = require('path');
const express = require('express');
const http = require('http');
const fs = require('fs');
const socketIO = require('socket.io');
const bodyParser = require('body-parser')
import env from '../env.config.json';
const PORT = require('../env.config.json').SERVER.PORT;
const publicPath = path.join(__dirname, '../public');
import api from './routers/api-routing';
//---------------------------------------------------------------------------
var smtpTransport = nodemailer.createTransport({
service: env.EMAIL.SERVICE,
host: env.EMAIL.HOST,
auth: {
user:env.EMAIL.AUTH.USER,
pass:env.EMAIL.AUTH.PASS
}
});
var mailCounter = 0;
var numPeople = 0;
var app = express();
var server = http.createServer(app);
const port = PORT || 3000;
app.use(express.static(publicPath));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true}));
const io = socketIO(server);
app.use('/api', api(app, io));
// require('./routers/api-routing')(app, io);
//$.get("/send", { to: to, subject: subject).value, text: 'html' }, function (data) { });
app.get('*', function (request, response) {
if (request.get('x-auth')) console.log("x-auth: ", request.get('x-auth'));
const proto = request.get('X-Forwarded-Proto');
if (proto) {
if (proto === 'http') response.redirect(301, "https://ourdomain.com".concat(request.url));
}
response.sendFile(path.resolve(__dirname, '../', 'public', 'index.html'))
if ((request.url).substring(0, 5) == "/send") {
var mailOptions = {
to: request.query.to,
bcc: request.query.bcc,
subject: request.query.subject,
text: request.query.text
}
//console.log(mailOptions); Read up on NodeMailer for details.
smtpTransport.sendMail({ //email options
from: "COMPANY <noreply#ouremail.com>", // sender address. Must be the same as authenticated user if using Gmail.
to: mailOptions.to,
bcc: "COMPANY <noreply#ouremail.com>", // sending to itself
subject: mailOptions.subject, // subject
html: mailOptions.text, // body
}, function (error, response) { //callback
if (error) {
console.log(error);
} else {
console.log("Message sent");
//console.log("Amount of people getting this email: " + response.accepted.length);
}
smtpTransport.close(); // shut down the connection pool, no more messages. Comment this line out to continue sending emails.
});
}
});
io.on('connection', (socket) => {
require('./middleware/sockets')(socket);
});
server.listen(port, () => {
console.log(`Server is up on port ${port}.`);
});
This is the part of our env.config.file that pertains to the emails.
"EMAIL": {
"SERVICE": "Godaddy",
"HOST": "smtp.gmail.com",
"AUTH": {
"USER": "noreply#ouremail.com",
"PASS": "OURPASS"
}
}
If anyone has any ideas or suggestions, I would be very appreciative, thanks!

Your email is being sent on any request sent to your server so if you access it via a browser, the browser will send two requests, one for the path requested and one for the favicon.ico and you're also sending an email when /favicon.ico is requested.
This can happen because you're route handler is configured as:
app.get('*', ...);
That means you're attempting to send an email for every single incoming http request regardless of path.
So, if you go to your host with a browser at http://yourdomain/, it will first request / and then the browser will request /favicon.ico, causing you to then send a second email.
My suggestion is to change from this:
app.get('*', ...);
to this:
app.get('/', ...);
or, even more specific such as:
app.get('/sendemail', ...);
So, you are only sending the email on one specific path request and it will not send the email no other requests such as the favicon. You probably want to add a generic express 404 handler for any other routes.
Note: In a REST design, you would probably send an email with a POST request, not a GET request. A GET would retrieve a resource in a read-only kind of way that does not change any state so it wouldn't have a side effect of sending an email. Note: this isn't related to your problem at all, just a comment about a typical REST design.

After some time I have finally figured out the reason for this behavior. The issue is partially related to what jfriend00 had posted. I ended up making a separate route handler for the emails themselves so that it didn't interfere with the main route handler. The problem is that every request will still go through that route since it is looking for any request indicated by the * and if a person is on the http route making the request instead of https, then it creates a second request or in my case a second email.
You can see that happening in this line here:
if (proto) {
if (proto === 'http') response.redirect(301, "https://ourdomain.com".concat(request.url));
}
The actual fix for this was to create a separate route handler for the emails themselves and then configure my nginx server to reroute to https if a person was going to the http route of the application instead. After this, we have not experienced anymore duplicate emails.
Another way of doing it would be to remove the ```*```` route handler completely and setup the other routes separately. Hopefully this will help somebody in the near future.

Related

Express middleware not capturing request events for socket.io

I'm using Express (v4.17.3), Socket.io, and Node.Js's http module. I'm adding a middleware for express to capture all incoming requests but that's failing.
I'll first show the code I'm using and the output then explain my understanding/expectation of the output (I'm new to Node and all the mentioned libraries so perhaps I'm missing something)
First of all, below is the code I'm referring to. Using Express's middleware I'm trying to capture all the incoming requests and log them, and doing the same for the http on("request"). However, requests going to socket.io aren't captured by the middleware.
// Express
const express = require("express");
const app = express()
// Socket
const server = require('http').createServer(app);
const {Server} = require("socket.io");
const io = new Server(server)
// Want to listen to all incoming requests using the middleware (this doesn't work)
app.use((req,res,next)=>{
console.log(`Express request = ${req.url}`)
next()
})
// Listening to all incoming requests (this works)
server.on("request", (req, res)=>{
console.log(`Http request = ${req.url}`)
})
server.listen(8080, () => {
console.log(`Listening on port 8080`)
})
output when I GET /
Express request = /
Http request = /
Http request = /socket.io/socket.io.js
Http request = /socket.io/?EIO=4&transport=polling&t=O0va...
Http request = /socket.io/?EIO=4&transport=polling&t=O0va24A&sid=c...
Http request = /socket.io/?EIO=4&transport=polling&t=O0va24F&sid=c...
Http request = /socket.io/?EIO=4&transport=polling&t=O0va27x&sid=c...
My expected output is to have equal logs for the middleware app.use() and on("request") ("Express request = " & "Http request = ")
My understanding:
1- When I add a middleware for express as in the code below, any incoming requests should be captured here first before going anywhere else. (correct?)
app.use((req,res,next)=>{...})
2- When I'm passing the express app as an argument to http'screateServer, that the express app will be treated as a listener and any request events will be passed to it. (correct?)
const server = require('http').createServer(app);
So if my understanding is correct, why aren't all the requests captured by the request event passed to the middleware as well?
This is normal. Socket.io puts itself in front of express (or any other listener for incoming requests on the http server) so that it takes the request before Express sees it. Thus, Express (or it's middleware) never see any socket.io connection requests.
Socket.io has its own middleware layer that you can use to participate in the initialization of socket.io requests.
Or, you can register for incoming socket.io connections (to be called after they are already connected) with the io.on('connection', ...) event handler.
When I add a middleware for express as in the code below, any incoming requests should be captured here first before going anywhere else. (correct?)
That is true except for code that registers directly request handlers right on the http server and inserts itself before Express in the listener chain, thus preventing Express from seeing any requests that are destined for socket.io.
When I'm passing the express app as an argument to http'screateServer, that the express app will be treated as a listener and any request events will be passed to it. (correct?)
That is true. But socket.io jumps in front of Express and takes/hides any requests it wants so that Express never sees them.
If you're curious, here's the socket.io code that jumps in line to the front of all listeners for the http server thus bypassing the express listener:
attachServe(srv) {
debug("attaching client serving req handler");
const evs = srv.listeners("request").slice(0);
srv.removeAllListeners("request");
srv.on("request", (req, res) => {
if (this.clientPathRegex.test(req.url)) {
this.serve(req, res);
}
else {
for (let i = 0; i < evs.length; i++) {
evs[i].call(srv, req, res);
}
}
});
}
It grabs all the existing listeners into an array. Then, it removes them all. Then, it registers for the request event itself and, if it is a socket.io request, then it does not call the prior listeners. If it is not a socket.io prefix, then it manually calls the prior listeners.
I believe you need to log the messages sent on socket.
io.on('connection', (socket) => { socket.on('chat message', (msg) => { console.log('message: ' + msg); }); });

Server not receiving HTTP requests from Client

First a quick preface I think may be helpful: I am new to splitting my client and server into separate web apps. My previous apps have all had my server.js at the root level in my directory and the client (typically a create-react-app) in a /client folder.
What I wanted to do: Host a single express.js server on the web which multiple other web applications could make requests to.
I did the first part using an express server and aws elastic beanstalk.
server.js
require('dotenv').config()
const express = require('express');
const app = express();
const cors = require('cors');
const PORT = process.env.PORT || 5000;
const Mongoose = require('mongoose');
const { Sequelize } = require("sequelize");
//ROUTES
const APIUser = require('./routes/api/mongo/api-user');
more routes...
//INITIATE DATA MAPPING CONNECTIONS START
Mongoose.connect(
process.env.MONGO_URI,
{ useNewUrlParser: true, useUnifiedTopology: true },
console.log("connected to MongoDB")
);
const Postgres = new Sequelize(process.env.PG_CONN_STRING);
try {
Postgres.authenticate()
.then(console.log("connected to Postgres"));
} catch {
console.log("Postgres connection failed")
}
//INITIATE DATA MAPPING CONNECTIONS END
//middleware
app.use(cors())
more middleware...
//home route
app.get('/api', (req, res) => {
console.log('RECEIVED REQ to [production]/api/')
res.status(200).send('models api home').end();
})
//all other routes
app.use('/api/user', APIUser);
more route definitions...
//launch
app.listen(PORT, () => console.log(`listening on port ${PORT}`));
The log file for successful boot up on aws: https://imgur.com/vLgdaxK
At first glance it seemed to work as my postman requests were working. Status 200 with appropriate response: https://imgur.com/VH4eHzH
Next I tested this from one of my actual clients in localhost. Here is one of my react client's api util files where axios calls are made to the backend:
import { PROXY_URL } from '../../config';
import { axiosInstance } from '../util';
const axiosProxy = axios.create({baseURL: PROXY_URL}); //this was the most reliable way I found to proxy requests to the server
export const setAuthToken = () => {
const route = "/api/authorization/new-token";
console.log("SENDING REQ TO: " + PROXY_URL + route)
return axiosProxy.post(route)
}
export const authorize = clientsecret => {
const route = "/api/authorization/authorize-survey-analytics-client";
console.log("SENDING REQ TO: " + PROXY_URL + route)
return axiosProxy.post(route, { clientsecret })
}
Once again it worked... or rather I eventually got it to work: https://imgur.com/c2rPuoc
So I figured all was well now but when I tried using the live version of the client the request failed somewhere.
in summary the live api works when requests are made from postman or localhost but doesn't respond when requests are made from a live client https://imgur.com/kOk2RWf
I have confirmed that the requests made from the live client do not make it to the live server (by making requests from a live client and then checking the aws live server logs).
I am not receiving any Cors or Cross-Origin-Access errors and the requests from the live client to the live server don't throw any loud errors, I just eventually get a net::ERR_CONNECTION_TIMED_OUT. Any ideas where I can look for issues or is there more code I could share?
Thank you!
Add a console.log(PROXY_URL) in your client application and check your browser console if that's logged correctly.
If it works on your local client build, and through POSTMAN, then your backend api is probably good. I highly suspect that your env variables are not being set. If PROXY_URL is an emplty string, your axios requests will be routed back to the origin of your client. But I assume they have different origins since you mention that they're separate apps.
Remember environment variables need to prefixed with REACT_APP_ and in a production build they have to be available at build time (wherever you perform npm run build)

how to fetch POST request body data from the client side?

Sorry for this noob question, student here and still learning
I'm trying to pass the request body of a POST request from server to client. I have an Arduino sensor making post requests with sensor data to an express server. The sensor data is inside the POST request body, and I push the data to an array called 'dataArray'. This part seems to be working.
My problem is that I'm now stuck on how to pass this data from the express server to a Vue component on the client side. Should I make a new route? I'm not asking anyone to write any code for me, I'm just hoping someone could point me in the right direction or suggest something, because I'm at a loss on exactly how I should go about doing this. Thank you.
server.js
var express = require("express")
var cors = require("cors")
var bodyParser = require("body-parser")
var app = express()
var mongoose = require("mongoose")
var Users = require("./routes/Users")
var port = process.env.PORT || 5000
var dataArray = []
app.use(bodyParser.json())
app.use(cors())
app.use(bodyParser.urlencoded({ extended: true }))
const mongoURI = 'my_connection_string'
mongoose.connect(mongoURI, { useNewUrlParser: true })
.then(() => console.log("MongoDB Connected"))
.catch(err => console.log(err))
app.use("/users", Users)
app.route("/api/:apikey1")
app.post("/api/:apikey1", function(request, response) {
var myData = request.body;
console.log(myData)
dataArray.push(myData)
response.send("Array Filled")
});
app.listen(port, function () {
console.log("Server is running on port: " + port)
})
If you want to keep it simple use the socket.io library.
It is available for both client and server, you can use in your Vue client too.
Also, you won't need any extra route, just in your app.post("/api/:apikey1") route, use the emit method on socket.io library to broadcast the data as it is received from the sensors
Any data flow from the server to client must be first initiated on the client side, either by polling, using WebSockets or Server-sent events.

Express.js Not letting me handle errors: Exception has occurred: Error _runMicrotasks();

So I'm querying the Blizzard API Battle.Net for some information, character name and the realm they're in. Ofcourse it's possible for a user to query for a character that Does Not Exist, so Blizzard throws a 404 back to me and my server.js file doesn't know what to do with it even though I put something in place to handle it.
Releveant server.js code:
var express = require('express');
var app = express();
var fs = require('fs');
var bodyParser = require('body-parser');
var jsonParser = bodyParser.json();
const blizzard = require('blizzard.js').initialize({apikey: "dummy"});
app.use(express.static(__dirname + '/Source'));
//Listen on port 3000
app.listen(3000, function() {
console.log("Launch successful. To access app, open your browser and insert the following URL into your address bar: http://localhost:3000/");
});
app.post('/buttonpress', jsonParser, function (req, res) {
blizzard.wow.character(['profile'], { origin: 'us', realm: req.body.realm.name, name: req.body.name })
.then(response => {
if(response.status != 200){
res.send("That character doesn't exist! Please enter a valid character name.");
} else {
console.log(response.data);
res.send(response.data);
}
});
});
I attempt to handle anything that's not a 200 by sending something to the client to tell the user: Character DNE!, but instead vscode gives me some red error codes mentioned in the title of this post (in vscode debugger anyway).
When I try this from a command line, just running node server.js, nothing happens when I click the Search Button. I've set breakpoints and it looks like the function doesn't get a response from the server. So the 404 is happening no matter what but I can't figure out how to handle it.
Try placing your app.listen below/after your app.post Express.js runs in a middleware functionality so your listen is blocking all preceding code.

Node.js express POST request not getting parameters

I have the following code:
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }));
app.post('/rasp', function(req, res) {
res.send("received");
res.send(req.body.data);
});
app.listen(process.env.PORT || 5000);
I used POSTMAN to see if it worked and apparently the "received" text is sent back, but the data parameter is blank. What could be the problem?
Basically, the client sends a request and waits for a single response from your server. Once the client receives that response, it stops waiting for another. Furthermore, Express only allows you to send one response per request (going along with the client stuff explained above). You may be able to change this setting, but I've never dealt with it, so my answer will be limited to that knowledge.
Your server is executing res.send('received'); and the response is handled. You cannot call res.send again. You should be getting an error on your server when you attempt the second call.
You should send all data that the client needs in the first (and only) res.send().
Server responses should not be handled like logging (ex: sending 'received', 'analyzing', etc). Keep the logging separate. The client doesn't want to know all that extra info, it just wants the expected data response.
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }));
app.listen(process.env.PORT || 5000);
app.post('/rasp', function(req, res) {
res.send({received:true,data:req.body});
});
can you try this one and writing the response here
I believe your post body is "data=Some Value".
If you want to send multiple chunks of data, you should use res.write, and res.end. In your code change the following lines
res.send("received");
res.send(req.body.data);
to
res.write("received");
res.end(req.body.data);

Categories

Resources