Related
I have been struggling with this issue for more than a day now. I am building a backend API for a food delivery web and mobile app.
I have setup passport with the local strategy to authenticate my users.
I have two different types of users currently 1. Subscribers 2. Restaurants each using their own mongoose model (shown in my code below)
I initially set everything up for subscriber registration login, authentication sessions and cookies, all was working fine.
I then moved on to setting up passport for my Restaurant users so that a a restaurant would be able to login to their profile and make various changes to their restaurant and its listings etc.
What I found was that I was able to create an account for the Restaurant user but when trying to log the user in passport would consistently return "Unauthorized", when trying to us isAutheticated() I got the same response back. Passport would however happily keep creating restaurant users every time I hit my register endpoint. A Restaurant user would appear in my database but when trying to log them in and store a cookie, I would just get returned "unauthorized".
With Subscribers all works fine, I have check authentication on my restricted endpoints, register new Subscribers and login subscribers with 0 issues.
Through my testing I have realized a couple things
Passport doesn't make use of the local strategy at all, I can literally black the whole thing out and it will still register subscribers log them in and check authentication via cookies with 0 issues. When placing a console.log into the strategy I can see the strategy is literally not being called at all. How could this be happening, surely passport needs the strategy in order to do its job? In this case its seems not, WHY?
Initially I tried to create a separate strategy for each of my Models, one strategy for Subscribers and one strategy for Restaurants using .createStrategy() this returned the same issue, all functionality for Subscriber model users worked fine but as soon as I tried to login or store a cookie for a user from my Restaurant model passport would just return "Unauthorized. Then you will see in the below code under server.js that I tried to create one local strategy and then build an if else in so passport checks both Subscriber and Restaurant models, through doing this I realized passport wasn't using the strategy at all.
I am ready to tear my hair out at this point what is the point of creating a strategy is passport is somehow just bypassing it completely. If I try to log a Subscriber in with the wrong credentials passport does its job and doesn't allow access so how is it actually managing authentication?
Below is my code, anyone who could help with this would more than make my day!
Server.js:
require("dotenv").config();
const express = require("express");
const mongoose = require("mongoose");
const app = express();
const cors = require("cors")
const bodyParser = require("body-parser");
const passport = require("passport");
const session = require("express-session");
const LocalStrategy = require('passport-local').Strategy;
mongoose.connect(process.env.DATABASE_URL)
const db = mongoose.connection
db.on("error", () => console.error(error))
db.once("open", () => console.log("connected to database"))
// app.use(express.json())
// Use JSON parser for all non-webhook routes
app.use((req, res, next) => {
// console.log(req.originalUrl)
if (req.originalUrl === "/subscribers/webhook") {
next();
} else {
bodyParser.json()(req, res, next);
}
});
app.use(cors())
//INITIAL STRATEGY i TRIED
// passport.use(new LocalStrategy(
// function (username, password, done) {
// Subscriber.findOne({
// username: username
// }, function (err, user) {
// if (err) {
// return done(err);
// }
// if (!user) {
// return done(null, false);
// }
// if (!user.verifyPassword(password)) {
// return done(null, false);
// }
// return done(null, user);
// });
// }
// ));
app.use(session({
secret: "foodsecrets",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
// sTRATEGY i WROTE TO TRY SEPERATE THE MODELS BEFORE I REALIZED THE STRATEGY WAS NOT BEING CALLED AT ALL
passport.use(new LocalStrategy(function (username, password, done) {
Subscriber.findOne({
username: username,
password: password
}, function (err, user) {
console.log("called")
// first method succeeded?
if (!err && user && passwordMatches(password)) {
return done(null, user);
}
// no, try second method:
Restaurant.findOne({
name: username,
password: password
}, function (err, user) {
// second method succeeded?
if (!err && user && passwordMatches(password)) {
return done(null, user);
}
// fail!
done(new Error('invalid user or password'));
});
});
}));
const subscribersRouter = require("./routes/subscribers")
const restaurantsRouter = require("./routes/restaurants")
const ordersRouter = require("./routes/orders")
const seederRouter = require("./routes/seeder");
app.use("/subscribers", subscribersRouter)
app.use("/restaurants", restaurantsRouter)
app.use("/orders", ordersRouter)
app.use("/seeder", seederRouter)
app.listen(3000, () => {
console.log("Server has started on port 3000")
});
Subscriber Model:
const mongoose = require("mongoose")
const passportLocalMongoose = require("passport-local-mongoose");
const cartSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
description: {
type: String,
required: true
},
categories: {
type: Array,
required: true
},
rating: {
type: String
},
restaurantname: {
type: String
}
});
const favouritesSchema = new mongoose.Schema({
favouritemeals: {
type: Array
},
favouriteresturants: {
type: Array
}
});
const pendingItemsSchema = new mongoose.Schema({
name: String,
price: Number,
description: String
});
const pendingOrderSchema = new mongoose.Schema({
userID: {
type: String,
required: true
},
total: {
type: Number,
required: true
},
items: [pendingItemsSchema],
removeItem: {
type: String
},
orderData: {
type: Date,
required: true,
default: Date.now
},
status: {
type: String
}
});
const subscriberSchema = new mongoose.Schema({
googleID: {
type: String
},
facebookID: {
type: String
},
username: {
type: String,
required: true
},
email: {
type: String,
},
subscribeData: {
type: Date,
required: true,
default: Date.now
},
orderHistory: {
type: Array,
},
favourites: {
favouritesSchema
},
cart: [cartSchema],
login: {
type: String,
},
pendingOrder: [pendingOrderSchema],
stripeCustId: {
type: String,
required: true
},
role:{
type: String,
// required: true
}
});
subscriberSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("subscriber", subscriberSchema);
Subscribers.js all required
const express = require("express");
const router = express.Router();
const Subscriber = require("../models/subscriber");
const Restaurant = require("../models/restaurant");
const passport = require("passport");
const Order = require("../models/order");
const bodyParser = require("body-parser");
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const facebookStrategy = require('passport-facebook').Strategy;
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY)
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET
// passport.use(Subscriber.createStrategy());
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
Subscriber.findById(id, function (err, user) {
done(err, user);
});
});
Subscribers.js Login endpoint:
// LOGIN USING PASSPORT JS
router.post("/login", (req, res) => {
const subscriber = new Subscriber({
username: req.body.username,
password: req.body.password,
email: req.body.email
});
req.login(subscriber, async function (err) {
if (err) {
console.log(err)
} else {
try {
passport.authenticate("LocalStrategy")(req, res, function () {
console.log("Authenticated")
res.status(201).json("authenticated")
})
} catch (err) {
res.status(400).json({
message: err.message
})
}
}
})
})
Subscribers.js Register Endpoint:
// REGISTER USING PASSPORT JS
router.post("/register", async (req, res) => {
const customer = await stripe.customers.create({
name: req.body.username,
email: req.body.email
});
console.log("customer", customer)
Subscriber.register({
username: req.body.username,
email: req.body.email,
stripeCustId: customer.id
}, req.body.password, async (err, subscriber) => {
if (err) {
console.log(err)
} else {
try {
await passport.authenticate("local")(req, res, function () {
console.log("is authenticated")
res.status(201).json(newSubscriber)
})
const newSubscriber = await subscriber.save()
} catch (err) {
res.status(400).json({
message: err.message
})
}
}
});
})
Restaurant Model:
const mongoose = require("mongoose")
const passportLocalMongoose = require("passport-local-mongoose");
const menueItemSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
description: {
type: String,
required: true
},
categories: {
type: Array,
required: true
},
rating: {
type: Number
},
restaurantname: {
type: String
}
})
const activeOrderSchema = new mongoose.Schema({
userID: {
type: String,
required: true
},
total: {
type: Number,
required: true
},
items: [menueItemSchema
],
orderData: {
type: Date,
required: true,
default: Date.now
},
status: {
type: String
}
})
const restaurantSchema = new mongoose.Schema({
username: {
type: String,
required: true
},
email: {
type: String,
},
src: {
type: String,
required: true
},
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
menue: [menueItemSchema],
rating: {
type: String
},
categories: {
type: String
},
subscribeData: {
type: Date,
required: true,
default: Date.now
},
activeOrders: [activeOrderSchema]
})
restaurantSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("restaurant", restaurantSchema)
Restaurants.js all required:
const express = require("express")
const router = express.Router()
const Restaurant = require("../models/restaurant")
const passport = require("passport");
const randomRest = require("randomrestgenerator")
// passport.use(Restaurant.createStrategy());
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
Restaurant.findById(id, function (err, user) {
done(err, user);
});
});
Restaurants.js Login endpoint:
router.post("/login", (req, res) => {
const restaurant = new Restaurant({
username: req.body.username,
password: req.body.password,
email: req.body.email
});
req.login(restaurant, async function (err) {
if (err) {
console.log(err)
} else {
try {
passport.authenticate("local")(req, res, function () {
console.log("Authenticated")
res.status(201).json("authenticated")
})
} catch (err) {
res.status(400).json({
message: err.message
})
}
}
})
})
Restaurants.js Register endpoint
// PASSPORT JS RESTAURANT REGISTRATION
router.post("/register", async (req, res) => {
const randomRestaurant = randomRest()
Restaurant.register({
username: req.body.username,
email: req.body.email,
src: randomRestaurant.img,
title: randomRestaurant.title,
description: randomRestaurant.description,
menue: randomRestaurant.menue,
rating: randomRestaurant.rating,
categories: randomRestaurant.categories
}, req.body.password, async (err, restaurant) => {
if (err) {
console.log(err)
} else {
try {
console.log("try called")
const newRestaurant = await restaurant.save()
await passport.authenticate("rest")(req, res, function () {
console.log("is authenticated")
res.status(201).json(newRestaurant)
})
} catch (err) {
console.log("here!")
res.status(400).json({
message: err.message
})
}
}
});
I managed to fix this a couple days ago, turns out I had left my JSON response outside of the passport.authenticate callback function in the restaurants login endpoint.
I moved serialize and deserialize into server.js as well as my session and passport initialization and passport session .use
Also had to setup my serialize and desearialize user functions with if elses' so that they serviced both models.
Then lastly I added an array of secrets to my session instead of just one string.
All is working fine now.
passport.serializeUser(function (user, done) {
if (user instanceof Subscriber) {
done(null, {
id: user.id,
type: "Subscriber"
});
console.log("sub user")
} else {
console.log("rest user")
done(null, {
id: user.id,
type: "Restaurant"
})
}
});
passport.deserializeUser(function (id, done) {
console.log("de-serialize called")
console.log("id type", id.type)
console.log("ID", id)
if (id.type === "Subscriber") {
Subscriber.findById(id.id, function (err, user) {
done(err, user);
})
} else {
Restaurant.findById(id.id, function (err, user) {
done(err, user);
})
}
});
I'm building a mock facebook app using MERN Stack. When I try to save posts to my database, it keeps throwing two errors. One is in the back end and says TypeError: Cannot read property 'create' of undefined and the other is in the front end and says Unhandled Rejection (Error): Request failed with status code 500
This is my front end API.js page
import axios from "axios";
export default {
// Gets all posts
getPosts: function() {
return axios.get("/api/users/posts");
},
// Gets the post with the given id
getPost: function(id) {
return axios.get("/api/users/posts/" + id);
},
// Deletes the post with the given id
deletePost: function(id) {
return axios.delete("/api/users/posts/" + id);
},
// Saves a post to the database
savePost: function(postData) {
return axios.post("/api/users/posts", postData);
}
};
This is my handleSubmit function
handleFormSubmit = (event) => {
// Preventing the default behavior of the form submit (which is to refresh the page)
event.preventDefault();
// Saving post to database
API.savePost(this.state)
.then(data => {
console.log("data: ", data);
this.setState({
title: data.data.title,
body: data.data.body,
});
});
};
This is my back end postController.js, where TypeError: Cannot read property 'create' of undefined is being thrown.
const db = require("../models");
console.log("--------------------------------------");
console.log("Controller Reached");
console.log("--------------------------------------");
// Defining methods for the postController
module.exports = {
findAll: function(req, res) {
console.log("----------------------findAll------------------------ ");
console.log("req.query: ", req.query);
db.User.posts
.find(req.query)
.sort({ date: -1 })
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
findById: function(req, res) {
db.User.posts
.findById(req.params.id)
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
create: function(req, res) {
console.log("create func");
console.log("req.body: ", req.body);
db.User.posts
.create(req.body) //error here
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
update: function(req, res) {
db.User.posts
.findOneAndUpdate({ _id: req.params.id }, req.body)
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
remove: function(req, res) {
db.User.posts
.findById({ _id: req.params.id })
.then(dbModel => dbModel.remove())
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
}
};
And these are my backend API routes for the posts (I removed other routes that weren't related to the issue)
const router = require("express").Router();
const db = require("../../models");
const passport = require("passport");
const postController = require("../../controllers/postController");
router
.route("/posts")
.post(postController.create)
.get(postController.findAll)
.put(postController.update)
.delete(postController.remove);
console.log("/posts reached");
// Matches with "/api/books/:id"
router
.route("/posts/:id")
.get(postController.findById)
.put(postController.update)
.delete(postController.remove);
//router get for login >>> router.get("/")
//router post for logout
//router.get for profile page
module.exports = router;
edit: This is my user model
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const passport = require("passport");
const Schema = mongoose.Schema;
//const passportLocalMongoose = require('passport-local-mongoose');
const userSchema = new Schema({
email: {type: String, required: true},
password: {type: String, required: true},
firstname: String,
lastname: String,
following: [{
User: String,
id: {type: mongoose.Schema.Types.ObjectId }
}],
followers: [{
User: String,
id: {type: mongoose.Schema.Types.ObjectId }
}],
posts: [{
title: String,
body: String,
postedBy: {type: mongoose.Schema.Types.ObjectId},
dateCreated: Date,
comments: [{body:"string", by: mongoose.Schema.Types.ObjectId}],
}],
dateCreated: Date,
savedFiles:[{}],
favoritePosts: [],
avatarImage: [{Image: String}],
jumboImg: [{Image: String}],
profile: {
job: String,
location: String,
school: String,
bio: String,
interests: []
}
});
var User = (module.exports = mongoose.model("User", userSchema));
module.exports.createUser = function (newUser, callback) {
console.log("createUser - newUser", newUser)
bcrypt.genSalt(10, function (err, salt) {
bcrypt.hash(newUser.password, salt, function (err, hash) {
newUser.password = hash;
newUser.save(callback);
});
});
};
module.exports.getUserByEmail = function (email, callback) {
console.log("getUserByEmail", email)
var query = { email: email };
console.log(query);
User.findOne(query, callback);
};
module.exports.getUserById = function (id, callback) {
console.log("getUserById", id);
User.findById(id, callback);
};
module.exports.comparePassword = function (candidatePassword, hash, callback) {
console.log("comparePassword")
bcrypt.compare(candidatePassword, hash, function (err, isMatch) {
if (err) throw err;
callback(null, isMatch);
});
};
var LocalStrategy = require("passport-local").Strategy;
passport.use(
new LocalStrategy({ usernameField: "email" }, function (
email,
password,
done
) {
console.log("LocalStrategy");
User.getUserByEmail(email, function (err, user) {
if (err) throw err;
if (!user) {
return done(null, false, { message: "Unknown User" });
}
User.comparePassword(password, user.password, function (err, isMatch) {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: "Invalid password" });
}
});
});
})
);
passport.serializeUser(function (user, done) {
console.log("serializeUser", user.id)
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
console.log("deserializeUser", id);
User.getUserById(id, function (err, user) {
console.log("deserializeUser - user", `name="${user.name}" \nemail="${user.email}"\npassword=${user.password} `);
done(err, user);
});
});
Any help would be greatly appreciated.
User.posts is undefined because .posts is a property of instance of User. Thus you need to instantiate the user first. In this case, by finding an existing object from User collection.
Since you defined User.posts as primitive arrays, not reference to another collection, the code will be something like below.
create: function (req, res) {
// 1. find the existing user (I guess passport does the job)
db.User.findById(req.body.userid).then((user) => {
// 2. add an post
user.posts.push({
title: req.body.title,
body: req.body.body,
postedBy: req.body.userid,
dateCreated: Date.now(),
comments: [],
});
// 3. persist the changes
user.save();
});
}
If you want to separate the collection, which I believe better, you need to create a new object on the separated collection with the reference to the user posted.
// Post schema
var postSchema = new Schema({
title: String,
body: String,
postedBy: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
dateCreated: Date,
comments: [{ body: "string", by: mongoose.Schema.Types.ObjectId }],
});
// The controller
create: function(req, res) {
// 1. find the existing user (or you get id from Passport session)
db.User.findById(req.body.userid).then((user) => {
// 2. add an post set "postedBy" as the user
return Post.create({
postedBy: user._id,
title: req.body.title,
body: req.body.body,
dateCreated: Date.now(),
});
});
}
Here is the official documentation about referencing: https://mongoosejs.com/docs/populate.html
Hope this helps.
I have three schemas.
User.js:
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
unique: true,
required: true,
},
password: {
type: String,
required: true,
},
});
userSchema.pre("save", function (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();
});
});
});
userSchema.methods.comparePassword = function (candidatePassword) {
const user = this;
return new Promise((resolve, reject) => {
bcrypt.compare(candidatePassword, user.password, (err, isMatch) => {
if (err) {
return reject(err);
}
if (!isMatch) {
return reject(false);
}
resolve(true);
});
});
};
mongoose.model("User", userSchema);
Project.js:
const mongoose = require("mongoose");
const diamondSchema = new mongoose.Schema({
criteria: {
novelty: String,
technology: String,
complexity: String,
pace: String,
},
});
const projectSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
projectName: {
type: String,
default: "",
},
projectBudget: {
type: Number,
},
projectDuration: {
type: Number,
},
industry: {
type: String,
},
companyName: {
type: String,
},
numberOfEmployees: {
type: Number,
},
diamond: [diamondSchema],
});
mongoose.model("Project", projectSchema);
Recommendation.js:
const mongoose = require("mongoose");
const diamondSchema = new mongoose.Schema({
criteria: {
novelty: String,
technology: String,
complexity: String,
pace: String,
},
});
const recommendationSchema = new mongoose.Schema({
diamond: [diamondSchema],
description: {
type: String,
},
});
mongoose.model("Recommendation", recommendationSchema);
And two route files.
authRoutes.js:
const express = require("express");
const mongoose = require("mongoose");
const User = mongoose.model("User");
const jwt = require("jsonwebtoken");
const router = express.Router();
router.post("/signup", async (req, res) => {
const { name, email, password } = req.body;
try {
const user = new User({ name, email, password });
await user.save();
const token =
//token has payload-->user id
jwt.sign({ userId: user._id }, "MY_SECRET_KEY");
res.send({ token });
} catch (err) {
//invalid data
return res.status(422).send(err.message);
}
});
router.post("/signin", async (req, res) => {
const { email, password } = req.body;
if (!email || !password) {
return res.status(422).send({ error: "Must provide email and password" });
}
const user = await User.findOne({ email });
if (!user) {
return res.status(404).send({ error: "Invalid email or password" });
}
try {
await user.comparePassword(password);
const token = jwt.sign({ userId: user._id }, "MY_SECRET_KEY");
res.send({ token });
} catch (err) {
return res.status(422).send({ error: "Invalid email or password" });
}
});
module.exports = router;
projectRoutes.js:
const express = require("express");
const mongoose = require("mongoose");
const requireAuth = require("../middlewares/requireAuth");
const Project = mongoose.model("Project");
const Recommendation = mongoose.model("Recommendation");
const router = express.Router();
router.use(requireAuth);
router.get("/projects", async (req, res) => {
const projects = await Project.find({ userId: req.user._id });
res.send(projects);
});
router.post("/projects", async (req, res) => {
const {
projectName,
projectBudget,
projectDuration,
industry,
companyName,
numberOfEmployees,
diamond,
} = req.body;
if (
!projectName ||
!projectBudget ||
!projectDuration ||
!industry ||
!companyName ||
!numberOfEmployees ||
!diamond
) {
return res.status(422).send({ error: "Must provide all project details" });
}
try {
const project = new Project({
projectName,
projectBudget,
projectDuration,
industry,
companyName,
numberOfEmployees,
diamond,
userId: req.user._id,
});
await project.save();
//res.send(project);
} catch (err) {
res.status(422).send({ error: err.message });
}
try {
const rec = await Recommendation.find({ diamond });
//console.log(diamond);
console.log(description);
res.send(rec);
} catch (err1) {
res.status(422).send({ error: err1.message });
}
});
module.exports = router;
Using postman, in the projectRoutes.js file, when I try to send the post request on
localhost:3000/projects, I am trying to create a new project and in response I want description. My logic is that, after I save the new project in projects collection, I am trying to find the document with the SAME DIAMOND OBJECT criteria in recommendations collection which is also present in projects collection. Meaning, I have pre-defined records in recommendations collection and projects collection::
So I need some way so that when I try to add a new project for a user, the criteria object in diamond array I set matches to the criteria object in diamond array in pre-defined one of recommendations documents and in the post request localhost:3000/projects I can return description in response. As I'm doing console.log(description) in projectRoutes.js, it's showing as undefined. I don't know why. Hope it makes sense.
Basically, the idea is there will be limited number of recommendations with unique criterias. So whenever a new project is created based on the criteria, a recommendation is displayed to the user.
Assuming you have only one array element in a project and in an a recommendation
const {
projectName,
projectBudget,
projectDuration,
industry,
companyName,
numberOfEmployees,
diamond,
} = req.body;
const [projectDiamond] = diamond // get the first object in the diamond array
const { criteria } = projectDiamond // extract criteria
const recommendation = await Recommendation.find({ 'diamond.criteria': criteria });
Please note that the order of criteria fields must match, since we are looking up a matching object in an array.
Reference: https://docs.mongodb.com/manual/tutorial/query-arrays/#query-an-array
I have this user schema
const Schema = mongoose.Schema;
const bcrypt = require("bcryptjs");
const userSchema = new Schema(
{
email: {
type: String,
required: true,
index: {
unique: true
}
},
password: {
type: String,
required: true
},
name: {
type: String,
required: true
},
website: {
type: String
},
bio: {
type: String
}
},
{
timestamps: {
createdAt: "created_at",
updatedAt: "updated_at"
},
toJSON: { virtuals: true }
}
);
userSchema.virtual("blogs", {
ref: "Blog",
localField: "_id",
foreignField: "author"
});
userSchema.pre("save", function(next) {
const user = this;
if (!user.isModified("password")) return next();
bcrypt.genSalt(10, function(err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
user.password = hash;
next();
});
});
});
userSchema.methods.comparePassword = function(password, next) {
bcrypt.compare(password, this.password, function(err, isMatch) {
if (err) return next(err);
next(null, isMatch);
});
};
const User = mongoose.model("User", userSchema);
module.exports = User;
I want to send notification to everyone when a user creates a blog or add a comment how can I implement this? should I use triggers?
The strategy behind this
You have multiple users.
You have multiple notifications that might be for a single user, for some users or for all users.
You need a notification "read" entry in the storage, to know if the user has read the notification or not.
I guess this can be easily achieved by using mongodb change streams.
You can see more about here with code examples.
power of mongodb change streams
Basically listen for insert/update operation type and react accordingly.
I made a login with bcrypt.
I also made a page where users can edit their information, like their bio etc.
Each time an user edit his bio on this page the hash from bcrypt change, which is normal i suppose, but the user login back, the password is wrong...
I used the same model for mongoDb for the user when he log in and when he edit his data.
I started node.js recently so I apologize if my question is stupid,,,
The controller code with the Post :
app.post('/settings-user', mid.requiresLogin, function(req, res, next){
User.findById(req.session.userId, function (err, user) {
// todo: don't forget to handle err
if (!user) {
return res.redirect('/edit');
}
// good idea to trim
var bio = req.body.bio.trim();
// validate
if (!bio) { // simplified: '' is a falsey
req.flash('error', 'One or more fields are empty');
return res.redirect('/settings-user'); // modified
}
// no need for else since you are returning early ^
user.bio = bio;
// don't forget to save!
user.save(function (err) {
// todo: don't forget to handle err
res.redirect('/settings-user/');
});
});
});
The User model :
app.post('/settings-user', mid.requiresLogin, function(req, res, next){
User.findById(req.session.userId, function (err, user) {
// todo: don't forget to handle err
if (!user) {
return res.redirect('/edit');
}
// good idea to trim
var bio = req.body.bio.trim();
// validate
if (!bio) { // simplified: '' is a falsey
req.flash('error', 'One or more fields are empty');
return res.redirect('/settings-user'); // modified
}
// no need for else since you are returning early ^
user.bio = bio;
// don't forget to save!
user.save(function (err) {
// todo: don't forget to handle err
res.redirect('/settings-user/');
});
});
});
The User model :
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
trim: true
},
name: {
type: String,
required: true,
trim: true
},
password: {
type: String,
required: true
},
bio: {
type: String
}
});
// authenticate input against database documents
UserSchema.statics.authenticate = function(email, password, callback) {
User.findOne({ email: email })
.exec(function (error, user) {
if (error) {
return callback(error);
} else if ( !user ) {
var err = new Error('User not found.');
err.status = 401;
return callback(err);
}
bcrypt.compare(password, user.password , function(error, result) {
if (result === true) {
return callback(null, user);
} else {
return callback();
}
})
});
}
// hash password before saving to database
UserSchema.pre('save', function(next) {
var user = this;
bcrypt.hash(user.password, 10, function(err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
})
});
var User = mongoose.model('User', UserSchema);
module.exports = User;
the pug file :
div
form(method='post', action='/settings-user')
label ADD BIO
br
input(type='text', name='bio', placeholder='Enter something', required='')
input(type='submit', value='Add Bio')
</body>
If anyone could help,,,
thank you!