I'm sending (nodemailer) a link to the user to verify his account. The email is sent successfully.
The problem: The link doesn't work. I get a 404 error "Not found".
Here's what I'm doing backend to send the email with the link:
// send email with verification link
handler.post(async (req, res) => {
const { email, id } = req.body;
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL_BASE_URL;
if (email && validator.isEmail(email)) {
const dataMail = {
from: " ... ",
to: " ... ",
attachments: [
{
filename: "logo.png",
path: "public/assets/logo.png",
cid: "/logo.png",
},
],
subject: "... ",
html: `<p>Please verify your account:</p><a
href="${baseUrl}/api/verification/${id}" target="_blank">ACTIVATE
ACCOUNT</a>`,
};
try {
await emailService.sendMail(dataMail);
await knex("User")
.update("status", "pending")
.where("id", id);
} catch (error) {
return res.status(400).json({
message: "Something went wrong",
});
}
And here's what should happen backend after the user clicks on the link:
// /api/verification/[id].js
handler.post(async (req, res) => {
try {
const user = await knex("User").where("id", req.query.id);
if (!user) {
res.sendStatus(401).json({ message: "Something went wrong." });
} else {
await knex("User")
.update({ status: "active", date: new Date() })
.where("id", req.query.id);
const redirectPath = process.env.NEXT_PUBLIC_REDIRECT_VERIFICATION;
res.redirect(redirectPath);
}
} catch (err) {
res.status(500).json({ message: "Something went wrong" });
}
});
My folder structure looks as follows:
api/verification/[id].js
Any ideas what's wrong with this approach?
Related
I am trying to get my e-commerce web app to send emails on purchasing but when I try to pay for things I get the mailgun.messages is not a function I've reverted changes and re coded it twice but this is a different error I'm not sure how to resolve this. Is there another way to use mailgun's api? Here is the code below:
orderRoutes.js:
orderRouter.put(
"/:id/pay",
isAuth,
expressAsyncHandler(async (req, res) => {
const order = await Order.findById(req.params.id).populate(
"user",
"email firstName"
);
if (order) {
order.isPaid = true;
order.paidAt = Date.now();
order.paymentResult = {
id: req.body.id,
status: req.body.status,
update_time: req.body.update_time,
email_address: req.body.email_address,
};
const updateOrder = await order.save();
mailgun.messages().send(
{
from: "Sales <sales#cocoTiCosmetics.com>",
to: `${order.user.firstName} <${order.user.email}>`,
subject: `New Order ${order._id}`,
html: payOrderEmailTemplate(order),
},
(error, body) => {
if (error) {
console.log(error);
} else {
console.log(body);
}
}
);
res.send({ message: "Order Paid", order: updateOrder });
} else {
res.status(404).send({ message: "Order Not Found" });
}
})
);
utils.js
export const mailgun = () =>
mg({
apiKey: process.env.MAILGUN_API_KEY,
domain: process.env.MAILGUN_DOMAIN,
});
I am using SendGrid to send the user the reset password link that goes with two parameters (The user._id and token). I have another component that saves the user's changed the password but all I get is an error user. save is not a function
Email helper Code.
import sendGrid from "#sendgrid/mail";
export class sendGridEmail {
static async sendResetPasswordEmail(email, token, id) {
sendGrid.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: `${email}`,
from: `${process.env.VERIFIED_SENDER}`, // Change to your verified sender
subject: "RESET YOUR PASSWORD",
text: `Follow this link to reset your password: ${process.env.BASE_URL}/${id}/${token}`,
};
return sendGrid
.send(msg)
.then(() => {
console.log(`password rest link has been sent to: ${email}`);
})
.catch((err) => {
console.log(err);
});
}
sendLink Component
export const resetUserPassword = asynchandler(async (req, res) => {
const { email } = req.body;
const user = await userModel.findOne({ email });
if (!user) {
res.status(404);
res.json({ message: "the email provided was not found" });
} else if (user) {
const token = AuthToken(user._id);
try {
await sendGridEmail.sendResetPasswordEmail(user.email, token, user._id);
res.status(200);
res.json({
message: `a link to reset your password has been sent to: ${user.email}`,
});
} catch (error) {
res.status(500);
res.json({ message: error });
}
} else {
res.status(500);
res.json({ message: "Internal Server Error" });
}
});
The Component that tries to update the password in the Database but I get an error user.save() is not a function
export const saveResetPassword = asynchandler(async (req, res) => {
const { id, authorization } = req.params;
const user = userModel.findOne(req.params.id);
const private_key=process.env.PRIVATE_KEY
const payload = jwt.verify(authorization, private_key);
if (user._id === id || payload.id) {
try {
user.password = req.body.password;
await user.save();
} catch (error) {
res.status(404);
res.json({ message: `an error occured: ${error}` });
}
}else{
res.status(500)
res.json({message: "an error occured"})
}
});
My Routes
import { loginUser, registerUser, resetUserPassword, saveResetPassword } from "./controllers/user.controller.js";
export const Routes =(app)=>{
app.get("/health", (req,res) => {
res.send(200).json({message:"Server health check is Ok"});
});
// user api's
app.post('/api/registeruser', registerUser);
app.post('/api/loginuser', loginUser);
app.post('/api/password-reset', resetUserPassword);
app.post("/api/save-password/:id/:authorization", saveResetPassword);
}
const user = await userModel.findOne(req.params.id);
You forgot await, model.findOne() returns a Promise
After making request to the server, am getting net::ERR_TOO_MANY_REDIRECTS. This was working earlier, now the App cant make any request to the server.
Though the API's are working when tested with Postman.
This is the action that makes the request to the server
//Login User
export const login = (email, password) => async (dispatch) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const body = JSON.stringify({ email, password });
console.log(email, password); //This is where the run time stops and catch error
try {
const res = await axios.post(authLogin, body, config);
console.log(res);
dispatch({
type: LOGIN_SUCCESS,
payload: res.data,
});
dispatch(loadUser());
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, "danger")));
}
dispatch({
type: LOGIN_FAIL,
});
}
};
This is the controller for the API that's been called
// #route POST api/auth/login
// #desc Login user and return JWT token
// #access Public
const loginUser = async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user)
return res.status(401).json({
errors: [
{
msg:
"The email address " +
email +
" is not associated with any account. Double-check your email address and try again.",
},
],
});
//validate password
if (!user.comparePassword(password))
return res
.status(401)
.json({ errors: [{ msg: "Invalid email or password" }] });
// Make sure the user has been verified
if (!user.isVerified)
return res.status(401).json({
errors: [
{
type: "not-verified",
message: "Your account has not been verified.",
},
],
});
// Login successful, write token, and send back user
res.status(200).json({ token: user.generateJWT() });
} catch (error) {
console.error(error.message);
res
.status(500)
.json({ errors: [{ msg: "Server unavailable, try again latter" }] });
}
};
This is a react, node.js, mongoDB and Redux project. Have not experience this before.
Kindly help if you have any idea what i did wrong.
Thanks.
Resolved. It turns out that the SSL-VPN i was using on my machine caused the issue. All the APIs started working immediately i disconnected the VPN.
I was working on a simple app with login functionality , but im unable to send username and password properly to nodejs server. I have tried encoding it, putting it as Map and FormData, but nothing seems to workout. I console logged the request body and it prints "undefind"
I'm using Dio dart package for making http requests and Redux and redux thunk to dispatch actions .
//Code on My flutter app
ThunkAction<AppState> login(LoginData data) {
return (Store<AppState> store) async {
store.dispatch(IsLoading(true));
try {
Response response = await Dio().post(
"http://10.0.2.2:4000/api/user/login",
data: json.encode({"phone": data.phone, "password": data.password}));
if (response.statusCode == 200) {
print(json.decode(response.data));
store.dispatch(IsLoading(false));
}
} catch (e) {
print("Error :(");
}
};
}
// Code on My nodejs
router.post("/login", (req, res) => {
//this log prints undefined
console.log("Login route: " + req.body.phone);
var cred = {
phone: req.body.phone,
password: req.body.password
};
User.findOne({ phone: cred.phone })
.then(result => {
if (!result) {
res.status(400).json({ msg: "no user" });
} else {
bcrypt.compare(req.body.password, result.password, (err, isMatch) => {
if (isMatch) {
const payload = { id: result._id };
console.log("Logged in :" + payload);
jwt.sign(
payload,
keys.secretOrKey,
{ expiresIn: 7200 },
(err, token) => {
res.status(200).json({
success: true,
token: "Bearer " + token
});
}
);
} else {
res.status(400).json({ msg: err });
}
});
}
})
.catch(err => {
res.status(400).json({ msg: err });
});
});
To access parameter in server side add this header to you request:
HttpHeaders.contentTypeHeader: 'application/x-www-form-urlencoded'
I am trying to add test data for my test:
const chai = require("chai");
const expect = require("chai").expect;
chai.use(require("chai-http"));
const app = require("../server.js"); // Our app
const user = require("../app/controllers/user.controller.js");
describe("API endpoint /users", function() {
this.timeout(5000); // How long to wait for a response (ms)
before(function() {
const users = [
{
email: "ssss#ss.com",
givenName: "eee",
familyName: "www2"
},
{
email: "ssss#ss.com",
givenName: "eee",
familyName: "www2"
}
];
user.create(users);
done();
});
// GET - List all data
it("should return all users", function() {
return chai.request(app).get("/users").then(function(res) {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.be.an("array");
});
});
});
I get the error:
1) API endpoint /users
"before all" hook:
TypeError: Cannot destructure property email of 'undefined' or 'null'.
at Object.exports.create (app\controllers\user.controller.js:5:13)
How can I add test data?
Controller:
const user = require("../models/user.model.js");
const validator = require("email-validator");
// Create and Save a new user
exports.create = (req, res) => {
const { query: { email, givenName, familyName } } = req;
// Validate request
if (!validator.validate(email) || !givenName || !familyName) {
return res.status(400).send({
message:
"Please use a valid email address, given name and family name."
});
}
// Create a user
const User = new user({
email,
givenName,
familyName
});
// Save user in the database
User.save()
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Error occurred while creating the user."
});
});
};
// Retrieve and return all users from the database.
exports.findAll = (req, res) => {
user
.find()
.then(users => {
res.send(users);
})
.catch(err => {
res.status(500).send({
message:
err.message || "An error occurred while retrieving users."
});
});
};
// Find a single user with a userId
exports.findOne = (req, res) => {
user
.findById(req.params.userId)
.then(user => {
if (!user) {
return res.status(404).send({
message: "user not found with id " + req.params.userId
});
}
res.send(user);
})
.catch(err => {
if (err.kind === "ObjectId") {
return res.status(404).send({
message: "user not found with id " + req.params.userId
});
}
return res.status(500).send({
message: "Error retrieving user with id " + req.params.userId
});
});
};
// Update a user identified by the userId in the request
exports.update = (req, res) => {
// Validate Request
if (!req.body.content) {
return res.status(400).send({
message: "user content can not be empty"
});
}
// Find user and update it with the request body
user
.findByIdAndUpdate(
req.params.userId,
{
title: req.body.title || "Untitled user",
content: req.body.content
},
{ new: true }
)
.then(user => {
if (!user) {
return res.status(404).send({
message: "user not found with id " + req.params.userId
});
}
res.send(user);
})
.catch(err => {
if (err.kind === "ObjectId") {
return res.status(404).send({
message: "user not found with id " + req.params.userId
});
}
return res.status(500).send({
message: "Error updating user with id " + req.params.userId
});
});
};
// Delete a user with the specified userId in the request
exports.delete = (req, res) => {
user
.findByIdAndRemove(req.params.userId)
.then(user => {
if (!user) {
return res.status(404).send({
message: "user not found with id " + req.params.userId
});
}
res.send({ message: "user deleted successfully!" });
})
.catch(err => {
if (err.kind === "ObjectId" || err.name === "NotFound") {
return res.status(404).send({
message: "user not found with id " + req.params.userId
});
}
return res.status(500).send({
message: "Could not delete user with id " + req.params.userId
});
});
};
create function expects a single user while it receives an array of users as an argument. The problem with it is that it's a middleware, it doesn't return a promise, so it cannot be efficiently chained. It also causes side effects and calls res.send while this is not needed for what it's used here.
Mongoose model should be used directly here, its create accepts an array. The block should return a promise in order to not cause race conditions in tests:
const User = require(".../user.model.js");
...
before(function() {
const users = [...];
return User.create(users);
});