Error with passeportjs and sequelize on express app - node.js

I have a problem and I cannot find a similar post for my problem, I want to go on a route, I have a message "Could not read property 'findOne' of undefined" but my model is good and works, I have it have tested with findall() in the file and works ....
I use express, sequelize and passeport.js in the back-end
Thank you in advance for your help
const { User } = require('../models')
const LocalStrategy = require('passport-local').Strategy
const passport = require('passport')
const bcrypt = require('bcrypt')
const verifPass = (password, user) => {
return bcrypt.compareSync(password, user.user_password)
}
passport.serializeUser((user, done) => {
done(null, user.user_email)
})
passport.deserializeUser((email, done) => {
User.findOne({where: {user_email: email}}).then(user => {
done(null, user)
}).catch(err => {
done(err, null)
})
})
passport.use(new LocalStrategy(
function(email, password, done) {
User.findOne({where: {user_email: email}}).then(user => {
if (!user){
return done(null, false)
}
if (!verifPass(password, user)){
return done(null, false)
}
return done(null, user)
}).catch(err => {
return done(err, null)
})
}
));
module.exports = passport
The user model
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate({ Role, Role_User }) {
// define association here
this.belongsTo(Role, {foreignKey: "user_role_id" })
}
};
User.init({
user_name: {
type: DataTypes.STRING,
allowNull: false
},
user_surname: {
type: DataTypes.STRING,
allowNull: false
},
user_phone: {
type: DataTypes.STRING,
allowNull: false
},
user_email: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
validate: {
isEmail: true
}
},
user_last_connection: {
type: DataTypes.DATE,
allowNull: false
},
user_password: {
type: DataTypes.STRING,
allowNull: false
},
user_function: {
type: DataTypes.STRING,
allowNull: true
},
user_data_hiring: {
type: DataTypes.DATE,
allowNull: true
},
user_data_departure: {
type: DataTypes.STRING,
allowNull: true
},
user_notes: {
type: DataTypes.STRING,
allowNull: true
},
user_role_id: {
type: DataTypes.STRING,
allowNull: false,
references: {
model: "Roles",
key: "id"
}
},
isDeleted: {
type: DataTypes.BOOLEAN,
allowNull: false,
}
}, {
sequelize,
modelName: 'User',
}, );
return User;
};
error log
-------------------EDIT-----------------
fix the first error message by create method outside passport.use
const findUser = async (email) => {
return await User.findOne({where: {user_email: email}})
}
passport.use(new LocalStrategy.Strategy({
usernameField: 'email',
passwordField: 'password'
}, async (email, password, done) => {
try {
const user = await findUser(email)
console.log(user)
if (user && verifPass(password, user)){
done(null, user)
}else{
done(null, false)
}
}catch (error) {
done(error)
}
}))
but now an other unknow function...
log error 2
I really don't understand in the doc it doesn't make sense to happen like that.
the login controller :
router.post('/',
passport.authenticate('local'),
function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
res.json(req.user)
});
-------------------SOLUTION-----------------
After several hours, I found that it was actually a sensible dependency to save sessions that was causing the problem: https://www.npmjs.com/package/connect-session-sequelize
I replaced it with: https://www.npmjs.com/package/express-mysql-session and it works perfectly

Related

Cannot read property 'findOne' of undefined"

I am writing an endpoint that would create a record but before then, I want to be able to validate if one of those data exist before allowing the data to be saved. This is being done using the custom validator in express-validator. I am also using Sequelize as well.
My migration files looks like this:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('merchant_temp', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
firstname: {
type: Sequelize.STRING,
allowNull: false
},
lastname: {
type: Sequelize.STRING,
allowNull: false
},
phone: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
password: {
type: Sequelize.STRING,
allowNull: false
},
ip_address: {
type: Sequelize.STRING
},
created_at: {
allowNull: false,
type: Sequelize.DATE
},
updated_at: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('merchant_temp');
}
};
Model file (merchanttemp.js)
'use strict';
const {
Model, Sequelize
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class MerchantTemp extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
};
MerchantTemp.init({
firstname: {
type: DataTypes.STRING,
allowNull: false
},
lastname: {
type: DataTypes.STRING,
allowNull: false
},
phone: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
ip_address: DataTypes.STRING,
}, {
sequelize,
modelName: 'merchant_temp',
});
return MerchantTemp;
};
validator.js
const { check, body, validationResult } = require('express-validator')
const bcrypt = require('bcrypt')
const models = require('../app/models')
const MerchantTemp = db.rest.models.MerchantTemp
const signupValidation = () => {
return [
body('firstname')
.not().isEmpty().trim().withMessage('Firstname field is required'),
body('lastname')
.not().isEmpty().trim().withMessage('Lastname field is required'),
body('phone')
.not().isEmpty().trim().withMessage('Phone Number field is required')
.isNumeric().withMessage('Phone Number field can only contain Numbers')
.isLength({min: 11, max: 13}).withMessage('Phone Number field can only contain minimum of 11 and max of 13 digits respectively'),
body('email')
.not().isEmpty().trim().withMessage('Email Address field is required')
.isEmail().withMessage('Email field is not a valid format').normalizeEmail()
.custom((value, { req }) => {
/**
** THIS PART IS WHERE I AM VALIDATING IF IT EXIST
**/
MerchantTemp.findOne({ where: { email: req.body.email } })
.then((result) => {
console.log(result)
}).catch(error => {
console.log(error)
})
}),
body('password')
.not().isEmpty().trim().withMessage('Password field is required')
.isStrongPassword(
{
minLength: 6,
minLowercase: 1,
minUppercase: 1,
minSymbols: 1
}).withMessage('Password is too weak. Field must contain min. of 6 characters, 1 lowercase and uppercase character and a symbol')
]
}
const validate = (req, res, next) => {
const errors = validationResult(req)
if (errors.isEmpty()) {
return next()
}
const extractedErrors = []
errors.array().map(err => extractedErrors.push({ msg: err.msg }))
res.status(200).json({
statusCode: 400,
errors: extractedErrors
})
}
module.exports = {
signupValidation,
validate
}
router file
require('dotenv').config()
const router = require('express').Router()
const account = require('../controllers/account.controller')
const { signupValidation, validate } = require('../../helpers/validator')
router.get('/', (req, res) => {
let p = "This serves as a repository of API calls for application"
res.status(200).json({message:p, statusCode: 200})
})
//Endpoint to create new merchant
router.post('/account/create-merchant', signupValidation(), validate, async (req, res) => {
res.status(200).json({
statusCode: 201,
message: req.body
})
})
//Endpoint to login merchant
router.post('/account/login', (req, res) => {
})
module.exports = router
The validation on the /account/create-merchant route works well prior to when I included the findOne part in the validator.js. What exactly I am doing wrong?

beforeBulkDestroy not finding model property to change

I am trying to use the beforeBulkDestory Sequelize hook on a user delete that will switch the deleted column boolean to true prior to updating the record to add a timestamp for deleted_at. However, when I console.log the function parameter it provides a list of options and not the model object that I can update for the record of focus. Am I approaching this the wrong way? Is this something that should be set using model instances?
API Call:
import db from '../../../models/index';
const User = db.users;
export default (req, res) => {
const {
query: { id },
} = req
console.log(User)
if (req.method === 'DELETE') {
User.destroy({
where: {
id: id
}
}).then(data => {
res.json({
message: 'Account successfully deleted!'
})
})
} else {
const GET = User.findOne({
where: {
id: id
}
});
GET.then(data => {
res.json(data)
})
}
}
Parameter Values (beforeBulkDestroy, afterBulkDestroy):
beforeBulkDestroy
{
where: { id: '5bff3820-3910-44f0-9ec1-e68263c0f61f' },
hooks: true,
individualHooks: false,
force: false,
cascade: false,
restartIdentity: false,
type: 'BULKDELETE',
model: users
}
afterDestroy
{
where: { id: '5bff3820-3910-44f0-9ec1-e68263c0f61f' },
hooks: true,
individualHooks: true,
force: false,
cascade: false,
restartIdentity: false,
type: 'BULKUPDATE',
model: users
}
Model (users.js):
'use strict';
const Sequelize = require('sequelize');
const { Model } = require('sequelize');
const bcrypt = require("bcrypt");
module.exports = (sequelize, DataTypes) => {
class users extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
};
users.init({
id: {
type: DataTypes.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true
},
first_name: DataTypes.STRING,
last_name: DataTypes.STRING,
password: {
type: DataTypes.STRING
},
email: DataTypes.STRING,
active: {
type: DataTypes.BOOLEAN,
defaultValue: true
},
deleted: {
type: DataTypes.BOOLEAN,
defaultValue: false
}
}, {
hooks: {
beforeDestroy: (user, options) => {
console.log("beforeDestroy")
console.log(user)
console.log(options)
user.deleted = true
}
},
sequelize,
freezeTableName: true,
modelName: 'users',
omitNull: true,
paranoid: true,
underscored: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
deletedAt: 'deleted_at',
hooks: {
beforeCreate: async function(user){
console.log("beforeCreate")
console.log(user)
const salt = await bcrypt.genSalt(12);
user.password = await bcrypt.hash(user.password, salt);
console.log(user.password)
},
beforeBulkDestroy: async function(user){
console.log("beforeBulkDestroy")
console.log(user)
},
afterBulkDestroy: async function(user){
console.log("afterDestroy")
console.log(user)
}
}
});
users.prototype.validPassword = async function(password) {
console.log("validatePassword")
console.log(password)
return await bcrypt.compare(password, this.password);
}
return users;
};
the before/after bulkDestroy hooks only receive the options, not the instances. One way you could do this is defining a before/after Destroy hook:
hooks: {
beforeDestroy: (user, { transaction }) => {
user.update({ deleted: true }, { transaction });
}
}
and calling User.destroy with the individualHooks option:
User.destroy({ where: { id: id }, individualHooks: true });
Be aware that this will load all selected models into memory.
Docs
Note: In your case, since you're only deleting one record by id, it would be better to just user = User.findByPk(id) then user.destroy(). This would always invoke the hooks and it also makes sure the record you want to delete actually exists.
Note 2: Not sure why you need a deleted column, you could just use deletedAt and coerce it into a boolean (with a virtual field if you want to get fancy).

The classMethods findMyMobile() when called from contoller is giving error

I have created number of function in the user model which are both instance and class methods. But when i am calling class method findMyMobile() from a controller it is giving 'not a function' error. I tried display it inside the controller but it seems it is undefined there.
model/user.js
const { Sequelize, sequelize } = require('../db/sequelize');
const jwt = require('jsonwebtoken');
const Model = Sequelize.Model;
class User extends Model {}
User.init({
id:{
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.STRING,
allowNull: false
},
mobile_number:{
field:'mobile_number',
type: Sequelize.BIGINT(10),
unique:true,
allowNull: false,
is:/^[1-9]\d{9}$/g,
},
type:{
type: Sequelize.ENUM('0','1','2'),
allowNull: false,
defaultValue: '1',
},
otp:{
type: Sequelize.STRING,
},
createdAt: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW
},
updatedAt: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW
}
},{ sequelize,
modelName:'user',
classMethods:{
findByMobile: function(){
var User = this;
return User.findOne({'mobile_number':data['mobile_number']}).then(user => {
return new Promise((resolve,reject)=>{
if(user)
resolve(user);
else
reject(new Error('No user found'));
});
}).catch(err=>{
return Promise.reject(new Error('Database error'));
})
}
}
})
User.sync();
module.exports = {
User
}
controller/login.js
const { User } = require('../model/user');
const _ = require('lodash');
exports.login = (req, res) => {
const mobile = _.pick(req.body, ['mobile_number']);
console.log(typeof User.findByMobile);
User.findByMobile(mobile).then((user) => {
console.log(user);
}).catch(err => {
var response = {
status: 'failure',
message: err.message
}
res.send(response);
});
};
ERROR:
TypeError: User.findByMobile is not a function
Since sequelize v4 classMethods and instanceMethod are removed from the configuration options : https://sequelize.org/v4/manual/tutorial/upgrade-to-v4.html#config-options
You have two ways to define them
class User extends Model {
//Class Method
static findByMobile() {}
//Instance Method
findByMobile() {}
}
OR
class User extends Model { }
//Class Method
User.findByMobile = function() {}
//Instance Method
User.prototype.findByMobile = function() {}
I think you have the export wrong. See this example for reference.
module.exports = {
getName: () => {
return 'Jim';
},
getLocation: () => {
return 'Munich';
},
dob: '12.01.1982',
};
Then on the import file:
const { getName, dob } = require('./user');
console.log(
`${getName()} was born on ${dob}.`
);
What I do suggest is export the function itself. See link below for ref:
What is the purpose of Node.js module.exports and how do you use it?

Sequelize - beforeCreate not being called

I am trying to create a user model with sequelize in nodejs, but it doesn't seem that my beforeCreate isn't actually being called. I have looked at the documentation and multiple examples on the internet but I can't get it working.
The model code is as follows:
"use strict";
const bcrypt = require('bcrypt');
require('dotenv').config()
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define("User", {
username: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
password: {
type: DataTypes.STRING,
allowNull: false
},
emailAddress: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
lastLogin: {
type: DataTypes.DATE,
allowNull: true
}
});
function cryptPassword(password, callback) {
bcrypt.genSalt(process.env.SALT_ROUNDS, function(err, salt) {
if (err)
return callback(err);
bcrypt.hash(password, salt, function(err, hash) {
return callback(err, hash);
});
});
};
User.beforeCreate(function(model, options) {
cryptPassword(model.password, function(err, hash) {
model.password = hash;
});
});
return User;
};
you should define your hook inside of your model
var User = sequelize.define("User", {
username: ...
}, {
hooks: {
beforeCreate: function(){...}
}
});

Cannot access Sequelize instance methods

I get the following error when I attempt to call the generateHash instance method I've defined on my User model:
User.generateHash(...).then is not a function
Here's the model definition itself:
const User = sequelize.define('User',
{
firstName: {
type: Sequelize.TEXT,
field: 'first_name'
},
lastName: {
type: Sequelize.TEXT,
allowNull: false,
field: 'last_name'
},
userName: {
type: Sequelize.TEXT,
field: 'user_name',
allowNull: false
},
password: {
type: Sequelize.TEXT,
allowNull: false
}
}, {
tableName: 'users',
underscored: true,
classMethods: {
associate: function(models) {
User.hasMany(
models.Trip,
{
as: 'trips',
foreignKey: {
name: 'userId',
field: 'user_id',
allowNull: false
},
onDelete: 'CASCADE'
});
},
},
instanceMethods: {
generateHash: function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
validatePassword: function(password) {
return bcrypt.compareSync(password, this.password);
},
apiRepr: function() {
return {
id: this.id,
firstName: this.firstName,
lastName: this.lastName,
userName: this.userName
};
}
}
});
Here's the endpoint where I attempt to call the method:
router.post('/', (req, res) => {
let {userName, password, firstName, lastName} = req.body;
// if no existing user, hash password
return User.generateHash(password)
.then(hash => {
// create new user
return User.create({
firstName: firstName,
lastName: lastName,
userName: userName,
password: hash
});
})
.then(user => {
// send back apirRepr data
return res.status(201).json(user.apiRepr());
})
// error handling
.catch(err => {
if (err.name === 'AuthenticationError') {
return res.status(422).json({message: err.message});
}
res.status(500).json({message: 'Internal server error'});
});});
I'm totally stuck. Any ideas?
In sequelize V4 class and instance methods are removed.
Now you have to make it this way:
const Model = sequelize.define('Model', {
...
});
// Class Method
Model.associate = function (models) {
...associate the models
};
// Instance Method
Model.prototype.someMethod = function () {..}
More information here Sequelize v4 breaking changes
You are calling .then() on something that does not return a promise. Try this:
router.post('/', (req, res) => {
let {userName, password, firstName, lastName} = req.body;
let hash = User.generateHash(password);
// if no existing user, hash password
return User.create({
firstName: firstName,
lastName: lastName,
userName: userName,
password: hash
})
.then(user => {
// send back apirRepr data
return res.status(201).json(user.apiRepr());
})
// error handling
.catch(err => {
if (err.name === 'AuthenticationError') {
return res.status(422).json({message: err.message});
}
return res.status(500).json({message: 'Internal server error'});
});

Resources