Node.js - Checking for specific CharSet - node.js

currently I'm trying to create a new user in my mongodb database. The user also has to submit his email.
My model looks like this:
const userSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
email: {
type: String,
required: true,
unique: true,
match: /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/
}
This is just the important part of it because there I state, that mail has to match this specific charset.
Now my problem: If I update the email with the PATCH method
router.patch('/user', checkAuth, async (req, res, next) => {
try {
const id = req.body._id;
const update = req.body;
const options = { new: true };
const result = await User.findByIdAndUpdate(id, update, options);
if (!result) {
throw createError(404, 'User does not exist');
}
res.send(result);
} catch (error) {
console.log(error.message);
if (error instanceof mongoose.CastError) {
return next(createError(400, 'Invalid User Id'));
}
next(error);
}
})
The user can enter whatever he wants into the email field without it being checked for the charset. Do you have any ideas how to fix it?
Thanks in advance, Tom

If you are asking for pattern-matching in HTML, then you can validate your email address using this <input> tag:
<input pattern="/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/" required />
Check ValidatorJS to validate at an API level.

Related

How do I write truly custom error messages and codes for Mongoose validation?

I'm trying to follow the MVC architectural pattern and do all of my validation in my Mongoose model, rather than my controller.
I'm wondering how I can set error codes and truly custom error messages in my model (I.E. without the part that mongoose adds to the beginning of the message.)
At the moment my error message for the name field is: "message": "User validation failed: email: Please enter a valid email address", where it should be "Please enter a valid email address".
The response code from the server was 200 until I changed it in my errorHandlerMiddleware file, which is not ideal as it should be a 400 not the general 500.
So, somebody please help me to set the status code in my model and also make a custom error message.
Many thanks in advance!
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const validator = require("validator");
const Schema = mongoose.Schema;
const UserSchema = new Schema(
{
name: {
type: String,
required: [true, "Please add a name"],
minLength: [3, "Name must be at least 3 characters"],
},
email: {
type: String,
required: [true, "Please add an email address"],
unique: [true, "It looks like you already have an account!"],
validate: {
validator: (value) => {
if (!validator.isEmail(value)) {
throw new Error("Please enter a valid email address");
}
},
},
},
password: {
type: String,
required: [true, "Please add a password"],
},
tokens: [
{
token: {
type: String,
required: true,
},
},
],
},
{ timestamps: true }
);
UserSchema.methods.toJSON = function () {
const user = this;
const userObject = user.toObject();
delete userObject.password;
delete userObject.tokens;
return userObject;
};
UserSchema.methods.generateAuthToken = async function () {
const user = this;
const token = jwt.sign({ _id: user._id.toString() }, process.env.JWT_SECRET, {
expiresIn: "7 days",
});
user.tokens = user.tokens.concat({ token });
await user.save();
return token;
};
UserSchema.statics.findByCredentials = async (email, password) => {
const user = await User.findOne({ email });
if (!user) {
statusCode(401);
throw new Error("Unable to login");
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
statusCode(401);
throw new Error("Unable to login");
}
return user;
};
UserSchema.pre("save", function (next) {
if (this.password.length < 6) {
throw new Error("Password must be at least 6 characters");
}
if (!this.isModified("password")) {
return next();
}
this.password = bcrypt.hashSync(this.password, 10);
return next();
});
module.exports = User = mongoose.model("User", UserSchema);
i need a real custom error code and message from mongoose
I decided to catch the errors in the try/catch block on the controller, as so:
try {
await user.save();
} catch (err) {
// Error handling for duplicate email address
if (err.code === 11000) {
return res.status(400).send("It looks like you already have an account.");
}
// Error handling for misc validation errors
if (err.name === "ValidationError") {
res.status(400);
return res.send(Object.values(err.errors)[0].message);
}
}

How to update user lastlogin time in react

i want to display last login time of user in table column after they logged in. i tried with some code in backend with nodejs and mongoose but sure it is not the right way. Can anyone help me how to handle it from backend and FE as well?
Here is Schema of user:
const userSchema = new Schema(
{
name: { type: String },
email: { type: String, unique: true},
password: { type: String },
status: {type: String, enum:["Active", "Blocked"], default:"Active"},
token: { type: String },
lastLogin: { type: Date, default: Date.now()},
},
{ timestamps: true }
);
Route:
userRouter.post("/login", async (req, res, next) => {
try {
const { email, password } = req.body;
if (!(email && password)) res.status(204).send({ msg: "All fields are required!" });
const user = await UsersModel.findOne({ email });
console.log(user);
if (user && (await bcrypt.compare(password, user.password))) {
const accessToken = await JWTAuthenticate(user);
user.token = accessToken;
user.lastLogin = Date.now()
res.status(200).send(user);
} else {
res.status(404).send({ msg: "User with this email not found!" });
}
UsersModel.findOneAndUpdate({lastLogin: Date.now()}, (err, data) => {
if(err) console.log(err);
else console.log("Successfully updated the lastLogin", data);
})
} catch (error) {
next(error);
}
});
You just have to call await user.save() after setting lastLogin (and remove findOneAndUpdate call)
or
actually query for the user in your findOneAndUpdate call. Currently you are querying for a 'lastLogin' field with the value now() which will never match. Adjust this call as following:
UsersModel.findByIdAndUpdate(user._id, {lastLogin: Date.now()}, (err, data) => {
if(err) console.log(err);
else console.log("Successfully updated the lastLogin", data);
})
Just a hint: JWTs are used for storing the session state on client side. You don't need to actually store these Tokens in your database. You just have to validate it on incoming request.

Pass a Button value and Update the database

Working on a personal project, one of the functions of the project is to update the user status on what event they are participating.
i wanted to submit a value using a button
<form action="/users/fooddrivebanner" method="POST"><button name="fooddrive" type="submit" value="fooddrive" id="fooddrive">Participate</button></form>
then pass the value to my route and save it inside my database
router.post('/fooddrivebanner', (req,res)=>{
const { fooddrive } = req.body;
const _id = ObjectId(req.session.passport.user._id);
User.findOne({ _id: _id }).then((user)=>{
if (!user) {
req.flash("error_msg", "user not found");
res.redirect("/fooddrivebanner");
}
if (typeof eventparticpating !== "undefined") {
user.eventparticpating = 'fooddrive';
}
user.save(function (err, resolve) {
if(err)
console.log('db error', err)
// saved!
});
})
.catch((err) => console.log(err));
Here is the User model
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
eventparticpating: {
type: String,
default: 'None At The Moment'
}
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
It showed a console error
TypeError: Cannot set property 'eventparticpating' of null
UPDATE
Edit 1:
I followed Mr Gambino instructions, error Gone yet cannot update the database, how would i be able to adjust and find my user?
Instead of saving within the findOne function,you can do this:
router.post('/fooddrivebanner', async (req,res) => {
const { fooddrive } = req.body;
const _id = ObjectId(req.session.passport.user._id);
await User.findOne({ _id: _id }, (error, user) => {
if (error) {
req.flash("error_msg", "user not found");
res.redirect("/fooddrivebanner");
}
}).updateOne({ eventparticpating: "foodrive" });
});
I hope that answers your question

Angular Put Method not updating data

i'm newbie with MEAN stack and i have problem when updating data with put Method, i have tested using postman and it works fine, but when i use it on angular its not working. nothing error appear, this what i got in console after updating data
[is give success update][1] but nothing change on data that i updated. i don't have problem with create and delete method, just update method that had problem
here's my code
update.service
updateData(id, data): Observable<any>{
let url = `${this.baseUri}/update/${id}`;
return this.http.put(url,data, { headers : this.headers }).pipe(
catchError(this.errorManagement)
)}
update.component
OnSubmit(id){
let record = this.updateForm.value
if(!record){
this.notif.showError('can\'t do update data','Update data Error')
return false;
}else{
return this.motorService.updateData(id, record).subscribe(res=>{
console.log(record)
},(error) => {
console.log(error)
});
}}}
update route
listDataRoute.route('/update/:id').put((req,res,next)=>{
listData.findByIdAndUpdate(req.params.id,{
$set : req.body
},{new: true, useFindAndModify: false},(error, data)=>{
if (data.n > 0) {
res.status(200).json({
message: 'profile updated'
});
} else {
res.status(401).json({
message: 'not authorized'
});
}
})
.catch(error => {
res.status(500).json({
message: 'updating profile failed'
});
})
})
any idea what i do wrong ? i'm already stuck like 5 hours with this error, thank you
[1]: https://i.stack.imgur.com/ryR8g.png
update : i got error 401 after adding some code in "update route", still don't know how to solve this error
id like to share my way of updating data with the mean stack. its a lil different from your code but it works and is tested with jest. id like to share my example with updating a userprofile
i create a userModel in my backend user.js
// user.js
const mongoose = require("mongoose");
const uniqueValidator = require("mongoose-unique-validator");
mongoose.set('useCreateIndex', true);
const userSchema = mongoose.Schema({
id: String,
email: { type: String, unique: true },
username: { type: String, unique: true },
password: { type: String, required: true },
currentLocation: String,
age: String,
});
userSchema.plugin(uniqueValidator);
module.exports = mongoose.model('User', userSchema);
then i write the update method in my profileController.js
// profileController.js
exports.update = (req, res, next) => {
const user = new User({
_id: req.body.id,
email: req.body.email,
username: req.body.username,
currentLocation: req.body.currentLocation,
age: req.body.age,
});
User.updateOne({ _id: req.params.id }, user).then(result => {
if (result.n > 0) {
res.status(200).json({
message: 'profile updated'
});
} else {
res.status(401).json({
message: 'not authorized'
});
}
})
.catch(error => {
res.status(500).json({
message: 'updating profile failed'
});
});
}
in my profilerService.ts (frontend) i also define my userclass with the same properties as the user.js model in my backend
// profile.service.ts
export class User {
id: string;
email: string;
username: string;
password: string;
currentLocation: string;
age: string;
constructor() {
this.id = '';
this.email = '';
this.username = '';
this.password = '';
this.currentLocation = '';
this.age = '';
}
}
i also add the update service call into my service file
// profile.service.ts
updateProfile(user: User, userId: string) {
return this.http.put(environment.backendUrl + '/api/user/' + userId, user);
}
then i can call the service function from my component where i want to update my user
// profile-page.component.ts
updateProfile(user: User) {
this.profileService.updateProfile(user, user.id).subscribe(response => {
console.log('update successfull');
});
}
i hope my code snippets are able to help you! let me know if anything still needs some clarification. sry if this isnt the most perfect answer as this is one of my first answers on SO :)
401 means Authentication error.
Is post working?

mongoose Model.create function returns undefined

The above query returns a 200 when I try to create a User, but whenever I log into MongoDB there is no collections created. Can anyone help ?
//user model
const userSchema = mongoose.Schema({
name: {
type : String,
required : true,
trim : true
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
validate: value => {
if(!validator.isEmail(value)){
throw new Error({error : 'Invalid email address'})
}
}
},
password: {
type: String,
required: true,
minLength: 5
},
// a user can have multiple jobs
jobs : [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Job'
}],
tokens: [{
token: {
type: String,
required: true
}
}]
})
const User = mongoose.model('User', userSchema)
module.exports = User
// user functions written
createUser(name, email, password){
return User.create({name: name, email: email, password : password}, (err, docs) => {
if(err){
throw err.message;
}
});
}
//routes.js
// user create
router.post('/users', async(req, res) => {
try{
const {name, email, password } = req.body
const user = userManager.createUser(name, email, password); [1]
res.status(200).json(user)
}
catch(error) {
res.status(400).send({error : error.message})
}
})
The line[1] returns undefined. Why ?
note : all module requirements are fulfilled
After you create the schema you need to create a Model FROM that schema.
Example from MDN:
// Define schema
var Schema = mongoose.Schema;
var SomeModelSchema = new Schema({
a_string: String,
a_date: Date
});
// Compile model from schema
var SomeModel = mongoose.model('SomeModel', SomeModelSchema );
Now after you create the model you can use SomeModel.create
EDIT:
line[1] will always return undefined because you are using callbacks and only way to get value out of callback is either push another callback(I would really discourage that). But best way is to use Promises now mongoose by default supports `Promises. So, basically for promises it will be,
// user functions written
async function createUser(name, email, password){
try {
return await User.create({ name: name, email: email, password: password });
} catch (err) {
throw err.message;
}
}
In the router adda await:
const user = await userManager.createUser(name, email, password);
The problem is you call an asynchronous function synchronously. It returned undefined because the function hasn't been resolved yet.
A solution could be to use promises or async/await.
Example:
async createUser(name, email, password) {
const createdUser = await User.create({name,email,password});
return creaatedUser;
}
Something I ran into was you need to pass in an empty object if your not setting any fields - i.e.
Good: Model.create({})
Bad: Model.create()

Resources