I have a MERN mobile app thats using passportjs to authenticate and login users (with mongodb database and axios), however, when i eventually get to the the screen to enter in data (a "log"), i cant associate that data/log with the signed in user. How can i grab the user id several screens later after they have already signed in to associate it with the entry? My mongodb database has a number of users, so i only want a specific user's data (eg calories), ie the one that is currently logged in:
// Mongoose schemas
// log.model.js
const Schema = mongoose.Schema;
const logSchema = new Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
calories: {
type: Number,
required: true,
},
},
{
timestamps: true,
}
);
const Log = mongoose.model("Log", logSchema);
// user.model.js
const userSchema = new Schema(
{
_id: Schema.Types.ObjectId, // user id
email: {
type: String,
required: true,
unique: true,
trim: true,
},
password: {
type: String,
required: true,
trim: true,
minlength: 6,
},
},
{
timestamps: true,
}
);
const User = mongoose.model("User", userSchema);
They are first prompted to signin in the app, where they will then navigate to Home. Not all features are added in yet, just in development stage now:
// ./frontend/screens/signin.js
function onLoginPress() {
axios({
method: "POST",
data: {
email: email,
password: password,
},
withCredentials: true,
url: 'http:localhost:5000/users/signin',
})
.then((res) => console.log(res.data))
.catch((error) =>
console.log("ERROR: Promise rejected (sign in): " + error)
);
navigation.navigate("Home");
}
// ./backend/routes/users.js
router.route("/signin").post((req, res, next) => {
passport.authenticate("local", (error, user, info) => {
if (error) {
res.json({
status: "FAILED",
message: error,
});
}
if (!user) {
res.json({
status: "FAILED",
message: "No user exists",
});
} else {
req.logIn(user, (error) => {
if (error) console.log("ERROR: " + error);
res.json({
status: "SUCCESS",
message: "Successfully authenticated",
});
console.log(req.user);
});
}
})(req, res, next);
});
After they sign in, and they wish to enter in calories, i attempt to associate that log (and any future logs they might add) with the signed in user when they hit a button:
// ./frontend/screens/log.js
const [calories, setCalories] = React.useState("");
function onSaveLog() {
axios({
method: "post",
url: "http://localhost:5000/log/add",
data: {
calories: calories,
// CANT GET USER ID HERE?
},
})
.then((res) => {
console.log(res.data);
})
.catch(function () {
console.log("LOG ERROR: promise rejected");
});
}
// ./backend/routes/log.js
router.route("/add").post((req, res) => {
const calories = Number(req.body.calories);
// const user = req.body.user; // CANT GET THE USER ID HERE
const newLog = new Log({
calories,
// user,
});
// saves Log data to mongodb
newLog
.save()
.then(() => res.json("Log added"))
.catch((err) => res.status(400).json("Error: " + err));
});
so, what you doubt is, correct me if I'm wrong is that you want an ID that can be accessed somewhere later in the app to retrieve the users' data.
There are many ways to achieve that,
after you get the id, you can pass it as Navparams. check this for more info RN- params
Next you can store the id in async storage and retrieve it anywhere, I would suggest this cause is the easiest rn--async storage
import AsyncStorage from '#react-native-async-storage/async-storage';
const storeData = async (value) => {
try {
await AsyncStorage.setItem('#storage_Key', value)
} catch (e) {
// saving error
}
}
// read
const getData = async () => {
try {
const value = await AsyncStorage.getItem('#storage_Key')
if(value !== null) {
// value previously stored
}
} catch(e) {
// error reading value
}
}
you can do it this way, do tell me if you're stuck
Related
I'm using mongoose and I would like that when I get all users send me uid instead of _id.
const allUssers = (req, res, next) => {
try {
User.find({})
.select("username")
.select("email")
.select("image")
.exec((err, users) => {
if (err) {
return res.status(400).json({
ok: false,
msg: "Error listing users",
});
}
return res.status(200).json({
ok: true,
users: users,
});
});
} catch (err) {
return res.status(500).json({
ok: false,
msg: "Please contact with administrator",
});
}
};
You can update your schema to use an alias:
let User = new Schema({
_id: { type: String, alias: "uid" }
});
Or you can map your users to something different:
return res.status(200).json({
ok: true,
users: users.map(({ _id, ...user }) => ({ uid: _id, ...user }),
});
I'm having trouble getting a variable within a database user to update using lodash. It seems to update in one route (password in /resetpassword) but not in the other. (resetLink in /forgotpassword)
I need to have "resetLink" update to the new token generated by JWT, in order to create a link to reset the users password.
/forgotpassword
router.put('/forgotpassword', (req, res) => {
const { email } = req.body;
if (!email) {
return res.status(400).json({ msg: "Please enter all fields" });
} else {
// Find existing User by email address
User.findOne({ email }).then((user) => {
if (!user) return res.status(400).json({ msg: "That email doesn't exist with us..." });
const payload = {
id: user._id,
}
const secret = process.env.JWT_SECRET;
const token = jwt.sign(payload, secret, {
expiresIn: '15m'
});
const obj = {
resetLink: token
}
console.log("obj is " + JSON.stringify(obj));
console.log("User is " + user);
user = _.extend(user, obj);
user.save((err, result) => {
if (err) {
return res.status(400).json({{ error: "Something went wrong..." + err }});
} else {
return res.status(200).json({ msg: "Success! These should match: TOKEN - " + token + " / RESET LINK - " + user.resetLink })
}
})
});
};
});
/resetpassword
router.put('/resetpassword', (req, res) => {
const { resetLink, newPass } = req.body;
if (resetLink) {
jwt.verify(resetLink, process.env.JWT_SECRET, function (err, decodedData) {
if (err) {
return res.status(401).json({ msg: "Incorrect token or it is expired." });
}
User.findOne({ resetLink }, (err, user) => {
if (err || !user) {
return res.status(400).json({ error: "User with this token doesn't exist" });
}
// newPass will be hashed by jwt eventually
const obj = {
password: newPass
}
user = _.extend(user, obj);
user.save((err, result) => {
if (err) {
return res.status(400).json({ error: "Reset password error" });
} else {
return res.status(200).json({ message: "Your password has been changed" });
}
})
})
})
} else {
return res.status(401).json({ error: "Authentication error!" });
}
});
Mongoose User model
const UserSchema = new Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
},
password: {
type: String,
required: true,
},
register_date: {
type: Date,
default: Date.now,
},
resetLink: {
data: String,
default: '',
}
});
As always I appreciate any help at all. Thanks.
EDIT: Here is the returned error -
"error": "Something went wrong...ValidationError: resetLink: Cast to Object failed for value \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVmM2JjN2MwNGVmMmRkNGE2ODkwZDEwYiIsImlhdCI6MTU5Nzc1NDY2MCwiZXhwIjoxNTk3NzU1NTYwfQ.BL8yYsqk2A5hGlNTPa2AxtD_iJ1lWELiCgtpcCkFB6I\" at path \"resetLink\""
My Schema was off. Apologies.
resetLink: {
data: String,
default: '',
}
Should be:
resetLink: {
Type: String,
default: '',
}
I am working through a traversy media tutorial and I've come across an error (in the title) that I'm not familiar with. I've been trying to learn about this but I'm still stumped as to why its appearing and where its coming from. Furthermore, I havent found any direct matches for this issue.
Here is the code in question, the catch at the bottom is returning the error.message.
edit: its also worth noting that I am able to successfuly add users to my database. So, it runs through the try block but also the catch ... so thats a big confusing. The only response I am getting on postman is the server error 500 message from the catch block.
const express = require('express');
const router = express.Router();
const gravatar = require('gravatar');
const bcrypt = require('bcryptjs');
const { check, validationResult } = require('express-validator');
const User = require('../../models/User');
// #route GET api/users
// #desc Test route
// #access Public
router.post(
'/',
[
check('name', 'Name is required').not().isEmpty(),
check('email', 'Please include a valid email').isEmail(),
check(
'password',
'Please enter a password with 6 or more characters'
).isLength({ min: 6 }),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { name, email, password } = req.body;
try {
let user = await User.findOne({ email });
// see if user exists
if (user) {
return res.status(400).json({
errors: [{ msg: 'User already exists' }],
});
}
// get users gravatar
const avatar = gravatar.url(email, {
s: '200',
r: 'pg',
d: 'mm',
});
user = new User({
name,
email,
avatar,
password,
});
// encrypt password
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
// return jsonwebtoken
return res.send('User registered');
} catch (error) {
console.log(error.message);
res.status(500).send('Server error');
}
}
);
module.exports = router;
The User schema
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
avatar: {
type: String,
},
date: {
type: Date,
default: Date.now,
},
});
const User = mongoose.model('user', UserSchema);
module.exports = User;
connection configuration:
const mongoose = require('mongoose');
const config = require('config');
const db = config.get('mongoURI');
const connectDB = async () => {
try {
await mongoose.connect(db, {
useUnifiedTopology: true,
useNewUrlParser: true,
useCreateIndex: true,
});
console.log('Connected to MongoDB');
} catch (err) {
console.error(err.message);
process.exit(1);
}
};
module.exports = connectDB;
When I am sending POST Request for Login, then show this Error. I have used mongoose & MongoDB Atlas.
If I send POST request with valid email & password, it also shows this error.
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent
to the client
But POST request for registration is working well.
User Model
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const userSchema = new Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true
},
balance: Number,
income: Number,
expense: Number,
transactions: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Transaction'
}]
}
})
const User = mongoose.model('User', userSchema)
module.exports = User
User Controller
const registorValidate = require('../validator/registrationValidate')
const User = require('../models/userModel')
const bcrypt = require('bcrypt')
const loginValidate = require('../validator/loginValidator')
const jwt = require('jsonwebtoken')
module.exports = {
login: (req, res) => {
const { email, password } = req.body
let logValidate = loginValidate({ email, password })
if (!logValidate.isValid) {
res.status(400).json(logValidate.error)
return
}
User.findOne({ email })
.then(user => {
if (!user) {
console.log(`${email} not found`)
res.json({
msg: `${email} not found`
})
}
bcrypt.compare(password, user.password, (err, result) => {
if (err) {
res.status(400).json({
msg: 'Error occured'
})
}
if (!result) {
res.status(404).json({
msg: `Password doesn't match`
})
}
let token = jwt.sign({
_id: user._id,
name: user.name,
email: user.email
}, 'SECRET', { expiresIn: '2h' })
res.status(200).json({
msg: 'Login successful',
token: `Bearer ${token}`
})
})
return
})
.catch(err => {
res.status(500).json({
msg: 'Error occured'
})
})
res.end()
},
registration: (req, res) => {
let { name, email, password, confirmPassword } = req.body
let validate = registorValidate({ name, email, password, confirmPassword })
if (!validate.isValid) {
res.status(400).json(validate.error)
} else {
User.findOne({ email })
.then(user => {
if (user) {
res.json({
msg: `${email} is already exist`
})
} else {
bcrypt.hash(password, 11, (err, hash) => {
if (err) {
res.status(500).json({
msg: 'Server error occured'
})
}
let user = new User({
name,
email,
password: hash
})
user.save()
.then(user => {
res.status(201).json({
msg: `Thanks ${name} for your registration`,
user
})
})
.catch(err => {
res.status(500).json({
msg: 'Error occured'
})
})
})
}
})
.catch(err => {
res.status(500).json({
msg: 'Error occured'
})
})
}
}
}
Login Validator
const validator = require('validator')
const validate = user => {
let error = {}
// Email validator
if (!user.email) {
error.email = 'Please provide an Email'
} else if (!validator.isEmail(user.email)) {
error.email = 'Please provide a valid Email'
}
// Password validate
if (!user.password) {
error.password = 'Please provide a password'
} else if (user.password.length < 6) {
error.password = 'Password Must be greater or Equal to 6 characters'
}
return {
error,
isValid: Object.keys(error).length === 0
}
}
module.exports = validate
Thanks.
You don't need to put res.end() because when you called res.json() earlier, it already sent the response.
Please be advised that you should return when you call res.end(), res.send(), 'res.json()' and other operations that send the response, just like what you did with res.status(400).json(logValidate.error)
This should be one of the ways prevent you from getting ERR_HTTP_HEADERS_SENT, but keep in mind that if you have nested callbacks, you should return from the outer scope as well
I've searched this site for days looking through the many different but similar questions on this topic to no avail.
Here's what I'd like to happen. A user signs in and their posts are automatically linked to the users collection. Eventually I'd like to link posts to the profile it was posted to, but i"m not quite there yet. Here's what I've tried so far.
In the User Schema:
const UserSchema = new Schema({
posts: [{
type: Schema.Types.ObjectId,
ref: 'posts'
}],
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
...
});
module.exports = User = mongoose.model('users', UserSchema);
In the Post Schema:
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
text: {
type: String,
required: true
},
name: {
type: String
},
...
});
module.exports = Post = mongoose.model('posts', PostSchema);
In my users api, here's how I'm signing the user in and attempting to populate the user's posts:
const User = require('../../models/User');
router.post('/login', (req, res) => {
const { errors, isValid } = validateLoginInput(req.body);
// Check Validation
if (! isValid) {
return res.status(400).json(errors);
}
const email = req.body.email;
const password = req.body.password;
// Find user by email
User.findOne({ email })
.populate('posts')
.then(user => {
if (! user) {
errors.email = 'User not found';
return res.status(400).json(errors);
}
// Check password
bcrypt.compare(password, user.password).then(isMatch => {
if (isMatch) {
// User Matched
// Create JWT Payload
const payload = {
id: user.id,
firstName: user.firstName,
lastName: user.lastName,
name: user.firstName + ' ' + user.lastName,
avatar: user.avatar,
posts: user.posts
};
jwt.sign(
payload,
keys.secretOrKey,
{ expiresIn: 3600 }, (err, token) => {
res.json({
success: true,
token: 'Bearer ' + token,
payload
});
});
} else {
errors.password = 'Password is incorrect';
return res.status(400).json(errors);
}
});
});
});
In the posts api, here's how the post is being submitted:
router.post('/', passport.authenticate('jwt', { session: false }), (req, res) => {
const { errors, isValid } = validatePostInput(req.body);
if (! isValid) {
// Return errors with 400 status
return res.status(400).json(errors)
}
const newPost = new Post({
text: req.body.text,
name: req.body.name,
avatar: req.body.avatar,
user: req.user.id
});
newPost.save().then(post => res.json(post));
});
Currently, all I'm seeing is an empty array and no errors. I've been spinning my wheels on this one for a couple days now so any help would be appreciated. Thanks!
I think you forgot to save the _id of your new post to the User model so that the populate() can lookup the posts to populate:
newPost.save().then(post => {
User.update({ _id: req.user.id }, { $push: { posts: post._id }}, (err) => {
res.json(post));
});
});