The solution seems to be in front of eyes, but everything I tried so far didn't work. I'm using nodemailer to send e-mail through a form and it works fine on local host but when I deploy it, it doesn't work. I think it has something to do with the routing but I can't figure it out. What should I have on "action" and "app.post"? I'm using a subdomain on inmotion to test the form.
const nodemailer = require ('nodemailer')
const express = require ('express')
const bodyParser = require ('body-parser')
const app = express()
app.use (express.static('./public'))
app.use (bodyParser.json())
app.use (bodyParser.urlencoded ({extended: true}))
const transporter = nodemailer.createTransport({
host: "biz260.inmotionhosting.com",
port: "465",
secure: true,
auth: {
user: "test#learningtodom.com",
pass: "xxxxxx"
}
})
function sendEmail(mail) {
var mailOptions = {
from: "test#learningtodom.com",
to: "pablo.football.coach#gmail.com",
subject: "Mail",
html: mail.body
}
transporter.sendMail (mailOptions, function (err, info){
if (err) {
console.log (err)
}
else {
console.log('Done' + info.response)
}
})
}
app.post ('/send_email_form', (req, res) => {
mail = {
to : req.body.name,
subject: req.body.second,
body: req.body.second
}
sendEmail(mail)
res.redirect ('/')
})
app.listen (8100, () => {
console.log ('Server Running at 8100')
})
Form
<form action="/send_email_form" method="POST">
<div class="form-row">
<div class="col-md-4 mb-3">
<label for="validationServer01">First name</label>
<input type="text" name ="name" class="form-control is-valid" id="validationServer01" placeholder="First name" value="Mark" required>
<div class="valid-feedback">
Looks good!
</div>
</div>
It may be your port settings, 8100 is not a normal port to be open on a production server. It is most common to see regular HTTP traffic on port 80, and HTTPS traffic on port 443.
If it is possible to open port 8100 on your remote server, you should do that to check if your server is reachable on that port. It can help to add a simple status checking endpoint such as
app.get ('/status', (req, res) => {
res.json({ status: 'ok' })
})
and then confirm that you can access that by trying http://[yourdomain]:8100/status in a browser.
I highly recommend reading your port number from your environment variables (like node.env.PORT) so that you can easily serve locally on 8100 but then switch to 80 and/or 443 when deployed on a remote server.
Once you confirm that you can hit a GET/status route, then any other issue you have will be to do with your nodemailer setup.
Also, be aware that using a library like nodemailer may make your mail likely to go into your spam folder. Paid services like Sendgrid and Mailgun work hard to ensure the authenticity of email senders, so emails sent using them are much more likely to be delivered as not spam.
If you are going to be the only recipient of this mail, and can find it in your spam folder, make sure to mark it as not spam, and try to let your inbox know that you trust emails from this address.
Good luck, hope this helps :)
Related
I'm on the final stretch of my web app, and I'm trying to deploy it with Heroku. The backend is very simple, and just uses express and while I was coding, a simple localhost server to create a token on login (localhost:8080/login). Unfortunately, on Heroku the login and token generation will not work unless I'm using my computer and I've ran node server.js already in my terminal. Also not sure if this context is needed, but locally, my react app is launched at localhost:3000 while the server is localhost:8080/login. I've heard that I should change them both to be the same localhost with some slight changes to the url, but nothing's worked so far. What changes do I need to make to the relevant code below so that Heroku will use its own server for the api call?
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.use('/login', (req, res) => {
res.send({
token: 'test123' // simple test send token
});
});
app.listen(8080, () => console.log('API is running on http://localhost:8080/login'));
Server.js (I have to run npm start, Ctrl+C, then node server.js in order to login properly)
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import './Login.css';
async function loginUser(credentials) {
return fetch('http://localhost:8080/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(credentials)
})
.then(data => data.json())
}
export default function Login({ setToken }) {
const [username, setUserName] = useState();
const [password, setPassword] = useState();
const handleSubmit = async e => {
e.preventDefault();
const token = await loginUser({
username,
password
});
setToken(token);
}
return(
<div className="login-wrapper">
<h1>Please Log In</h1>
<form onSubmit={handleSubmit}>
<label>
<p>Username</p>
<input type="text" onChange={e => setUserName(e.target.value)} />
</label>
<label>
<p>Password</p>
<input type="password" onChange={e => setPassword(e.target.value)} />
</label>
<div>
<button type="submit">Submit</button>
</div>
</form>
</div>
)
}
Login.propTypes = {
setToken: PropTypes.func.isRequired
};
Login component where token will be generated
This is due to your Heroku app's localhost is not pointing to your server application.
The most straight forward way to resolve this is to publish your server application on Heroku as well, then change your function loginUser to point to the URL to your server application's Heroku URL. You may later refactor the URL to refer to an environment variable so you have zero code change when developing locally and pushing to Heroku. This deploy guide will probably come in handy for you in this case.
If you do not intend to have publish your server application on Heroku due to any reason, you may also start your server on a local machine and expose it through ngrok, but you will need to refactor the fetch URL now as ngrok will have different URL every time you start it.
I put a contact form on my personal portfolio website, and every couple of weeks, the email address associated with the contact form (which is used nowhere else) gets compromised. Through some means that I'm not aware of, someone is able to gain access to the password (which is used nowhere else) and attempts to log in to my account with it.
When the account is compromised, I find I have three emails from seemingly random addresses with seemingly random, single-word subjects and content.
All passwords are over 10 characters long, using capitals, lowercase, numbers, and symbols.
The website is static, hosted on Netlify. The email is handled by a Node.js microservice hosted on Heroku. Here's my contact form.
<form id="contact-form" autocomplete="off">
<label for="name">Name</label>
<input type="text" id="name" name="name" value="" required />
<label for="email">Email</label>
<input type="email" id="email" name="email" value="" required />
<label for="subject">Subject</label>
<input type="text" id="subject" name="subject" value="" required />
<label for="text">Message</label>
<textarea id="text" name="text" value="" required></textarea>
<button type="submit">Submit</button>
</form>
Here's the code that handles the contact form:
$("#contact-form").submit((e) => {
e.preventDefault();
$.ajax({
url: "https://my-microservice.herokuapp.com/email",
type: "POST",
dataType: "json",
data: $("#contact-form").serialize(),
success: function (response) {
$("#contact-form").html(
"<div class='form-success'><h2>Your message has been sent! Thanks for getting in touch.</h2></div>"
);
},
error: function (xhr, status, error) {
$("#contact-form").html(
"<div class='form-failure'><h2>Uh-oh! Something went horribly wrong. You can email me directly at <a href='mailto: anotheremailaddress#gmail.com'>anotheremailaddress#gmail.com</a></h2></div>"
);
},
});
});
Here's the microservice that handles the email:
require("dotenv").config();
const express = require("express");
const nodemailer = require("nodemailer");
const app = express();
const bodyParser = require("body-parser");
const cors = require("cors");
var corsOptions = {
origin: "https://www.my-portfolio-site.com",
optionsSuccessStatus: 200
};
app.use(
bodyParser.urlencoded({
extended: true
})
);
var transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL,
pass: process.env.PASSWORD
}
});
app.post("/email", cors(corsOptions), (req, res) => {
var mailOptions = {
from: "my-portfolio-site.com",
to: process.env.EMAIL,
subject: "PORTFOLIO MESSAGE",
text: `from: ${req.body.email}\nsubject: ${req.body.subject}\nmessage:\n\n${req.body.text}`
};
transporter.sendMail(mailOptions, function(error, info) {
if (error) {
console.log(error);
res.send(error);
} else {
console.log("Email sent: " + info.response);
res.send(info);
}
});
});
let port = process.env.PORT;
if (port == null || port == "") {
port = 8000;
}
const listener = app.listen(port, function() {
console.log("Your app is listening on port " + listener.address().port);
});
Any insight into how my password might be being compromised and how I might stop it would be very much appreciated. Thanks for your time.
on the microservice there is a piece of code that allows CSRF attack thi is the part:
app.use(
bodyParser.urlencoded({
extended: true
})
);
for more details you can take a look here or search on google what that extended : true means.
I'm trying to send a post request from one server to another
Say I've got two servers (on node.js) running on localhost:3000 and localhost:4000, now I'm trying to send a post request from 3000 to 4000.
I've tried this
app.post('/some_routes', (req, res) => { console.log('request recieved')} )
// on recieving side, ie. localhost:4000
<form action="localhost:4000/some_routes" method="POST">
<input type="submit" value="Submit">
<form>
<!-- on sending side, ie. localhost:3000 >
But that doesn't seem to work. Any suggestions?
Also please add both client and server side snippets.
Have a look at axios
You can then do a post like this
axios.post('/some_routes', {
some: 'data',
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
I have the same scenario and it is working, as shown below
On Local Host 4000:
router.post('/some_routes', async function(req, res) { console.log('request recieved')} );
On Local Host 3000:
<form name="submitForm" method="POST" action="http://localhost:4000/some_routes/">
<input type="submit" value="Submit">
Click on the link
</form>
The problem that I face is that it is not working on different servers.
I built a Firebase HTTP Event function with Node and Express. The function is working, but when I invoke the function on the client side I get 403 Forbidden. The first time I invoked the function I was asked to sign in with a Google account. I signed in with the same account I use for Firebase, but when I invoked the function I got:
Screenshot of 403 error
I looked at the use roles on Google cloud platform and the permission to invoke the function is set to allUsers. I signed out and back in again in the Firebase CLI.
Here is the index.js in the functions folder:
const functions = require('firebase-functions');
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const port = process.env.port || 5600
const nodemailer = require('nodemailer');
app.use(express.static('Public'));
app.use(bodyParser.urlencoded({ extended: true }));
const urlencodedParser = bodyParser.urlencoded({extended: true});
app.post("/api/user", urlencodedParser, (req, res) => {
res.sendFile('../Public/bedankt.html', {root: __dirname})
const persGegevens = req.body
const string = JSON.stringify(persGegevens, (key, value) => {
if (typeof value === "string"){
return value.toUpperCase();
} else {
return value
}
}, 1);
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'gietvloermakers#gmail.com',
pass: 'Gietvloermakers2020!'
}
});
var mailOptions = {
from: 'gietvloermakers#gmail.com',
to: 'gvbeusekom84#hotmail.com',
subject: 'Nieuwe bestelling op Gietvloermakers',
html: string
};
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
});
});
exports.app1 = functions.https.onRequest(app);
app.listen(port);
console.log(port);
Here is the html:
<form id="controlleer-form" action="/api/user" method="post" enctype="application/x-www-form-urlencoded">
<div class="controleer-div">
<h2>Uw bestelling</h2>
<p>Aantal m2</p>
<input class="controle-input" type="text" name="aantalM2" id="aantalM2" readonly>
<p>Kleur</p>
<input class="controle-input" type="text" name="kleur" id="kleur" readonly>
<p>Assistentie</p>
<input class="controle-input" type="text" name="assistentie" id="assistentie" readonly>
<p>Gereedschappen</p>
<input class="controle-input" type="text" name="gereedschappen" id="gereedschappen" readonly>
<p>Totale prijs</p>
<input class="controle-input" type="text" name="totale-prijs" id="totale-prijs" readonly>
<p id="andere-kleur">Bestelling aanpassen</p>
</div>
<div class="controleer-div">
<h2>Uw gegevens</h2>
<p>Voornaam</p>
<input type="text" name="voornaam" placeholder="Voornaam">
<p>Achternaam</p>
<input type="text" name="Achternaam" placeholder="Achternaam">
<p>Straatnaam en huisnummer</p>
<input type="text" name="Achternaam" placeholder="Straatnaam en huisnummer">
<p>Postcode</p>
<input type="text" name="Achternaam" placeholder="Postcode">
<p>Telefoonnummer</p>
<input type="tel" name="telefoonnummer" placeholder="Telefoonnummer">
<p>Emailadres</p>
<input type="email" name="email" placeholder="Emailadres"><br>
<input id="verzenden" type="submit">
</div>
</form>
Here is the firebase.json:
{
"hosting": {
"public": "Public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [{
"source": "**",
"function": "app1"
}]
}
}
I tried but I exhausted all possible solutions I've found online so far.
I encountered this recently. It turns out that as of January 15, 2020 new functions require authentication by default.
See the docs here for details.
The solution was to manually add the Cloud Functions Invoker permission to the allUsers user in the Cloud Functions page in the Google Cloud Console.
If you are getting 403 forbidden error like below
Error: Forbidden Your client does not have permission to get URL
/api/test from this server.
Please follow below steps to grant access to all users. Basically this is to allow unauthenticated clients to access your api endpoint.
Go to https://console.cloud.google.com/functions/list
Select the function to which you want to give public access
Click on PERMISSIONS
Click on ADD MEMBER
Type allUsers
Select role Cloud Functions -> Cloud Functions Invoker
Save
That's it, now test your api.
This has to do with permission access to your cloud functions http requests and cloud function events, you need to edit your cloud function IAM permission.
https://cloud.google.com/functions/docs/securing/managing-access-iam#allowing_unauthenticated_function_invocation
Had the same problem (was asked to login with my Google Account, then denied access). It turned out that functions do currently not work outside the default region. In my case, I had to make a change here:
exports.app = functions
.region('europe-west6') // does not work, delete this line
.https.onRequest(app);
Your code exports the express application as the Cloud Function app1 on this line:
exports.app1 = functions.https.onRequest(app);
In your screenshot, you have tried to access the non-existent app Cloud Function instead resulting in the 403 Forbidden response.
This means the correct URL to call from your client is
http://us-central1-gietvloermakers.cloudfunctions.net/app1/api/user
^^^^
(or you could change the name of the export to app)
Having a closer look at your source code, you should also remove the following lines. If you wanted to test your code you would instead use firebase serve.
const port = process.env.port || 5600
/* ... */
app.listen(port);
On the following lines, you also inject the body parser twice.
app.use(bodyParser.urlencoded({ extended: true })); // use this
const urlencodedParser = bodyParser.urlencoded({extended: true}); // or this, not both
app.post("/api/user", urlencodedParser, ...
In your code, you also have:
app.post("/api/user", urlencodedParser, (req, res) => {
res.sendFile('../Public/bedankt.html', {root: __dirname})
/* do some other stuff */
})
This is invalid for a Cloud Function, because as soon as the Cloud Function handler (your code) calls end(), redirect() or send(), the Cloud Function is allowed to be terminated at any time which means that your email may never be sent. To fix this you need to send the file last.
app.post("/api/user", urlencodedParser, (req, res) => {
/* do some other stuff */
res.sendFile('../Public/bedankt.html', {root: __dirname})
});
My last observation, is that the error may be caused by the folder Public not existing on the server. Based on your sendFile call, you are expecting that the folder "Public" is available to your deployed function but as it is not inside the functions folder, it will not be deployed with your code.
res.sendFile('../Public/bedankt.html', {root: __dirname})
As this file would also be accessible at your-domain.com/bedankt.html, we'll redirect to it. If you wanted to send the HTML content of this file instead, move it inside your deployed functions directory.
res.redirect('/bedankt.html')
Because you appear to be trying to use your express function behind Firebase hosting, we can trim your index.js file to the following:
const functions = require('firebase-functions');
const express = require('express');
const bodyParser = require('body-parser');
const nodemailer = require('nodemailer');
const apiApp = express();
apiApp.use(bodyParser.urlencoded({ extended: true }));
apiApp.post("/api/user", (req, res) => {
const persGegevens = req.body
const string = JSON.stringify(persGegevens, (key, value) => {
if (typeof value === "string"){
return value.toUpperCase();
} else {
return value
}
}, 1);
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'gietvloermakers#gmail.com',
pass: 'Gietvloermakers2020!'
}
});
var mailOptions = {
from: 'gietvloermakers#gmail.com',
to: 'gvbeusekom84#hotmail.com',
subject: 'Nieuwe bestelling op Gietvloermakers',
html: string
};
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
res.redirect('/bedankt.html?success=0');
} else {
console.log('Email sent: ' + info.response);
res.redirect('/bedankt.html?success=1');
}
});
});
// note rename to api
exports.api = functions.https.onRequest(apiApp);
which requires updating your firebase.json file to:
{
"hosting": {
"public": "Public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [{
"source": "/api/**",
"function": "api"
}]
}
}
This configuration will first attempt to find a matching file in your Public directory. If it can't find a match, it will check if the requested path starts with /api and if so, launch your Cloud Function. If it still can't find a match, it will show your 404 page (or the built in one if it doesn't exist).
I'm totally new to web development and working with web related concepts and frameworks (IPs, networks, routers, I cringe everytime :D ).
But, thanks to some internship work I have "forced myself" to work with this and thanks to lots of digging on the internet I have managed to develop a super simple app that can send emails if I am under my localhost. But, I have some (many) questions, but first, the files that comprise all of my code:
Package.json file
{
"name": "email-node",
"version": "1.0.0",
"dependencies": {
"nodemailer": "~0.7.1",
"express": "~4.5.1"
}
}
Index.html
<html>
<head>
<title>Node.JS Email application</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>// <![CDATA[
$(document).ready(function(){
var from,to,subject,text;
$("#send_email").click(function(){
to=$("#to").val();
subject=$("#subject").val();
text=$("#content").val();
$("#message").text("Sending E-mail...Please wait");
$.get("http://localhost:3000/send",{to:to,subject:subject,text:text},function(data) {
if(data=="sent") {
$("#message").empty().html("Email is been sent at "+to+" . Please check inbox !");
}
});
});
});
</script>
</head>
<body>
<div id="container">
<h1>Mailer In Node.JS</h1>
<input id="to" type="text" placeholder="Enter E-mail ID where you want to send" />
<input id="subject" type="text" placeholder="Write Subject" />
<textarea id="content" cols="40" rows="5" placeholder="Write what you want to send"></textarea>
<button id="send_email">Send Email</button>
<span id="message"></span>
</div>
</body>
</html>
app.js File
var express=require('express');
var nodemailer = require("nodemailer");
var app=express();
/*
Here we are configuring our SMTP Server details.
STMP is mail server which is responsible for sending and recieving email.
*/
var smtpTransport = nodemailer.createTransport("SMTP",{
service: "Gmail",
auth: {
user: "MYGMAIL#gmail.com",
pass: "MYGMAILPASS"
}
});
/*------------------SMTP Over-----------------------------*/
/*------------------Routing Started ------------------------*/
app.get('/',function(req,res){
res.sendfile('index.html');
});
app.get('/send',function(req,res){
var mailOptions={
to : req.query.to,
subject : req.query.subject,
text : req.query.text
}
console.log(mailOptions);
smtpTransport.sendMail(mailOptions, function(error, response){
if(error){
console.log(error);
res.end("error");
}else{
console.log("Message sent: " + response.message);
res.end("sent");
}
});
});
/*--------------------Routing Over----------------------------*/
app.listen(3000,function(){
console.log("Express Started on Port 3000");
});
From what I have read here is what I concluded:
Node.js + Express applications always "listens" to incoming connections on port 3000 so that is the value I use.
Ideally, to send stuff from a website instead of my localhost I believe that the problem lies on the index.html file.
Specifically, this line:
$.get("http://localhost:3000/send",{to:to,subject:subject,text:text},function(data){
where instead of using http://localhost:3000/send I would use something like:
http://bruno-oliveira.github.io:3000/send
I have read countless forums and posts, tried everything from:
$.get("/send",{to:to,subject:subject,text:text},function(data){
to searching for github servers IP adress range ( https://developer.github.com/v3/meta/ ) to try and use them directly there and nothing seems to work...
The application is supposedly host here:
http://bruno-oliveira.github.io/
Can someone help me?
Best,
Bruno