How to Append key: value pairs to existing Mongodb objects using Mongoose - node.js

const express = require('express');
const bcrypt = require('bcryptjs');
const passport = require('passport');
const { ensureAuthenticated } = require('../config/auth');
const router = express.Router();
//User/Post model
const User = require('../models/DBmusevista');
//Welcome/register page
router.get('/', (req, res) => res.render('register'));
//Login page
router.get('/login', (req, res) => res.render('login'));
//Home page
router.get('/home', ensureAuthenticated, (req, res) => res.render('home', {user_mv: req.user}));
//Create topic page
router.get('/create-topic', ensureAuthenticated, (req, res) => res.render('create-topic', {user_mv: req.user}));
//Register handle
router.post('/', (req, res) => {
const {firstname, lastname, username, email, password} = req.body;
let errors =[];
if(!firstname || !lastname || !username || !email || !password) {
errors.push({msg: 'Please check all fields.'});
};
//Check password length
if(password.length < 6) {
errors.push({msg: 'Password should be at least 6 characters.'});
};
if(errors.length > 0){
res.render('register', {
errors,
firstname,
lastname,
username,
email,
password
});
}else {
//Validation passed
User.findOne({email: email})
.then(user => {
if(user){
//User exists
errors.push({msg: 'Email is already registered.'})
res.render('register', {
errors,
firstname,
lastname,
username,
email,
password
});
} else {
const newUser = new User({ //Check User
firstname,
lastname,
username,
email,
password
});
//Hash password
bcrypt.genSalt(10, (err, salt) =>
bcrypt.hash(newUser.password,salt, (err, hash) => {
if(err) throw err;
//Set password to hash
newUser.password = hash;
//Save user
newUser.save()
.then(user => {
req.flash('success_msg', 'You are now registered and can now log in');
res.redirect('/login');
})
.catch(err => console.log(err));
}))
}
});
}
});
//Post handle
router.post('/create-topic', (req, res) => {
let newObj = {
title: req.body.title,
category: req.body.category,
vista: req.body.vista,
description: req.body.description
}
User.updateOne({username: user_mv.username}, { $set: {post: newObj}});
res.send('Hello');
//This is the code that does not work. Anything else I've tried has not worked.
});
//Login handle
router.post('/login', (req, res, next) => {
passport.authenticate('local', {
successRedirect: '/home',
failureRedirect: '/login',
failureFlash: true
})(req, res, next);
});
module.exports = router;
const mongoose = require('mongoose');
const DBmusevistaSchema = mongoose.Schema({
firstname: {
type: String,
required: false
},
lastname: {
type: String,
required: false
},
username: {
type: String,
required: false
},
email: {
type: String,
required: false
},
password: {
type: String,
required: false
},
date: {
type: Date,
default: Date.now
}
});
const DBmusevista = mongoose.model('DBmusevista', DBmusevistaSchema);
module.exports = DBmusevista;
When an individual comes to my website the beginning page ('/') is a registration for for my site. I have a post request for my MongoDB collection that creates a 'user register' object (fname, lname, username, email, pass). Once logged in ('/home') a user can then create a new post(s) form that is located in my create-topic.ejs file('/create-topic'). I'm new to using MongoDB so my question is how can I append the post submitted (title, category, vista, description) to an array for the users' object.
I'm not sure if I should add an array (posts: []) to DBmusevistaSchema and create a Post.Schema({key: value}) in my DBmusevista file? I tried using User.findOne but I cannot get it to work. Please help.

try {
const pirs = await Pir.find();
pirs.forEach(async (pir) => {
let pir2 = new Pir();
pir.isSubmitted = false;
pir2 = pir;
const out = await pir2.save();
console.log(out);
});
} catch (err) {
next(err);
}

Related

Getting error data and salt arguments required on bcrypt?

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.

Why my data cannot be saved into MongoDB?

I try to sign up, i submit the form then it logs me undefined, and it doesn't save the user into the database, even the code from the tutorial works perfectly.
Here's my code
exports.postSignup = (req, res, next) => {
const email = req.body.email;
const password = req.body.password;
const confirmPassword = req.body.confirmPassword;
User.findOne({ email: email })
.then(userDoc => {
if (userDoc) {
return res.redirect('/signup');
}
const user = new User({
email: email,
password: password,
cart: { items: [] }
});
return user.save();
})
.then(result => {
res.redirect('/login');
})
.catch(err => {
console.log(err);
});
};
I have to mention that the user model is correct.
Im not sure, but in my own code I dont save an object within the return line.
Maybe try this
exports.postSignup = (req, res, next) => {
const email = req.body.email;
const password = req.body.password;
const confirmPassword = req.body.confirmPassword;
User.findOne({ email: email })
.then(userDoc => {
if (userDoc) {
return res.redirect('/signup');
}
const user = new User({
email: email,
password: password,
cart: { items: [] }
});
try{
user.save();
}catch(err){
res.send(err);
}
return user; //only if you want to return a user ofc
})
Other things that maybe going on:
Is your ip whitelisted?
Did you include the connection string?
Do you return a jason object? then make sure to use a parser in your middleware.

I tried using Populate in mongoose but it didn't work

Hello everyone i am in a problem, may be you can help me.
So basically i have created two collection in mongoose, one for user details when they sign Up or login and other for Recipe they post.
I also want to save the each recipe a user post in the user collection (like populate property of mongoose). like if user1 post a recipe1 then it should save in the recipe collection and also in the user data who posted this.
I tried to use Populate method for this as shown in the code below but when ever i post some recipe it stored in recipe collection only and the "posts" key in the users collection is always empty. I want to save the recipe in the "posts" key of the users collection also.
Please Guide me how to do this.
require('dotenv').config()
const express = require("express");
const bodyParser = require("body-parser");
const ejs = require("ejs");
const requestObj = require("request");
const https = require("https");
const multer = require("multer");
const path = require("path");
const mongoose = require('mongoose');
const session = require('express-session');
const passport = require("passport");
const passportLocalMongoose = require("passport-local-mongoose");
const findOrCreate = require('mongoose-findorcreate');
const app = express();
const GoogleStrategy = require('passport-google-oauth20').Strategy;
app.set("view engine", "ejs");
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(express.static("public"));
app.use(session({
secret: "This is my secret",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
// Mongoose connection
mongoose.connect("mongodb://localhost:27017/recipeUsers", {
useNewUrlParser: true,
useUnifiedTopology: true
});
mongoose.set('useCreateIndex', true);
//mongoose schema
const userSchema = new mongoose.Schema({
username: String,
password: String,
googleId: String,
name: String,
posts: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Recipe'
}]
});
const recipeSchema = new mongoose.Schema({
genre: String,
name: String,
description: String,
ingredients: [{
type: String
}],
method: [{
type: String
}],
imageName: String,
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
});
userSchema.plugin(passportLocalMongoose);
userSchema.plugin(findOrCreate);
// model
const Recipe = mongoose.model("Recipe", recipeSchema);
const User = mongoose.model("User", userSchema);
module.exports = {
User,
Recipe,
}
passport.use(User.createStrategy());
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new GoogleStrategy({
clientID: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
callbackURL: "http://localhost:5000/auth/google/index",
userProfileURL: "https://www.googleapis.com/oauth2/v3/userinfo"
},
function(accessToken, refreshToken, profile, cb) {
// console.log(profile);
User.findOrCreate({
googleId: profile.id,
name: profile.displayName
}, function(err, user) {
return cb(err, user);
});
}
));
var storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "public/img/uploads")
},
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
var upload = multer({
storage: storage,
limits: {
fileSize: 5 * 1024 * 1024
}
});
app.use((req, res, next) => {
res.locals.isAuthenticated = req.isAuthenticated();
if (req.isAuthenticated()) {
currentUser = req.user.name;
}
next();
});
app.get("/", function(req, res) {
res.render("index");
});
app.get("/index", (req, res) => {
res.render('index');
});
app.get("/about", function(req, res) {
res.render("about");
});
app.route("/search")
.get(function(req, res) {
res.render("search");
})
.post(function(req, res) {
const searchRecipe = req.body.recipeName;
Recipe.findOne({
name: searchRecipe
}, (err, foundRecipe) => {
if (err) {
res.send(err);
} else {
if (foundRecipe) {
res.render("recipe", {
dishName: foundRecipe.name,
descrip: foundRecipe.description,
genre: foundRecipe.genre,
ingredients: foundRecipe.ingredients,
steps: foundRecipe.method,
author: foundRecipe.author,
dishImage: foundRecipe.imageName
});
} else {
res.redirect("/Failure");
}
}
});
});
app.get("/Failure", function(req, res) {
res.render("Failure");
});
app.route("/post")
.get(function(req, res) {
if (req.isAuthenticated()) {
res.render("post", {
message: ""
});
} else {
res.redirect("/login");
}
})
.post(upload.single('dishImage'), function(req, res, next) {
const dishType = req.body.recipeGenre;
const description = req.body.description;
const dishName = req.body.dishName;
const ingredient = req.body.ingredients;
const step = req.body.steps;
// const authorName = req.body.author;
// const url = "https://polar-oasis-10822.herokuapp.com/recipes";
const file = req.file;
if (!file) {
const error = new Error("Please upload a image");
error.httpStatusCode = 400;
return next(error);
}
// console.log(req.user);
const dish = new Recipe({
genre: dishType,
name: dishName,
description: description,
ingredients: ingredient,
method: step,
imageName: file.filename,
});
dish.save((err) => {
if (err) {
res.send(err);
} else {
User.findOne({
_id: req.user._id
})
.populate('posts').exec((err, posts) => {
console.log("Populated User " + posts);
})
res.render("post", {
message: "Your Recipe is successfully posted."
});
}
});
});
app.get("/menu", function(req, res) {
res.render("menu");
});
app.get('/auth/google',
passport.authenticate('google', {
scope: ["profile"]
})
);
app.get('/auth/google/index', passport.authenticate('google', {
failureRedirect: '/login'
}), function(req, res) {
// Successful authentication, redirect home.
res.redirect('/');
});
app.get("/logout", (req, res) => {
req.logout();
res.redirect('/');
});
app.route("/signup")
.get((req, res) => {
res.render("signup");
})
.post((req, res) => {
User.register({
username: req.body.username,
name: req.body.name,
}, req.body.password, (err, user) => {
if (err) {
console.log(err);
res.redirect("/signup");
} else {
passport.authenticate("local")(req, res, () => {
res.redirect('/');
});
}
});
})
app.route("/login")
.get((req, res) => {
res.render("login");
})
.post((req, res) => {
const user = new User({
username: req.body.username,
password: req.body.password
});
req.login(user, (err) => {
if (err) {
console.log(err);
} else {
passport.authenticate("local")(req, res, () => {
res.redirect("/");
});
}
});
})
app.listen(process.env.PORT || 5000, function() {
console.log("server is running on port 5000");
});
I believe your problem is here:
dish.save((err) => {
if (err) {
res.send(err);
} else {
User.findOne({
_id: req.user._id
})
.populate('posts').exec((err, posts) => {
console.log("Populated User " + posts);
})
res.render("post", {
message: "Your Recipe is successfully posted."
});
}
});
});
You are using populate as a function that changes the dataset elements, but it is just a trick for printing results from different collection, in the roots, they are different; if you want them to be in the same document, you must use subdocuments. populate does not change document paths.
Assuming I have not typed anything wrong, that you must correct yourself or let me know, this should solve your problem; just use populate to print the documents our as one, not to save.
app
.route("/post")
.get(function(req, res) {
if (req.isAuthenticated()) {
res.render("post", {
message: ""
});
} else {
res.redirect("/login");
}
})
.post(upload.single("dishImage"), function(req, res, next) {
const dishType = req.body.recipeGenre;
const description = req.body.description;
const dishName = req.body.dishName;
const ingredient = req.body.ingredients;
const step = req.body.steps;
const username = req.body.author; //make sure this information is passed to req.body
// const url = "https://polar-oasis-10822.herokuapp.com/recipes";
const file = req.file;
if (!file) {
const error = new Error("Please upload a image");
error.httpStatusCode = 400;
return next(error);
}
// console.log(req.user);
const dish = new Recipe({
genre: dishType,
name: dishName,
description: description,
ingredients: ingredient,
method: step,
imageName: file.filename
});
dish.save(err => {
if (err) {
res.send(err);
} else {
User.findOne({
_id: req.user._id
}).then(user => {//I have changed just here! I have eliminated the populate call
user.posts.push(dish._id);
user.save().then(() => {
console.log("okay");
});
});
res.render("post", {
message: "Your Recipe is successfully posted."
});
}
});
});
Since your code is big, I may have missed something, please, let me know if I have misunderstood what you wanted.
References
https://mongoosejs.com/docs/populate.html#refs-to-children

How can i access values from database

How can i access for example username and put it in profile page ?
model/db.js
const mongoose = require('mongoose');
const stDB = mongoose.Schema({
username : {
type: String,
required: true
},
email : {
type: String,
required: true
},
password : {
type: String,
required: true
}
});
module.exports = mongoose.model('db', stDB);
views/profiles/instructor.hbs
<h5>I want access username from db and put it here!</h5>
index.js
const users = require('../model/db'); // db that username stored in it (model/db.js)
//instructor
router.get('/profiles/instructor', function (req, res, next) {
res.render('./profiles/instructor', {
title: 'Instructor'
});
});
router.post('/signup', function (req, res, next){
const newUser = new users({
username : req.body.username,
email : req.body.email,
password : req.body.password,
});
users.findOne({email : req.body.email}, (err, doc)=>{
if(err){
console.log('ERR while getting username =>' + err);
return ;
}
if(doc){
res.send('this email is already registered before!');
return ;
}
newUser.save((err, doc)=>{
if(err){
console.log('err' + err)
}else{
console.log(doc)
res.redirect('/login')
}
});
});
// etc.....

Hot to set authentication in passport-jwt with different role of user?

I'm trying to add a role called admin to authenticate admins logged into dashboard web app while the normal user can just access the regular pages.
For a normal user, I require the passport in server.js like
// use passport
app.use(passport.initialize());
require("./config/passport")(passport);
In the config/passport.js, like the code in official example, I try this:
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
const mongoose = require('mongoose');
const User = mongoose.model("users");
const key =require("../config/key");
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = key.secretKey;
module.exports = passport => {
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
// console.log(jwt_payload);
User.findById(jwt_payload.id)
.then(user => {
if(user) {
return done(null, user);
}
return done(null, false);
})
.catch(err => console.log(err));
}));
};
This way works fine, and I use them in the route
router.get("/current", passport.authenticate("jwt", {session: false}), (req, res) => {
res.json({
id: req.user.id,
name: req.user.name,
username: req.user.username,
email: req.user.email,
avatar: req.user.avatar,
});
})
However, while I'm adding a role in the token rule:
const rule = {id:admin.id, email: admin.email, avatar: admin.avatar, admin: admin.admin};
How could I check if the admin property is true to query different Collections in passport.js
I tried this, which doesn't work for me with the error seems like the server run twice:
module.exports = passport => {
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
// console.log(jwt_payload);
if(jwt_payload.admin){
Admin.findById(jwt_payload.id)
.then(user => {
if(user) {
return done(null, user);
}
return done(null, false);
})
.catch(err => console.log(err));
} else {
User.findById(jwt_payload.id)
.then(user => {
if(user) {
return done(null, user);
}
return done(null, false);
})
.catch(err => console.log(err));
}
}));};
The error is :
Error
Here is what I do and it works pretty well... I simply include the isAdmin: Boolean in my user model like so:
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 5,
maxlength: 50
},
email: {
type: String,
required: true,
minlength: 5,
maxlength: 255,
unique: true
},
password: {
type: String,
required: true,
minlength: 5,
maxlength: 1024
},
isAdmin: Boolean
});
and then include this in the jwt like so:
userSchema.methods.generateAuthToken = function() {
const token = jwt.sign({ _id: this._id, isAdmin: this.isAdmin }, config.get('jwtPrivateKey'));
return token;
}
then a custom middleware to check the value of isAdmin like so:
module.exports = function (req, res, next) {
if (!req.user.isAdmin) return res.status(403).send('Access denied.');
next();
}
then I simply import it and use it as the second param for any route like so:
router.patch('/:id', [auth, isAdmin, validateObjectId], async (req, res) => {
// handle the route (in order to do anything in this route you would need be an admin...)
});
EDIT: If you're curious about the other two middleware here they are...
auth.js:
const jwt = require('jsonwebtoken');
const config = require('config');
module.exports = function (req, res, next) {
const token = req.header('x-auth-token');
if (!token) return res.status(401).send('Access denied. No token provided.');
try {
const decoded = jwt.verify(token, config.get('jwtPrivateKey'));
req.user = decoded;
next();
}
catch (ex) {
res.status(400).send('Invalid token.');
}
}
validateObjectId:
const mongoose = require('mongoose');
module.exports = function(req, res, next) {
if (!mongoose.Types.ObjectId.isValid(req.params.id))
return res.status(404).send('Invalid ID.');
next();
}

Resources