In my User controller, I create a token in which I save this user's id when he login to my application.
exports.findOne = (req, res) => {
User.findOne({
where: {
login: req.body.login,
},
})
.then(user => {
if (user) {
if (bcrypt.compareSync(req.body.password, user.password)) {
const token = jwt.sign(
{
id: user.id, // this is the id I need.
},
env.SECRET_KEY,
{
expiresIn: 129600,
},
);
return res.status(200).json({
message: 'Auth successful',
token,
});
}
...
}
})
.catch(err => {
res.status(400).json({ error: err });
});
};
Now in another controller I would like to read this id and use it for my purpose. How can I get to it?
const loginId = '?'; // here I want to give it to id
Bill.update(
{
available_funds: available_funds - amountMoney,
},
{ where: { id_owner: loginId } },
).then(() => {
res.status(200).send(`ok`);
});
Make a middleware which checks the incoming token before forwarding to your update route.
This middleware should be responsible for validating the incoming token which you pass from the client side code after logging in (storing token in cookies is commonly practiced).
Now in your middleware, you can do something similar to this:
app.use(function(req,res,next) {
JWT.verify(req.cookies['token'], 'YOUR_SECRET', function(err, decodedToken) {
if(err) { /* handle token err */ }
else {
req.userId = decodedToken.id; // Add to req object
next();
}
});
});
Then, finally in your upcoming controller, you can access the id from the request object:
const loginId = req.userId;
Bill.update(
{
available_funds: available_funds - amountMoney,
},
{ where: { id_owner: loginId } },
).then(() => {
res.status(200).send(`ok`);
});
You don't need to add extra codes. To access the userId use this:
req.payload.id
Related
I have a method that deletes products and before it does it check if the user who is trying to delete the product is the user who created it. When i execute it with Insomnia it successfully removes the product but i get an error on the console saying cannot set headers after they are sent to the client.
My method:
exports.deleteProduct = (req, res) => {
const id = req.params.productId;
Product.deleteOne({ _id: id, userId: req.user._id }, () => {
return res.status(401).json("Not authorized");
})
.then(() => {
return res.status(200).json("Product deleted");
})
.catch((err) => {
return res.status(500).json({
error: err,
});
});
};
I'm pretty sure this is happening because I'm chaining a .then() and .catch() after executing it.
I tried to do this but it didn't work because the err parameter that I'm sending to the callback function is null.:
exports.deleteProduct = (req, res) => {
const id = req.params.productId;
Product.deleteOne({ _id: id, userId: req.user._id }, (err) => {
if (err) {
return res.status(401).json("Not authorized");
}
return res.status(200).json("Product deleted");
});
};
When i tried this second approach I always got the 200 status, meanwhile the product didn't delete.
Any idea how to deal with this?
You can try something like this:
Product.deleteOne({ _id: id, userId: req.user._id }, (err, result) => {
if(err) {
return "something"
}
return "something else"
});
or: in async / await way
try {
await Product.deleteOne({ _id: id, userId: req.user._id });
} catch (err) {
// handle error here
}
By the way, why you are passing userId at the deleteOne method?
I am writing an application in Express.js with a separate controller layer and a service layer. Here is my current code:
user.service.js
exports.registerUser = async function (email, password) {
const hash = await bcrypt.hash(password, 10);
const countUser = await User.countDocuments({email: email});
if(countUser > 0) {
throw ({ status: 409, code: 'USER_ALREADY_EXISTS', message: 'This e-mail address is already taken.' });
}
const user = new User({
email: email,
password: hash
});
return await user.save();
};
exports.loginUser = async function (email, password) {
const user = await User.findOne({ email: email });
const countUser = await User.countDocuments({email: email});
if(countUser === 0) {
throw ({ status: 404, code: 'USER_NOT_EXISTS', message: 'E-mail address does not exist.' });
}
const validPassword = await bcrypt.compare(password, user.password);
if (validPassword) {
const token = jwt.sign({ email: user.email, userId: user._id }, process.env.JWT_KEY, { expiresIn: "10s" });
return {
token: token,
expiresIn: 3600,
userId: user._id
}
} else {
throw ({ status: 401, code: 'LOGIN_INVALID', message: 'Invalid authentication credentials.' });
}
};
user.controller.js
exports.userRegister = async function (req, res, next) {
try {
const user = await UserService.registerUser(req.body.email, req.body.password);
res.status(201).json({ data: user });
} catch (e) {
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
}
}
exports.userLogin = async function (req, res, next) {
try {
const user = await UserService.loginUser(req.body.email, req.body.password);
res.status(200).json({ data: user });
} catch (e) {
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
}
}
The code works, but requires some corrections. I have a problem with error handling. I want to handle only some errors. If another error has occurred, the 500 Internal Server Error will be returned.
1) Can I use "throw" object from the service layer? Is this a good practice?
2) How to avoid duplication of this code in each controller:
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
3) Does the code require other corrections? I'm just learning Node.js and I want to write the rest of the application well.
Yes, you can throw errors from service layer, it is good practice to catch errors with try/catch block in controller
I handle this with a custom error middleware, just use a next function in a catch block.
catch (e) {
next(e)
}
Example of error middleware (for more info check docs, fill free to move a middleware to file)
app.use(function (err, req, res, next) {
// err is error from next(e) function
// you can do all error processing here, logging, parsing error messages, etc...
res.status(500).send('Something broke!')
})
From my point of view it looks good. If you looking for some best practice and tools, try eslint (with AirBnb config for example) for linting, dotenv for a environment variables management, also check Node.js Best Practice
i want to give you an example:
this code in your controller
findCar(idCar)
} catch (error) {
switch (error.message) {
case ErrorConstants.ELEMENT_NOT_FOUND('LISTING'): {
return {
response: {
message: ErrorMessages.ELEMENT_NOT_FOUND_MESSAGE('LISTING'),
},
statusCode,
}
}
default: {
return {
response: {
message: ErrorMessages.UNKNOWN_ERROR_MESSAGE,
},
statusCode,
}
}
}
}
and this code in your service
findCar: async listingId => {
try {
if (some condition) {
throw new Error(ErrorConstants.ELEMENT_NOT_FOUND('LISTING'))
}
return { ... }
} catch (error) {
console.error(error.message)
throw new Error(ErrorConstants.UNKNOWN_ERROR)
}
},
controller is going to catch the service's errors
Hello everyone I'd like to get some tips on how to solve my problem.
I got table where I stores user details like:
ID, FIRST_NAME, LAST_NAME EMAIL, PASSWORD, ROLE, DOMAIN
also got table named Aliases where I got
ID, DOMAIN_ID, DOMAIN, DESTINATION
In user table role means admin, moderator, user etc. and Domain for example is aaa.pl, bbb.pl. Also Aliases table got domain like aaa.pl, bbb.pl
I want to select from table only aliases where the domain is the same as assigned to the user.
So user X can sees only aliases Where is the same domain.
backend
This is my controller
public async aliasesListByDomain(req: Request, res: Response): Promise<void> {
const { domain } = req.params;
const aliasesListByDomain = await pool.query('SELECT * FROM virtual_aliases WHERE domain= ?', [domain]);
if (aliasesListByDomain.length > 0) {
res.json(aliasesListByDomain);
} else {
res.status(404).json({ message: "Alias doesn't exists" });
}
}
There is how I authenticate user
router.post('/authenticate', (req, res) => {
User.findOne({
where: {
email: req.body.email,
password: req.body.password
}
})
.then(user => {
if (user) {
let token = jwt.sign(user.dataValues, process.env.SECRET_KEY, {
expiresIn: 1440
})
res.json({ token: token })
} else {
res.send('User does not exist')
}
})
.catch(err => {
res.send('error: ' + err)
})
})
And now I have problem with my frontend.
This is my Service where I get all aliases ( no matter of domain )
getAliases(): Observable<Alias> {
return this.http.get(`${this.API_URI}/aliases`);
}
and there is my component where I get all Aliases
getAliases() {
this.aliasesService.getAliases().subscribe(
res => {
this.alias = res;
console.log(this.alias);
},
err => console.error(err)
);
}
Now how can I select only aliases based on users permission.
I tried something like this:
Service
getAliasesByDomain(domain: string): Observable<Alias> {
return this.http.get(`${this.API_URI}/aliases/${domain}`);
}
This is my auth service
to login
public login(user: TokenPayload): Observable<any> {
const base = this.http.post(`http://localhost:3000/users/authenticate`, user);
const request = base.pipe(
map((data: TokenResponse) => {
console.log(data);
if (data.token) {
this.saveToken(data.token);
}
return data;
})
)
return request;
}
and UserDetails
public getUserDetails(): UserDetails {
const token = this.getToken();
let payload;
if (token) {
payload = token.split('.')[1];
payload = window.atob(payload);
return JSON.parse(payload);
} else {
return null;
}
}
Should I in my component get usertoken then JSON.parse() it and get domain
details from logged user? Then send it to api?
What is best solution for this?
-Edit
I did something like this:
private getToken(): string {
if (!this.token) {
this.token = localStorage.getItem('usertoken');
}
return this.token;
}
getAliasesByDomain(){
const token = this.getToken();
let user;
if (token) {
user = token.split('.')[1];
user = window.atob(user);
user = JSON.parse(user);
console.log('user z from getAliasesByDomain: '+user.domain);
this.aliasesService.getAliasesByDomain(user.domain).subscribe(
res => {
console.log(res);
this.alias = res;
},
err => console.error(err)
);
}
}
I have created an api using nodejs, express and mongodb. I am fetching data now without sending any query. But in my frontend I have an input where the user can search for a recipe. So for example if a user types "Today" i should get response related to today only. How to check that in db and retrieve data?
module.exports = function(app, db) {
app.get("/dates/", (req, res) => {
db
.collection("dates")
.find()
.toArray((err, item) => {
if (err) {
res.send({ error: "An error has occured" });
} else {
res.send(item);
}
});
});
While making the api call , pass the dish as query parameter
For example '/recipes/?dish="Pizza" '
and in the express use the following.
module.exports = function(app, db) {
app.get("/recipes/", (req, res) => {
let queryDish = req.query.dish; // assuming /recipes/?dish="Pizza"
let query = { 'title' : { '$regex' : queryDish, '$options' : 'i' } };
db
.collection("recipes")
.find(query)
.toArray((err, item) => {
if (err) {
res.send({ error: "An error has occured" });
} else {
res.send(item);
}
});
});
I'm trying to validate a user email by decoding a JWT token given as parameter in a GET request.
In case the token is valid and the User is not verified yet, I want to update isVerified column to true.
Why is my Promise always rejected ?
const jwt = require('jsonwebtoken');
const request = require('request');
const models = require('../models');
/**
* GET /verify-email/:token
*/
exports.verifyEmail = function(req, res) {
jwt.verify(req.params.token, process.env.TOKEN_SECRET, function(err, decoded) {
if (err) {
res.send({
msg: 'Token is invalid or has expired'
})
}
models.User.findOne({
where: {
email: decoded.email,
id: decoded.id
}
}).then(record => {
if (!record) {
res.send({
msg: 'User not found'
})
} else if (record.isVerified) {
res.send({
msg: 'User already verified'
})
} else {
console.log('user mail ' + record.email + ' will be verified in db')
record.update({
isVerified: true,
})
.then(() => res.status(200).send({
msg: 'User has been verified'
}))
.catch((error) => res.status(400).send(error));
}
})
});
}
Problem solved, I had a beforeValidation hook in my model :
if (!user.changed('password')) {
return sequelize.Promise.reject("not modified");
}