I'm trying test an API that we are developing in Express. I'm testing it in Postman; and the app seems to start fine. But when I'm testing the users's API it return many error messages:
This is my model
User.js
const mongoose = require('mongoose');
const userSchema = mongoose.Schema({
name: {
type: String,
required: true,
min: 6,
max: 255
},
email: {
type: String,
required: true,
min: 6,
max: 1024
},
password: {
type: String,
required: true,
minlength: 6
},
subscribe: {
type: Boolean,
default: false
},
date: {
type: Date,
default: Date.now
},
role: {
type: String,
required: false,
minlength: 4
},
address: {
type: String,
required: false,
minlength: 4,
defaultValue: ""
},
nonce: {
type: String,
required: false,
minlength: 4,
defaultValue: ""
},
})
module.exports = mongoose.model('User', userSchema);
And this is my controller(user.js):
const Joi = require('#hapi/joi');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const nodemailer = require("nodemailer");
//models
const User = require('../models/user');
const expired_token = require('../models/expired_token');
module.exports = {
register: async (req, res) => {
const schemaRegister = Joi.object({
name: Joi.string().min(3).max(255).required(),
email: Joi.string().min(4).max(255).required().email(),
password: Joi.string().min(6).max(1024).required()
})
// validate user
const { error } = schemaRegister.validate(req.body)
if (error) {
return res.status(400).json({
data: error,
message: error.details[0].message,
status: "error"
})
}
const isEmailExist = await User.findOne({ email: req.body.email });
if (isEmailExist) {
return res.status(400).json({
data: "",
message: "email already registered",
status: "error"
})
}
// hash password
const salt = await bcrypt.genSalt(10);
const password = await bcrypt.hash(req.body.password, salt);
const user = new User({
name: req.body.name,
email: req.body.email,
role: "user",
password: password
});
try {
const savedUser = await user.save();
console.log(savedUser);
const token = jwt.sign({
name: savedUser.name,
id: savedUser._id,
email: savedUser.email,
role: savedUser.role
}, process.env.TOKEN_SECRET);
res.status(200).json({
data: {
email:savedUser.email,
name: savedUser.name,
role: savedUser.role,
token: token
},
message:"Successful request",
status: "success"
});
} catch (error) {
res.status(401).json({
data: error,
message:"Error creating user",
status: "error"
});
}
},
login: async (req, res) => {
const schemaLogin = Joi.object({
email: Joi.string().min(6).max(255).required().email(),
password: Joi.string().min(6).max(1024).required()
})
const { error } = schemaLogin.validate(req.body);
if (error) {
return res.status(400).json({
data: error,
message: error.details[0].message,
status: "error"
})
}
const user = await User.findOne({ email: req.body.email });
if (!user) {
return res.status(400).json({
data: "",
message: "User not found",
status: "error"
})
}
const validPassword = await bcrypt.compare(req.body.password, user.password);
if (!validPassword) {
return res.status(400).json({
data: {},
message: "Invalid password",
status: "error"
})
}
const token = jwt.sign({
name: user.name,
id: user._id,
email: user.email,
role: user.role
}, process.env.TOKEN_SECRET);
res.status(200).json({
data: {
email:user.email,
name: user.name,
token: token
},
message:"Successful request",
status: "success"
});
},
list_users: async (req, res) => {
const users = await User.find({});
res.status(200).json({
data: users,
message:"Successful request",
status: "success"
});
},
logout: async (req, res) => {
let user_info = req.decoded;
let token = req.token;
const expired_token_exists = await expired_token.findOne({ token: token });
if (expired_token_exists) {
return res.status(200).json({
data: "",
message: "user already logged out",
status: "success"
})
}
const data = new expired_token({
id_user: user_info.id,
token: token
});
try {
const saved_expired_token = await data.save();
res.status(200).json({
data: saved_expired_token,
message:"Successful request",
status: "success"
});
} catch (error) {
res.status(401).json({
data: error,
message:"Error creating user",
status: "error"
});
}
},
get_user_data: async (req, res) => {
let user_info = req.decoded;
try {
const user = await User.find({
_id:user_info.id
});
const user_json=user[0];
const user_data = {
id:user_info.id,
name:user_json.name,
email:user_json.email,
subscribe:user_json.subscribe,
role:user_json.role,
date:user_json.date
};
res.status(200).json({
data: user_data,
message:"Successful request",
status: "success"
});
} catch (error) {
res.status(401).json({
data: error,
message:"Error getting user data",
status: "error"
});
}
}
}
And this my routes(index.js):
const { func } = require('#hapi/joi');
const controller = require('../../v1/controllers/users');
const verifyToken = require('../../v1/utils').verifyToken;
module.exports = (router) => {
router.route('/list_users')
.get(verifyToken, controller.list_users);
router.route('/register')
.post(controller.register);
router.route('/login')
.post(controller.login);
router.route('/logout')
.post(verifyToken, controller.logout);
router.route('/get_user_data')
.get(verifyToken, controller.get_user_data);
return router;
}
And of course my index file(index.js):
let users = require('./v1/routes/users.js');
app.users('/api/v1/users', users(router));
However, when I try to get a POST Request of type http://localhost:3000/api/v1/users in Postman, the result is Cannot POST /api/v1/users
What is the reason for that?
You need to add /api/v1/ in any route
like this
module.exports = (router) => {
router.route('/api/v1/list_users')
.get(verifyToken, controller.list_users);
router.route('/api/v1/register')
.post(controller.register);
router.route('/api/v1/login')
.post(controller.login);
router.route('/api/v1/logout')
.post(verifyToken, controller.logout);
router.route('/api/v1/get_user_data')
.get(verifyToken, controller.get_user_data);
return router;
}
and other thing , you need to create route for users.
I created a simple restful api with express in github you can see any example for create a route in this repository
Github Repo
Related
This is My User Model
const mongoose = require('mongoose')
const bcrypt = require('bcrypt');
const userSchema = mongoose.Schema({
contact: {
type: Number,
required: true,
},
email: {
type: String,
required: true,
unique: true
},
score: {
type: Number,
default:0,
},
password: {
type: String,
required: true,
},
role: {
type: String
},
blocked: {
type: Boolean, default: false
}
}, { timestamp: true }
)
userSchema.statics.hashPassword = function hashPassword(password){
return bcrypt.hashSync(password,10);
}
userSchema.methods.isValid = function(hashedpassword){
return bcrypt.compareSync(hashedpassword, this.password);
}
module.exports = mongoose.model('user',userSchema)
This is my Controller
const User = require('../models/user')
const Otp = require('../models/otp')
const jwt = require('jsonwebtoken')
const sendMail = require('../mail/mail')
const bcrypt = require('bcryptjs')
exports.getCheck = (req, res, next) => {
res.json({ msg: "All ok" })
}
exports.registerStudent = async (req, res) => {
// const x = await check(req,res,req.body.email);
const user = new User({
contact: req.body.phone,
email: req.body.email,
role: "student",
password: User.hashPassword(req.body.p1),
});
User.find({ email: req.body.email }, (err, users) => {
if (err) {
console.log("err in finding email ");
res.json({ msg: "some baler error!" });
}
if (users.length != 0) {
console.log("already user with this email");
res.json({ msg: "already user exist with this email!" });
}
else {
user.save((error, registeredUser) => {
if (error) {
console.log(error);
res.json({ msg: "some error!" });
}
else {
let payload = { subject: registeredUser._id }
let token = jwt.sign(payload, 'secretkey')
res.status(200).json({ token: token })
}
})
}
})
}
PLeasee HELP me out i'm getting confused
IGNORE
I ran into the same problem. 1. I saved my ATLAS_URI ID to a file called .env 2. My .env file was in the wrong directory, that's how the problem cause 3. Solution: I used "ls -a" command to make sure my .env file is in the same location as my server
IGNORE
PLeasee HELP me out i'm getting confused
Hey I am testing on postman as an admin to add category on my project, I have successfully created admin user and login, but when I tried to add category, postman say: TypeError: Cannot read properties of undefined (reading 'role') can anyone help?
Here is my user model:
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const userSchema = new mongoose.Schema(
{
firstName: {
type: String,
required: true,
trim: true,
},
lastName: {
type: String,
required: true,
trim: true,
},
email: {
type: String,
required: true,
trim: true,
unique: true,
},
password: {
type: String,
required: true,
},
role: {
type: String,
enum: ["user", "admin"],
default: "user",
},
},
{ timestamps: true }
);
module.exports = mongoose.model("User", userSchema);
here is my auth middleware:
const jwt = require("jsonwebtoken");
const User = require("../models/user");
const { signupUser, loginUser } = require("../controller/adminauth");
exports.auth = (req, res, next) => {
try {
if (req.header.authorization) {
const token = req.header.authorization.split("")[1];
const isCustomAuth = token.length < 500;
let decodeData;
if (token && isCustomAuth) {
decodeData = jwt.verify(token, process.env.JWT_SECRET);
req.UserId = decodeData?.id;
} else {
decodeData = jwt.decode(token);
req.UserId = decodeData?.sub;
}
}
} catch (error) {
console.log(error);
// res.status(400).json({ message: "Authorization required" });
} next ()
};
exports.adminMiddleware = (req, res, next) => {
if (!req.userId.role === "admin") {
return res.status(400).json({ message: "Access denied" });
}
next();
};
Here is my admin auth controller:
const User = require("../models/user");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcrypt");
exports.loginUser = async (req, res) => {
const { email, password } = req.body;
try {
const existingUser = await User.findOne({ email });
if (!existingUser) {
return res.status(400).json({ message: "User does not exists." });
}
if (!existingUser.role === "admin") {
return res.status(400).json({ message: "User is not admin." });
}
const isPasswordCorrect = await bcrypt.compare(
password,
existingUser.password
);
if (!isPasswordCorrect)
return res.status(400).json({ message: "Invalid credentials." });
const token = jwt.sign(
{
email: existingUser.email,
id: existingUser._id,
role: existingUser.role,
},
process.env.JWT_SECRET,
{ expiresIn: "3d" }
);
res.status(200).json({ result: existingUser, token });
} catch (error) {
console.log(error);
}
};
exports.signupUser = async (req, res) => {
const { firstName, lastName, email, password, confirmPassword } = req.body;
try {
const existingUser = await User.findOne({ email });
if (existingUser)
return res.status(400).json({ message: "Admin already exists." });
if (!password == confirmPassword)
return res.status(400).json({ message: "Password don't match" });
const hashedPassword = await bcrypt.hash(password, 12);
const result = await User.create({
email,
password: hashedPassword,
firstName,
lastName,
role: "admin",
});
const token = jwt.sign(
{ email: result.email, id: result._id, role: result.role },
process.env.JWT_SECRET,
{ expiresIn: "3d" }
);
res.status(200).json({ result, token });
} catch (error) {
console.log(error);
}
};
Here is my category route:
const express = require("express");
const { addCategory, getCategories } = require("../controller/category");
const { auth, adminMiddleware } = require("../middleware/auth");
const router = express.Router();
router.post("/category/create", auth, adminMiddleware, addCategory);
router.get("/category/getcategory", getCategories);
module.exports = router;
In your auth middleware,
change your exports.auth with the following code:
exports.auth = (req, res, next) => {
try {
if (req.header.authorization) {
const token = req.header.authorization.split("")[1];
const isCustomAuth = token.length < 500;
let decodeData;
if (token && isCustomAuth) {
decodeData = jwt.verify(token, process.env.JWT_SECRET);
req.UserId = decodeData||{}; //change this line
} else {
decodeData = jwt.decode(token);
req.UserId = decodeData?.sub;
}
}
} catch (error) {
console.log(error);
res.status(400).json({ message: "Authorization required" });
} next ()
};
I am trying to save a user to MongoDB as follows, but I am getting the error bcrypt Error: data and hash arguments required. I have checked the same error question asked by other Dev on StackOverflow but it doesn't help. I have attached the codes of the model file and router file.
User Model file
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const uSchema = new mongoose.Schema({
fullName: {
type: String,
required: true,
min: 4,
max: 30
},
email: {
type: String,
required: true,
trim: true,
unique: true,
index: true
},
hash_password: {
type: String,
required: true,
min: 6,
max: 12
},
role: {
type: String,
enum: ['user', 'admin', 'moderator'],
default: 'admin'
}
}, { timestamps: true });
uSchema.virtual('password')
.set(function (password) {
this.hash_password = bcrypt.hashSync(password, 10);
});
uSchema.methods = {
authenticate: function (password) {
return bcrypt.compareSync(password, this.hash_password);
}
}
module.exports = mongoose.model('User', uSchema);
User Router file
const express = require('express');
const router = express.Router();
const User = require('../models/user.model');
router.post('/login', (req, res) => {
});
router.post('/signin', (req, res) => {
User.findOne({ email: req.body.email })
.exec((error, user) => {
if (user) return res.status(400).json({
message: 'User already exists.'
});
const {
fullName,
email,
password
} = req.body;
const _user = new User({
fullName,
email,
password
});
_user.save((error, data) => {
if (error) {
return res.status(400).json({
message: 'Something went wrong'
});
} if (data) {
return res.status(201).json({
user: data
})
}
})
});
});
module.exports = router;
You can do it in the router file instead.
const bcrypt = require("bcrypt")
// ...
router.post('/signin', (req, res) => { // Change this to signup
User.findOne({ email: req.body.email })
.exec((error, user) => {
if (user) return res.status(400).json({
message: 'User already exists.'
});
const {
fullName,
email,
password
} = req.body;
const hashedPassword = bcrypt.hashSync(password, 10);
const _user = new User({
fullName,
email,
hashedPassword
});
_user.save((error, data) => {
if (error) {
return res.status(400).json({
message: 'Something went wrong'
});
} if (data) {
return res.status(201).json({
user: data
})
}
})
});
});
module.exports = router;
and delete the password virtual from the model.
i need help with this problem been dealing it for days.
i am trying to make a verification email route by using passport to hash passwords while issuing a verification token to the user.
here is my code for index.js in controllers folder
const User = require("../models/user");
const Token = require("../models/token")
const crypto = require("crypto");
const nodemailer = require("nodemailer");
var smtpTransport = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: process.env.GMAILUSER,
pass: process.env.GMAILPW
}
});
module.exports = {
async postRegister(req, res, next) {
var user = new User({
name: req.body.name,
email: req.body.email,
isVerified: false,
username: req.body.username
});
await User.register(user, req.body.password);
res.redirect('/');
var token = new Token({ _userId: user._id, token: crypto.randomBytes(16).toString('hex') });
token.save(function (err) {
if (err) { return res.status(500).send({ msg: err.message
});
}
var mailOptions = {
to: user.email,
from: 'xxxt#xxx.com',
subject: 'xxxxx verify email',
text:'You are receiving this because you need to verify your email for your account.\n\n' +
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
'http://' + req.headers.host + '/confirmation/' + token.token + '\n\n' +
'If you did not request this, please ignore this email.\n'
};
smtpTransport.sendMail(mailOptions, function(err) {
if (err) { return res.status(500).send({ msg: err.message }); }
res.status(200).send('A verification email has been sent to ' + user.email + '.');
});
})
},
confirmationPost(req,res, next) {
Token.findOne({ token: req.params.token }, function (err, token) {
if (!token)
{console.log("sss")
} else {
User.findOne({ _id: token._userId, email: req.body.email }, function (err, user) {
if (!user) return console.log(user)
if (user.isVerified) return res.status(400).send({ type: 'already-verified', msg: 'This user has already been verified.' });
user.isVerified = true;
user.save(function (err) {
if (err) { return res.status(500).send({ msg: err.message }); }
res.status(200).send("The account has been verified. Please log in.");
})
});
};
})
}
}
This is my Token Schema
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const Schema = mongoose.Schema;
const tokenSchema = new mongoose.Schema({
_userId: {
type: Schema.Types.ObjectId,
ref: 'User' },
token: {
type: String,
required: true },
createdAt: {
type: Date, required: true,
default: Date.now, expires: 43200 }
});
tokenSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('Token', tokenSchema);
lastly my user schema
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
username: String,
name: String,
email: { type: String, unique: true },
image: String,
isVerified: { type: Boolean, default: false },
password: String,
passwordResetToken: String,
passwordResetExpires: Date,
posts: [
{
type: Schema.Types.ObjectId,
ref: 'Post'
}
]
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', UserSchema);
everything works fine until the part where the email verification was sent to my email and when i clicked on the link. It gives an error, i tried to console.log
and found that this line from controllers folder index.js
confirmationPost(req,res, next) {
Token.findOne({ token: req.params.token }, function (err, token) {
if (!token)
{console.log("err")
} else {
User.findOne({ _id: token._userId, email: req.body.email }, function (err, user) {
gives me back null.
how do i link that current line to get the token from the registered user?
i've used postman to send a get request to the confirmation route while giving it back the same token and it works.
I have a route for creating users in Node/Express. I am getting a weird error about a method on the model not existing.
Here is the model for users:
'use strict';
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
mongoose.Promsie = global.Promise;
const UserSchema = mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
email: { type: String, required: true },
firstName: { type: String },
lastName: { type: String },
families: [
{
family_key: { type: String, required: true },
family_name: { type: String }
}
]
});
UserSchema.methods.apiRepr = function() {
return {
id: this._id,
firstName: this.firstName,
lastName: this.lastName,
username: this.username,
email: this.email,
families: this.families
};
};
UserSchema.methods.hashPassword = function(password) {
return bcrypt.hash(password, 10);
}
UserSchema.methods.validatePassword = function(password) {
return bcrypt.compare(password, this.password);
}
const User = mongoose.models.User || mongoose.model('User', UserSchema);
module.exports = { User };
Not particularly complicated. BUT, my the route is having trouble with the "hashPassword" method. When I try to use this route, I get an error that says "TypeError: User.hashPassword is not a function"
Here is the route (the issue is close to the bottom):
router.post('/', jsonParser, (req, res) => {
// checking that required fields are present
const requiredFields = ['username', 'password', 'email'];
const missingField = requiredFields.find(field => !(field in req.body));
if(missingField) {
return res.status(422).json({
code: 422,
reason: 'Validation Error',
message: 'Missing field',
location: missingField
});
}
// checking the format of string fields
const stringFields = ['username', 'password', 'email', 'lastname', 'firstname'];
const nonStringField = stringFields.find(
field => field in req.body && typeof req.body[field] !== 'string'
);
if (nonStringField) {
return res.status(422).json({
code: 422,
reason: 'Validation Error',
message: 'Incorrect field type: expected string',
location: nonStringField
});
}
// checking the trimming on fields
const trimmedFields = ['username', 'password', 'email'];
const nonTrimmedField = trimmedFields.find(
field => req.body[field].trim() !== req.body[field]
);
if (nonTrimmedField) {
return res.status(422).json({
code: 422,
reason: 'Validation Error',
message: 'Cannot start or end with whitespace',
location: nonTrimmedField
});
}
// checking length of fields with required length
const sizedFields = {
username: { min: 1 },
password: { min: 10, max: 72 }
};
const tooSmallField = Object.keys(sizedFields).find(field =>
'min' in sizedFields[field] &&
req.body[field].trim().length < sizedFields[field].min
);
const tooLargeField = Object.keys(sizedFields).find(field =>
'max' in sizedFields[field] &&
req.body[field].trim().length > sizedFields[field].max
);
if (tooSmallField || tooLargeField) {
return res.status(422).json({
code: 422,
reason: 'Validation Error',
message: tooSmallField
? `Must be at least ${sizedFields[tooSmallField].min} characters long`
: `Must be at most ${sizedFields[tooLargeField].max} characters long`,
location: tooSmallField || tooLargeField
});
}
// creating the user
let { username, firstname, lastname, families, email, password } = req.body;
return User.find({ username })
.count()
.then(count => {
if(count > 0) {
return Promise.reject({
code: 422,
reason: 'Validation Error',
message: 'Username already taken',
location: 'username'
});
}
return User.hashPassword(password);
})
.then(hash => {
return User.create({ username, firstname, lastname, families, email, password: hash })
})
.then(user => {
return res.status(201).json(user.apiRepr());
})
.catch(err => {
console.error(err)
res.status(500).json({ code: 500, message: 'Internal server error'})
})
})
It does not like the return User.hashPassword(password) part. Any thoughts about what is causing this? I'm copying from a working app. Not sure what I'm doing wrong here.
The methods in node.js can not be used directly using the SchemaName you need to create an object of the schema name and then use the methods of the schema.
Ex:
var AnimalSchema = new Schema({
name: String
, type: String
});
AnimalSchema.methods.findSimilarType = function findSimilarType (cb) {
return this.model('Animal').find({ type: this.type }, cb);
};
var Animal = mongoose.model('Animal', AnimalSchema);
var dog = new Animal({ name: 'Rover', type: 'dog' });
dog.findSimilarType(function (err, dogs) {
if (err) return ...
dogs.forEach(..);
})
Source: http://mongoosejs.com/docs/2.7.x/docs/methods-statics.html
In your code you are trying to access the methods from the model.
Instantiate the model then use the methods.
If need use like the way you are using in the code try using function instead of methods.
module.exports.funtionName = function(/*function params*/){
//function body here
};