I have here an API created in NodeJS. When I run the API, it works on the first time, but if run it again its give me an error The parameter name Username has already been declared. Parameter names must be unique at Request.input. I checked other threads on how to fix this thing. Some answers that it needs to be cleared, but I think there is no function in NodeJS mssql library that clears parameter.(correct me if I'm wrong)
My code:
const res = require("express/lib/response")
const {sql,request} = require("../config/connection")
module.exports = {
checkExist: (data,callBack) => {
if(data.key == process.env.KEY) {
var username = data.username
var firstname = data.firstname
var middlename = data.middlename
var lastname = data.lastname
if(username == "" || firstname == "" || middlename == "" || lastname == "") {
return callBack("Invalid Input")
}
else {
request.input('Username', sql.VarChar(50), username);
request.input('FirstName', sql.VarChar(50), firstname);
request.input('MiddleName', sql.VarChar(50), middlename);
request.input('LastName', sql.VarChar(50), lastname);
request.output('Result', sql.Int);
request.execute('sp_INS_User').then(function(recordsets, returnValue, affected) {
if(recordsets.output.Result == -100) {
return callBack("Player Exist with different Username")
}
else if(recordsets.output.Result == -4) {
return callBack("Something went wrong while processing your request, please try again later.")
}
else {
if(recordsets.output.Result == 1000) {
return callBack(null,recordsets)
}
}
// console.dir(err);
}).catch(function(err) {
//return callBack(err)
return callBack("Something went wrong while processing your request, please try again later.")
});
}
}
else {
return callBack("Invalid Access")
}
}
}
My code in : ../config/connection/
const sql = require("mssql")
require("dotenv").config()
const sqlConfig = {
user: process.env.USER,
password: process.env.PASS,
database: process.env.DB_MAIN,
server: process.env.HOST,
pool: {
max: 10,
min: 0,
idleTimeoutMillis: 30000
},
options: {
encrypt:false,
trustServerCertificate: true
}
}
const con = sql.connect(sqlConfig).then(function() {
console.log(`Database connection successful!`);
}).catch(function(err) {
console.log(`Database connection ${err}!`);
});
const request = new sql.Request();
module.exports = {
sql,request
}
This looks like an issue of reusing the request object so the second time this function gets called, you're using the same request object that was previously used the first time this function was called. As such, you're registering duplicate names with request.input().
Apparently, you will need a new copy of const request = new sql.Request(); each time you want to use it because the error message is telling you that you can't repeat statements like request.input('Username',...) over and over on the same request object. So, export a function to get a new request object rather than exporting just one pre-made object.
You can do that like this:
../config/connection/
const sql = require("mssql")
require("dotenv").config()
const sqlConfig = {
user: process.env.USER,
password: process.env.PASS,
database: process.env.DB_MAIN,
server: process.env.HOST,
pool: {
max: 10,
min: 0,
idleTimeoutMillis: 30000
},
options: {
encrypt:false,
trustServerCertificate: true
}
}
const con = sql.connect(sqlConfig).then(function() {
console.log(`Database connection successful!`);
}).catch(function(err) {
console.log(`Database connection ${err}!`);
});
const getRequest = function() { return new sql.Request(); };
module.exports = {
sql, getRequest
};
And, your other code:
const res = require("express/lib/response")
const { sql, getRequest } = require("../config/connection")
module.exports = {
checkExist: (data, callBack) => {
const request = getRequest();
if (data.key == process.env.CAMS_INPLAY_KEY) {
var username = data.username
var firstname = data.firstname
var middlename = data.middlename
var lastname = data.lastname
if (username == "" || firstname == "" || middlename == "" || lastname == "") {
return callBack("Invalid Input")
} else {
request.input('Username', sql.VarChar(50), username);
request.input('FirstName', sql.VarChar(50), firstname);
request.input('MiddleName', sql.VarChar(50), middlename);
request.input('LastName', sql.VarChar(50), lastname);
request.output('Result', sql.Int);
request.execute('sp_INS_User').then(function(recordsets, returnValue, affected) {
if (recordsets.output.Result == -100) {
return callBack("Player Exist with different Username")
} else if (recordsets.output.Result == -4) {
return callBack(
"Something went wrong while processing your request, please try again later."
)
} else {
if (recordsets.output.Result == 1000) {
return callBack(null, recordsets)
}
}
// console.dir(err);
}).catch(function(err) {
//return callBack(err)
return callBack(
"Something went wrong while processing your request, please try again later."
)
});
}
} else {
return callBack("Invalid Access")
}
}
}
Related
I need help to get a simple query for MongoDB (SQL equivalent: "select speed from values"). But I can't find a solution for that.
Node.js backend for Vue.js
// a Part of Init-function:
await collection.replaceOne({}, {
speed: {
value: 65,
type: 2,
},
eightSpeed: {
value: 0.0043,
type: 2,
},
oSpeed: {
value: 0.31,
type: 2,
},
minusSpeed: {
value: 2.42,
type: 2,
}
}
//...
Now I need the query like this: http://192.168.220.220:3000/settings/getValue/speed to get an object speed.
const express = require("express");
const mongodb = require("mongodb");
const SettingsRouter = new express.Router();
SettingsRouter.get("/getValue/:value", async function (req, res) {
try {
const val = req.params.value; // req.body.text;
const select = {};
select[`${val}.value`] = 1;
console.log("settings.js:", "/getValue/:name", select);
const collection = await loadMongoCollection();
const value = await collection.findOne({},select)//.toArray();
res.status(201).send(value);
} catch (error) {
res.status(500).send("Failed to connect Database");
console.error("settings.js:", "/getValue/:name:", error);
}
});
async function loadMongoCollection() {
const dbUri = process.env.MONGODB_CONNSTRING || config.dbUri;
const MongoDB = process.env.MONGODB_DB || config.db;
try {
const client = await mongodb.MongoClient.connect(dbUri, {
useNewUrlParser: true,
});
const conn = client.db(MongoDB).collection("values");
return conn;
} catch (error) {
console.error("settings.js:", "loadMongoCollection:", error);
}
}
When I try it, I get all (not only the Speed Object) what I expected, or nothing.
What I do wrong?
EDIT 2022.01.06:
I try it to change my database:
data = {
speed: {
value: 65,
type: 2,
},//...
}
//...
await collection.replaceOne({}, {values:data}, { upsert: true });
And then the query:
const select = {[`values.${val}.value`]:"1"};
const where = {}; //{"values":val};
const value = await collection.find(where,select,).toArray();
But it will not work in rest... is there an issue in mongo package?
When I do it in https://cloud.mongodb.com - it works:
But my request returns all values...
Log shows:"'settings.js:', '/getValue/:name', { 'values.speed.value': '1' }"
In the screenshot you show the projection is values.speed.value, but in your endpoint you have:
const select = {};
select[`${val}.value`] = 1;
Which would evaluate to speed.value. To mimic what you have on the MongoDB console this should be:
const select = {};
select[`values.${val}.value`] = 1;
So, I changed my Frondend (vue) to spred all Values. I maked some Helper Functions:
CheckForChangedObj
check two object for changes
updateObj
copy all data from object into another
export function CheckForChangedObj(targetObject, obj) {
if (targetObject === typeof undefined || targetObject == {}) {
return true
}
if (JSON.stringify(targetObject) !== JSON.stringify(obj)) {
return true
}
return false
}
export function updateObj(targetObject, obj) {
let keys = Object.keys(obj)
for (let k in keys) {
try {
let key = keys[k]
if (!targetObject.hasOwnProperty(key)) {
//define target, if not exist
targetObject[key] = {}
}
//Workaround for References
if (obj[key].hasOwnProperty("__v_isRef")) {
if (targetObject[key].hasOwnProperty("__v_isRef")) {
targetObject[key].value = obj[key].value
} else {
targetObject[key] = obj[key].value
}
} else {
//Source i a Norm Value
//check for an Object inside, then go in...
if ("object" === typeof obj[key] && !Array.isArray(obj[key])) {
updateObj(targetObject[key], obj[key]) // recurse
} else {
//not else-if!!! __v_isRef: true
if (targetObject[key].hasOwnProperty("__v_isRef")) {
targetObject[key].value = obj[key]
} else {
targetObject[key] = obj[key]
}
}
}
} catch (error) {
console.log(
"key:",
keys[k],
"Error:",
error
)
}
}
}
Then spreading...
import {updateObj,CheckForChangedObj } from "../tools/tools"
beforeMount() {
this.getAllValuesAPI()
setInterval(() => {
this.checkForChanges()
// ml("TABLE", this.values)
}, 10000)
},
setup() {
let prevValues = {}
let values = {
speed: {
id:1,
type: SettingType.Slider,
value: ref(0)
}
//...
}
function checkForChanges() {
//intervaled function to get changes from API
let changed = CheckForChangedObj(this.values, this.prevValues)
if (changed) {
updateObj(this.prevValues, this.values)
setValuesToAPI()
}
else{
getAllValuesFromAPI()
}
}
async function getAllValuesFromAPI() {
//get ALL Settings-Data from API, and put them in values
const res = await axios.get(`settings/getAll`)
return updateObj(this.values, res.data[0].values) u
}
}
When you have a better solution, please help...
In this application, I am saving semester_id, course_id and Subject in Mongodb. All I need is to save the Subject in Json format. I want to save semester_id , course_id and save Subject in Json(not in array) with same ids - For semeter and course. I am saving subject in array and I am new to Angular. Can anyone help me out. Thanks in advance.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var SubjectSchema = new Schema({
semesterId:{type: String,ref:'semesterNew'},
courseId :{type: String,ref:'CollegeCourse'},
subject:{
subject :{ type:String}
},
createdOn : {type:Date,default:Date.now},
updatedOn : {type:Date,default:Date.now},
});
mongoose.model('Subject',SubjectSchema);
router.post('/addSubject',function(req,res){
var subjects = JSON.stringify(req.body.subject);
var subjectData = new subjectModel({
semesterId:req.body.semesterId,
courseId: req.body.courseId,
subject: subjects,
});
subjectData.save(function (err, result) {
if (err) {
console.error(err);
return res.status(400).json({
message: 'Bad Request'
});
} else {
res.json({
status: 200,
data: result
})
console.log('Check',result);
}
});
});
addSubject(item){
return this.api.post(`${this.apiController}/addSubject`,item);
}
saveSubject() {
const config = {
position: NbGlobalPhysicalPosition.BOTTOM_RIGHT
};
const formData = new FormData();
this.subjectMasterForm.controls.semesterCtrl.markAsDirty();
this.subjectMasterForm.controls.collegeCourseCtrl.markAsDirty();
// this.subjectMasterForm.controls.image.markAsDirty();
var all_subject_array = [];
if (this.subjectMasterForm.valid && this.subjectMasterForm.value.subjects.length > 0) {
if (this.semesterSubjectId == '' || this.semesterSubjectId == null || this.semesterSubjectId == 'undefined' || this.semesterSubjectId== undefined) {
var subjects_values = this.subjectMasterForm.value.subjects
var subjects_length = this.subjectMasterForm.value.subjects.length;
subjects_values.forEach(function (element) {
all_subject_array.push(element.name);
console.log('Check2',element.name);
});
this.overview_data = {
courseId: this.courseId,
semesterId:this.semesterId,
subject: all_subject_array,
semesterSubjectId: this.semesterSubjectId,
}
this.collegeTemplateApi.addSubject(this.overview_data).subscribe(data => {
if (data['status'] == 200) {
this.toasterService.show("Subject successfully Added!!!..", `Success`, config);
} else {
this.toasterService.show("Subject Already exists in our Database!!!...", `Success`, config)
}
});
} else {
if(this.courseId!=undefined && this.semesterId!=undefined){
if (this.subjectMasterForm.value.subjects.length > 0) {
var subjects_values = this.subjectMasterForm.value.subjects
var subjects_length = this.subjectMasterForm.value.subjects.length;
subjects_values.forEach(function (element) {
all_subject_array.push(element.name);
});
this.overview_data = {
courseId: this.courseId,
semesterId:this.semesterId,
subject: all_subject_array,
semesterSubjectId: this.semesterSubjectId
}
}
this.collegeTemplateApi.updateSubject(this.overview_data).subscribe(data => {
if (data['status'] == 200) {
this.toasterService.show("Subject successfully Updated!!!..", `Success`, config);
} else {
this.toasterService.show(data['message'], `Success`, config)
}
});
}
}
} else if (this.subjectMasterForm.value.subjects.length == 0) {
this.subjecterror = true;
}
setTimeout(() => this.ngOnInit(), 3000);
}
In your first code segment, the Subject model is not assigned to a variable.
mongoose.model('Subject',SubjectSchema);
Yet later in your code you declare a new instance of subjectModel.
Try assigning the model to this name.
var subjectModel = mongoose.model('Subject', SubjectSchema);
I new to node.js, express, and async/await functions, promises, callbacks, etc. Most relevant parts of what I am asking about are toward the bottom of the code I have posted.
The code does what I need it to, but I have a question about why user.password_hash has a set value (as intended) in the debug I have after the comment that says
// next line works,
but has a value of null in the line after the comment
// but this says value of the password_hash is null?.
The latter debug statement comes after the code that sets the value of user.password_hash, so why wouldn't it have the hash value too?
Apparently, I do not understand what is going on with async/await functions. Is there a way to have the value of user.password_hash accessible outside the block it is in? Kindly make the explanation super simple, as intended for an inexperienced programmer like me. Thanks!
const { request } = require('express');
const express = require('express');
const config = require('config');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const moment = require('moment');
const db = require('../db');
const authMiddleware = require('../middleware/auth');
const debug = require('debug')('app:routes:account');
// creating instance of router.
const router = express.Router();
router.use(express.urlencoded({ extended: false }));
router.use(express.json());
router.post('/register', async (req, res, next) => {
try {
let user = { username: null, email: null, password: null, password_hash: null };
user.username = req.body.username;
user.email = req.body.email;
user.password = req.body.password;
const password_confirm = req.body.password_confirm;
const emailRegExp = /^([A-Za-z0-9_.+-])+\#[A-Za-z0-9_+-]+[\.]{1}[A-Za-z0-9]+$/;
debug(
`username = ${user.username}, email = ${user.email}, password = ${user.password}, passwordConfirm = ${password_confirm}`
);
const data = {
title: 'Register for an account',
isPost: true,
passwordsMatch: false,
isValid: true,
};
if (!user.username) {
data.isValid = false;
data.usernameError = 'username is required';
}
if (!user.email) {
data.isValid = false;
data.emailError = 'email is required';
} else if (!emailRegExp.test(user.email)) {
data.isValid = false;
data.emailError = 'email must be 8 characters or longer.';
} else {
}
debug(`user.username ${user.username}`);
if (!user.password) {
data.isValid = false;
data.passwordError = 'Password missing.';
} else if (user.password.length < 8) {
data.isValid = false;
data.passwordError = 'password not long enough';
} else if (user.password != password_confirm) {
data.isValid = false;
data.password_confirmError = 'Passwords must match.';
} else {
}
if (data.isValid) {
(async () => {
try {
const saltRounds = 10;
const hash = await bcrypt.hash(user.password, saltRounds);
debug(`password to hash ${user.password} and hash ${hash}`);
user.password_hash = hash;
// next line works.
debug(`in async, password_hash ${user.password_hash}`);
const result = await db.registerUser(user);
debug(`BIG RESULTS: ${result}`);
} catch (err) {
console.error(err);
}
})();
// but this says value of the password_hash is null?
debug(`***user is ${user.username}, email ${user.email}, hash ${user.password_hash}`);
}
res.render('account/register', { title: 'Errors!', data });
} catch (err) {
next(err);
}
});
module.exports = router;
UPDATE, I am trying this code, now it won't update the database....why? And I am trying to pass data on to the res.render for success in the update, but that was never working either. Why does it only work with async await? How can I access the data put in the database with async-await there?
router.post('/register', (req, res, next) => {
try {
let user = { username: null, email: null, password: null, password_hash: null };
user.username = req.body.username;
user.email = req.body.email;
user.password = req.body.password;
const password_confirm = req.body.password_confirm;
const emailRegExp = /^([A-Za-z0-9_.+-])+\#[A-Za-z0-9_+-]+[\.]{1}[A-Za-z0-9]+$/;
debug(
`username = ${user.username}, email = ${user.email}, password = ${user.password}, passwordConfirm = ${password_confirm}`
);
const data = {
title: 'Register for an account',
isPost: true,
passwordsMatch: false,
isValid: true,
};
if (!user.username) {
data.isValid = false;
data.usernameError = 'username is required';
}
if (!user.email) {
data.isValid = false;
data.emailError = 'email is required';
} else if (!emailRegExp.test(user.email)) {
data.isValid = false;
data.emailError = 'email must be 8 characters or longer.';
} else {
}
debug(`user.username ${user.username}`);
debug(
`username = ${user.username}, email = ${user.email}, password = ${user.password}, passwordConfirm = ${password_confirm}`
);
if (!user.password) {
data.isValid = false;
data.passwordError = 'Password missing.';
} else if (user.password.length < 8) {
data.isValid = false;
data.passwordError = 'password not long enough';
} else if (user.password != password_confirm) {
data.isValid = false;
data.password_confirmError = 'Passwords must match.';
} else {
///
// res.render('account/registration-success');
// but this says value of the password_hash is null?
// debug(`***user is ${user.username}, email ${user.email}, hash ${user.password_hash}`);
}
if (!data.isValid) {
res.render('account/register', { title: 'Errors!', data });
} else {
try {
const data = {};
const saltRounds = 10;
const hash = bcrypt.hash(user.password, saltRounds);
debug(`password to hash ${user.password} and hash ${hash}`);
user.password_hash = hash;
data.username = user.username;
// next line works.
debug(`in async, password_hash ${user.password_hash}`);
const result = db.registerUser(user);
// debug(`BIG RESULTS: ${JSON.stringify(result, null, 4)}`);
debug(`BIG RESULTS: ${result}`);
res.render('account/registration-success', data);
// const payload = {
// user_id: user.user_id,
// username: user.username,
// email: user.email,
// registration_time: user.registration_time,
// last_login_time: moment(),
// };
} catch (err) {
console.error(err);
}
}
///
// if (data.isValid) {
// const saltRounds = 10;
// bcrypt.hash(user.password, saltRounds, (err, hash) => {
// if (err) {
// console.error(err);
// } else {
// user.password_hash = hash;
// debug(`password to hash ${user.password} and hash ${hash}`);
// const result = db.registerUser(user);
// debug(`result is ${result}`);
// res.render('account/registration-success', result);
// }
// });
// }
// res.render('account/register', { title: 'Errors!', data });
} catch (err) {
next(err);
}
});
SOLUTION --- THIS IS THE CODE THAT FINALLY WORKED, cleaned up a bit.. I suppose what got it working was putting await in front of each part. Thanks to jsfriend00 for his patience with me.
if (!data.isValid) {
res.render('account/register', { title: 'Errors!', data });
} else {
user.registration_time = moment();
user.password_hash = await bcrypt.hash(user.password, 10);
await db.registerUser(user);
await res.render('account/registration-success', { user });
}
The construct you're using:
(async () => {
console.log("0");
await xxx()
// more code
console.log("1");
})();
console.log("2");
Does not work the way you apparently think it does. The above code will show in the console as:
0
2
1
The moment an async function hits its first await, it immediately returns a promise and execution continues after the function call. Sometime later, the promise that you're using await on resolves and then the internals of the function finish and then that previously returned promise is resolved.
It appears that in your particular function here, you can just remove the (async () => {...})() construct entirely. You're already inside an async function so you can already use await and then the rest of your code WILL wait for the await.
I have a requirement that requires me to validate if a user is selecting a correct manager. The association between them is the group id each belongs to. A user can be associated to one group id and a manager can be associated to more than one group id. When allowing a user to select his manager, I need to validate if the user's group id exists in the manager's list of group ids. Here is the code i have implemented below but I get UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'roleId' of undefined
My route file account.ts has the following codeblock
let promise = accountController.update(user);
promise.then(function(data) {
if (data.IsSuccessful == true)
{
result.IsSuccessful = true;
result.SuccessMessage = process.env.USER_UPDATED || "User changed";
return res.status(200).json(result);
}
else
{
result.IsSuccessful = false;
result.ReasonForFailure = data.ReasonForFailure;
res.status(200).json(result);
}
}).catch(function(err){
result.IsSuccessful = false;
result.ReasonForFailure = err.message;
res.status(200).json(result);
});
My controller file account.ts has the following code block for the update method
update = (account: Account) : Promise<Result<Account>> => {
var thisObj = this;
return new Promise<Result<Account>>(function (resolve, reject){
let result = new Result<Account>();
result.Data = account;
MongoClient.connect(config.database.uri, { useUnifiedTopology: true } , async function(err: any, db: any) {
if (err) {
// throw err;
result.IsSuccessful = false;
result.ReasonForFailure = err.message;
reject(result);
}
var dbo = db.db(config.database.name);
var newvalues = { $set:
{
name: account.name,
title: account.title,
organization: account.organization,
reportingTo: account.reportingTo,
workNumber: account.workNumber,
mobileNumber: account.mobileNumber,
allowNotification: account.allowNotification,
allowEmail: account.allowEmail,
groups: account.groups,
updatedDate: account.updatedDate,
isProfileSetupComplete: true,
photo:account.photo
}
};
let existingUser: any;
var query = {email:account.email};
var resultArray = await dbo.collection("users").find(query).toArray();
if (resultArray.length > 0) {
existingUser = resultArray[0];
} else {
db.close();
result.ReasonForFailure = process.env.INVALID_USER_ID || "Invalid User Id";
result.IsSuccessful = false;
reject(result);
}
console.log(existingUser);
if (existingUser.roleId == "1") { //roleId="1" means user
//validate manager id. reportingTo id must be a manager
var queryReporting = { _id: account.reportingTo };
let managerUser: any;
var resultManagerArray = await dbo.collection("users").find(queryReporting).toArray();
if (resultManagerArray.length > 0) {
console.log("managerUser in");//<--This is not printing in the console log
managerUser = resultManagerArray[0];
} else {
db.close();
result.ReasonForFailure = "Invalid reporting id.";// process.env.INVALID_USER_ID || "Invalid User Id";
result.IsSuccessful = false;
resolve(result);
}
//validate manager user
console.log("managerUser out");
console.log(managerUser);
if (managerUser.roleId !== "2"){//<--UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'roleId' of undefined
result.IsSuccessful = false;
result.ReasonForFailure = "Reporting to must be a Manager";
reject(result);
}
//manager and user must be associated with same group
if (!managerUser.groups.includes(account.groups)) {
result.IsSuccessful = false;
result.ReasonForFailure = "Incorrect Manager selection. Employee must be associated with a group the manager is associated with."
reject(result);
}
}
dbo.collection("users").updateOne(query, newvalues, function(err: any, resultSet: any) {
if (err) {
//throw err;
db.close();
result.IsSuccessful = false;
result.ReasonForFailure = err.message;
reject(result);
} else {
result.SuccessMessage = oldImage;
result.IsSuccessful = true;
db.close();
resolve(result);
}
});
});
});
}
managerUser.roleId !== "2" in the above code is the place where I get the UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'roleId' of undefined
I have put await in first call to the find query and it worked as expected waiting for the call to finish but the next await has no effect it seems. The execution didn't wait for the call to finish.
Can you please assist what I'm missing here?
Thanks,
Hemant.
You're probably missing return statements after all the resolve(result);/reject(result); calls. Unlike return and throw, they're ordinary function calls and do not terminate the execution of the function.
Additionally you shouldn't pass an async function as a callback to a function that doesn't handle the returned promise. You should only promisify the connect call alone, wait for its result, and continue the rest of your function after await or in a then callback to ensure errors are handled appropriately.
update = async (account: Account) : Promise<Result<Account>> => {
let result = new Result<Account>();
result.Data = account;
try {
const db = await MongoClient.connect(config.database.uri, { useUnifiedTopology: true });
try {
const dbo = db.db(config.database.name);
const newvalues = {
$set: {
name: account.name,
title: account.title,
organization: account.organization,
reportingTo: account.reportingTo,
workNumber: account.workNumber,
mobileNumber: account.mobileNumber,
allowNotification: account.allowNotification,
allowEmail: account.allowEmail,
groups: account.groups,
updatedDate: account.updatedDate,
isProfileSetupComplete: true,
photo:account.photo
}
};
var resultArray = await dbo.collection("users").find({email: account.email}).toArray();
if (resultArray.length == 0) {
result.ReasonForFailure = process.env.INVALID_USER_ID || "Invalid User Id";
result.IsSuccessful = false;
return result;
}
const existingUser = resultArray[0];
console.log(existingUser);
if (existingUser.roleId == "1") {
var resultManagerArray = await dbo.collection("users").find({ _id: account.reportingTo }).toArray();
if (resultManagerArray.length == 0) {
result.ReasonForFailure = "Invalid reporting id.";// process.env.INVALID_USER_ID || "Invalid User Id";
result.IsSuccessful = false;
return result;
}
console.log("managerUser in");
const managerUser = resultManagerArray[0];
console.log("managerUser out");
console.log(managerUser);
if (managerUser.roleId !== "2") {
result.IsSuccessful = false;
result.ReasonForFailure = "Reporting to must be a Manager";
return result;
}
//manager and user must be associated with same group
if (!managerUser.groups.includes(account.groups)) {
result.IsSuccessful = false;
result.ReasonForFailure = "Incorrect Manager selection. Employee must be associated with a group the manager is associated with."
return result;
}
}
const resultSet = await dbo.collection("users").updateOne(query, newvalues);
result.SuccessMessage = oldImage;
result.IsSuccessful = true;
return result;
} finally {
db.close()
}
} catch(err) {
result.IsSuccessful = false;
result.ReasonForFailure = err.message;
return result;
}
};
I have a mailListenerWrapper.js where I define my imap class:
mailListenerWrapper.js:
const simpleParser = require("mailparser").simpleParser;
const Imap = require("imap");
const striptags = require("striptags");
const BlackListModel = require("./models/blacklist");
const RecEmailConfigModel = require("./models/emailConfig");
const ReceivedEmailModel = require("./models/receivedMail");
const Base64 = require("js-base64").Base64;
const _ = require("lodash");
const logger = require("./helpers/logger");
require("dotenv").config();
class MailListenerWrapper {
constructor() {
this.imap = null;
this.init();
}
async init() {
try {
const getRecEmailConfig = await RecEmailConfigModel.findOne({});
if (getRecEmailConfig) {
getRecEmailConfig.password = Base64.decode(getRecEmailConfig.password);
this.imap = new Imap({
user: getRecEmailConfig.user,
password: getRecEmailConfig.password,
host: getRecEmailConfig.host,
port: getRecEmailConfig.port,
tls: getRecEmailConfig.tls,
tlsOptions: {
rejectUnauthorized: false,
authTimeout: 10000
}
});
} else {
return false;
}
let lastMessage = 0;
const openInbox = cb => {
this.imap.openBox("INBOX", true, cb);
};
const regEmail = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/g;
this.imap.once("ready", () => {
openInbox((err, box) => {
if (err) {
logger.log({ level: "error", message: err });
return false;
}
lastMessage = box.messages.total;
this.imap.on("mail", numberOfNewMessages => {
const newEmail = {};
// const numberOfNewMessages = 0
lastMessage = lastMessage + numberOfNewMessages;
const f = this.imap.seq.fetch(lastMessage + ":" + lastMessage, {
bodies: ["HEADER.FIELDS (SUBJECT FROM)", ""],
struct: true
});
f.on("message", msg => {
msg.on("body", async stream => {
try {
const parsed = await simpleParser(stream);
if (parsed.headers.get("subject")) {
// ################ HEADER ################ \\
newEmail.title = parsed.headers.get("subject");
const getFrom = parsed.headers.get("from").text.split(" <");
// ############################ //
console.log(getFrom); // executes twice here already
// ########################### //
if (getFrom.length === 2) {
newEmail.name = getFrom[0];
newEmail.email = getFrom[1].match(regEmail).join("\n");
}
// ################ HEADER ################ \\
}
if (parsed.text) {
// ################ TEXT ################ \\
newEmail.text = striptags(parsed.text).toString("utf8");
// ################ TEXT ################ \\
}
if (
newEmail.name &&
newEmail.email &&
newEmail.title &&
newEmail.text
) {
const getEmailHostname = newEmail.email.replace(/.*#/, "");
const blacklists = await BlackListModel.find({
hostname: new RegExp(".*" + getEmailHostname + ".*", "i")
});
if (blacklists.length > 0) {
logger.log({
level: "info",
message:
"A BLACKLIST ENTRY WAS TRYING TO SUBMIT AN APPLICATION"
});
return false;
}
const savedEmail = new ReceivedEmailModel(newEmail);
await savedEmail.save();
logger.log({
level: "info",
message:
"SAVED RECEIVED EMAIL =>" + JSON.stringify(savedEmail)
});
}
} catch (err) {
logger.log({ level: "error", message: err });
}
}); // msg on body
});
}); // on mail
this.imap.once("error", err => {
logger.log({
level: "error",
message: err
});
}); // on error
}); // openInbox
}); // ready
this.imap.connect();
} catch (err) {
logger.log({
level: "error",
message: err
});
}
}
close() {
if (this.imap) this.imap.end();
}
}
module.exports = MailListenerWrapper;
In app.js:
global.mailListenerWrapper = new mailListenerWrapper();
The receivedEmail.js model:
const mongoose = require("mongoose");
// Schema variable
const Schema = mongoose.Schema;
// Tags
const receivedMailSchema = new Schema(
{
title: {
type: String,
required: true
},
from: {
name: {
type: String
},
email: {
type: String
}
},
text: {
type: String
}
},
{
collection: "receivedEmail",
timestamps: true
}
);
const ReceivedEmail = mongoose.model("ReceivedEmail", receivedMailSchema);
module.exports = ReceivedEmail;
THE PROBLEM
The console log executes twice and the email is saved on my database twice, and does not save the address that it was sent from.
I have no idea why this could be happening. Could there be some way that the imap class is being instantiated twice?
I guess I'm pretty late, but maybe it could still be useful for anyone else.
I experienced this issue as well. But I realized that node-imap documetation specifies the use of .once() instead of .on() in the sample code.
Try to change the event below:
msg.on("body", async stream => {
....
}
To:
msg.once("body", async stream => {
....
}
It did work for me!