I'm tryng to register a user in my database.
const sequelize = require('sequelize')
const { Model, DataTypes } = sequelize
const bcrypt = require('bcrypt')
class User extends Model {
isPasswordValid(encondedPassword, password) {
return bcrypt.compareSync(password, encondedPassword)
}
static init(sequelize) {
super.init({
email: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: true,
},
},
password: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: true,
},
}
}, {
hooks: {
beforeCreate: (user, options) => {
const salt = bcrypt.genSaltSync()
user.setAttributes('password', bcrypt.hashSync(user.password, salt))
}
},
sequelize
})
}
}
module.exports = User
But when I call User.create({email, password}) it gives me and error:
UnhandledPromiseRejectionWarning: SequelizeDatabaseError: null value in column "password" violates not-null constraint .
If a delete my hook the code works fine but the password will not be encrypted.
setAttributes receive 3 arguments or an object
Try this
user.setAttributes('setAttributes', 'password', bcrypt.hashSync(user.password, salt))
//OR
user.setAttributes({password: bcrypt.hashSync(user.password, salt)})
Simpler use setDataValue
user.setDataValue('password', bcrypt.hashSync(user.password, salt))
Related
I'm working on an API and have a special problem. When a make a GET request, i received my JSON Data but the server crash with an error :
for (const key of Object.keys(this.constructor._attributeManipulation)) {
TypeError: Cannot convert undefined or null to object
at Function.keys (<anonymous>)
at Timeout._onTimeout (/Users/quentin/O'Clock/motogpapi/node_modules/sequelize/lib/model.js:83:34)
at listOnTimeout (node:internal/timers:564:17)
at process.processTimers (node:internal/timers:507:7)
I can't undersant origin of this crash. you can find below the code. Is it possible this problem due to as wrong data into db ?
Model team.js:
const { DataTypes, Model } = require("sequelize");
const sequelize = require("../database/client");
class Teams extends Model {}
Teams.init({
name: {
type: DataTypes.STRING,
allowNull: false
},
constructorId: {
type: DataTypes.INTEGER,
allowNull: false
},
isOfficial: {
type: DataTypes.BOOLEAN,
allowNull: false
},
championshipId: {
type: DataTypes.INTEGER,
allowNull: false
},
}, {
sequelize,
tableName: "team"
});
module.exports = Teams;
Model Constructor.js
const { DataTypes, Model } = require("sequelize");
const sequelize = require("../database/client");
class Constructors extends Model {}
Constructors.init({
name: {
type: DataTypes.STRING,
allowNull: false
},
model: {
type: DataTypes.STRING,
allowNull: false
},
engine: {
type: DataTypes.STRING,
allowNull: false
},
}, {
sequelize,
tableName: "constructor"
});
module.exports = Constructors;
Model Index.js i used for associations
const Teams = require("./teams");
const Championships = require("./championships");
const Constructors = require("./constructors");
Constructors.hasMany(Teams, {
foreignKey: "constructorId",
as: "teamsList"
});
Teams.belongsTo(Constructors, {
foreignKey: "constructorId",
as: "constructor"
});
Championships.hasMany(Teams, {
foreignKey: "championshipId",
as: "teamsList"
});
Teams.belongsTo(Championships, {
foreignKey: "championshipId",
as: "championship"
});
module.exports = {
Teams,
Championships,
Constructors
};
The Controller :
const { Teams } = require("../models");
const teamsController = {
async getAllTeams(_, response) {
try {
const teamsList = await Teams.findAll({
include: ["constructor", "championship"]
});
response.json(teamsList);
} catch (error) {
console.log(error);
}
}
};
module.exports = teamsController;
And database/client.js
require("dotenv").config();
const { Sequelize } = require("sequelize");
const sequelize = new Sequelize(process.env.PG_URL, {
define: {
underscored: true
}
});
(async () => {
try {
await sequelize.authenticate();
console.log("Connection has been established successfully.");
} catch (error) {
console.error("Unable to connect to the database:", error);
}
})();
module.exports = sequelize;
In my my mern project when the user do login in the response my server sends him the response of with hash password and reset password token is there any way to fix it
Here is my user model
const mongoose = require("mongoose");
const validator = require("validator");
const bcrypt = require("bcryptjs");
const crypto = require("crypto")
const jwt = require("jsonwebtoken");
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, "please Ente the your name"],
},
email: {
type: String,
required: [true, "Please Enter the email"],
unique: [true],
validate: [validator.isEmail, "Please Enter a valid email"],
},
password: {
type: String,
required: true,
minlength: [8, "Passwaord must be more that 8 characters"],
select: false,
},
avatar: {
public_id: {
type: String,
required: true,
},
url: {
type: String,
required: true,
},
},
role: {
type: String,
default: "user",
},
resetPasswordToken: String,
resetPasswordExpire: Date,
createdAt: {
type: Date,
default: Date.now,
},
});
userSchema.pre("save", async function (next) {
if (!this.isModified("password")) {
next();
}
this.password = await bcrypt.hash(this.password, 10);
});
userSchema.methods.comparePassword = async function (password) {
return await bcrypt.compare(password, this.password);
};
userSchema.methods.getJWTToken = function () {
return jwt.sign({ id: this._id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRE,
});
};
// compare password
// userSchema.methods.comparePassword = async function(enterPass){
// return await bcyrypt.compare(enterPass,this.password)
// }
// Reset Password
userSchema.methods.getResetPasswordToken = function () {
// Generating Token
const resetToken = crypto.randomBytes(20).toString("hex");
// Hashing and adding resetPasswordToken to userSchema
this.resetPasswordToken = crypto
.createHash("sha256")
.update(resetToken)
.digest("hex");
this.resetPasswordExpire = Date.now() + 15 * 60 * 1000;
return resetToken;
};
module.exports = mongoose.model("User", userSchema);
as suggest I have used already select false and also I select reset password token I am getting error when I try to return anything except user
Trying to test a nodejs backend using jest.
this test is passing 70% of the time but sometimes fails.
my test:
both userInput and copycatUserInput are saved to the database and create a duplicate email.
test("Register with the same email twice shoud throw an error.", async () => {
const userInput: ICreateUserInput = {
email: "same-email#gmail.com",
username: "username",
password: "tesT$1234",
};
const copycatUserInput: ICreateUserInput = {
email: "same-email#gmail.com",
username: "differentUsername",
password: "tesT$1234",
};
> await registerUser(userInput);
> await expect(registerUser(copycatUserInput)).rejects.toThrow(/(Email address is already exists)/);
});
the fail reason:
expect(received).rejects.toThrow()
Received promise resolved instead of rejected
Resolved to value: {"token": "eyJhb...
here is my mongoose schema:
both username and email fields are unique
const schemaOptions: SchemaOptions = { timestamps: true };
const userSchema = new Schema(
{
username: {
type: String,
required: true,
unique: true,
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
},
password: {
type: String,
required: true,
},
status: String,
},
schemaOptions,
);
export default model<IUser>("User", userSchema);
and registerUser function:
export default async (registerInput: ICreateUserInput): Promise<ILoginUserResult> => {
/**
* validate props.
*/
if (!isEmail(registerInput.email)) throw new Error("Email address is not valid.");
if (!isValidUsername(registerInput.username)) throw new Error("Username is not valid.");
if (!isStrongPassword(registerInput.password)) throw new Error("Password is not valid.");
/**
* gnerate hashed password.
*/
try {
const hashPassword = await createHashedPassword(registerInput.password);
/**
* create new user.
*/
const doc: ICreateUserInput = {
email: registerInput.email,
username: registerInput.username,
password: hashPassword,
status: registerInput.status ? registerInput.status : "",
};
/**
* save the user in the database.
*/
const user = await new User(doc).save();
return {
user,
token: getAuthToken(user),
};
} catch (error) {
/**
* throws a duplicate email error.
*/
if (error.message && `${error.message}`.includes("email_1 dup key:")) {
throw new Error("Email address is already exists");
}
/**
* throws a duplicate username error.
*/
if (error.message && `${error.message}`.includes("username_1 dup key:")) {
throw new Error("Username is already exists");
}
/**
* may be that mongoose or bcrypt are throwing..
*/
Logger.error(`auth.registerUser => ${error}`);
throw new Error(`Error: Failed to register user: ${error.message}`);
}
i also got useCreateIndex: true in the connection options
and tried to wait for the indexes to be created:
user.once("index", () => {
user = await new User(doc).save();
});
Thank you for your help
EDIT:
ended up running tests separately, make a new connection on each testsuit (beforeAll) can knock things out :(
"scripts": {
"test": "jest --runInBand",
},
There is model code:
'use strict';
const bcrypt = require('bcrypt');
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
}, {
hooks: {
beforeCreate: user => {
const salt = bcrypt.genSaltSync();
user.password = bcrypt.hashSync(user.password, salt);
}
},
});
User.prototype.isPasswordValid = password => {
console.log('current_email');
console.log(this.email);
//return bcrypt.compareSync(password, this.password);
};
User.associate = models => {
// associations can be defined here
};
return User;
};
When I execute this code:
const user = await User.findOne({ where: { email } });
if (!user || !user.isPasswordValid(password)) {
ctx.body = {
result: RESULT_CODE.ERROR,
error: ERROR_CODE.UNAUTHORIZED,
};
return;
}
I see the following output:
current_email
undefined
I don't understand why I can't get access to fields of user.
Versions:
"sequelize": "4.3.1",
"sequelize-cli": "4.0.0"
Try using an usual function and not an arrow function
User.prototype.isPasswordValid = function(password) {
console.log('current_email');
console.log(this.email);
//return bcrypt.compareSync(password, this.password);
};
How can I set default value (like bdhash which is async) to one field in my mongoose schema?
Now I see only promise inside. But why? Seems that I'm using async/await in a right way. Also I tried to do this in a hook ('validate')
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
bcrypt = require('bcrypt');
hashIt = async () => {
let pwd = new Date();
pwd = pwd.toUTCString() + Math.random();
return await bcrypt.hash(pwd, Number(process.env.SALT_WORK_FACTOR));
};
const businessSchema = new mongoose.Schema(
{
name: {
type: String,
trim: true,
unique: 'Please enter a unique business name',
required: 'Please enter a business name'
},
salt: {
type: String,
trim: true,
default: () => {
return hashIt();
},
required: 'Please enter a business salt'
},
created: {
type: Date,
default: Date.now
}
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true }
}
);
/* Also tried this way
businessSchema.pre('validate', next => {
if (this.salt) {
return next();
}
this.salt = hashIt();
next();
}); */
module.exports = mongoose.model('Business', businessSchema);
Is it possible to do? And how? The best way :)
see http://mongoosejs.com/docs/defaults.html
Check this example :
var schema = new Schema({
title: String,
genre: {type: String, default: 'Action'}
});
var Movie = db.model('Movie', schema);
var query = {};
var update = {title: 'The Terminator'};
var options = {
// Create a document if one isn't found. Required
// for `setDefaultsOnInsert`
upsert: true,
setDefaultsOnInsert: true
};
Movie.
findOneAndUpdate(query, update, options, function (error, doc) {
assert.ifError(error);
assert.equal(doc.title, 'The Terminator');
assert.equal(doc.genre, 'Action');
});