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
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 working on the backend with nodejs and mongodb . After coding authentication part i'm having unknown issue and i think it's from bcrypt :(
please have a look at my code.
USER MODEL CODE -
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const validator = require("validator");
require("dotenv").config();
const userSchema = mongoose.Schema(
{
email: {
type: String,
required: true,
unique: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail) {
throw new Error("Invalid Email");
}
},
},
password: {
type: String,
required: true,
trim: true,
},
role: {
type: String,
enum: ["user", "admin"],
default: "user",
},
name: {
type: String,
required: true,
maxlength: 21,
},
phone: {
required: true,
type: Number,
maxlength: 12,
},
},
{
timestamps: true,
},
);
userSchema.pre("save", async function (next) {
if (this.isModified("password")) {
// hash the password
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(this.password, salt);
this.password = hash;
}
next();
});
userSchema.methods.generateToken = function () {
const userObj = { id: this._id.toHexString(), email: this.email };
const token = jwt.sign(userObj, process.env.DB_SECRET, {
expiresIn: "3d",
});
return token;
};
userSchema.statics.emailTaken = async function (email) {
const user = await this.findOne({ email });
return !!user;
};
const User = mongoose.model("User", userSchema);
module.exports = {
User,
};
USER API CODE (Please avoid those bunch of console log statements as i had added them for debugging purpose) -
const express = require("express");
const router = express.Router();
require("dotenv").config();
const { User } = require("../../models/userModel");
const bcrypt = require("bcrypt");
// const comparePassword = async (userPassword) => {
// console.log("Comparing password with bcrypt");
// const match = await bcrypt.compare(userPassword, this.password);
// console.log(match);
// return match;
// };
router.route("/signup").post(async (req, res) => {
const { email, password, name, phone } = req.body;
console.log(req.body);
try {
// Check if user email exists
if (await User.emailTaken(email)) {
return res.status(400).json({ message: "Email already exists" });
}
// create user instance and hash password
const user = new User({
email: email,
password: password,
name: name,
phone: phone,
});
// generate jwt token
console.log("user is saving");
const token = user.generateToken();
const userDoc = await user.save();
// send email
// save....send token with cookie
res.cookie("access-token", token).status(200).send(returnUserDoc(userDoc));
} catch (error) {
res
.status(400)
.json({ message: "Error while creating user", error: error });
}
});
router.route("/login").post(async (req, res) => {
try {
// Find user
console.log("Finding User........");
let user = await User.find({ email: req.body.email });
if (!user) {
return res.status(400).json({ message: "Invalid Credentials" });
}
// Compare Password
console.log("Comparing password with bcrypt");
const match = await bcrypt.compare(req.body.password, user.password);
console.log("password compared");
console.log(match);
if (match == false) {
return res.status(400).json({ message: "Invalid Credentials" });
}
console.log("Password Matched");
// Generate Token
console.log("Generating Token........");
const token = user.generateToken();
// Response
console.log("Sending Response........");
res.cookie("access-token", token).status(200).send(returnUserDoc(user));
} catch (error) {
console.log("Error");
res.status(400).json({ message: "Error while loggin in", error: error });
}
});
// functions
const returnUserDoc = (userDoc) => {
return {
_id: userDoc._id,
name: userDoc.name,
email: userDoc.email,
phone: userDoc.phone,
};
};
module.exports = router;
CONSOLE LOGS -
listening on port 3001
Database Connected
Finding User........
Comparing password with bcrypt
Error
I have found that code is executing successfully just before const match = await bcrypt.compare(req.body.password, user.password); this line
On console.log(error.message); i'm getting this -
Error: data and hash arguments required
at Object.compare (D:\CovidHelpers\CovidHelpers\node_modules\bcrypt\bcrypt.js:208:17)
at D:\CovidHelpers\CovidHelpers\node_modules\bcrypt\promises.js:29:12
at new Promise (<anonymous>)
at Object.module.exports.promise (D:\CovidHelpers\CovidHelpers\node_modules\bcrypt\promises.js:20:12)
at Object.compare (D:\CovidHelpers\CovidHelpers\node_modules\bcrypt\bcrypt.js:204:25)
at D:\CovidHelpers\CovidHelpers\server\routes\api\users.js:60:32
at processTicksAndRejections (internal/process/task_queues.js:93:5)
data and hash arguments required
Please help me fix this :)
Thank You
I am stuck on Test #3 and at a loss. Any help, advice or suggestions are welcome. I am using Glitch to write my code. Everything syncs up to my database which is MongoDB. Just can't pass that test and I would suspect you would need to in order to get 4, 5, and 6 done. I am new to coding, so be nice lol Thank you
https://github.com/rbill314/Exercise-Tracker-.git
const express = require("express");
const app = express();
const cors = require("cors");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const moment = require("moment");
const shortId = require("shortid");
/*Connect to database*/
mongoose.connect(process.env.URI, {
useNewUrlParser: true,
useUnifiedTopology: true
});
if (mongoose.connection.readyState) {
console.log("Holy Crap! It Connected");
} else if (!mongoose.connection.readyState) {
console.log("WHACHA DO!!!");
}
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static("public"));
app.get("/", (req, res) => {
res.sendFile(__dirname + "/views/index.html");
});
/*Model*/
const userSchema = new mongoose.Schema({
_id: { type: String, required: true, default: shortId.generate },
username: { type: String, required: true },
count: { type: Number, default: 0 },
log: [
{
description: { type: String },
duration: { type: Number },
date: { type: Date }
}
]
});
const User = mongoose.model("User", userSchema);
/*Test 1: You can POST to /api/users with form data username to create a new user.
The returned response will be an object with username and _id properties.*/
app.post("/api/users", (req, res) => {
User.findOne({ username: req.body.username }, (err, foundUser) => {
if (err) return;
if (foundUser) {
res.send("Username Taken");
} else {
const newUser = new User({
username: req.body.username
});
newUser.save();
res.json({
username: req.body.username,
_id: newUser._id
});
}
});
});
/*Test 2: You can make a GET request to /api/users to get an array of all users.
Each element in the array is an object containing a user's username and _id.*/
app.get("/api/users", (req, res) => {
User.find({}, (err, users) => {
if (err) return;
res.json(users);
});
});
/*Test 3: You can POST to /api/users/:_id/exercises with form data description, duration, and optionally date.
If no date is supplied, the current date will be used.
The response returned will be the user object with the exercise fields added.*/
app.post("/api/users/:_id/exercises", (req, res) => {
const { _id, description, duration, date } = req.body || req.params;
User.findOne({ _id }, (err, userFound) => {
if (err)
return res.json({
error: "Counld not find Carmen Sandiego"
});
let exerDate = new Date();
if (req.body.date && req.body.date !== "") {
exerDate = new Date(req.body.date);
}
const exercise = {
description: description,
duration: duration,
date: exerDate
};
userFound.log.push(exercise);
userFound.count = userFound.log.length;
userFound.save((err, data) => {
if (err)
return res.json({
error: "Not letting you save today"
});
const lenOfLog = data.log.length;
let displayDate = moment(exercise.date)
.toDate()
.toDateString();
let sendData = {
username: data.username,
description: data.log[lenOfLog - 1].description,
duration: data.log[lenOfLog - 1].duration,
_id: data._id,
date: displayDate
};
res.send(sendData);
});
});
});
/*Test 4: You can make a GET request to /api/users/:_id/logs to retrieve a full exercise log of any user.
The returned response will be the user object with a log array of all the exercises added.
Each log item has the description, duration, and date properties.*/
/*Test 5: A request to a user's log (/api/users/:_id/logs) returns an object with a count
property representing the number of exercises returned.*/
/*Test 6: You can add from, to and limit parameters to a /api/users/:_id/logs request to retrieve part
of the log of any user. from and to are dates in yyyy-mm-dd format. limit is an integer of how many
logs to send back.*/
/*listener*/
const listener = app.listen(process.env.PORT || 3000, () => {
console.log("Shhhhh!!!! Spying on port " + listener.address().port);
});
//Excercise Schema
const exSchema=new Schema({
description:{
type:String,
required:true
},
duration:{
type:Number,
required:true
},
date:String
})
//User Schema
const userSchema=new Schema({
username:{
type:String,
required:true
},
log:[exSchema]
})
const User=mongoose.model("User",userSchema)
const ExModel=mongoose.model("Excercise",exSchema)
app.use(cors())
app.use(express.static('public'))
app.use(express.urlencoded({extended:true}))
app.use(express.json())
app.get('/', (req, res) => {
res.sendFile(__dirname + '/views/index.html')
});
//Endpoind Of user
app.route("/api/users").post(async(req,res)=>{
const{username}=req.body
const user=await User.create({username:username})
res.json(user)
}).get(async(req,res)=>{
const user=await User.find()
res.json(user)
})
//Excercise Endpoint
app.post("/api/users/:_id/exercises",async(req,res)=>{
const{description,duration,date}=req.body
const{_id}=req.params
let excercise=await ExModel.create({description,duration:parseInt(duration),date})
if(excercise.date===""){
excercise.date=new Date(Date.now()).toISOString().substr(0,10)
}
await User.findByIdAndUpdate(_id,{$push:{log:excercise} },{new:true},(err,user)=>{
let responseObj={}
responseObj["_id"]=user._id
responseObj["username"]=user.username
responseObj["date"]=new Date(excercise.date).toDateString(),
responseObj["description"]=excercise.description,
responseObj["duration"]=excercise.duration
res.json(responseObj)
})
res.json({})
})
//Logs Endpoint
app.get("/api/users/:_id/logs",async(req,res)=>{
if(req.params._id){
await User.findById(req.params._id,(err,result)=>{
if(!err){
let responseObj={}
responseObj["_id"]=result._id
responseObj["username"]=result.username
responseObj["count"]=result.log.length
if(req.query.limit){
responseObj["log"]=result.log.slice(0,req.query.limit)
}else{
responseObj["log"]=result.log.map(log=>({
description:log.description,
duration:log.duration,
date:new Date(log.date).toDateString()
}))
}
if(req.query.from||req.query.to){
let fromDate=new Date(0)
let toDate=new Date()
if(req.query.from){
fromDate=new Date(req.query.from)
}
if(req.query.to){
toDate=new Date(req.query.to)
}
fromDate=fromDate.getTime()
toDate=toDate.getTime()
responseObj["log"]=result.log.filter((session)=>{
let sessionDate=new Date(session.date).getTime()
return sessionDate>=fromDate&&sessionDate<=toDate
})
}
res.json(responseObj)
}else{
res.json({err:err})
}
})
}else{
res.json({user:"user not found with this id"})
}
})
const express = require('express')
const app = express()
const cors = require('cors')
const mongoose = require('mongoose')
const { Schema } = require('mongoose')
const bodyParser = require('body-parser')
require('dotenv').config()
const MONGO_URL = process.env.MONGO_URL;
mongoose.connect(MONGO_URL);
const userSchema = new Schema ({
username: {
type: String,
required: true
},
log: [{
date: String,
duration: {type: Number,
required: true
},
description: {type: String,
required: true
},
}],
count: Number
});
const User = mongoose.model('User', userSchema); //creates new user
app.use(cors())
app.use(bodyParser.urlencoded({extended: false}))
app.use(express.static('public'))
app.get('/', (req, res) => {
res.sendFile(__dirname + '/views/index.html')
});
app.route('/api/users')
.post((req, res) => {
const username = req.body.username
const user = new User({ username, count: 0 })
user.save((err, data) => {
if (err) {
res.json({error: err})
}
res.json(data)
})
})
.get((req, res) => {
User.find((err, data) => {
if (data) {
res.json(data)
}
})
})
app.post('/api/users/:_id/exercises', (req, res) => {
const { description } = req.body
const duration = parseInt(req.body.duration)
const date = req.body.date ? 'Mon Jan 01 1990' : 'Thu Nov 04 2021'
const id = req.params._id
const exercise = {
date,
duration,
description,
}
User.findByIdAndUpdate(id, {
$push: { log: exercise },
$inc: {count: 1}
}, {new: true}, (err, user) => {
if (user) {
const updatedExercise = {
_id: id,
username: user.username,
...exercise
};
res.json(updatedExercise)
}
})
})
app.get('/api/users/:_id/logs', (req, res) => {
const { from, to, limit } = req.query
console.log(from, to, limit)
User.findById(req.params._id, (err, user) => {
if (user) {
if (from || to || limit) {
const logs = user.log
const filteredLogs = logs
.filter(log => {
const formattedLogDate = (new Date(log.date)).toISOString().split('T')[0]
return true
})
const slicedLogs = limit ? filteredLogs.slice(0, limit) : filteredLogs
user.log = slicedLogs
}
res.json(user)
}
})
})
app.get('/mongo-health', (req, res) => {
res.json({ status: mongoose.connection.readyState
})
})
const listener = app.listen(process.env.PORT || 3000, () => {
console.log('Your app is listening on port ' + listener.address().port)
})
I have creating one ecommerce application, Inside this i have facing some issue regarding req.gravatar() is not a function.
Whenever I have send data through postman give me error, those error I have defined above.
account.js file code
const router = require('express').Router();
const jwt = require('jsonwebtoken');
const User = require('../models/user');
const config = require('../config');
var gravatar = require('gravatar');
router.post('/signup', (req, res, next) => {
let user = new User();
user.name = req.body.name;
user.email = req.body.email;
user.password = req.body.password;
user.picture = req.gravatar();
user.isSeller = req.body.isSeller;
User.findOne({ email: req.body.email }, (err, existingUser) => {
if(existingUser) {
res.json({
success: false,
message: 'Account with that email is already exist'
});
}
else{
user.save();
var token = jwt.sign({
user: user
}, config.secret, {
expiresIn: '7d'
});
res.json({
success: true,
message: 'Enjoy your token',
token: token
});
}
});
});
module.exports = router;
User.js file code
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bcrypt = require('bcrypt-nodejs');
const crypto = require('crypto');
const UserSchema = new Schema({
email: { type: String, unique: true, lowercase: true },
name: String,
password: String,
picture: String,
isSeller: { type: Boolean, default: false },
address: {
add1: String,
add2: String,
city: String,
state: String,
country: String,
postalCode: String
},
created: { type: Date, default: Date.now }
});
UserSchema.pre('save', function(next) {
var user = this;
if(!user.isModified('password')) return next();
bcrypt.hash(user.password, null, null, function(err, hash) {
if(err) return next(err);
user.password = hash;
next();
});
});
UserSchema.methods.comparePassword = function(password) {
return bcrypt.compareSync(password, this.password);
}
UserSchema.methods.gravatar = function(size) {
if(!this.size) size = 200;
if(!this.email) {
return 'https://gravatar.com/avatar/?s' + size + '&d=retro';
}
else{
var md5 = crypto.createHash('md5').update(this.email).digest('hex');
return 'https://gravatar.com/avatar/' + md5 + '?s' + size + '&d=retro';
}
}
module.exports = mongoose.model('User', UserSchema);
please help me to solve this question as fast as possible.
because i am Amateur developer in node.js technology.
In the following line, gravatar is not an attribute of req and therefore cannot be invoked as a function
user.picture = req.gravatar();
I suppose that what you want to do, is something like:
user.picture = gravatar.url(user.email);
With this change, user.picture will contain the URL of the gravatar user profile picture for that email.
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.