I'm working on my contact page for my static website, and normally, it'd post an email message to by Node.js app on Heroku and send me an email. However, Axios isn't posting the email, because I'm not seeing it on the console logs in Heroku.
Right now, I have a domain name, mydomainname.com, linked to my static website on Firebase, while api.mydomainname.com, is linked to my Heroku app.
I changed the Axios.post between '/api/email', 'api.mydomainname.com/api/email', and 'mydomainname.com/api/email', but neither seemed to work. I thought that because my Heroku app and static website are linked to the same domain name, it'd work.
What would I configure Axios.post to?
Here's what I have at the moment:
Relevant Code
ContactPage.js
handleSubmit = (e) => {
e.preventDefault();
if (this.validate()) {
this.setState({
disabled: true,
emailSent: null
});
Axios.post('api.mydomainname.com/api/email', this.state)
.then(res => {
if(res.data.success) {
this.setState({
emailSent: true
});
this.clearForm();
} else {
this.setState({
disabled: false,
emailSent: false
});
}
})
.catch(err => {
console.log(err);
this.setState({
disabled: false,
emailSent: false
});
})
}
}
index.js
const express = require('express'); //Needed to launch server.
const bodyParser = require('body-parser');
const cors = require('cors'); //Needed to disable sendgrid security.
const sendGrid = require('#sendgrid/mail'); //Access SendGrid library to send emails.
sendGrid.setApiKey(process.env.SENDGRID_API_KEY);
const app = express(); //Alias from the express function.
app.use(bodyParser.json());
app.use(cors());
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*'); // Change later to only allow our server
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
app.get('/api', (req, res, next) => {
res.send('API Status: Running');
});
app.post('/api/email', (req, res, next) => {
console.log(req.body);
const msg = {
to: 'my#email.com',
from: req.body.email,
subject: req.body.subject,
text: req.body.message
}
sendGrid.send(msg)
.then(result => {
res.status(200).json({
success: true
});
})
.catch(err => {
console.log('error: ', err);
res.status(401).json({
success: false
});
});
});
app.listen(process.env.PORT || 4000);
This is my console for my static website.
I eventually found out that the connection to api.mydomainname.com was not secure and needed an SSL certificate. So, I registered my domain to Cloudflare.
Related
here is some problem that i have... i have been tried days to figure it out but nothing make it working.
i have Node.js Express session on backend and there are users, when i make login i set req.session.userId = "userid" and i have middleware that check if req.session.userId exist and then next() so on Localhost everything worked fine but when i hosted my website i can’t access the req.session.userId it's mean the Middleware don’t next()
Frondend hosted: Netlify
Backend hosted: Nodechef
Mysql hosted: Nodechef
i don’t know, maybe i missed something or that i have to made some
changes when hosting...
i hope you guys have solution for me 🙌🏻
index.js
const express = require('express');
const cookieParser = require("cookie-parser");
const session = require('express-session');
const cors = require('cors');
const app = express();
app.use(express.json());
app.use(cookieParser());
app.use(cors({
origin: '***********',
credentials: true
}));
app.all('/', function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Credentials", true);
next();
});
app.use(session({
secret: "******",
name: "*****",
saveUninitialized: true,
resave: true,
cookie: {
domain: '*******',
secure: true,
maxAge: 1000 * 60 * 60 * 24 * 365,
}
}))
app.get('/', (req, res) => {
res.send({ msg: "seccess" })
})
app.use('/users', require('./routes/users'))
const PORT = 3000
app.listen(process.env.PORT || PORT, () => {
console.log(`Express server listening on port ${PORT} `);
});
users/login
const router = require('express').Router()
router.post('/login', (req, res) => {
const { userEmail, userPassword } = req.body
db.query(`SELECT * FROM users WHERE userEmail = ?`, userEmail,
(err, result) => {
if (err) {
return res.send({ err: err })
} else {
if (!userEmail || !userPassword)
req.session.userId = user.id
res.send({ msg: "Login Succes", req: req.session, user: user.id })
}
})
});
Middleware
module.exports.onlyLoggedUsers = (req, res, next) => {
if (req.session.userId) {
next()
} else {
res.status(200).send({err:"sensetive content for logged users only, plesae log in"})
}
}
I just added app.enable('trust proxy')
and it is work temporarily, in safari this not work and also on smartphone, if I got to setting in safari and turn of Prevent Cross-Site Tracking it is working so my question is how I can run the application without turn of Prevent Cross-Site Tracking.
I'm using csurf to handle CSRF tokens in my express application, but I don't know where I'm supposed to create the token. I can't use the sign-in route, because the req.csrfToken() function is not available.
app.use(csrf({ cookie: true }))
app.post('/signin', function (req, res) {
// Authentication ...
res.cookie('XSRF-TOKEN', req.csrfToken()); // Not possible (post request)
})
Should I create a new route for this that I use every time a user opens the front-end of my website?
app.use(csrf({ cookie: true }))
app.get('/csrf', function (req, res) {
res.cookie('XSRF-TOKEN', req.csrfToken());
})
Thanks in advance!
Edit: My frontend (react) is separate from the backend (express server)
There are several ways that you can set the CSRF Token:
You can use it when rendering forms
JS:
var csrfProtection = csrf({ cookie: true })
app.get('/form', csrfProtection, function (req, res) {
// pass the csrfToken to the view
res.render('send', { csrfToken: req.csrfToken() })
});
HTML
<input type="hidden" name="_csrf" value="{{csrfToken}}">
You can use it as a cookie for all the routes. Recommended for Single Page Applications:
app.all('*', function (req, res) {
res.cookie('XSRF-TOKEN', req.csrfToken())
res.render('index')
})
If you have a separate backend and frontend, you can use a middleware like below:
JS:
app.use(express.csrf());
app.use(function(req, res, next) {
res.locals._csrf = req.csrfToken();
next();
});
HTML:
input(type='hidden', name='_csrf', value=_csrf)
More info about this : http://sahatyalkabov.com/jsrecipes/#!/backend/csrf-protection-with-express
I ended up using the following code:
Express server:
app.use(cookieParser());
app.use(csrf({ cookie: true }));
app.get('/csrf', (req, res, next) => {
res.send({'csrf_token': req.csrfToken()});
});
App component in react:
useEffect(async () => {
try {
const res = await axios.get('http://api.myserver.com/csrf', { withCredentials: true });
const csrfToken = (await axios.get('http://api.myserver.com/csrf')).data.csrf_token;
// save csrfToken to redux store and include it in every request (-> axios interceptor)
} catch (err) {
//...
}
}, [])
When I try to submit information on my web application, I am getting an error saying TypeError: Items.save is not a function, does anyone know how I can fix this? I am using mongoDB in the backend to submit information to the database from the web app.
Below is my JavaScript code:
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
//specify where to find the schema
const Items = require('./models/item')
// connect and display the status
mongoose.connect('mongodb://localhost:27017/items', { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => { console.log("connected"); })
.catch(() => { console.log("error connecting"); });
// use the following code on any request that matches the specified mount path
app.use((req, res, next) => {
console.log('This line is always called');
res.setHeader('Access-Control-Allow-Origin', '*'); //can connect from any host
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, OPTIONS'); //allowable methods
res.setHeader('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept');
next();
});
app.get('/items', (req, res, next) => {
//call mongoose method find (MongoDB db.Items.find())
Items.find()
//if data is returned, send data as a response
.then(data => res.status(200).json(data))
//if error, send internal server error
.catch(err => {
console.log('Error: ${err}');
res.status(500).json(err);
});
});
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
// serve incoming post requests to /items
app.post('/items', (req, res, next) => {
// create a new item variable and save request’s fields
const items = new Items ({
itemName: req.body.itemName,
servings: req.body.servings
});
//send the document to the database
Items.save()
//in case of success
.then(() => { console.log('Success');})
//if error
.catch(err => {console.log('Error:' + err);});
});
//to use this middleware in other parts of the application
module.exports=app;
Please try items.save() instead of the one with capital I [Items.save()]
I have created a backend server using express and deployed to firebase. which allows me to receive emails from my contact page in my portfolio.
I am able to receive emails when I run both client and backend server locally. But when I deploy to firebase and tried with the firebase app URL, it's not working. Then I tried hosting my app in Github and tried the contact page so that request is from SSL page(https). But still, I get error 500
I tried the solution mentioned and didn't work
sending-email-via-node-js-using-nodemailer-is-not-working
My express js codes
index.js
const express = require("express");
const functions = require("firebase-functions");
const path = require("path");
const sendMail = require("./mail");
const log = console.log;
var cors = require("cors");
const app = express();
app.use(cors({ origin: true }));
// data parsing
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.post("/email", (req, res) => {
// expect to receive some data from client
const { name, email, message } = req.body;
log("Data", req.body);
sendMail(email, name, message, function(err, data) {
if (err) {
res.status(500).json({ message: "Internal Error" });
} else {
res.json({ message: "success", data });
}
});
//email, name, text
});
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "../views", "index.html"));
});
exports.app = functions.https.onRequest(app);
mail.js
const mailGun = require("nodemailer-mailgun-transport");
const auth = {
auth: {
api_key: "xxxxxxxxx",
domain: "xxxxxxxxx"
}
};
const transporter = nodemailer.createTransport(mailGun(auth));
const sendMail = (email, name, message, callBack) => {
const mailOptions = {
from: email,
to: "faizhameedv#gmail.com",
subject: `your website mail from ${name}`,
text: message
};
transporter.sendMail(mailOptions, function(err, data) {
if (err) {
console.log("err", err);
callBack(err, null);
} else {
callBack(null, data);
console.log("Message Sent!!");
}
});
};
module.exports = sendMail;
It works when I run locally using firebase serve --only functions,hosting and then when I deploy it to firebase which is successful. But I cant req the new URL given by firebase with /email
My issue is similar to this one, all answers there didnt help me.
I'm using Passport.js with local strategy (passport-local-mongoose).
The middleware below works on Postman but fails whenever I try from my React client.
exports.isLoggedIn = (req, res, next) => {
console.log(req.user) // undefined with react, but works from postman
if (req.isAuthenticated()) {
return next();
}
}
Here's my app.js:
require('./handlers/passport');
app.use(cors())
app.use(session({
secret: process.env.SECRET,
key: process.env.KEY,
resave: true,
saveUninitialized: true,
store: new MongoStore({ mongooseConnection: mongoose.connection })
}));
app.use(passport.initialize());
app.use(passport.session());
handlers/passport.js:
const passport = require('passport');
const mongoose = require('mongoose');
const User = mongoose.model('User');
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
user.js (model):
...
userSchema.plugin(passportLocalMongoose, { usernameField: 'email' });
CLIENT CODE:
const url = 'http://localhost:7777/users/<id>';
const config = {
headers: {
'Content-Type': 'application/json',
},
};
axios.put(url, data, config)
.then(res => console.log(res))
.catch(err => console.log(err));
Am I missing something? does passportLocal strategy means that I can't use with an API and a client like my example? Thanks :D
So, after some time I could find an answer:
Server sends SetCookie header then the browser handle to store it, and then the cookie is sent with requests made to the same server inside a Cookie HTTP header.
I had to set withCredentials: true in my client. (axios.js)
const config = {
withCredentials: true,
headers: {
'Content-Type': 'application/json',
},
};
axios.put(url, { page: '123' }, config)
.then(res => console.log('axios', res))
.catch(err => console.log('axios', err));
Then I had CORS problems.
So I added this to my express server:
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
if ('OPTIONS' == req.method) {
res.send(200);
} else {
next();
}
});