Should a new Collection be created upon Model.create() - express

Am working with mongoose and have two models. The User model and the Service model, when a user logs in the method will findOne() user if one exists or create() a new user based on the what's passed in from req.body.
My Service Schema is like this:
const serviceSchema = new mongoose.Schema({
name: {
type: String,
default: 'contentEditor'
},
display: {
type: String,
default: 'Content Editor'
},
accessLevel: {
type: Number,
min: 0,
max: 4,
default: 4
}
});
My User Schema is a bit bigger, I've removed some of the field/value pairs but the part where I embed the Service Schema looks like this:
const userSchema = new mongoose.Schema(
{
email: {
type: String,
required: [true, 'Must have a email address'],
trim: true,
unique: true,
},
firstName: {
type: String,
},
lastName: {
type: String,
},
services: {
type: [serviceSchema],
ref: 'Services',
default: [serviceSchema],
},
},
);
When I hit the /api/v1/login endpoint a new user will be created with the Service document correctly but within the Mongoose database only a User collection exists. How do I make it so that both a Users collection and Services collection are created?
Edit: Below is the function that I create/find the user with when they login. When an existing User is found, by their email it will return that user if the user is not found then it will create a new one...
Both behaviours are as expected including adding the Services to the newly created User. What isn't expected is that only ONE collection is added to the DB.
const login = catchAsync(async ({ body: { email, password } }, res, next) => {
if (!email || !password) {
return next(new AppError('Please provide email and password', 400));
}
const { Success } = await webApi(email, password);
const mongoUser = await User.findOne({ email });
if (Success && mongoUser) {
return createSendtoken(mongoUser, 200, res);
}
if (Success && !mongoUser) {
const newUser = await User.create({ email });
return createSendtoken(newUser, 201, res);
}
return next(new AppError('User not found', 404));
});

Make sure you are making the serviceSchema a mongoose model.
const Services = mongoose.model('Service', serviceSchema)
You also have to save it using mongooses model.save() function

Related

How to modify currentUserStore in mobx-state-tree

I'm trying to wrap my head around mobx-state-tree and whipped up a simple currentUserStore to hold some data for a logged in user and allow login/logout:
import { types } from "mobx-state-tree";
import { client } from '../../../helpers/client';
const User = types
.model("User", {
name: types.string,
email: types.string,
type: types.string,
token: types.string,
roles: types.array(types.string),
})
export const CurrentUserStore = types
.model("CurrentUserStore", {
user: types.optional(types.maybeNull(User), () => null)
})
.actions((currentUserStore) => ({
async login(login, password) {
const result = await client.post(`auth`, {
email: login,
password,
});
const { name, email, type, token, roles } = result
currentUserStore.user = User.create({ name, email, type, token, roles })
localStorage.setItem('authUser', JSON.stringify(result));
},
logout() {
currentUserStore.user = null
localStorage.removeItem('authUser')
},
}));
When calling the login function, I get the error Cannot modify 'CurrentUserStore#/currentUserStore', the object is protected and can only be modified by using an action.. There's something I'm missing here, but not exactly sure why I shouldn't be able to do something like this after reading through example where the store is modified directly in an action like this.
Once I moved the modification out of an async func, it worked. Just changed to this:
async login(login, password) {
const result = await client.post(`auth`, {
email: login,
password,
});
this.setUser(User.create(result))
localStorage.setItem('authUser', JSON.stringify(result));
},
logout() {
currentUserStore.user = null
localStorage.removeItem('authUser')
},
setUser(user) {
self.user = user
}

Invalid credentials postman

I hard coded the users in my seed file
I can view the the users in the database
But when i try to test the details in the database i get an error "invalid credentials"
I don't know why it says that when the user is already in the database
This is the seeds file
const noPassword = "$2a$12$ZQwXBTq7UMgmugpy5zz9SOdG4JvEa3Bj5MofQl9fIMFb1wTSGU9.C"; exports.seed = function (knex) { // Deletes ALL existing entries return knex("users") .truncate() .then(function () { // Inserts seed entries return knex("users").insert([ { email: "danielAsuquo15#gmail.com", first_name: "Daniel", last_name: "Asuquo", password:noPassword, }, { email: "josiahdamiwilliams#gmail.com", first_name: "josiah", last_name: "williams", password:noPassword, }, ]); }); };

User authentication error using express not working

So I'm having trouble authenticating a user login using express for the backend. If I do a simple res.send I could get a response in postman. but if I do a check if the user and password check and generate a token if says error 401 invalid usernames and password. mind the tokens work for register and update profiles. I also attached the user schema.
Simple Approach
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
res.send({ email, password })
})
When I try to check and generate a token
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
const user = await User.findOne({ email })
if (user && (await user.matchPassword(password))) {
res.json({
_id: user._id,
name: user.name,
email: user.email,
isAdmin: user.isAdmin,
token: generateToken(user._id),
})
} else {
res.status(401)
throw new Error('Invalid Email or password')
}
})
Also here's my user schema using mongoose and added a function to check password since in the database it's encrypted using bcrypt.js
import mongoose from 'mongoose'
import bcrypt from 'bcryptjs'
const userSchema = mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
required: true,
default: false,
},
},
{
timestamps: true,
}
)
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password)
}
const User = mongoose.model('User', userSchema)
export default User
You see, your code is absolutely correct, but only if you have saved the user correctly in your model at the first. In addition, the password used during storage is hashed in the same way and from the same module.
Now you have to change your checking terms because there may not really be a user with this email in the database to match your password and return true value.
import mongoose from 'mongoose'
import bcrypt from 'bcryptjs'
const userSchema = mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
required: true,
default: false,
},
},
{
timestamps: true,
}
)
userSchema.pre('save', function (next) {
if (this.password) {
bcrypt.hash(this.password, bcrypt.genSaltSync(15), (err, hash) => {
if (err) console.log(err);
this.password = hash;
next();
});
}
}
userSchema.methods.Save = async (data) => {
let model=new User(data)
await model.save();
}
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password)
}
const User = mongoose.model('User', userSchema)
export default User
and update this code:
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
const user = await User.findOne({ email })
if (user) {
if(await user.matchPassword(password)) {
res.json({
_id: user._id,
name: user.name,
email: user.email,
isAdmin: user.isAdmin,
token: generateToken(user._id),
})
} else {
res.status(401)
throw new Error('Invalid password')
}
} else {
res.status(401)
throw new Error('Invalid Email')
}
})
Now you know which part of the job is the problem. No user or no wrist password

Mongoose - Promise then() not triggered on save

I'm using ExpressJS + Mongoose + TypeScript. I have created a schema as below
const schema: Schema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
email: {
type: String,
required: true
},
gender: {
type: Boolean,
required: true
},
mobile: {
type: String,
required: false
},
password: {
type: String,
required: true
},
activationKey: {
type: String,
required: false
},
isActivated: {
type: Boolean,
required: true
},
imagePath: {
type: String,
required: false
},
});
I'm using the below code to save (insert) a new entry
MongoClient.connect('mongodb://MyUsername:MyPassword#ds135757.mlab.com:35777/my-db-us', (err, db) => {
if (err) {
console.log('mongoose error: ' + err);
} else {
console.log('mongoose db: ' + db);
const user = new User({
firstName: 'ee',
lastName: 'ee',
email: 'eee#fff.com',
gender: true,
mobile: '333',
password: '333',
isActivated: true
});
user.save().then((someValue) => {
console.log('saved');
}).catch((err) => {
console.log('not saved:' + err);
});
}
});
Console Messages
When correct data is sent. saved isn't printed
mongoose db: [object Object]
When incorrect data is sent
mongoose db: [object Object]
not saved:ValidationError: gender: Path `gender` is required.
When unable to connect to MongoDB if internet is disconnected
mongoose error: MongoError: failed to connect to server [ds135777.mlab.com:35777] on first connect [MongoError: connection 0 to ds135777.mlab.com:35777 timed out]
Module versions
"mongodb": "^2.2.34",
"#types/mongodb": "^3.0.5",
"mongoose": "^5.0.4",
"#types/mongoose": "^5.0.2",
Re-written example (Solution)
const mongoose = require('mongoose');
mongoose.connect('mongodb://MyUsername:MyPassword#ds135757.mlab.com:35777/my-db-us', function(err) {
if (err) {
console.log('err: ' + err);
} else {
console.log('connected');
const user = new User({
firstName: 'ee',
lastName: 'ee',
email: 'eee#fff.com',
gender: true,
mobile: '333',
password: '333',
isActivated: true
});
user.save().then((someValue) => {
console.log('saved');
}).catch((err) => {
console.log('not saved:' + err);
});
}
});
The messages printed are
connected
saved
I am guessing a funny problem with your code(Just guessing looking at your variable name convention ;-)).
You are saying you use mongoose but you connect using native MongoClient (again guessing based on variable name) you must be connecting using mongoose
const mongoose = require('mongoose');
then just replace your MongoClient with mongoose
then doesn't print anything as nothing is happening there and catch prints error as validation happens before connection
The reason is you are using native client for connecting and using mongoose for modelling which is not the correct way. Do connect to the Mongo DB URI using mongoose and save schema.
You are creating a standard MongoClient connection, this will not effect mongoose models. The connection that created the User model must be open for the various database actions to work. Assuming that the User model was created using the global mongoose object (e.g. mongoose.model('User', userSchema)) then you must call mongoose.connect() to activate the model's connection. If the User model was created via a non-global connection (e.g. mongoose.createConnection()) then you should ensure that the connection is in the open state.

How can I override builtin login method in Loopback?

I've created a new User model, based on builtin one. I'm trying this:
module.exports = function(TiUser) {
TiUser.on('dataSourceAttached', function(obj) {
var login = TiUser.login;
TiUser.login = function(credentials, include, cb) {
var result = login.apply(this, credentials);
// Do my stuff
cb(null, my_data);
};
});
};
But I can't get it working... What is wrong? or how could this be done right?
Thanks
You may want to consider adding an afterRemote() hook to login(). Now you can achieve to add role( using Role model ) to user. For example:
TiUser.afterRemote('login', function(ctx, next) {
//add role to the user.
next();
});
At the end I've created a new method instead of overriding a current one:
module.exports = function(TiUser) {
TiUser.auth = function(credentials, include, fn) {
var self = this;
self.login(credentials, include, function(err, token) {
authInfo = {
token: token
};
fn(err, authInfo);
});
};
TiUser.remoteMethod(
'auth',
{
description: 'Login method with Role data information embedded in return',
accepts: [
{arg: 'credentials', type: 'object', required: true, http: {source: 'body'}},
{arg: 'include', type: ['string'], http: {source: 'query' },
description: 'Related objects to include in the response. ' +
'See the description of return value for more details.'}
],
returns: {
arg: 'accessToken', type: 'object', root: true,
description: 'User Model'
},
http: {verb: 'post'}
}
);
};