This is my User.js
var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
var UserSchema = mongoose.Schema({
email: {
type: String,
unique: true
},
password: String,
});
var User = mongoose.model('User', UserSchema);
function createDefaultUsers() {
User.find({}).exec(function (err, collection) {
if (collection.length === 0) {
User.create({
email: 'name#eemail.com',
password: 'password0',
});
}
exports.createDefaultUsers = createDefaultUsers;
module.exports = mongoose.model('User', UserSchema);
I call createDefaultUsers in another file to create initial users.
But when this gives me the following error:
userModel.createDefaultUsers();
^ TypeError: Object function model(doc, fields, skipId) {
if (!(this instanceof model))
return new model(doc, fields, skipId);
Model.call(this, doc, fields, skipId); } has no method 'createDefaultUsers'
But if I comment out module.exports = mongoose.model('User', UserSchema); it compiles fine.
What am I doing wrong.
Cheers.
In this case, you should attach that function as a static method and export the model.
var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
var UserSchema = mongoose.Schema({
email: {
type: String,
unique: true
},
password: String,
});
UserSchema.statics.createDefaultUsers = function createDefaultUsers(cb) {
return User.find({}).exec(function (err, collection) {
if (collection.length === 0) {
User.create({
email: 'name#eemail.com',
password: 'password0',
}, cb);
} else {
if (cb) {
cb(err, collection);
}
}
});
};
var User = mongoose.model('User', UserSchema);
module.exports = User;
Now you can use it directly from the model (which is likely very similar to how you're already using it):
require('./models/user').createDefaultUsers();
Related
Trying to wrap my head around the following issue for couple days now.
I have a web app, that takes login, afterwards, the user can create a list of students. I am trying to make the list being visible to the user who posted it ONLY.
Below is my work so far.
students.controller.js
const express = require('express');
var router = express.Router();
var bodyParser = require('body-parser')
const mongoose = require('mongoose');
const passport = require('passport');
const Student = require('../models/student');
const passportConfig = require('../config/passport');
const app = express();
router.get('/',passportConfig.isAuthenticated,(req, res) => {
res.render('students');
});
router.post('/',(req, res) => {
InsertRecord(req, res);
});
function InsertRecord(req, res){
var student = new Student();
student.fullName = req.body.fullname;
student.phone = req.body.phone;
student.save((err, doc) => {
if (!err)
res.redirect('students/list');
else {
console.log(' Error during insertion: '+ err);
}
});
}
router.get('/list',passportConfig.isAuthenticated, (req, res) => {
Student.find((err, docs) => {
if (!err) {
res.render('list', {
list:docs
});
}
else {
console.log('Error in retrieving students: '+ err);
}
});
});
module.exports = router;
Model Schema Student.js
const mongoose = require('mongoose');
var ObjectId = mongoose.Schema.Types.ObjectId;
const studentSchema = new mongoose.Schema({
fullName: {
type: String
},
phone: {
type: Number
},
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
}
});
const Student = mongoose.model('Student', studentSchema);
module.exports = Student;
User.js Schema
const bcrypt = require('bcrypt');
const crypto = require('crypto');
const mongoose = require('mongoose');
const Student = require('../models/student');
const userSchema = new mongoose.Schema({
email: { type: String, unique: true },
password: String,
passwordResetToken: String,
passwordResetExpires: Date,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
},
snapchat: String,
facebook: String,
twitter: String,
google: String,
github: String,
instagram: String,
linkedin: String,
steam: String,
tokens: Array,
profile: {
name: String,
gender: String,
location: String,
website: String,
picture: String
}
}, { timestamps: true });
/**
* Password hash middleware.
*/
userSchema.pre('save', function save(next) {
const user = this;
if (!user.isModified('password')) { return next(); }
bcrypt.genSalt(10, (err, salt) => {
if (err) { return next(err); }
bcrypt.hash(user.password, salt, (err, hash) => {
if (err) { return next(err); }
user.password = hash;
next();
});
});
});
/**
* Helper method for validating user's password.
*/
userSchema.methods.comparePassword = function comparePassword(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, (err, isMatch) => {
cb(err, isMatch);
});
};
/**
* Helper method for getting user's gravatar.
*/
userSchema.methods.gravatar = function gravatar(size) {
if (!size) {
size = 200;
}
if (!this.email) {
return `https://gravatar.com/avatar/?s=${size}&d=retro`;
}
const md5 = crypto.createHash('md5').update(this.email).digest('hex');
return `https://gravatar.com/avatar/${md5}?s=${size}&d=retro`;
};
const User = mongoose.model('User', userSchema);
module.exports = User;
Please let me know if anything else is needed.
How about using JWT?
JWT is token-based Authorization way. You can store user's email or _id in jwt.
When user logged in, the server provide jwt that store user's email. When user requested the list with jwt, you can find the student with user's _id in jwt like student.findById({author: token.id}).
You don't have to make jwt module but you can use jsonwebtoken that already provided.
I am using mongoose v5.2.17.
I was wondering is it possible to have multiple models map to the 1 schema.
For example - I have the following model
const mongoose = require('mongoose');
const validator = require('validator');
const jwt = require('jsonwebtoken');
const _ = require('lodash');
const bcrypt = require('bcryptjs');
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
trim: true,
minlength: 1,
unique: true,
validate: {
validator: validator.isEmail,
message: '{VALUE} is not a valid email',
},
},
password: {
type: String,
required: true,
minlength: 6,
},
isTrialUser: {
type: Boolean,
default: true,
},
isAdminUser: {
type: Boolean,
default: false,
}
});
UserSchema.methods.toJSON = function () {
const user = this;
const userObject = user.toObject();
return _.pick(userObject, ['_id', 'email', 'isTrialUser']);
};
UserSchema.pre('save', function (next) {
const user = this;
if (user.isModified('password')) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(user.password, salt, (hashErr, hash) => {
user.password = hash;
next();
});
});
} else {
next();
}
});
const User = mongoose.model('User', UserSchema);
module.exports = { User, UserSchema };
Is it possible for me to create another AdminModel where admin specific methods can live?
I also want to return all data from the toJSON method from the AdminModel.
Please let me know if this is possible or if there is a better way to perform such a task
Thanks
Damien
If I am understanding you correctly you want to inherit the UserModel in an AdminModel and decorate that one with extra methods etc. For that you can use util.inherits (or the so called Mongoose discriminators) like so:
function BaseSchema() {
Schema.apply(this, arguments);
this.add({
name: String,
createdAt: Date
});
}
util.inherits(BaseSchema, Schema);
var UserSchema = new BaseSchema();
var AdminSchema = new BaseSchema({ department: String });
You can read more about it in Mongoose docs.
There is also a good article on the mongoose discriminators here
I'm pretty new with nodejs and mongoDB. I have created a registration and user schema but it doesn't recognize this and send the following error:
ReferenceError: userModel is not defined
When I trace the error, I found that it doesn't recognize this keyword.
Here is user.js code:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt');
var userSchema = new Schema({
teamName: {
type: String,
unique: true,
trim: true,
required: true
},
faculty: {
type: String,
required: true
},
email: {
required: true,
unique: true,
trim: true,
type: String
},
password: {
required: true,
type: String
},
score: {
type: Number,
default: 0
}
});
userSchema.pre('save', function(next) {
var user = this;
bcrypt.hash(user.password, 10, (err, hash) => {
if (err) return next(err)
user.password = hash;
next();
});
})
var userModel = mongoose.model('User', userSchema);
module.exports = userModel;
server.js
router.post('/register', (req, res) => {
var newUser = {
teamName: req.body.teamName,
faculty: req.body.faculty,
email: req.body.email,
password: req.body.password
}
userModel.create(newUser, (err, user) => {
if (err) {
console.log('[Registratoin]: ' + err);
} else {
console.log(user)
console.log('[Registration]: Done');
// req.session.userID = user._id;
res.redirect('/scoreboard')
}
});
});
The this keyword in the pre-save hook in your model is not the issue.
ReferenceError: userModel is not defined
ReferenceError: "x" is not defined means that the userModel is undefined from the scope you're calling it from. (Thus you're referencing a non-existent value)
I have include it like var userModel = require('the_path')
As you're requiring and exporting the userModel correctly. I would double check the path you're importing the module from.
I got this error:
"User is not a constructor TypeError"
I couldn't find out what is wrong in the code. Any advice is appreciated!!
routes/users.js
// Initialised user.js into users.js
// var User = require('../models/user');
if (errors) {
// code here is working
} else {
console.log('PASSED'); // logs "passed" msg successfully
var newUser = new User({
name: name,
email: email,
phone: phone,
password: password2
});
User.createUser(newUser, function (err, user) {
if (err) throw err;
console.log(user);
});
req.flash('success_msg', 'You had been succesfully registered! Try signin.');
res.redirect('/users/signin');
}
models/user.js
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
var UserScheme = mongoose.Schema({
name: {
type: String,
index: true
},
email: {
type: String
},
phone: {
type: String
},
password: {
type: String
}
});
var User = module.export = mongoose.model('User', UserScheme);
// encrypting password into hash
module.export.createUser = function(newUser, callback){
bcrypt.genSalt(saltRounds, function(err, salt) {
bcrypt.hash(newUser.password, salt, function(err, hash) {
newUser.password = hash;
newUser.save(callback);
});
});
};
I can update the post if more code is necessary.
you need to change module.export to module.exports
node docs here * note that exports is a shortcut that points to module.exports. module.exports is the default object that gets returned when the module is required. In your example you have created a new property of module that is called export. here is a blog post that describes the relationship of exports and module.exports.
consider the following example:
stack: cat ./49275169.js
console.log('export', module.export)
console.log('exports', module.exports)
stack: node ./49275169.js
export undefined
exports {}
stack:
here is a mongoose example:
stack: cat module.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const schema = new Schema({ name: String })
const User = module.exports = mongoose.model('Users', schema)
module.exports.createUser = function (newUser, callback) {
console.log(new User({ name: 'Rohit' }))
}
stack: cat ./49275169.js
const mongoose = require('mongoose')
const User = require('./module')
mongoose.connect('mongodb://localhost/test')
const user = new User({
name: 'Branford'
})
user.save((err, doc) => {
if (err) { return console.error(err) }
mongoose.connection.close()
return console.log(doc)
})
User.createUser()
stack: node ./49275169.js
{ name: 'Rohit', _id: 5aa9138819d660182938b128 }
{ name: 'Branford', _id: 5aa9138819d660182938b127, __v: 0 }
stack:
I want users to have the ability to click a button that pushes their username and id into an array associated with a collection in a database, but only if they're not already in that array.
My solution is:
var isInGroup = function(user, arr){
var match = arr.indexOf(user);
console.log(">>>>>>>" + match);
if(match === -1){
arr.push(user);
console.log("added user");
} else {
console.log("Already in group");
}
};
This works when I test it against example arrays in the console, but not when I'm querying the database. When I execute the function in my app, arr.indexOf = -1 even if the user is already in the array.
This is the relevant code:
Player.js
var express = require("express"),
router = express.Router({mergeParams:true}),
Game = require("../models/game"),
Player = require("../models/player"),
User = require("../models/user"),
middleware = require("../middleware");
//Add A Player
router.post("/", middleware.isLoggedIn, function(req, res){
//find game
Game.findById(req.body.game, function(err, foundGame){
console.log(">>>>" + foundGame);
if(err){
req.flash("error", "Something went wrong.");
} else {
//create player
Player.create(req.user, function(err, player){
if(err){
console.log(">>>> error" + player);
res.redirect("back");
} else {
player.id = req.user_id;
player.username = req.user.username;
middleware.isInGroup(player, foundGame.players);
foundGame.save();
res.redirect("back");
}
});
}
});
});
Game Schema
var mongoose = require("mongoose");
var gameSchema = new mongoose.Schema({
name:String,
author:{
id:{
type: mongoose.Schema.Types.ObjectId,
ref:"User"
},
username:String,
},
court:{
id:{
type:mongoose.Schema.Types.ObjectId,
ref:"Court"
},
name:String,
},
players:[
{
id:{ type:mongoose.Schema.Types.ObjectId,
ref:"Player",
},
username:String
}
],
time:{
start:String,
end:String
},
date:String,
});
module.exports = mongoose.model("Game", gameSchema)
Player Schema
var mongoose = require("mongoose");
var playerSchema = new mongoose.Schema({
id:{type:mongoose.Schema.Types.ObjectId,
ref:"User"
},
username: String
});
module.exports = mongoose.model("Player", playerSchema);
User Schema
var mongoose = require("mongoose"),
passportLocalMongoose = require("passport-local-mongoose");
var userSchema = new mongoose.Schema({
username: String,
password: String
});
userSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", userSchema);
As mentioned above, arr.indexOf(user) returns -1 even if user is already in the array. Why is this happening? Is there better solution to this problem? Thanks for the help. I've been banging my head for awhile on this one.