From 357e69459717e6c77b86b37719c4d04285e4b564 Mon Sep 17 00:00:00 2001 From: Mark Moffat Date: Sat, 9 Nov 2019 14:51:48 +1030 Subject: [PATCH] Refactoring customer route --- routes/customer.js | 359 ++++++++++++++++++++-------------------- test/specs/customers.js | 16 -- 2 files changed, 177 insertions(+), 198 deletions(-) diff --git a/routes/customer.js b/routes/customer.js index 2049963..a0e4964 100644 --- a/routes/customer.js +++ b/routes/customer.js @@ -7,7 +7,7 @@ const common = require('../lib/common'); const { restrict } = require('../lib/auth'); // insert a customer -router.post('/customer/create', (req, res) => { +router.post('/customer/create', async (req, res) => { const db = req.app.db; const doc = { @@ -25,93 +25,90 @@ router.post('/customer/create', (req, res) => { }; // check for existing customer - db.customers.findOne({ email: req.body.email }, (err, customer) => { - if(customer){ - res.status(400).json({ - err: 'A customer already exists with that email address' - }); - return; - } + const customer = await db.customers.findOne({ email: req.body.email }); + if(customer){ + res.status(400).json({ + err: 'A customer already exists with that email address' + }); + return; + } // email is ok to be used. - db.customers.insertOne(doc, (err, newCustomer) => { - if(err){ - if(newCustomer){ - console.error(colors.red('Failed to insert customer: ' + err)); - res.status(400).json({ - err: 'A customer already exists with that email address' - }); - return; - } - console.error(colors.red('Failed to insert customer: ' + err)); - res.status(400).json({ - err: 'Customer creation failed.' - }); - return; - } - + try{ + await db.customers.insertOne(doc, (err, newCustomer) => { // Customer creation successful - req.session.customer = newCustomer.ops[0]; + req.session.customer = newCustomer.insertedId; res.status(200).json({ message: 'Successfully logged in', customer: newCustomer }); }); - }); + }catch(ex){ + console.error(colors.red('Failed to insert customer: ', ex)); + res.status(400).json({ + err: 'Customer creation failed.' + }); + } }); // render the customer view -router.get('/admin/customer/view/:id?', restrict, (req, res) => { +router.get('/admin/customer/view/:id?', restrict, async (req, res) => { const db = req.app.db; - db.customers.findOne({ _id: common.getId(req.params.id) }, (err, customer) => { - if(err){ - console.info(err.stack); - } + const customer = await db.customers.findOne({ _id: common.getId(req.params.id) }); - // If API request, return json + if(!customer){ + // If API request, return json if(req.apiAuthenticated){ - return res.status(200).json(customer); + return res.status(400).json({ message: 'Customer not found' }); } + req.session.message = 'Customer not found'; + req.session.message_type = 'danger'; + return res.redirect('/admin/customers'); + } - return res.render('customer', { - title: 'View customer', - result: customer, - admin: true, - session: req.session, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - config: req.app.config, - editor: true, - helpers: req.handlebars.helpers - }); + // If API request, return json + if(req.apiAuthenticated){ + return res.status(200).json(customer); + } + + return res.render('customer', { + title: 'View customer', + result: customer, + admin: true, + session: req.session, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + config: req.app.config, + editor: true, + helpers: req.handlebars.helpers }); }); // customers list -router.get('/admin/customers', restrict, (req, res) => { +router.get('/admin/customers', restrict, async (req, res) => { const db = req.app.db; - db.customers.find({}).limit(20).sort({ created: -1 }).toArray((err, customers) => { - // If API request, return json - if(req.apiAuthenticated){ - return res.status(200).json(customers); - } + const customers = await db.customers.find({}).limit(20).sort({ created: -1 }).toArray(); - return res.render('customers', { - title: 'Customers - List', - admin: true, - customers: customers, - session: req.session, - helpers: req.handlebars.helpers, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - config: req.app.config - }); + // If API request, return json + if(req.apiAuthenticated){ + return res.status(200).json(customers); + } + + return res.render('customers', { + title: 'Customers - List', + admin: true, + customers: customers, + session: req.session, + helpers: req.handlebars.helpers, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + config: req.app.config }); }); // Filtered customers list -router.get('/admin/customers/filter/:search', restrict, (req, res, next) => { +router.get('/admin/customers/filter/:search', restrict, async (req, res, next) => { const db = req.app.db; const searchTerm = req.params.search; const customersIndex = req.app.customersIndex; @@ -122,29 +119,25 @@ router.get('/admin/customers/filter/:search', restrict, (req, res, next) => { }); // we search on the lunr indexes - db.customers.find({ _id: { $in: lunrIdArray } }).sort({ created: -1 }).toArray((err, customers) => { - if(err){ - console.error(colors.red('Error searching', err)); - } + const customers = await db.customers.find({ _id: { $in: lunrIdArray } }).sort({ created: -1 }).toArray(); - // If API request, return json - if(req.apiAuthenticated){ - return res.status(200).json({ - customers - }); - } - - return res.render('customers', { - title: 'Customer results', - customers: customers, - admin: true, - config: req.app.config, - session: req.session, - searchTerm: searchTerm, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - helpers: req.handlebars.helpers + // If API request, return json + if(req.apiAuthenticated){ + return res.status(200).json({ + customers }); + } + + return res.render('customers', { + title: 'Customer results', + customers: customers, + admin: true, + config: req.app.config, + session: req.session, + searchTerm: searchTerm, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + helpers: req.handlebars.helpers }); }); @@ -152,41 +145,35 @@ router.get('/admin/customers/filter/:search', restrict, (req, res, next) => { router.post('/customer/login_action', async (req, res) => { const db = req.app.db; - db.customers.findOne({email: common.mongoSanitize(req.body.loginEmail)}, (err, customer) => { // eslint-disable-line - if(err){ - // An error accurred - return res.status(400).json({ + const customer = await db.customers.findOne({ email: common.mongoSanitize(req.body.loginEmail) }); + // check if customer exists with that email + if(customer === undefined || customer === null){ + res.status(400).json({ + message: 'A customer with that email does not exist.' + }); + return; + } + // we have a customer under that email so we compare the password + bcrypt.compare(req.body.loginPassword, customer.password) + .then((result) => { + if(!result){ + // password is not correct + res.status(400).json({ message: 'Access denied. Check password and try again.' }); + return; } - // check if customer exists with that email - if(customer === undefined || customer === null){ - return res.status(400).json({ - message: 'A customer with that email does not exist.' - }); - } - // we have a customer under that email so we compare the password - bcrypt.compare(req.body.loginPassword, customer.password) - .then((result) => { - if(!result){ - // password is not correct - return res.status(400).json({ - message: 'Access denied. Check password and try again.' - }); - } - - // Customer login successful - req.session.customer = customer; - return res.status(200).json({ - message: 'Successfully logged in', - customer: customer - }); - }) - .catch((err) => { - return res.status(400).json({ - message: 'Access denied. Check password and try again.' - }); + // Customer login successful + req.session.customer = customer; + res.status(200).json({ + message: 'Successfully logged in', + customer: customer + }); + }) + .catch((err) => { + res.status(400).json({ + message: 'Access denied. Check password and try again.' }); }); }); @@ -206,97 +193,105 @@ router.get('/customer/forgotten', (req, res) => { }); // forgotten password -router.post('/customer/forgotten_action', (req, res) => { +router.post('/customer/forgotten_action', async (req, res) => { const db = req.app.db; const config = req.app.config; const passwordToken = randtoken.generate(30); // find the user - db.customers.findOne({ email: req.body.email }, (err, customer) => { - // if we have a customer, set a token, expiry and email it - if(customer){ - const tokenExpiry = Date.now() + 3600000; - db.customers.updateOne({ email: req.body.email }, { $set: { resetToken: passwordToken, resetTokenExpiry: tokenExpiry } }, { multi: false }, (err, numReplaced) => { - // send forgotten password email - const mailOpts = { - to: req.body.email, - subject: 'Forgotten password request', - body: `You are receiving this because you (or someone else) have requested the reset of the password for your user account.\n\n - Please click on the following link, or paste this into your browser to complete the process:\n\n - ${config.baseUrl}/customer/reset/${passwordToken}\n\n - If you did not request this, please ignore this email and your password will remain unchanged.\n` - }; + const customer = await db.customers.findOne({ email: req.body.email }); + // if we have a customer, set a token, expiry and email it + if(!customer){ + req.session.message = 'Account does not exist'; + req.session.message_type = 'danger'; + res.redirect('/customer/forgotten'); + return; + } + try{ + const tokenExpiry = Date.now() + 3600000; + await db.customers.updateOne({ email: req.body.email }, { $set: { resetToken: passwordToken, resetTokenExpiry: tokenExpiry } }, { multi: false }); + // send forgotten password email + const mailOpts = { + to: req.body.email, + subject: 'Forgotten password request', + body: `You are receiving this because you (or someone else) have requested the reset of the password for your user account.\n\n + Please click on the following link, or paste this into your browser to complete the process:\n\n + ${config.baseUrl}/customer/reset/${passwordToken}\n\n + If you did not request this, please ignore this email and your password will remain unchanged.\n` + }; - // send the email with token to the user - // TODO: Should fix this to properly handle result - common.sendEmail(mailOpts.to, mailOpts.subject, mailOpts.body); - req.session.message = 'An email has been sent to ' + req.body.email + ' with further instructions'; - req.session.message_type = 'success'; - return res.redirect('/customer/forgotten'); - }); - }else{ - req.session.message = 'Account does not exist'; - res.redirect('/customer/forgotten'); - } - }); + // send the email with token to the user + // TODO: Should fix this to properly handle result + common.sendEmail(mailOpts.to, mailOpts.subject, mailOpts.body); + req.session.message = 'An email has been sent to ' + req.body.email + ' with further instructions'; + req.session.message_type = 'success'; + res.redirect('/customer/forgotten'); + }catch(ex){ + req.session.message = 'Account does not exist'; + req.session.message_type = 'danger'; + res.redirect('/customer/forgotten'); + } }); // reset password form -router.get('/customer/reset/:token', (req, res) => { +router.get('/customer/reset/:token', async (req, res) => { const db = req.app.db; // Find the customer using the token - db.customers.findOne({ resetToken: req.params.token, resetTokenExpiry: { $gt: Date.now() } }, (err, customer) => { - if(!customer){ - req.session.message = 'Password reset token is invalid or has expired'; - req.session.message_type = 'danger'; - res.redirect('/forgot'); - return; - } + const customer = await db.customers.findOne({ resetToken: req.params.token, resetTokenExpiry: { $gt: Date.now() } }); + if(!customer){ + req.session.message = 'Password reset token is invalid or has expired'; + req.session.message_type = 'danger'; + res.redirect('/forgot'); + return; + } - // show the password reset form - res.render('reset', { - title: 'Reset password', - token: req.params.token, - route: 'customer', - config: req.app.config, - message: common.clearSessionValue(req.session, 'message'), - message_type: common.clearSessionValue(req.session, 'message_type'), - show_footer: 'show_footer', - helpers: req.handlebars.helpers - }); + // show the password reset form + res.render('reset', { + title: 'Reset password', + token: req.params.token, + route: 'customer', + config: req.app.config, + message: common.clearSessionValue(req.session, 'message'), + message_type: common.clearSessionValue(req.session, 'message_type'), + show_footer: 'show_footer', + helpers: req.handlebars.helpers }); }); // reset password action -router.post('/customer/reset/:token', (req, res) => { +router.post('/customer/reset/:token', async (req, res) => { const db = req.app.db; // get the customer - db.customers.findOne({ resetToken: req.params.token, resetTokenExpiry: { $gt: Date.now() } }, (err, customer) => { - if(!customer){ - req.session.message = 'Password reset token is invalid or has expired'; - req.session.message_type = 'danger'; - return res.redirect('/forgot'); - } + const customer = await db.customers.findOne({ resetToken: req.params.token, resetTokenExpiry: { $gt: Date.now() } }); + if(!customer){ + req.session.message = 'Password reset token is invalid or has expired'; + req.session.message_type = 'danger'; + return res.redirect('/forgot'); + } - // update the password and remove the token - const newPassword = bcrypt.hashSync(req.body.password, 10); - db.customers.updateOne({ email: customer.email }, { $set: { password: newPassword, resetToken: undefined, resetTokenExpiry: undefined } }, { multi: false }, (err, numReplaced) => { - const mailOpts = { - to: customer.email, - subject: 'Password successfully reset', - body: 'This is a confirmation that the password for your account ' + customer.email + ' has just been changed successfully.\n' - }; + // update the password and remove the token + const newPassword = bcrypt.hashSync(req.body.password, 10); + try{ + await db.customers.updateOne({ email: customer.email }, { $set: { password: newPassword, resetToken: undefined, resetTokenExpiry: undefined } }, { multi: false }); + const mailOpts = { + to: customer.email, + subject: 'Password successfully reset', + body: 'This is a confirmation that the password for your account ' + customer.email + ' has just been changed successfully.\n' + }; - // TODO: Should fix this to properly handle result - common.sendEmail(mailOpts.to, mailOpts.subject, mailOpts.body); - req.session.message = 'Password successfully updated'; - req.session.message_type = 'success'; - return res.redirect('/pay'); - }); - return''; - }); + // TODO: Should fix this to properly handle result + common.sendEmail(mailOpts.to, mailOpts.subject, mailOpts.body); + req.session.message = 'Password successfully updated'; + req.session.message_type = 'success'; + return res.redirect('/pay'); + }catch(ex){ + console.log('Unable to reset password', ex); + req.session.message = 'Unable to reset password'; + req.session.message_type = 'danger'; + return res.redirect('/forgot'); + } }); // logout the customer diff --git a/test/specs/customers.js b/test/specs/customers.js index fdfabf6..6ab9509 100644 --- a/test/specs/customers.js +++ b/test/specs/customers.js @@ -9,14 +9,6 @@ test.before(async () => { }); test('[Success] Create a customer', async t => { - // Login - await g.request - .post('/admin/login_action') - .send({ - email: g.users[0].userEmail, - password: 'test' - }); - const customer = { email: 'sarah.jones@test.com', firstName: 'Sarah', @@ -39,14 +31,6 @@ test('[Success] Create a customer', async t => { }); test('[Fail] Try create a duplicate customer', async t => { - // Login - await g.request - .post('/admin/login_action') - .send({ - email: g.users[0].userEmail, - password: 'test' - }); - const customer = { email: 'sarah.jones@test.com', firstName: 'Sarah',