access the info of the user that created the post - node.js

I am making a react-native mobile app and I am having trouble passing the users info that created the post to the home page in the post detail. I can pass the userID but for some reason when I add the rest of the info to the payload I can't create a post. Please help.
BACKEND
This is the requireAuth file that requires authentication before performing a tast. My code for the user is here as well at the bottom---
const mongoose = require("mongoose");
const User = mongoose.model("User");
module.exports = (req, res, next) => {
const { authorization } = req.headers;
if (!authorization) {
return res.status(401).send({ error: "You must be logged in." });
}
const token = authorization.replace("Bearer ", "");
jwt.verify(token, "mySecretKey", async (err, payload) => {
if (err) {
return res.status(401).send({ error: "You must be logged in." });
}
const { userId, name, phone, email } = payload;
const user = await User.findById(userId);
req.user = user;
console.log(req.user);
next();
});
};
This is the POST route for the Item---
router.post("/items", requireAuth, async (req, res) => {
const { title, category, detail, condition, price } = req.body;
if (!title || !category || !detail || !condition || !price) {
return res.status(422).send({
error: "You must provide a title, category, detail, condition, and price"
});
}
try {
const item = new Item({
title,
category,
detail,
condition,
price,
userId: req.user._id
});
await item.save();
res.send(item);
} catch (err) {
res.status(422).send({ error: err.message });
}
});
FRONT-END
This is my createItem function in the itemContext file---
const createItem = dispatch => async ({
title,
category,
detail,
condition,
price
}) => {
try {
const response = await sellerApi.post("/items", {
title,
category,
detail,
condition,
price
});
//this is the other place the error might be happening i need this to save in the phone local storage
dispatch({ type: "create_item", payload: response.data });
navigate("Home");
} catch (err) {
console.log(err);
}
};
All I am trying to do it is when the post is being displayed so is the info of the post creator

For existing post in the database: If you are referencing your user in post model like this
const Post = mongoose.model('Post', {
// other fields
userId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
}
})
then you can use populate to fetch user of that post.
const post= await Post.findById('5c2e505a3253e18a43e612e6')
await post.populate('userId').execPopulate()
console.log(post.userId)

Related

Mongoose .save() not working inside Node/Express backend API

PostsRouter.post("/ALL", (req, res) => {
const { subreddit, title, user, body } = req.body;
const post = new PostMessage({
postID: uuidv4(),
subreddit: subreddit,
title: title,
user: user,
body: body,
});
console.log(post);
async function _internal() {
try {
const newPost = await post.save();
res.status(201).json(newPost);
} catch (error) {
res.status(400);
}
}
_internal();
});
This function exactly as it is was working earlier but now this method and another PATCH method I defined elsewhere are failing because the promise is never fulfilled on the first line of the try block that saves the new data to the database "const newPost = await post.save()". Would anyone know what could be causing an issue like this? When I make a call to this API there are no error messages from the backend. I also included an image showing how I am making the POST request and the error I am getting.
If you want to create a document in the database and use that document for any further operations then you can use the below code for that purpose.
PostsRouter.post("/ALL", async(req, res) => {
const { subreddit, title, user, body } = req.body;
try {
// PostMessage is the Model
const newPost = await PostMessage.create({
postID: uuidv4(),
subreddit: subreddit,
title: title,
user: user,
body: body,
});
console.log(newPost);
res.status(201).json(newPost);
}catch (error) {
res.status(400);
}
}
});
Hope this helps.

How can I restrict the ability to post and save data to the database to only the admin in node.js?

In the users table, I have two collections, one of which is admin and the other which is not.
Now I only want admin user to post data.
Here is the post request:
router.post("/bus/add", auth, async (req, res) => {
const bus = new Bus(req.body);
const user = await User.find({ admin: true });
try {
if (user) {
await bus.save();
res.status(201).send(bus);
} else {
return res.status(401).send("You are not allowed to perform this action");
}
} catch (e) {
res.status(500).json({
message: "Please enter the valid data",
});
}
});
I'm using JWT to determine whether or not the user is an admin. I've set one of the users' admin roles to 'true' in the user schema.
Authentication middleware:
const authentication = async (req, res, next) => {
try {
const token = req.header("Authorization").replace("Bearer ", "");
const decoded = jwt.verify(token, process.env.JWT_SECRET_KEY);
const user = await User.findOne({ _id: decoded._id, "tokens.token": token });
if (!user) {
throw new error();
}
req.token = token
req.user = user
next();
} catch (e) {
res.status(401).send(e);
}
};
However, even non-admin users can post data, which is then saved to the database.
I want to restrict this.
I'm not sure how I can prevent non-admin users from posting data.
You need to check if the user is admin in the Auth middleware.
const authentication = async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer ', '');
const decoded = jwt.verify(token, process.env.JWT_SECRET_KEY);
const user = await User.findOne({
_id: decoded._id,
'tokens.token': token,
admin: true
});
if (!user) {
throw new error();
}
req.token = token;
req.user = user;
next();
} catch (e) {
res.status(401).send(e);
}
};
And remove the line const user = await User.find({ admin: true }); and related if check in the route.
router.post("/bus/add", auth, async (req, res) => {
const bus = new Bus(req.body);
try {
await bus.save();
res.status(201).send(bus);
} catch (e) {
res.status(500).json({
message: "Please enter the valid data",
});
}
});

"Unauthorized" when attempting to send email with SendGrid in node.js

Not sure what's going on but every time I try to send an email i'm met with a error message. I've literally tried everything I can think of and Nothing. This is the message i'm receiving from postman when I hit the localhost:4000/api/send-mail route. I've been trying to work through this for hours now, if anyone can help that would be amazing. Thanks in advance!!!
{
"message": "Unauthorized",
"code": 401,
"response": {
"headers": {
"server": "nginx",
"date": "Mon, 21 Mar 2022 01:57:04 GMT",
"content-type": "application/json",
"content-length": "116",
"connection": "close",
"access-control-allow-origin": "https://sendgrid.api-docs.io",
"access-control-allow-methods": "POST",
"access-control-allow-headers": "Authorization, Content-Type, On-behalf-of, x-sg-elas-acl",
"access-control-max-age": "600",
"x-no-cors-reason": "https://sendgrid.com/docs/Classroom/Basics/API/cors.html",
"strict-transport-security": "max-age=600; includeSubDomains"
},
"body": {
"errors": [
{
"message": "The provided authorization grant is invalid, expired, or revoked",
"field": null,
"help": null
}
]
}
}
}
my user controllers code
const expressAsyncHandler = require("express-async-handler");
const sgMail = require("#sendgrid/mail");
const generateToken = require("../../config/token/generateToken");
const User = require("../../model/user/User");
const validateMongodbId = require("../../utils/validateMongodbID");
sgMail.setApiKey(process.env.SEND_GRID_API_KEY);
//-------------------------------------
//Register
//-------------------------------------
const userRegisterCtrl = expressAsyncHandler(async (req, res) => {
//Check if user Exist
const userExists = await User.findOne({ email: req?.body?.email });
if (userExists) throw new Error("User already exists");
try {
//Register user
const user = await User.create({
firstName: req?.body?.firstName,
lastName: req?.body?.lastName,
email: req?.body?.email,
password: req?.body?.password,
});
res.json(user);
} catch (error) {
res.json(error);
}
});
//-------------------------------
//Login user
//-------------------------------
const loginUserCtrl = expressAsyncHandler(async (req, res) => {
const { email, password } = req.body;
//check if user exists
const userFound = await User.findOne({ email });
//Check if password is match
if (userFound && (await userFound.isPasswordMatched(password))) {
res.json({
_id: userFound?._id,
firstName: userFound?.firstName,
lastName: userFound?.lastName,
email: userFound?.email,
profilePhoto: userFound?.profilePhoto,
isAdmin: userFound?.isAdmin,
token: generateToken(userFound?._id),
});
} else {
res.status(401);
throw new Error("Invalid Login Credentials");
}
});
//------------------------------
//Users
//-------------------------------
const fetchUsersCtrl = expressAsyncHandler(async (req, res) => {
console.log(req.headers);
try {
const users = await User.find({});
res.json(users);
} catch (error) {
res.json(error);
}
});
//------------------------------
//Delete user
//------------------------------
const deleteUsersCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
//check if user id is valid
validateMongodbId(id);
try {
const deletedUser = await User.findByIdAndDelete(id);
res.json(deletedUser);
} catch (error) {
res.json(error);
}
});
//----------------
//user details
//----------------
const fetchUserDetailsCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
//check if user id is valid
validateMongodbId(id);
try {
const user = await User.findById(id);
res.json(user);
} catch (error) {
res.json(error);
}
});
//------------------------------
//User profile
//------------------------------
const userProfileCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
validateMongodbId(id);
try {
const myProfile = await User.findById(id);
res.json(myProfile);
} catch (error) {
res.json(error);
}
});
//------------------------------
//Update profile
//------------------------------
const updateUserCtrl = expressAsyncHandler(async (req, res) => {
const { _id } = req?.user;
validateMongodbId(_id);
const user = await User.findByIdAndUpdate(
_id,
{
firstName: req?.body?.firstName,
lastName: req?.body?.lastName,
email: req?.body?.email,
bio: req?.body?.bio,
},
{
new: true,
runValidators: true,
}
);
res.json(user);
});
//------------------------------
//Update password
//------------------------------
const updateUserPasswordCtrl = expressAsyncHandler(async (req, res) => {
//destructure the login user
const { _id } = req.user;
const { password } = req.body;
validateMongodbId(_id);
//Find the user by _id
const user = await User.findById(_id);
if (password) {
user.password = password;
const updatedUser = await user.save();
res.json(updatedUser);
} else {
res.json(user);
}
});
//------------------------------
//following
//------------------------------
const followingUserCtrl = expressAsyncHandler(async (req, res) => {
//1.Find the user you want to follow and update it's followers field
//2. Update the login user following field
const { followId } = req.body;
const loginUserId = req.user.id;
//find the target user and check if the login Id exists
const targetUser = await User.findById(followId);
const alreadyFollowing = targetUser?.followers?.find(
(user) => user?.toString() === loginUserId.toString()
);
if (alreadyFollowing) throw new Error("You are already folliwig this user");
//1. Find the user you want to follow and update it's followers field
await User.findByIdAndUpdate(
followId,
{
$push: { followers: loginUserId },
},
{ new: true }
);
//2. Update the login user following field
await User.findByIdAndUpdate(
loginUserId,
{
$push: { following: followId },
isFollowing: true,
},
{ new: true }
);
res.json("You are now following this user");
});
//------------------------------
//unfollow
//------------------------------
const unfollowUserCtrl = expressAsyncHandler(async (req, res) => {
const { unFollowId } = req.body;
const loginUserId = req.user.id;
await User.findByIdAndUpdate(
unFollowId,
{
$pull: { followers: loginUserId },
isFollowing: false,
},
{ new: true }
);
await User.findByIdAndUpdate(
loginUserId,
{
$pull: { following: unFollowId },
},
{ new: true }
);
res.json("You have successfully unfollowed this user");
});
//------------------------------
//Block Users
//------------------------------
const blockUserCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
validateMongodbId(id);
const user = await User.findByIdAndUpdate(
id,
{
isBlocked: true,
},
{ new: true }
);
res.json(user);
});
//------------------------------
//Block user
//------------------------------
const unBlockUserCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
validateMongodbId(id);
const user = await User.findByIdAndUpdate(
id,
{
isBlocked: false,
},
{ new: true }
);
res.json(user);
});
//------------------------------
// Account Verification - Send email
//------------------------------
const generateVerificationTokenCtrl = expressAsyncHandler(async (req, res) => {
try {
//build your message
const msg = {
to: "MizTamaraAndrea#gmail.com",
from: "Tamara18_1985#msn.com",
subject: "My first Node js email sending",
text: "I hope this goes through",
};
await sgMail.send(msg);
res.json("Email sent");
} catch (error) {
res.json(error);
}
});
module.exports = {
generateVerificationTokenCtrl,
userRegisterCtrl,
loginUserCtrl,
fetchUsersCtrl,
deleteUsersCtrl,
fetchUserDetailsCtrl,
userProfileCtrl,
updateUserCtrl,
updateUserPasswordCtrl,
followingUserCtrl,
unfollowUserCtrl,
blockUserCtrl,
unBlockUserCtrl,
};
My user Routes Code
const express = require("express");
const {
userRegisterCtrl,
loginUserCtrl,
fetchUsersCtrl,
deleteUsersCtrl,
fetchUserDetailsCtrl,
userProfileCtrl,
updateUserCtrl,
updateUserPasswordCtrl,
followingUserCtrl,
unfollowUserCtrl,
blockUserCtrl,
unBlockUserCtrl,
generateVerificationTokenCtrl,
} = require("../../controllers/users/usersCtrl");
const authMiddleware = require("../../middleware/auth/authMiddleware");
const userRoutes = express.Router();
userRoutes.post("/register", userRegisterCtrl);
userRoutes.post("/login", loginUserCtrl);
userRoutes.post("/send-mail", generateVerificationTokenCtrl);
userRoutes.get("/", authMiddleware, fetchUsersCtrl);
userRoutes.put("/password", authMiddleware, updateUserPasswordCtrl);
userRoutes.put("/follow", authMiddleware, followingUserCtrl);
userRoutes.put("/unfollow", authMiddleware, unfollowUserCtrl);
userRoutes.put("/block-user/:id", authMiddleware, blockUserCtrl);
userRoutes.put("/unblock-user/:id", authMiddleware, unBlockUserCtrl);
userRoutes.get("/profile/:id", authMiddleware, userProfileCtrl);
userRoutes.put("/:id", authMiddleware, updateUserCtrl);
userRoutes.delete("/:id", deleteUsersCtrl);
userRoutes.get("/:id", fetchUserDetailsCtrl);
module.exports = userRoutes;
You have one of two problems here. Either, your API key does not have the permission to send messages, or has been deleted. Create yourself a new API key that has permission to send emails and try again.
Or, I see you appear to be trying to send from an msn.com email address. In order to send from a third party domain like that, you need to have set up single sender verification. This lets SendGrid know that you do have access to that email address and that you are not trying to spoof someone else's.
(Note that when you ship to production, we recommend you use your own domain and authenticate that domain with SendGrid, which will give you much better deliverability.)

TypeError: newUser.find is not a function

I am very new to the MERN stack and I would like some help figuring out this error. I'm trying to check if an email is already in the database upon creating a new user. Can anyone tell me why I am getting this error?
The model and scheme
//schema
const Schema = mongoose.Schema;
const VerificationSchema = new Schema({
FullName: String,
email: String,
password: String,
date: Date,
isVerified: Boolean,
});
// Model
const User = mongoose.model("Users", VerificationSchema);
module.exports = User;
The Api
const express = require("express");
const router = express.Router();
const User = require("../Models/User");
router.get("/VerifyEmail", (req, res) => {
console.log("Body:", req.body);
const data = req.body;
const newUser = new User();
newUser.find({ email: data.email }, function (err, newUser) {
if (err) console.log(err);
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
});
res.json({
msg: "We received your data!!!",
});
});
module.exports = router;
The api caller using axios
const isEmailValid = (value) => {
const info = {
email: value,
};
axios({
url: "http://localhost:3001/api/VerifyEmail",
method: "get",
data: info,
})
.then(() => {
console.log("Data has been sent");
console.log(info);
})
.catch(() => {
console.log("Internal server error");
});
};
if you have body in your request, change the type of request to POST...
after that for use find don't need to create a instance of model, use find with Model
router.get("/VerifyEmail", (req, res) => {
console.log("Body:", req.body);
const data = req.body;
User.find({ email: data.email }, function (err, newUser) {
if (err) console.log(err);
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
});
res.json({
msg: "We received your data!!!",
});
});
I prefer to use async/await and don't use Uppercase world for routing check the article: like this
router.post("/verify-email", async (req, res) => {
try {
let { email } = req.body;
let newUser = await User.findOne({ email });
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
} catch (error) {
res.json({
msg: "somthing went wrong",
});
}
res.json({
msg: "We received your data!!!",
});
});
The proper way to query a Model is like so:
const User = mongoose.model('Users');
User.find({<query>}, function (err, newUser) {...
So you need to get the model into a variable (in this case User) and then run the find function directly against it, as opposed to running it against an object you instantiate from it. So this is incorrect:
const newUser = new User();
newUser.find(...
So assuming all your files and modules are linked up correctly, this should work:
const User = require("../Models/User");
User.find({<query>}, function (err, newUser) {...
The problem wasn't actually the mongoose function but I needed to parse the object being sent.
let { email } = JSON.parse(req.body);
Before parsing the object looked like {"email" : "something#gmail.com"}
and after parsing the object looked like {email: 'something#gmail.com'}
I also changed the request from 'get' to 'post' and instead of creating a new instance of the model I simply used User.find() instead of newUser.find()

Patch Request Method in Node.js and Mongoose

For update I use the following code that works:
router.put('/:id', async (req, res) => {
const { error } = validateProduct(req.body);
if (error) return res.status(400).send(error.details[0].message);
const product = await Product.findByIdAndUpdate(req.params.id,
{
name: req.body.name,
description: req.body.description,
category: req.body.category,
tags: req.body.tags,
withdrawn: req.body.withdrawn,
extraData: {
brand: req.body.extraData.brand,
quantity: req.body.extraData.quantity,
type: req.body.extraData.type
}
},
{new: true}
);
if (!product) return res.status(404).send('The product with the given ID was not found.');
res.send(product);
});
What I want to do is to create a Patch operation that updates only certain fields and not all of them as the update above. These fields are not standard but they are one of the above fields of the update operation.
You can try this snippet (didn't test it locally though). The idea is to only update those fields in Product, which were mentioned in req.body. Be sure your validator is secure, otherwise you can end up with nosql injection.
router.put('/:id', async (req, res) => {
const { error } = validateProduct(req.body);
if (error) return res.status(400).send(error.details[0].message);
const product = await Product.findById(req.params.id).exec();
if (!product) return res.status(404).send('The product with the given ID was not found.');
let query = {$set: {}};
for (let key in req.body) {
if (product[key] && product[key] !== req.body[key]) // if the field we have in req.body exists, we're gonna update it
query.$set[key] = req.body[key];
const updatedProduct = await Product.updateOne({_id: req.params.id}, query}).exec();
res.send(product);
});
Also I'm sure you can leverage lodash in the line, where I you use a for-in loop ;)
The downside of this approach is that it takes 2 queries to mongo, because you need a real document to compare the thing. Also update operation doesn't trigger post save hooks of your model. If you need them - you should findById() first, update necessary fields and then hit .save() on the very document you found.
Hope it helps
You could do this as well:
// define your middlewares here
// validateObjectId.js
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();
}
// 404.js
module.exports = function (str, id) {
if (!str || !id) throw new Error('the string name and id must be defined');
return `The ${str} with the given ID (${id}) was not found`;
}
// import your middlewares into your product route
const validateObjectId = require('../middleware/validateObjectId'); // respect your middleware path
const fourOfour = require('../middleware/404'); // respect your middleware path
router.put('/:id', validateObjectId, async (req, res) => {
const { error } = validateProduct(req.body);
const { name, description, category, tags, withdrawn, extraData, } = req.body
if (error) return res.status(400).send(error.details[0].message);
let product = await Product.findById(req.params.id).exec();
if (!product) return res.status(404).send(fourOfour("Product", req.params.id));
product = await Product.findByIdAndUpdate(req.params.id, {
name: name || product.name,
description: description || product.description,
category: category || product.category,
withdrawn: withdrawn || product.withdrawn,
extraData: extraData || product.extraData
}, { new: true });
product = await product.save();
if (!product) return res.status(404).send(fourOfour("Product", req.params.id));
res.send(product);
});

Resources