react/node MongooseError: Operation `users.insertOne()` buffering timed out after 10000ms - javascript

thank you in advance for reading this.
Id like to start by saying im completely new to react/node/mongoose and mongoDB, so sorry if I miss something very basic, ive only really been developing in react since christmas on and off, and have now decided to add a node back end to it. I have pretty much everything working, a react form that passes variables to a node backend. The problem im struggling with is when my API decides to try and save something using an atlas hosted mongoDB. I havent always had this issue, when developing this in another node app it seemed to be intermitent until i came off my work VPN and which case it inserted everytime, so i thought great, time to take it into my react project. This is currently what the API looks like (see below, have replaced the username and password in the DB const for obvious reasons but i can assure you these are correct)
module.exports = {
registerUser: async (username, password) => {
//const User = require("./database/schema/userSchema.js");
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
username: {
type: String,
require: [true, "a user must have a username"],
unique: true,
},
password: {
type: String,
require: [true, "a user must have a password"],
},
});
var User = mongoose.model("User", userSchema);
const DB =
"mongodb+srv://<username>:<password>#cluster0.mh4jhqg.mongodb.net/tehcooky?retryWrites=true&w=majority";
console.log("DB: " + DB);
mongoose
.connect(DB, {
userNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
useUnifiedTopology: true,
})
.then(() => console.log("DB Connection Successful!"))
.catch((err) => {
console.log(err);
});
var message = "";
var user = new User({
username: username,
password: password,
});
console.log("user object: " + user);
console.log("attempting to register user");
await user
.save()
.then((doc) => {
console.log(doc);
message = "user data send to database";
console.log("sent to database");
})
.catch((err) => {
console.log("error 🤩: ", err);
message = `error: ${err}`;
});
mongoose.disconnect();
//const response = "nice";
const response = `message: ${message}, user: ${username}, password: ${password}`;
return response;
},
};
i have tried resetting my connection, changing my dns, putting model commands later then the db connection and making it await before it continues in most cases. I also have my IP stored in the access place on atlas, as well as the accept all connections from anywhere setting just incase.
Wondering if anyone can help me, im sure that theres some glaring flaws with what ive written as i haven't done really any tidying but as far as i can see everything is fine until it hits the save, which im assuming is a promise that mongoDB takes over, and then possibly its having some kind of authentication issue with trying to actually insert the data? the collection is being hit according to the graphs

thanks for the suggestions, got our solutions architect on a call today and we found a solution, apart from the fact i need to clean up the code and refactor, this is it
module.exports = {
registerUser: async (username, password) => {
// const User = require("./database/schema/userSchema.js");
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
username: {
type: String,
require: [true, "a user must have a username"],
unique: true,
},
password: {
type: String,
require: [true, "a user must have a password"],
},
});
const User = mongoose.model("users", userSchema);
const message = "";
console.log("attempting to register user");
const DB =
connection string (taken out);
console.log("DB: " + DB);
const connection = await mongoose.connect(DB);
if (!connection) {
console.log("No connection");
throw new Error("no connection");
}
const user = new User({
username,
password,
});
const data = await user.save();
console.log("the data send back by promise is: " + data);
await mongoose.disconnect();
// const response = "nice";
const response = `message: ${message}, user: ${username}, password: ${password}`;
return response;
},
};

Related

How can I catch Error from Node.js user Model

In the user model schema, I give first_name to require and email to unique and require, when I save the data in the database I would like to send a response if the user will not send the first_name. how can we do this without add manually conditions?
I'm adding manually conditions for this operation
exports.saveuser = async (req, res) => {
const { first_name, email } = req.body;
if (!first_name || !email)
return res.status(401).json({ error: 'All the data require' });
const user = User.findOne({ email });
if (user) {
return res.status(401).json({ error: 'Email already exist' });
}
const user = new User({
first_name,
email,
});
await user.save();
};
in the above code, I add conditions manually, but I don't want to add all the conditions every time.
You can follow express Validator. It pretty industry standard. https://express-validator.github.io/docs/
Below is the example:
router.PUT(
`/api/user`,
auth,
validate(UserValidator.saveUser),
UserController.saveUser,
);

Error while log in authentication in Node js

I have created an API for LogIn authentication. I have used Sequelize ORM for Mysql Database. Given below is an image of my users model which i have imported in my authentication code for login.
Models/users image
module.exports = (sequelize, Sequelize) => {
const Tutorial = sequelize.define("users", {
age: { type: Sequelize.INTEGER },
name: { type: Sequelize.STRING },
email: { type: Sequelize.STRING },
password: { type: Sequelize.STRING }
});
return Tutorial
};
Here below is the code for log in authentication. I have used findOne function for getting the email & password and i have used .then which is a promise function that returns the response.
var users = require('./models/users');
app.post('/login', (req, res) => {
var email = req.body.email;
var password = req.body.password;
users.findOne({ email: email, password: password })
.then(users => {
if (users == null) {
res.status(404).json({
message: 'Auth Failed'
})
}
else {
res.status(200).json({
message: 'Logg In succesfull'
})
}
})
})
But when i hit the API, it shows error
TypeError: users.findOne is not a function
at D:\Node\Task\server.js:39:11
Please help me fix it.
module.exports = (sequelize, Sequelize) => {
Because your function accepts 2 parameters, you'll have to pass the values as well in the require to get it working.
const sequel = require('sequelize');
const DataTypes = sequel.DataTypes;
const sequelizeInstance = new sequel(...) // import your sequelize config
var users = require('./models/users')(sequelizeInstance, DataTypes);
I know nothing about Sequelize, but inspired by #sid's answer, I guess something like this is cleaner than importing Sequelize in the main file, then pass it to the imported file... Just import your dependencies in each file.
models/users :
const sequel = require('sequelize');
const DataTypes = sequel.DataTypes;
const sequelizeInstance = new sequel(...) // import your sequelize config
module.exports = sequelize.define("users", {
age: { type: DataTypes.INTEGER },
name: { type: DataTypes.STRING },
email: { type: DataTypes.STRING },
password: { type: DataTypes.STRING }
})
Then simply import it :
const users = require('./models/users');
users.findOne({ email, password })
If this is still unanswered it could be because of two reasons. Either you're importing the model wrong, or its because you're calling the query without it being async (As I've had similar issues with this as a cause):
const { users } = require('./models')
async app.post('/login', (req, res) => {
try {
const email = req.body.email
const password = req.body.password
const user = await users.findOne({
where: {
email: email,
password: password // Remove this if you have a verify password func
}
})
if (!user) {
res.status(403).send({
error: "Password or email is wrong"
})
}
// You should really encrypt the passwords and have a compare/verify function instead
// Example
if (!bcrypy.compareSync(password, user.password)) {
res.status(403).send({
error: "Password or email is wrong"
})
}
res.staus(200).send(user) // Save user to store/cache on client
} catch (err) {
console.log(err)
res.status(500).send({
error: "Error occured during login"
})
}
})
Once you have more of your frontend setup, you should send the user back to the client and then store it in client side cache or smth, so you don't need to query the server for logged in user data everytime.

Mongoose: How do I pass data from a document created afterwards into a document created right before?

In my signup request, I use create two documents (in separate collections) based on two different schemas: a User model and a Client model. For context, a client will be one object referencing an array of many Users.
The User scheme includes 'clientID' field, which should hold the User's Client's ._id. Likewise, the Client's 'users' field would include an array of Users attached. The latter works OK.
On signup, I create a User, and then a Client. I am able to pass the User._id into the Client's array of users no problem. But, how do I get the Client's ._id into the User's clientID field?
The code below errors saying: Cannot access 'client' before initialization - I understand why this is happening because of the order of code.
But how do I get my code to reciprocate so that I can add the Client._id to the User's clientID field? I am sure this is a common problem, but I can't find the relevant doc on Mongoose's docs.
If anyone could help? Many thanks!
module.exports.signup = async (req, res) => {
// extract data from the req.body
const { email, password, fName, lName, companyName, teams } = req.body;
try {
// create a new user on the User model
const user = await User.create({
email: email,
password: password,
clientID: client._id,
});
// create a new client on the Client model
const client = await Client.create({
companyName: companyName,
users: user._id,
});
res.status(201).json(user);
} catch (err) {
const errors = handleErrors(err);
res.status(400).json(errors);
}
};
You can instead use a specific objectId for the client when you create it and reference it later.
Just copy past the whole code:
const {ObjectID} = require('mongodb');
module.exports.signup = async (req, res) => {
// extract data from the req.body
const { email, password, fName, lName, companyName, teams } = req.body;
try {
const clientId = ObjectID();
// create a new user on the User model
const user = await User.create({
email: email,
password: password,
clientID: clientId,
});
// create a new client on the Client model
const client = await Client.create({
companyName: companyName,
users: user._id,
_id: clientId
});
res.status(201).json(user);
} catch (err) {
const errors = handleErrors(err);
res.status(400).json(errors);
}
};
Another solution is that you can create a user without clientId and after you create client you can update the user with the created client.id:
const user = await User.create({
email: email,
password: password,
// clientID: clientId,
});
// create a new client on the Client model
const client = await Client.create({
companyName: companyName,
users: user._id,
});
user.clientId = client._id;
await user.save();

OPTIONS request inconsistency (in deployment)?

Introduction
So, I'm using the MERN stack (with Heroku + Netlify), and I'm having some really strange consistency problems with how the DELETE request is being handled. I've tried countless solutions in the last
three days trying to get this to work and none of them have worked. A lot of these solutions have
come from stack overflow, so if you want to direct me to another post, the chance is that I've already seen it. I've scoured every part of the web and making this post is my last resort.
The Problem
So, when I make a delete request, I'm getting the per-usual OPTIONS request since I'm sending a token in a custom header of the request ('x-auth-token'). The OPTIONS request always resolves with a 204, meaning that everything should be alright. However, afterward, there is no DELETE request like there should be. This, in essence, is my problem. I've checked my Heroku logs, and all I can see is the OPTIONS request, and nothing else.
Inconsistencies?
So this is where I've been very confused. The thing is, that sometimes it DOES work. And other routes I use in my API (like login, and creating a new post) work, even though I'm using the same middleware.
Every time it works, I get the OPTIONS request and then the DELETE request (with a 200 status) like I would expect to happen.
If you want an example of a re-creatable scenario:
I create X number posts after logging in and getting a valid token, then I can see those posts rendering in the posts listing on my home page. I then navigate one of the posts and delete it by clicking and then a confirmation button. I automatically get redirected to the next post in the list. I repeat this till I get to the last post. I delete that post, and since there are no more posts left, I get redirected to the posts listing which is... not empty! The last post I tried deleting is still there.
Keep in mind, that the DELETE requests all get sent in exactly the same way, so I'm pretty sure this isn't a front-end issue, so no need to poke around in the code there. I've logged everything and debugged, and it's 100% consistent with what I would expect.
(The create post doesn't redirect, while the delete post does? I don't see how this would effect anythign as the DELETE request gets sent as per usual... Though maybe a solution lies within this fact.)
Solutions I've tried
Cors
First off, you might already be rushing to your keyboard to tell me that this is a CORS issue. I thought the same thing yesterday, but I'm not so sure now. I've tried messing with all the config settings possible in CORS to get this to work. Since my two websites are on different domains, then CORS verifies the requests. I've already added my front-end website to a whitelist, and all other requests are going through properly, so no problem there. I've tried adding an allowHeaders option in the config, but it didn't do anything more than the default setting. I've also added 'OPTIONS' to the allowed methods in the config, still nothing. I'm also using app.use(cors({config})). I'll include some code later to see some more of this in detail.
Debugging
I've basically tested things out by inserting console.logs everywhere and discovered that neither the middleware, the options route (I tried making an options route with same route url), or the original post route get executed when the OPTIONS request doesn't result in a DELETE request.
Static Server
This is maybe where some of my inexperience shows (this is my first Web project). I saw some solutions telling that a static server is needed. So I tried setting up a static server, but I didn't see any results. So I'm not too sure what this accomplished.
Async and Await
I was just trying things at this point, so I made all my routes async to see if it would do anything. It didn't.
Others
I've also messed around with environment variables and dotenv, and other stuff I can't remember. I think everything here should already be sufficient information understand the situation.
Code
index.js
const express = require('express');
require("dotenv").config({ path: "variables.env" });
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const routes = require("./routes/router");
const cors = require("cors");
const morgan = require('morgan')
const app = express();
const whitelist = [
process.env.ORIGIN
];
app.use(
cors({
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true);
} else {
console.log(origin);
callback(new Error("Not allowed by CORS"));
}
}, //frontend server localhost:3000
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
credentials: true, // enable set cookie
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(morgan('dev'));
mongoose.connect(process.env.MONGODB_URL, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true
});
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
console.log('connected to db');
});
const userSchema = mongoose.Schema({
name: String,
password: String
});
// Routes
// TODO: make seperate routers/routes
app.use("/", routes);
// Serve static assets if in production
if (process.env.NODE_ENV === 'production') {
// Set static folder
app.use(express.static('client/build'));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
// TODO: set up custom port in future
app.listen(process.env.PORT, () => console.log(`Server listening at http://localhost:${process.env.PORT}`));
// Callback functions?
router.js
const express = require('express');
const router = express.Router();
const Post = require('../models/Post');
const User = require('../models/User');
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const auth = require('../middleware/auth');
const adminAuth = require('../middleware/adminAuth');
const cors = require("cors");
require("dotenv").config({ path: "variables.env" });
// import 'moment'
// second onwards are handlers => triggers like the post body then next() to go to the next handler
router.post('/api/add_post', adminAuth, async (req, res, next) => {
try{
newPost = new Post({
title: req.body.title,
body: req.body.body,
author: req.body.author,
created: req.body.created,
});
const savedPost = await newPost.save();
if (!savedUser) throw Error('Something went wrong saving the post');
res.send(savedPost);
} catch (e) {
res.status(400).json({ msg: e.message });
}
});
router.delete('/api/delete_post/:id', adminAuth, async (req, res, next) => {
// timeout?
// console.log(req.body);
try{
const id = req.params.id;
if(!id) throw Error('Invalid ID');
const post = await Post.findById(id);
if (!post) throw Error('Post doesn\'t exist');
const removed = await post.remove();
if(!removed) throw Error('Problem with deleting the post');
res.status(200).json({ success: true });
} catch(e) {
console.log("Error: ", e.message);
res.status(400).json({ msg: e.message, success: false });
}
});
// TODO : UPDATE for async soon
router.post('/api/update_post', adminAuth, async (req, res, next) => {
const id = req.body._id;
test_post_data = {
title: req.body.title,
body: req.body.body,
author: req.body.author,
modified: req.body.modified,
};
console.log(test_post_data, id);
Post.updateOne({ _id: id }, test_post_data, (err) => {
if(err) return next(err);
return res.status(200);
});
});
router.get('/api/get_posts', async (req, res, next) => {
try{
const posts = await Post.find();
if(!posts) throw Error('Error with fetching the posts')
res.send(posts.reverse());
} catch (e) {
res.status(400).json({ msg: e.message });
}
});
router.get('/api/get_chapter/:id', async (req, res, next) => {
try{
const id = req.params.id;
const post = await Post.findOne({_id: id})
if(!post) throw Error('No post was found')
res.send(post);
} catch(e) {
res.status(400).json({ msg: e.message })
}
});
// User routes
// TODO : make in seperate file
router.post('/api/user/register', async (req, res) => {
const { name, email, password } = req.body;
// Simple validation
if (!name || !email || !password) {
return res.status(400).json({ msg: 'Please enter all fields' });
}
try {
const user = await User.findOne({ email });
if (user) throw Error('User already exists');
const salt = await bcrypt.genSalt(10);
if (!salt) throw Error('Something went wrong with bcrypt');
const hash = await bcrypt.hash(password, salt);
if (!hash) throw Error('Something went wrong hashing the password');
const newUser = new User({
name,
email,
password: hash,
admin: false
});
const savedUser = await newUser.save();
if (!savedUser) throw Error('Something went wrong saving the user');
// TODO : check up on expires stuff : 3600 = 1 hr
const token = jwt.sign({ id: savedUser._id, admin: savedUser.admin }, process.env.JWT_SECRET, {
expiresIn: 3600
});
res.status(200).json({
token,
user: {
id: savedUser.id,
name: savedUser.name,
email: savedUser.email,
admin: savedUser.admin
}
});
} catch (e) {
res.status(400).json({ error: e.message });
}
});
router.post('/api/user/login', async (req, res) => {
const { name, password } = req.body;
// Simple validation
if (!name || !password) {
return res.status(400).json({ msg: 'Please enter all fields' });
}
try {
// Check for existing user
const user = await User.findOne({ name });
if (!user) throw Error('User Does not exist');
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) throw Error('Invalid credentials');
const token = jwt.sign({ id: user._id, admin: user.admin }, process.env.JWT_SECRET, { expiresIn: 3600 });
if (!token) throw Error('Couldnt sign the token');
res.status(200).json({
token,
user: {
id: user._id,
name: user.name,
email: user.email,
admin: user.admin
}
});
} catch (e) {
res.status(400).json({ msg: e.message });
}
});
module.exports = router;
adminAuth.js
const jwt = require('jsonwebtoken')
require("dotenv").config({ path: "variables.env" });
module.exports = (req, res, next) => {
console.log(req.header('x-auth-token'));
const token = req.header('x-auth-token');
// Check for token
if (!token)
return res.status(401).json({ msg: 'No token, authorizaton denied' });
try {
// Verify token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
console.log('decoded:', decoded);
if(!decoded.admin)
return res.status(401).json({ msg: 'Not an admin, authorization denied' });
// Add user from payload
// console.log('decoded:', decoded);
req.user = decoded;
next();
} catch (e) {
res.status(400).json({ msg: 'Token is not valid' });
}
};
Link for request examples, and Heroku log since Stackoverflow says it's spam:
https://gist.github.com/macklinhrw/b2fec97642882ba406c49cce3e195c39
Edit
I pasted the Chrome request and response headers into the gist at the bottom, but there was no response data to go along with either.
I've debugged a little using this to check the difference and I discovered that with delete action that ends up working, the red (canceled) request has headers, while the non-working is completely empty (filled with 'provisional headers' if that means anything).
I couldn't copy-paste the request headers into the gist for the working red (canceled) one. But, I pasted everything that I thought could possibly be useful from chrome, hopefully it helps.
Also, I didn't see any DELETE requests when I was using the Chrome network tool, and I was seeing them on the other tool. Not sure if it matters, probably just a config option somewhere.
So, I haven't found an exact answer, but I've found a workaround.
As it turns out, it might have something to do with axios, and I've been searching for the wrong things in the last 3 days.
This thread helped me: https://github.com/axios/axios/issues/1428
I've added an e.preventDefault() to the onClick method I use for the delete button.
This fixed the problem, but doesn't redirect (I use href={link}), so I'm going to add a conditional render for react-router to redirect the page. I don't know of a better method so maybe give me some ideas. I'll edit if I have further troubles.

How to create an admin and allow access to admin panel in node.js?

I am very new to coding and am writing a personal project using node.js, express, mongoDB, and mongoose. I wrote most of it myself, however I hired someone to help me with the more advanced parts. I have lost contact with him and went back under the hood to create an admin panel I could use to write blog posts and other things. I am trying to write a middleware that only allows myself access to the route. However it is not working.
function adminAuth(req, res, next){
if(req.user.isAdmin){
return next();
} else {
res.redirect("/");
}
}
I am a bit confused of the syntax he has used to create a user schema and I am not sure how to add this isAdmin key value pair. Any help updating my users with an isAdmin key value would be extremely appreciated, and also helping me finish the middleware as (req.user.isAdmin) is not working! (If I do not provide the necessary code, please excuse my inexperience and tell me what you would like to see).
Here is the Auth route the coder I hired wrote that I am having trouble deciphering how to pass in new data to the user model.
const isAdmin = false;
const passwordHash = await bcrypt.hash(req.body.password, saltRounds);
const db = client.db(dbName);
const col = db.collection('users');
const user = {
email, firstName, lastName, password: passwordHash, isAdmin,
};
local strategy
module.exports = function localStrategy() {
passport.use(new Strategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
}, (req, email, password, done) => {
const url = process.env.MONGOLAB_URI;
const dbName = 'giftgrab';
(async function addUser() {
let client;
try {
client = await MongoClient.connect(url);
const db = client.db(dbName);
const col = db.collection('users');
const user = await col.findOne({ email });
debug('Found user by email');
debug(user);
if (!user) {
req.flash('error', 'The username or password is wrong');
done(null, false);
} else {
const match = await bcrypt.compare(password, user.password);
if (match) {
done(null, user);
} else {
req.flash('error', 'The username or password is wrong');
// we pass null because it did not error, just failed
done(null, false);
}
}
} catch (e) {
debug(e.stack);
}
client.close();
}());
}
Here is the Auth route the coder I hired wrote that I am having trouble deciphering how to pass in new data to the user model.
// add logic to check if the user is admin
const isAdmin = false;
// user data collected here. If you want to add an "isAdmin" property, this is the right place
const user = {
email, firstName, lastName, password: passwordHash, isAdmin,
};
// checking if the user already exists
const check = await col.findOne({ email });
if (check) {
req.flash('error', 'The user with this email already exists');
res.redirect('back');
} else {
// the user does not exist, insert a new one and authenticate
const results = await col.insertOne(user);
req.login(results.ops[0], () => {
res.redirect('/');
});
}
This is what related to adding the isAdmin property. In order to use req.user and req.isAuthenticated() you are going to need Passport.js. The user data stored in you session (req.user) is defined by your passport strategy so if you want to use the isAdmin property this way, you are going to need to set it there.

Categories