cannot send ejs file through nodemailer - javascript

it shows this error
The "chunk" argument must be of type string or an instance of Buffer or Uint8Array. Received an instance of Promise
const transporter = nodemailer.createTransport({
service: "gmail",
auth:{
user:process.env.email,
pass: process.env.password
}
})
const mailOptions = {
from:process.env.email,
to : req.user.username,
subject:"Test",
html: ejs.renderFile(path.join(__dirname,'views/home.ejs'))
}
transporter.sendMail(mailOptions,function(err,info){
if(err){
console.log("err");
}
else{
console.log("sent" +info.response);
}
})

ejs.renderFile is asynchronous, use a callback to fetch the rendered HTML
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.email,
pass: process.env.password
}
})
const mailOptions = {
from: process.env.email,
to: req.user.username,
subject: "Test"
}
ejs.renderFile(path.join(__dirname, 'views/home.ejs'), { name: name }, (err, data) => {
if (err) {
console.log(err);
} else {
mailOptions.html = data
transporter.sendMail(mailOptions, function (err, info) {
if (err) {
console.log("err");
}
else {
console.log("sent" + info.response);
}
})
}
});
Or Using Asyn/Await
export default async (nodemailer) => {
try {
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.email,
pass: process.env.password
}
})
const mailOptions = {
from: process.env.email,
to: req.user.username,
subject: "Test"
}
const data = await new Promise((resolve, reject) => {
ejs.renderFile(path.join(__dirname, 'views/home.ejs'), { name }, (err, data) => {
if (err) {
reject(err)
}
resolve(data)
});
})
const response = await new Promise((resolve, reject) => {
// eslint-disable-next-line consistent-return
transporter.sendMail(mailOptions, error => {
if (error) {
return reject(error);
}
resolve('Email sent');
});
});
console.log(response);
return response
} catch (error) {
console.log(error);
}
}

Related

Failed to load resource: the server responded with a status of 400 (Bad Request) http://localhost:5000/api/refresh_token

Im getting this error, but I don't know what's wrong with my code, since it works perfectly on Postman, but doesn't when I run it on Node.js or localhost:3000 (which is my client side).
Here's the controller authCtrl.js code:
const Users = require("../models/userModel");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const authCtrl = {
register: async (req, res) => {
try {
const { fullname, username, email, password, gender } = req.body;
let newUserName = username.toLowerCase().replace(/ /g, "");
const user_name = await Users.findOne({ username: newUserName });
if (user_name)
return res.status(400).json({ msg: "This user name already exists." });
const user_email = await Users.findOne({ email });
if (user_email)
return res.status(400).json({ msg: "This email already exists." });
if (password.length < 6)
return res
.status(400)
.json({ msg: "Password must be at least 6 characters." });
const passwordHash = await bcrypt.hash(password, 12);
const newUser = new Users({
fullname,
username: newUserName,
email,
password: passwordHash,
gender,
});
const access_token = createAccessToken({ id: newUser._id });
const refresh_token = createRefreshToken({ id: newUser._id });
res.cookie("refreshtoken", refresh_token, {
httpOnly: true,
path: "http://localhost:5000/api/refresh_token",
maxAge: 30 * 24 * 60 * 60 * 1000, // 30days
});
await newUser.save();
res.json({
msg: "Register Success!",
access_token,
user: {
...newUser._doc,
password: "",
},
});
} catch (err) {
return res.status(500).json({ msg: err.message });
}
},
login: async (req, res) => {
try {
const { email, password } = req.body;
const user = await Users.findOne({ email }).populate(
"followers following",
"avatar username fullname followers following"
);
if (!user)
return res.status(400).json({ msg: "This email does not exist." });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch)
return res.status(400).json({ msg: "Password is incorrect." });
const access_token = createAccessToken({ id: user._id });
const refresh_token = createRefreshToken({ id: user._id });
res.cookie("refreshtoken", refresh_token, {
httpOnly: true,
path: "http://localhost:5000/api/refresh_token",
maxAge: 30 * 24 * 60 * 60 * 1000, // 30days
});
res.json({
msg: "Login Success!",
access_token,
user: {
...user._doc,
password: "",
},
});
} catch (err) {
return res.status(500).json({ msg: err.message });
}
},
logout: async (req, res) => {
try {
res.clearCookie("refreshtoken", {
path: "http:/localhost:5000/api/refresh_token",
});
return res.json({ msg: "Logged out!" });
} catch (err) {
return res.status(500).json({ msg: err.message });
}
},
generateAccessToken: async (req, res) => {
try {
const rf_token = req.cookies.refreshtoken;
if (!rf_token)
return res.status(400).json({ msg: "Refresh token failed." });
jwt.verify(
rf_token,
process.env.REFRESH_TOKEN_SECRET,
async (err, result) => {
if (err) return res.status(400).json({ msg: "JWT Verify failed." });
const user = await Users.findById(result.id)
.select("-password")
.populate(
"followers following",
"avatar username fullname followers following"
);
if (!user)
return res.status(400).json({ msg: "This does not exist." });
const access_token = createAccessToken({ id: result.id });
res.json({
access_token,
user,
});
}
);
} catch (err) {
return res.status(500).json({ msg: err.message });
}
},
};
const createAccessToken = (payload) => {
return jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET, {
expiresIn: "1d",
});
};
const createRefreshToken = (payload) => {
return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET, {
expiresIn: "30d",
});
};
module.exports = authCtrl;
Here's the util fetchData.js (where I use axios to fetch the data):
import axios from "axios";
export const getDataAPI = async (url, token) => {
const res = await axios.get(`http://localhost:5000/api/${url}`, {
headers: { Authorization: token },
});
return res;
};
export const postDataAPI = async (url, post, token) => {
const res = await axios.post(`http://localhost:5000/api/${url}`, post, {
headers: { Authorization: token },
});
return res;
};
export const putDataAPI = async (url, post, token) => {
const res = await axios.put(`http://localhost:5000/api/${url}`, post, {
headers: { Authorization: token },
});
return res;
};
export const patchDataAPI = async (url, post, token) => {
const res = await axios.patch(`http://localhost:5000/api/${url}`, post, {
headers: { Authorization: token },
});
return res;
};
export const deleteDataAPI = async (url, token) => {
const res = await axios.delete(`http://localhost:5000/api/${url}`, {
headers: { Authorization: token },
});
return res;
};
Here's the redux action authAction.js file:
import { GLOBALTYPES } from "./globalTypes";
import { postDataAPI } from "../../utils/fetchData";
export const login = (data) => async (dispatch) => {
try {
dispatch({ type: GLOBALTYPES.ALERT, payload: { loading: true } });
const res = await postDataAPI("login", data);
dispatch({
type: GLOBALTYPES.AUTH,
payload: {
token: res.data.access_token,
user: res.data.user,
},
});
localStorage.setItem("firstLogin", true);
dispatch({
type: GLOBALTYPES.ALERT,
payload: {
success: res.data.msg,
},
});
} catch (err) {
dispatch({
type: GLOBALTYPES.ALERT,
payload: {
error: err.response.data.msg,
},
});
}
};
export const refreshToken = () => async (dispatch) => {
const firstLogin = localStorage.getItem("firstLogin");
if (firstLogin) {
dispatch({ type: GLOBALTYPES.ALERT, payload: { loading: true } });
try {
const res = await postDataAPI("refresh_token");
dispatch({
type: GLOBALTYPES.AUTH,
payload: {
token: res.data.access_token,
user: res.data.user,
},
});
dispatch({ type: GLOBALTYPES.ALERT, payload: {} });
} catch (err) {
dispatch({
type: GLOBALTYPES.ALERT,
payload: {
error: err.response.data.msg,
},
});
}
}
};

User pool does not exist: AWS adminConfirmSignUp

For some reason, only the adminConfirmSignUp gives this error. Authentication using CognitoUser doesn't give this error. Both of them are configured to the same user pool and region.
Below is the code for authentication using cognitoUser, which is working:
public async login(req: UserLoginRequest): Promise<object> {
return new Promise<any>(async (resolve, reject) => {
console.log(`This is the req password-----> ${req.password}`)
try {
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails({
Username: req.emailAddress,
Password: req.password
});
var userData = {
Username: req.emailAddress,
Pool: this.userPool
};
this.cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
await this.authenticateUser(this.cognitoUser, authenticationDetails).then(value=> {
resolve(value)
}).catch(error => {
console.log(`This is the login error catch----> ${JSON.stringify(error)}`)
});
} catch (error) {
console.log(`I entered into catch .......${JSON.stringify(error)}`)
reject(error);
}
});
}
private async authenticateUser(cognitoUser: AmazonCognitoIdentity.CognitoUser, authenticationDetails: AmazonCognitoIdentity.AuthenticationDetails) {
console.log(`Inside authenticate user`);
console.log(`This is the cognitoUser -----> ${JSON.stringify(cognitoUser)} and these are the authentication details-----> ${JSON.stringify(authenticationDetails)}`)
try{
return new Promise<any>(async (resolve, reject) => {
await cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: (result) => {
console.log(`This is the success result ----> ${JSON.stringify(result)}`);
resolve(result)
},
onFailure: (error) => {
console.log(`This is the error being returned in onFailure ----> ${JSON.stringify(error)}`)
resolve(error)
}
})
})
}
catch(error){
rejects(error);
}
}
The confirm user, which uses AWS.CognitoIdentityServiceProvider doesn't work. Gives out an error user pool doesn't exist:
private async confirmUser(resetDetails: ResetPassword, cognitoAdmin: AWS.CognitoIdentityServiceProvider) {
console.log(`This is the admin config cognito ----> ${JSON.stringify(cognitoAdmin)}`)
return new Promise<any>(async (resolve,reject)=>{
console.log(`Inside confirm`);
const confirmParams = {
UserPoolId: process.env.COGNITO_USER_POOL_ID!,
Username: resetDetails.emailAddress!
}
console.log(`This is the user pool ID -----> ${confirmParams.UserPoolId} and this is the region----> ${cognitoAdmin.config.region}`);
await cognitoAdmin.adminConfirmSignUp(confirmParams, async (err, data) => {
if (err) {
console.log(`This is the admin user confirm error ---> ${err}`)
reject(err);
}
else {
cognitoAdmin.adminUpdateUserAttributes({
UserAttributes: [{
Name: 'email_verified',
Value: 'true'
}],
UserPoolId: process.env.COGNITO_USER_POOL_ID!,
Username: resetDetails.emailAddress!
}, (err, data) => {
if (err) {
reject(err)
}
else {
resolve(data)
}
}
)
}
})
})
}
This is the cognitoAdmin argument details sent to the cognitoAdmin parameter of the above confirmUser method:
var cognitoAdmin = new AWS.CognitoIdentityServiceProvider({ region: process.env.COGNITO_POOL_REGION!});

_http_outgoing.js:470 throw new ERR_HTTP_HEADERS_SENT('set'); Error: Cannot set headers after they are sent to the client : Nodejs web application

The Code in Users.js gets an error in the snippet at: qrcode.toDataURL(secret.otpauth_url, (err, data_url) => {.
I've tried adding return statement to make sure I'm not sending the response multiple times. I can see that the data_url when converted to image online shows me a QR code but I'm unable to see that when I'm using Postman.
router.post(
"/",
[
check("name", "Name is required")
.not().isEmpty(),
check("email", "Please include a valid email").isEmail(),
check(
"password",
"Please enter a password with 6 or more characters"
).isLength({ min: 6 })
],
async (req, res) => {
console.log("hi");
console.log(JSON.stringify(req.body));
const errors = validationResult(req);
if (!errors.isEmpty()) {
// return res.status(400).json({ errors: errors.array() });
}
const {
name,
email,
password,
type_of_user,
question1,
answer1,
question2,
answer2
} = req.body;
try {
let user = await User.findOne({ email }); // await User.findOne({ email });
user = new User({
name,
email,
avatar,
password,
type_of_user,
question1,
answer1,
question2,
answer2
});
const salt = await bcrypt.genSalt(10); //await
user.password = await bcrypt.hash(password, salt); // await
user
.save()
.then(result => {
// MFAOptions & secret will generate a secret
const MFAOptions = {
issuer: "xyz",
user: req.body.email,
length: 64
};
const secret = speakEasy.generateSecret(MFAOptions);
const token = jwt.sign(
{
name: user.name,
email: user.email,
twofactor: false
},
config.get("jwtSecret"), // chnaged from process env jwt
{
expiresIn: "1h"
}
);
// update the user that is just created:
user
.update(
{ email: req.body.email },
{
$set: { twoFASecret: secret.base32 }
}
)
.exec()
.then(result => {
console.log(result);
qrcode.toDataURL(secret.otpauth_url, (err, data_url) => {
console.log(data_url);
res.status(200).json({
img: data_url,
token: token
});
});
return;
})
//if anything wrong, throws an error
.catch(err => {
console.log(err);
// res.status(500).json({ error: err });
});
})
// originaly this will end here, but now it should redirect to twoFA route,
// if something wrong, shows an error
.catch(err => {
console.log(err);
// res.status(500).json({ error: err });
});
// user with an id, primise which returns an id
const payload = {
user: {
id: user.id
}
};
jwt.sign(
payload,
config.get("jwtSecret"),
{ expiresIn: 3600 },
(err, token) => {
if (err) throw err;
res.json({ token });
}
);
// } //else end
} catch (err) {
console.error(err.message);
res.status(500).send("Server error");
}
}
);
module.exports = router;
I think your problem with executing this line qrcode.toDataURL(secret.otpauth_url, (err, data_url) => {
this calling has callback which means that you will continue in executing the rest of the code and send a response using res.json then after qrcode finish it executes will enter the callback and send another response which is not allowed.
you have multi execution for res.json you need to remove one of them and refactor your code
I tried to refactor your code :
const validation = [check('name', 'Name is required').not().isEmpty(),
check('email', 'Please include a valid email').isEmail(),
check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 })]
const toDataURL = (otpauth_url) => new Promise((resolve, reject) => {
qrcode.toDataURL(secret.otpauth_url, (err, data_url) => {
if(err)reject(err)
resolve(data_url)
res.status(200).json({
img: data_url,
token,
})
})
});
const signJwt = (payload)=>new Promise((resolve,reject)=>{
return jwt.sign(
payload,
config.get('jwtSecret'),
{ expiresIn: 3600 },
(err, token) => {
if (err) reject(err)
resolve(token)
}
)
})
const postRequest = async (req, res) => {
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() })
}
const { name, email, password, type_of_user, question1, answer1, question2, answer2, } = req.body
try {
let user = await User.findOne({ email })
user = new User({
name,
email,
avatar,
password,
type_of_user,
question1,
answer1,
question2,
answer2,
})
const salt = await bcrypt.genSalt(10) // await
user.password = await bcrypt.hash(password, salt) // await
await user.save()
// MFAOptions & secret will generate a secret
const MFAOptions = {
issuer: 'xyz', user: req.body.email, length: 64,
}
const secret = speakEasy.generateSecret(MFAOptions)
const token = jwt.sign(
{
name: user.name,
email: user.email,
twofactor: false,
},
config.get('jwtSecret'), { expiresIn: '1h', })
// update the user that is just created:
await user.update({ email: req.body.email },
{ $set: { twoFASecret: secret.base32 }, }).exec()
const data_url= await toDataURL(secret.otpauth_url)
if(data_url) return res.status(200).json({
img: data_url,
token,
})
const payload = {
user: {
id: user.id,
},
}
const token= await signJwt(payload)
return res.json({token})
} catch (err) {
console.error(err.message)
return res.status(500).send('Server error')
}
}
router.post('/', validation, postRequest)
module.exports = router

Nodemailer with new elements

I have this code in NodeMailer for sends email:
var mailOptions = {
from: 'test#test.com',
to: 'send#send.com',
subject: 'mail',
text:'test'
}
And I have this function:
mens.message(params, (err, response) => {
if (err){
res.status(500).json(err);
}
else {
if(response.context.reporte){
-----------> var report = response.context.reporte;
response.context.reporte=null;
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
}
else {
console.log('Email Send: ' + info.response) ;
}
});
}
}
});
How can I do that, this variable "report", that have a message, can replace the "text" attribute of the nodemailer?
Thank you very much!
You need to override the text with the new value
mens.message(params, (err, response) => {
if (err){
res.status(500).json(err);
}
else {
if(response.context.reporte){
var report = response.context.reporte;
response.context.reporte=null;
mailOptions = { ...mailOptions, ...{ text: report } }; // <<<< CHANGE IS HERE
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
}
else {
console.log('Email Send: ' + info.response) ;
}
});
}
}
});
if your node version doesn't support the spread operator then you can replace the mailOptions = { ...mailOptions, ...{ text: report }}, with Object.assign(mailOptions, { text: report });

Cannot read property "rid" of undefined

[TypeError: Cannot read property 'rid' of undefined]
Is the error that I get when I try to execute this controller on my post route.
I've tested it out with Postman.
I've tried to console.log(result) but I get undefined.
My query gets executed and my row is inserted into my table. I've checked it. Password is also hashed.
The problem is that I don't get any out binds that should be returned.
Problematic code (IMO) is
...
.then(function(result) {
console.log(result);
cb(null, {
id: result.outBinds.rid[0],
email: result.outBinds.remail[0],
role: result.outBinds.rrole[0]
});
})
...
oracle-NodeDB Wrapper
var oracledb = require('oracledb');
module.exports.OBJECT = oracledb.OBJECT;
function executeSQL(config ,sql, bindParams , options) {
return new Promise(function(resolve, reject) {
oracledb.getConnection(
config,
function(err, connection) {
if (err) {
return reject(err);
}
connection.execute(
sql,
bindParams,
options,
function(err, result) {
if (err) {
doRelease(connection);
return reject(err);
}
resolve(result);
doRelease(connection);
});
});
});
}
function doRelease(connection) {
connection.release(
function(err) {
if (err) {
console.log(err.message);
}
}
);
}
module.exports.executeSQL = executeSQL;
Controller
var database = require('../database/oracledbWrapper');
var dbconfig = require('../database/dbconfig').dbconfig;
var jwt = require('jsonwebtoken');
var bcrypt = require('bcrypt');
exports.createUser = function(req, res, next) {
var user = {
email: req.body.email
};
var unhashedPassword = req.body.password;
bcrypt.genSalt(10, function(err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(unhashedPassword, salt, function(err, hash) {
if (err) {
return next(err);
}
user.hashedPassword = hash;
insertUser(user, function(err, user) {
var payload;
if (err) {
return next(err);
}
payload = {
sub: user.email,
role: user.role
};
res.status(200).json({
user: user,
token: jwt.sign(payload, config.jwtSecretKey, {expiresInMinutes: 60})
});
});
});
});
}
function insertUser(user, cb) {
var bindParams = {
email: user.email.toLowerCase(),
password: user.hashedPassword,
rid: {
type: database.NUMBER,
dir: database.BIND_OUT
},
remail: {
type: database.STRING,
dir: database.BIND_OUT
},
rrole: {
type: database.STRING,
dir: database.BIND_OUT
}
};
database.executeSQL(
dbconfig,
'insert into express_users (email, password, role ) values ( :email, :password, \'BASE\' ) returning id, email, role into :rid , :remail, :rrole',
bindParams,
{}
)
.then(function(result) {
console.log(result);
cb(null, {
id: result.outBinds.rid[0],
email: result.outBinds.remail[0],
role: result.outBinds.rrole[0]
});
})
.catch(function(err) {
console.log(err);
next(err);
});
}
Route
var RESTfulAPICon = require('../controllers/RESTfulAPI');
var indexCon = require('../controllers/index');
var views = require('express').Router();
views.route('/users').post(RESTfulAPICon.createUser);
exports.views = views;
The problem was in my wrapper , mainly here
module.exports.OBJECT = oracledb.OBJECT;
I export only the OBJECT property , but I try to access BIND_OUT properties later on. And they are non existent.
If I do the full export like this
module.exports.OBJECT = oracledb;
Then I can access BIND_OUT properties.

Categories