From a3e2a6c7c1b47b7fb2aebdb9d871683299d9d9bb Mon Sep 17 00:00:00 2001 From: Mark Moffat Date: Mon, 5 Feb 2018 14:20:44 +0100 Subject: [PATCH] Route separation --- routes/admin.js | 750 --------------------------------------------- routes/customer.js | 71 +++++ routes/index.js | 2 + routes/order.js | 144 +++++++++ routes/product.js | 352 +++++++++++++++++++++ routes/user.js | 196 ++++++++++++ 6 files changed, 765 insertions(+), 750 deletions(-) create mode 100644 routes/order.js create mode 100644 routes/product.js create mode 100644 routes/user.js diff --git a/routes/admin.js b/routes/admin.js index ef0cd84..ed10aef 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -3,8 +3,6 @@ const common = require('./common'); const escape = require('html-entities').AllHtmlEntities; const colors = require('colors'); const bcrypt = require('bcryptjs'); -const rimraf = require('rimraf'); -const url = require('url'); const fs = require('fs'); const path = require('path'); const multer = require('multer'); @@ -16,166 +14,6 @@ router.get('/', common.restrict, (req, res, next) => { res.redirect('/admin/orders'); }); -// Admin section -router.get('/orders', common.restrict, (req, res, next) => { - const db = req.app.db; - - // Top 10 products - db.orders.find({}).sort({'orderDate': -1}).limit(10).toArray((err, orders) => { - if(err){ - console.info(err.stack); - } - res.render('orders', { - title: 'Cart', - orders: orders, - admin: true, - config: common.getConfig(), - session: req.session, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - helpers: req.handlebars.helpers - }); - }); -}); - -// Admin section -router.get('/orders/bystatus/:orderstatus', common.restrict, (req, res, next) => { - const db = req.app.db; - - if(typeof req.params.orderstatus === 'undefined'){ - res.redirect('/admin/orders'); - return; - } - - // case insensitive search - let regex = new RegExp(['^', req.params.orderstatus, '$'].join(''), 'i'); - db.orders.find({orderStatus: regex}).sort({'orderDate': -1}).limit(10).toArray((err, orders) => { - if(err){ - console.info(err.stack); - } - res.render('orders', { - title: 'Cart', - orders: orders, - admin: true, - filteredOrders: true, - filteredStatus: req.params.orderstatus, - config: common.getConfig(), - session: req.session, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - helpers: req.handlebars.helpers - }); - }); -}); - -// render the editor -router.get('/order/view/:id', common.restrict, (req, res) => { - const db = req.app.db; - db.orders.findOne({_id: common.getId(req.params.id)}, (err, result) => { - if(err){ - console.info(err.stack); - } - let productOptions = ''; - if(result.options !== {}){ - productOptions = result.options; - } - res.render('order', { - title: 'View order', - result: result, - productOptions: productOptions, - config: common.getConfig(), - session: req.session, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - editor: true, - admin: true, - helpers: req.handlebars.helpers - }); - }); -}); - -// Admin section -router.get('/orders/filter/:search', common.restrict, (req, res, next) => { - const db = req.app.db; - let searchTerm = req.params.search; - let ordersIndex = req.app.ordersIndex; - - let lunrIdArray = []; - ordersIndex.search(searchTerm).forEach((id) => { - lunrIdArray.push(common.getId(id.ref)); - }); - - // we search on the lunr indexes - db.orders.find({_id: {$in: lunrIdArray}}).toArray((err, orders) => { - if(err){ - console.info(err.stack); - } - res.render('orders', { - title: 'Order results', - orders: orders, - admin: true, - config: common.getConfig(), - session: req.session, - searchTerm: searchTerm, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - helpers: req.handlebars.helpers - }); - }); -}); - -// order product -router.get('/order/delete/:id', common.restrict, (req, res) => { - const db = req.app.db; - - // remove the article - db.orders.remove({_id: common.getId(req.params.id)}, {}, (err, numRemoved) => { - if(err){ - console.info(err.stack); - } - // remove the index - common.indexOrders(req.app) - .then(() => { - // redirect home - req.session.message = 'Order successfully deleted'; - req.session.messageType = 'success'; - res.redirect('/admin/orders'); - }); - }); -}); - -// update order status -router.post('/order/statusupdate', common.restrict, (req, res) => { - const db = req.app.db; - db.orders.update({_id: common.getId(req.body.order_id)}, {$set: {orderStatus: req.body.status}}, {multi: false}, (err, numReplaced) => { - if(err){ - console.info(err.stack); - } - res.status(200).json({message: 'Status successfully updated'}); - }); -}); - -// Admin section -router.get('/products', common.restrict, (req, res, next) => { - const db = req.app.db; - // get the top results - db.products.find({}).sort({'productAddedDate': -1}).limit(10).toArray((err, topResults) => { - if(err){ - console.info(err.stack); - } - res.render('products', { - title: 'Cart', - top_results: topResults, - session: req.session, - admin: true, - config: common.getConfig(), - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - helpers: req.handlebars.helpers - }); - }); -}); - // logout router.get('/logout', (req, res) => { req.session.user = null; @@ -280,360 +118,6 @@ router.get('/setup', (req, res) => { }); }); -// Admin section -router.get('/products/filter/:search', common.restrict, (req, res, next) => { - const db = req.app.db; - let searchTerm = req.params.search; - let productsIndex = req.app.productsIndex; - - let lunrIdArray = []; - productsIndex.search(searchTerm).forEach((id) => { - lunrIdArray.push(common.getId(id.ref)); - }); - - // we search on the lunr indexes - db.products.find({_id: {$in: lunrIdArray}}).toArray((err, results) => { - if(err){ - console.error(colors.red('Error searching', err)); - } - res.render('products', { - title: 'Results', - results: results, - admin: true, - config: common.getConfig(), - session: req.session, - searchTerm: searchTerm, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - helpers: req.handlebars.helpers - }); - }); -}); - -// insert form -router.get('/product/new', common.restrict, (req, res) => { - res.render('product_new', { - title: 'New product', - session: req.session, - productTitle: common.clearSessionValue(req.session, 'productTitle'), - productDescription: common.clearSessionValue(req.session, 'productDescription'), - productPrice: common.clearSessionValue(req.session, 'productPrice'), - productPermalink: common.clearSessionValue(req.session, 'productPermalink'), - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - editor: true, - admin: true, - helpers: req.handlebars.helpers, - config: common.getConfig() - }); -}); - -// insert new product form action -router.post('/product/insert', common.restrict, (req, res) => { - const db = req.app.db; - - let doc = { - productPermalink: req.body.frmProductPermalink, - productTitle: req.body.frmProductTitle, - productPrice: req.body.frmProductPrice, - productDescription: req.body.frmProductDescription, - productPublished: req.body.frmProductPublished, - productTags: req.body.frmProductTags, - productOptions: req.body.productOptJson, - productAddedDate: new Date() - }; - - db.products.count({'productPermalink': req.body.frmProductPermalink}, (err, product) => { - if(err){ - console.info(err.stack); - } - if(product > 0 && req.body.frmProductPermalink !== ''){ - // permalink exits - req.session.message = 'Permalink already exists. Pick a new one.'; - req.session.messageType = 'danger'; - - // keep the current stuff - req.session.productTitle = req.body.frmProductTitle; - req.session.productDescription = req.body.frmProductDescription; - req.session.productPrice = req.body.frmProductPrice; - req.session.productPermalink = req.body.frmProductPermalink; - req.session.productPermalink = req.body.productOptJson; - req.session.productTags = req.body.frmProductTags; - - // redirect to insert - res.redirect('/admin/insert'); - }else{ - db.products.insert(doc, (err, newDoc) => { - if(err){ - console.log(colors.red('Error inserting document: ' + err)); - - // keep the current stuff - req.session.productTitle = req.body.frmProductTitle; - req.session.productDescription = req.body.frmProductDescription; - req.session.productPrice = req.body.frmProductPrice; - req.session.productPermalink = req.body.frmProductPermalink; - req.session.productPermalink = req.body.productOptJson; - req.session.productTags = req.body.frmProductTags; - - req.session.message = 'Error: Inserting product'; - req.session.messageType = 'danger'; - - // redirect to insert - res.redirect('/admin/product/new'); - }else{ - // get the new ID - let newId = newDoc.insertedIds[0]; - - // add to lunr index - common.indexProducts(req.app) - .then(() => { - req.session.message = 'New product successfully created'; - req.session.messageType = 'success'; - - // redirect to new doc - res.redirect('/admin/product/edit/' + newId); - }); - } - }); - } - }); -}); - -// render the editor -router.get('/product/edit/:id', common.restrict, (req, res) => { - const db = req.app.db; - - common.getImages(req.params.id, req, res, (images) => { - db.products.findOne({_id: common.getId(req.params.id)}, (err, result) => { - if(err){ - console.info(err.stack); - } - let options = {}; - if(result.productOptions){ - options = JSON.parse(result.productOptions); - } - - res.render('product_edit', { - title: 'Edit product', - result: result, - images: images, - options: options, - admin: true, - session: req.session, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - config: common.getConfig(), - editor: true, - helpers: req.handlebars.helpers - }); - }); - }); -}); - -// Update an existing product form action -router.post('/product/update', common.restrict, (req, res) => { - const db = req.app.db; - - db.products.findOne({_id: common.getId(req.body.frmProductId)}, (err, product) => { - if(err){ - console.info(err.stack); - req.session.message = 'Failed updating product.'; - req.session.messageType = 'danger'; - res.redirect('/admin/product/edit/' + req.body.frmProductId); - return; - } - db.products.count({'productPermalink': req.body.frmProductPermalink, _id: {$ne: common.getId(product._id)}}, (err, count) => { - if(err){ - console.info(err.stack); - req.session.message = 'Failed updating product.'; - req.session.messageType = 'danger'; - res.redirect('/admin/product/edit/' + req.body.frmProductId); - return; - } - if(count > 0 && req.body.frmProductPermalink !== ''){ - // permalink exits - req.session.message = 'Permalink already exists. Pick a new one.'; - req.session.messageType = 'danger'; - - // keep the current stuff - req.session.productTitle = req.body.frmProductTitle; - req.session.productDescription = req.body.frmProductDescription; - req.session.productPrice = req.body.frmProductPrice; - req.session.productPermalink = req.body.frmProductPermalink; - req.session.productTags = req.body.frmProductTags; - req.session.productOptions = req.body.productOptJson; - - // redirect to insert - res.redirect('/admin/product/edit/' + req.body.frmProductId); - }else{ - common.getImages(req.body.frmProductId, req, res, (images) => { - let productDoc = { - productTitle: req.body.frmProductTitle, - productDescription: req.body.frmProductDescription, - productPublished: req.body.frmProductPublished, - productPrice: req.body.frmProductPrice, - productPermalink: req.body.frmProductPermalink, - productTags: req.body.frmProductTags, - productOptions: req.body.productOptJson - }; - - // if no featured image - if(!product.productImage){ - if(images.length > 0){ - productDoc['productImage'] = images[0].path; - }else{ - productDoc['productImage'] = '/uploads/placeholder.png'; - } - }else{ - productDoc['productImage'] = product.productImage; - } - - db.products.update({_id: common.getId(req.body.frmProductId)}, {$set: productDoc}, {}, (err, numReplaced) => { - if(err){ - console.error(colors.red('Failed to save product: ' + err)); - req.session.message = 'Failed to save. Please try again'; - req.session.messageType = 'danger'; - res.redirect('/admin/product/edit/' + req.body.frmProductId); - }else{ - // Update the index - common.indexProducts(req.app) - .then(() => { - req.session.message = 'Successfully saved'; - req.session.messageType = 'success'; - res.redirect('/admin/product/edit/' + req.body.frmProductId); - }); - } - }); - }); - } - }); - }); -}); - -// delete product -router.get('/product/delete/:id', common.restrict, (req, res) => { - const db = req.app.db; - - // remove the article - db.products.remove({_id: common.getId(req.params.id)}, {}, (err, numRemoved) => { - if(err){ - console.info(err.stack); - } - // delete any images and folder - rimraf('public/uploads/' + req.params.id, (err) => { - if(err){ - console.info(err.stack); - } - - // remove the index - common.indexProducts(req.app) - .then(() => { - // redirect home - req.session.message = 'Product successfully deleted'; - req.session.messageType = 'success'; - res.redirect('/admin/products'); - }); - }); - }); -}); - -// users -router.get('/users', common.restrict, (req, res) => { - const db = req.app.db; - db.users.find({}).toArray((err, users) => { - if(err){ - console.info(err.stack); - } - res.render('users', { - title: 'Users', - users: users, - admin: true, - config: common.getConfig(), - isAdmin: req.session.isAdmin, - helpers: req.handlebars.helpers, - session: req.session, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType') - }); - }); -}); - -// edit user -router.get('/user/edit/:id', common.restrict, (req, res) => { - const db = req.app.db; - db.users.findOne({_id: common.getId(req.params.id)}, (err, user) => { - if(err){ - console.info(err.stack); - } - // if the user we want to edit is not the current logged in user and the current user is not - // an admin we render an access denied message - if(user.userEmail !== req.session.user && req.session.isAdmin === 'false'){ - req.session.message = 'Access denied'; - req.session.messageType = 'danger'; - res.redirect('/Users/'); - return; - } - - res.render('user_edit', { - title: 'User edit', - user: user, - admin: true, - session: req.session, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - helpers: req.handlebars.helpers, - config: common.getConfig() - }); - }); -}); - -// update a user -router.post('/user/update', common.restrict, (req, res) => { - const db = req.app.db; - - let isAdmin = req.body.user_admin === 'on' ? 'true' : 'false'; - - // get the user we want to update - db.users.findOne({_id: common.getId(req.body.userId)}, (err, user) => { - if(err){ - console.info(err.stack); - } - // if the user we want to edit is not the current logged in user and the current user is not - // an admin we render an access denied message - if(user.userEmail !== req.session.user && req.session.isAdmin === 'false'){ - req.session.message = 'Access denied'; - req.session.messageType = 'danger'; - res.redirect('/admin/users/'); - return; - } - - // create the update doc - let updateDoc = {}; - updateDoc.isAdmin = isAdmin; - updateDoc.usersName = req.body.usersName; - if(req.body.userPassword){ - updateDoc.userPassword = bcrypt.hashSync(req.body.userPassword); - } - - db.users.update({_id: common.getId(req.body.userId)}, - { - $set: updateDoc - }, {multi: false}, (err, numReplaced) => { - if(err){ - console.error(colors.red('Failed updating user: ' + err)); - req.session.message = 'Failed to update user'; - req.session.messageType = 'danger'; - res.redirect('/admin/user/edit/' + req.body.userId); - }else{ - // show the view - req.session.message = 'User account updated.'; - req.session.messageType = 'success'; - res.redirect('/admin/user/edit/' + req.body.userId); - } - }); - }); -}); - // insert a user router.post('/setup_action', (req, res) => { const db = req.app.db; @@ -671,172 +155,6 @@ router.post('/setup_action', (req, res) => { }); }); -// insert a user -router.post('/user/insert', common.restrict, (req, res) => { - const db = req.app.db; - - // set the account to admin if using the setup form. Eg: First user account - let urlParts = url.parse(req.header('Referer')); - - let isAdmin = 'false'; - if(urlParts.path === '/admin/setup'){ - isAdmin = 'true'; - } - - let doc = { - usersName: req.body.usersName, - userEmail: req.body.userEmail, - userPassword: bcrypt.hashSync(req.body.userPassword, 10), - isAdmin: isAdmin - }; - - // check for existing user - db.users.findOne({'userEmail': req.body.userEmail}, (err, user) => { - if(user){ - // user already exists with that email address - console.error(colors.red('Failed to insert user, possibly already exists: ' + err)); - req.session.message = 'A user with that email address already exists'; - req.session.messageType = 'danger'; - res.redirect('/admin/user/new'); - return; - } - // email is ok to be used. - db.users.insert(doc, (err, doc) => { - // show the view - if(err){ - if(doc){ - console.error(colors.red('Failed to insert user: ' + err)); - req.session.message = 'User exists'; - req.session.messageType = 'danger'; - res.redirect('/admin/user/edit/' + doc._id); - return; - } - console.error(colors.red('Failed to insert user: ' + err)); - req.session.message = 'New user creation failed'; - req.session.messageType = 'danger'; - res.redirect('/admin/user/new'); - return; - } - req.session.message = 'User account inserted'; - req.session.messageType = 'success'; - - // if from setup we add user to session and redirect to login. - // Otherwise we show users screen - if(urlParts.path === '/admin/setup'){ - req.session.user = req.body.userEmail; - res.redirect('/admin/login'); - return; - } - res.redirect('/admin/users'); - }); - }); -}); - -// render the customer view -router.get('/customer/view/:id?', common.restrict, (req, res) => { - const db = req.app.db; - - console.log('here'); - - db.customers.findOne({_id: common.getId(req.params.id)}, (err, result) => { - if(err){ - console.info(err.stack); - } - - res.render('customer', { - title: 'View customer', - result: result, - admin: true, - session: req.session, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - config: common.getConfig(), - editor: true, - helpers: req.handlebars.helpers - }); - }); -}); - -// customers list -router.get('/customers', common.restrict, (req, res) => { - const db = req.app.db; - - db.customers.find({}).limit(20).sort({created: -1}).toArray((err, customers) => { - 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: common.getConfig() - }); - }); -}); - -// Filtered customers list -router.get('/customers/filter/:search', common.restrict, (req, res, next) => { - const db = req.app.db; - let searchTerm = req.params.search; - let customersIndex = req.app.customersIndex; - - let lunrIdArray = []; - customersIndex.search(searchTerm).forEach((id) => { - lunrIdArray.push(common.getId(id.ref)); - }); - - // 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)); - } - res.render('customers', { - title: 'Customer results', - customers: customers, - admin: true, - config: common.getConfig(), - session: req.session, - searchTerm: searchTerm, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - helpers: req.handlebars.helpers - }); - }); -}); - -// users new -router.get('/user/new', common.restrict, (req, res) => { - res.render('user_new', { - title: 'User - New', - admin: true, - session: req.session, - helpers: req.handlebars.helpers, - message: common.clearSessionValue(req.session, 'message'), - messageType: common.clearSessionValue(req.session, 'messageType'), - config: common.getConfig() - }); -}); - -// delete user -router.get('/user/delete/:id', common.restrict, (req, res) => { - const db = req.app.db; - if(req.session.isAdmin === 'true'){ - db.users.remove({_id: common.getId(req.params.id)}, {}, (err, numRemoved) => { - if(err){ - console.info(err.stack); - } - req.session.message = 'User deleted.'; - req.session.messageType = 'success'; - res.redirect('/admin/users'); - }); - }else{ - req.session.message = 'Access denied.'; - req.session.messageType = 'danger'; - res.redirect('/admin/users'); - } -}); - // settings update router.get('/settings', common.restrict, (req, res) => { res.render('settings', { @@ -1103,75 +421,7 @@ router.post('/api/validate_permalink', (req, res) => { }); }); -// update the published state based on an ajax call from the frontend -router.post('/product/published_state', common.restrict, (req, res) => { - const db = req.app.db; - - db.products.update({_id: common.getId(req.body.id)}, {$set: {productPublished: req.body.state}}, {multi: false}, (err, numReplaced) => { - if(err){ - console.error(colors.red('Failed to update the published state: ' + err)); - res.writeHead(400, {'Content-Type': 'application/text'}); - res.end('Published state not updated'); - }else{ - res.writeHead(200, {'Content-Type': 'application/text'}); - res.end('Published state updated'); - } - }); -}); - -// set as main product image -router.post('/product/setasmainimage', common.restrict, (req, res) => { - const db = req.app.db; - - // update the productImage to the db - db.products.update({_id: common.getId(req.body.product_id)}, {$set: {productImage: req.body.productImage}}, {multi: false}, (err, numReplaced) => { - if(err){ - res.status(400).json({message: 'Unable to set as main image. Please try again.'}); - }else{ - res.status(200).json({message: 'Main image successfully set'}); - } - }); -}); - -// deletes a product image -router.post('/product/deleteimage', common.restrict, (req, res) => { - const db = req.app.db; - - // get the productImage from the db - db.products.findOne({_id: common.getId(req.body.product_id)}, (err, product) => { - if(err){ - console.info(err.stack); - } - if(req.body.productImage === product.productImage){ - // set the produt_image to null - db.products.update({_id: common.getId(req.body.product_id)}, {$set: {productImage: null}}, {multi: false}, (err, numReplaced) => { - if(err){ - console.info(err.stack); - } - // remove the image from disk - fs.unlink(path.join('public', req.body.productImage), (err) => { - if(err){ - res.status(400).json({message: 'Image not removed, please try again.'}); - }else{ - res.status(200).json({message: 'Image successfully deleted'}); - } - }); - }); - }else{ - // remove the image from disk - fs.unlink(path.join('public', req.body.productImage), (err) => { - if(err){ - res.status(400).json({message: 'Image not removed, please try again.'}); - }else{ - res.status(200).json({message: 'Image successfully deleted'}); - } - }); - } - }); -}); - // upload the file - let upload = multer({dest: 'public/uploads/'}); router.post('/file/upload', common.restrict, upload.single('upload_file'), (req, res, next) => { const db = req.app.db; diff --git a/routes/customer.js b/routes/customer.js index 8c09e16..a9acc71 100644 --- a/routes/customer.js +++ b/routes/customer.js @@ -58,6 +58,77 @@ router.post('/customer/create', (req, res) => { }); }); +// render the customer view +router.get('/customer/view/:id?', common.restrict, (req, res) => { + const db = req.app.db; + + db.customers.findOne({_id: common.getId(req.params.id)}, (err, result) => { + if(err){ + console.info(err.stack); + } + + res.render('customer', { + title: 'View customer', + result: result, + admin: true, + session: req.session, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + config: common.getConfig(), + editor: true, + helpers: req.handlebars.helpers + }); + }); +}); + +// customers list +router.get('/customers', common.restrict, (req, res) => { + const db = req.app.db; + + db.customers.find({}).limit(20).sort({created: -1}).toArray((err, customers) => { + 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: common.getConfig() + }); + }); +}); + +// Filtered customers list +router.get('/customers/filter/:search', common.restrict, (req, res, next) => { + const db = req.app.db; + let searchTerm = req.params.search; + let customersIndex = req.app.customersIndex; + + let lunrIdArray = []; + customersIndex.search(searchTerm).forEach((id) => { + lunrIdArray.push(common.getId(id.ref)); + }); + + // 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)); + } + res.render('customers', { + title: 'Customer results', + customers: customers, + admin: true, + config: common.getConfig(), + session: req.session, + searchTerm: searchTerm, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + helpers: req.handlebars.helpers + }); + }); +}); + // login the customer and check the password router.post('/customer/login_action', (req, res) => { let db = req.app.db; diff --git a/routes/index.js b/routes/index.js index 5751e9b..1fcf4af 100644 --- a/routes/index.js +++ b/routes/index.js @@ -5,6 +5,8 @@ const async = require('async'); const _ = require('lodash'); const common = require('./common'); +// These is the customer facing routes + router.get('/payment/:orderId', async (req, res, next) => { let db = req.app.db; let config = common.getConfig(); diff --git a/routes/order.js b/routes/order.js new file mode 100644 index 0000000..116836c --- /dev/null +++ b/routes/order.js @@ -0,0 +1,144 @@ +const express = require('express'); +const common = require('./common'); +const router = express.Router(); + +// Show orders +router.get('/orders', common.restrict, (req, res, next) => { + const db = req.app.db; + + // Top 10 products + db.orders.find({}).sort({'orderDate': -1}).limit(10).toArray((err, orders) => { + if(err){ + console.info(err.stack); + } + res.render('orders', { + title: 'Cart', + orders: orders, + admin: true, + config: common.getConfig(), + session: req.session, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + helpers: req.handlebars.helpers + }); + }); +}); + +// Admin section +router.get('/orders/bystatus/:orderstatus', common.restrict, (req, res, next) => { + const db = req.app.db; + + if(typeof req.params.orderstatus === 'undefined'){ + res.redirect('/admin/orders'); + return; + } + + // case insensitive search + let regex = new RegExp(['^', req.params.orderstatus, '$'].join(''), 'i'); + db.orders.find({orderStatus: regex}).sort({'orderDate': -1}).limit(10).toArray((err, orders) => { + if(err){ + console.info(err.stack); + } + res.render('orders', { + title: 'Cart', + orders: orders, + admin: true, + filteredOrders: true, + filteredStatus: req.params.orderstatus, + config: common.getConfig(), + session: req.session, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + helpers: req.handlebars.helpers + }); + }); +}); + +// render the editor +router.get('/order/view/:id', common.restrict, (req, res) => { + const db = req.app.db; + db.orders.findOne({_id: common.getId(req.params.id)}, (err, result) => { + if(err){ + console.info(err.stack); + } + let productOptions = ''; + if(result.options !== {}){ + productOptions = result.options; + } + res.render('order', { + title: 'View order', + result: result, + productOptions: productOptions, + config: common.getConfig(), + session: req.session, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + editor: true, + admin: true, + helpers: req.handlebars.helpers + }); + }); +}); + +// Admin section +router.get('/orders/filter/:search', common.restrict, (req, res, next) => { + const db = req.app.db; + let searchTerm = req.params.search; + let ordersIndex = req.app.ordersIndex; + + let lunrIdArray = []; + ordersIndex.search(searchTerm).forEach((id) => { + lunrIdArray.push(common.getId(id.ref)); + }); + + // we search on the lunr indexes + db.orders.find({_id: {$in: lunrIdArray}}).toArray((err, orders) => { + if(err){ + console.info(err.stack); + } + res.render('orders', { + title: 'Order results', + orders: orders, + admin: true, + config: common.getConfig(), + session: req.session, + searchTerm: searchTerm, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + helpers: req.handlebars.helpers + }); + }); +}); + +// order product +router.get('/order/delete/:id', common.restrict, (req, res) => { + const db = req.app.db; + + // remove the article + db.orders.remove({_id: common.getId(req.params.id)}, {}, (err, numRemoved) => { + if(err){ + console.info(err.stack); + } + // remove the index + common.indexOrders(req.app) + .then(() => { + // redirect home + req.session.message = 'Order successfully deleted'; + req.session.messageType = 'success'; + res.redirect('/admin/orders'); + }); + }); +}); + +// update order status +router.post('/order/statusupdate', common.restrict, (req, res) => { + const db = req.app.db; + db.orders.update({_id: common.getId(req.body.order_id)}, {$set: {orderStatus: req.body.status}}, {multi: false}, (err, numReplaced) => { + if(err){ + console.info(err.stack); + } + res.status(200).json({message: 'Status successfully updated'}); + }); +}); + +module.exports = router; diff --git a/routes/product.js b/routes/product.js new file mode 100644 index 0000000..39a4f6a --- /dev/null +++ b/routes/product.js @@ -0,0 +1,352 @@ +const express = require('express'); +const common = require('./common'); +const colors = require('colors'); +const rimraf = require('rimraf'); +const fs = require('fs'); +const path = require('path'); +const router = express.Router(); + +router.get('/products', common.restrict, (req, res, next) => { + const db = req.app.db; + // get the top results + db.products.find({}).sort({'productAddedDate': -1}).limit(10).toArray((err, topResults) => { + if(err){ + console.info(err.stack); + } + res.render('products', { + title: 'Cart', + top_results: topResults, + session: req.session, + admin: true, + config: common.getConfig(), + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + helpers: req.handlebars.helpers + }); + }); +}); + +router.get('/products/filter/:search', common.restrict, (req, res, next) => { + const db = req.app.db; + let searchTerm = req.params.search; + let productsIndex = req.app.productsIndex; + + let lunrIdArray = []; + productsIndex.search(searchTerm).forEach((id) => { + lunrIdArray.push(common.getId(id.ref)); + }); + + // we search on the lunr indexes + db.products.find({_id: {$in: lunrIdArray}}).toArray((err, results) => { + if(err){ + console.error(colors.red('Error searching', err)); + } + res.render('products', { + title: 'Results', + results: results, + admin: true, + config: common.getConfig(), + session: req.session, + searchTerm: searchTerm, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + helpers: req.handlebars.helpers + }); + }); +}); + +// insert form +router.get('/product/new', common.restrict, (req, res) => { + res.render('product_new', { + title: 'New product', + session: req.session, + productTitle: common.clearSessionValue(req.session, 'productTitle'), + productDescription: common.clearSessionValue(req.session, 'productDescription'), + productPrice: common.clearSessionValue(req.session, 'productPrice'), + productPermalink: common.clearSessionValue(req.session, 'productPermalink'), + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + editor: true, + admin: true, + helpers: req.handlebars.helpers, + config: common.getConfig() + }); +}); + +// insert new product form action +router.post('/product/insert', common.restrict, (req, res) => { + const db = req.app.db; + + let doc = { + productPermalink: req.body.frmProductPermalink, + productTitle: req.body.frmProductTitle, + productPrice: req.body.frmProductPrice, + productDescription: req.body.frmProductDescription, + productPublished: req.body.frmProductPublished, + productTags: req.body.frmProductTags, + productOptions: req.body.productOptJson, + productAddedDate: new Date() + }; + + db.products.count({'productPermalink': req.body.frmProductPermalink}, (err, product) => { + if(err){ + console.info(err.stack); + } + if(product > 0 && req.body.frmProductPermalink !== ''){ + // permalink exits + req.session.message = 'Permalink already exists. Pick a new one.'; + req.session.messageType = 'danger'; + + // keep the current stuff + req.session.productTitle = req.body.frmProductTitle; + req.session.productDescription = req.body.frmProductDescription; + req.session.productPrice = req.body.frmProductPrice; + req.session.productPermalink = req.body.frmProductPermalink; + req.session.productPermalink = req.body.productOptJson; + req.session.productTags = req.body.frmProductTags; + + // redirect to insert + res.redirect('/admin/insert'); + }else{ + db.products.insert(doc, (err, newDoc) => { + if(err){ + console.log(colors.red('Error inserting document: ' + err)); + + // keep the current stuff + req.session.productTitle = req.body.frmProductTitle; + req.session.productDescription = req.body.frmProductDescription; + req.session.productPrice = req.body.frmProductPrice; + req.session.productPermalink = req.body.frmProductPermalink; + req.session.productPermalink = req.body.productOptJson; + req.session.productTags = req.body.frmProductTags; + + req.session.message = 'Error: Inserting product'; + req.session.messageType = 'danger'; + + // redirect to insert + res.redirect('/admin/product/new'); + }else{ + // get the new ID + let newId = newDoc.insertedIds[0]; + + // add to lunr index + common.indexProducts(req.app) + .then(() => { + req.session.message = 'New product successfully created'; + req.session.messageType = 'success'; + + // redirect to new doc + res.redirect('/admin/product/edit/' + newId); + }); + } + }); + } + }); +}); + +// render the editor +router.get('/product/edit/:id', common.restrict, (req, res) => { + const db = req.app.db; + + common.getImages(req.params.id, req, res, (images) => { + db.products.findOne({_id: common.getId(req.params.id)}, (err, result) => { + if(err){ + console.info(err.stack); + } + let options = {}; + if(result.productOptions){ + options = JSON.parse(result.productOptions); + } + + res.render('product_edit', { + title: 'Edit product', + result: result, + images: images, + options: options, + admin: true, + session: req.session, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + config: common.getConfig(), + editor: true, + helpers: req.handlebars.helpers + }); + }); + }); +}); + +// Update an existing product form action +router.post('/product/update', common.restrict, (req, res) => { + const db = req.app.db; + + db.products.findOne({_id: common.getId(req.body.frmProductId)}, (err, product) => { + if(err){ + console.info(err.stack); + req.session.message = 'Failed updating product.'; + req.session.messageType = 'danger'; + res.redirect('/admin/product/edit/' + req.body.frmProductId); + return; + } + db.products.count({'productPermalink': req.body.frmProductPermalink, _id: {$ne: common.getId(product._id)}}, (err, count) => { + if(err){ + console.info(err.stack); + req.session.message = 'Failed updating product.'; + req.session.messageType = 'danger'; + res.redirect('/admin/product/edit/' + req.body.frmProductId); + return; + } + if(count > 0 && req.body.frmProductPermalink !== ''){ + // permalink exits + req.session.message = 'Permalink already exists. Pick a new one.'; + req.session.messageType = 'danger'; + + // keep the current stuff + req.session.productTitle = req.body.frmProductTitle; + req.session.productDescription = req.body.frmProductDescription; + req.session.productPrice = req.body.frmProductPrice; + req.session.productPermalink = req.body.frmProductPermalink; + req.session.productTags = req.body.frmProductTags; + req.session.productOptions = req.body.productOptJson; + + // redirect to insert + res.redirect('/admin/product/edit/' + req.body.frmProductId); + }else{ + common.getImages(req.body.frmProductId, req, res, (images) => { + let productDoc = { + productTitle: req.body.frmProductTitle, + productDescription: req.body.frmProductDescription, + productPublished: req.body.frmProductPublished, + productPrice: req.body.frmProductPrice, + productPermalink: req.body.frmProductPermalink, + productTags: req.body.frmProductTags, + productOptions: req.body.productOptJson + }; + + // if no featured image + if(!product.productImage){ + if(images.length > 0){ + productDoc['productImage'] = images[0].path; + }else{ + productDoc['productImage'] = '/uploads/placeholder.png'; + } + }else{ + productDoc['productImage'] = product.productImage; + } + + db.products.update({_id: common.getId(req.body.frmProductId)}, {$set: productDoc}, {}, (err, numReplaced) => { + if(err){ + console.error(colors.red('Failed to save product: ' + err)); + req.session.message = 'Failed to save. Please try again'; + req.session.messageType = 'danger'; + res.redirect('/admin/product/edit/' + req.body.frmProductId); + }else{ + // Update the index + common.indexProducts(req.app) + .then(() => { + req.session.message = 'Successfully saved'; + req.session.messageType = 'success'; + res.redirect('/admin/product/edit/' + req.body.frmProductId); + }); + } + }); + }); + } + }); + }); +}); + +// delete product +router.get('/product/delete/:id', common.restrict, (req, res) => { + const db = req.app.db; + + // remove the article + db.products.remove({_id: common.getId(req.params.id)}, {}, (err, numRemoved) => { + if(err){ + console.info(err.stack); + } + // delete any images and folder + rimraf('public/uploads/' + req.params.id, (err) => { + if(err){ + console.info(err.stack); + } + + // remove the index + common.indexProducts(req.app) + .then(() => { + // redirect home + req.session.message = 'Product successfully deleted'; + req.session.messageType = 'success'; + res.redirect('/admin/products'); + }); + }); + }); +}); + +// update the published state based on an ajax call from the frontend +router.post('/product/published_state', common.restrict, (req, res) => { + const db = req.app.db; + + db.products.update({_id: common.getId(req.body.id)}, {$set: {productPublished: req.body.state}}, {multi: false}, (err, numReplaced) => { + if(err){ + console.error(colors.red('Failed to update the published state: ' + err)); + res.writeHead(400, {'Content-Type': 'application/text'}); + res.end('Published state not updated'); + }else{ + res.writeHead(200, {'Content-Type': 'application/text'}); + res.end('Published state updated'); + } + }); +}); + +// set as main product image +router.post('/product/setasmainimage', common.restrict, (req, res) => { + const db = req.app.db; + + // update the productImage to the db + db.products.update({_id: common.getId(req.body.product_id)}, {$set: {productImage: req.body.productImage}}, {multi: false}, (err, numReplaced) => { + if(err){ + res.status(400).json({message: 'Unable to set as main image. Please try again.'}); + }else{ + res.status(200).json({message: 'Main image successfully set'}); + } + }); +}); + +// deletes a product image +router.post('/product/deleteimage', common.restrict, (req, res) => { + const db = req.app.db; + + // get the productImage from the db + db.products.findOne({_id: common.getId(req.body.product_id)}, (err, product) => { + if(err){ + console.info(err.stack); + } + if(req.body.productImage === product.productImage){ + // set the produt_image to null + db.products.update({_id: common.getId(req.body.product_id)}, {$set: {productImage: null}}, {multi: false}, (err, numReplaced) => { + if(err){ + console.info(err.stack); + } + // remove the image from disk + fs.unlink(path.join('public', req.body.productImage), (err) => { + if(err){ + res.status(400).json({message: 'Image not removed, please try again.'}); + }else{ + res.status(200).json({message: 'Image successfully deleted'}); + } + }); + }); + }else{ + // remove the image from disk + fs.unlink(path.join('public', req.body.productImage), (err) => { + if(err){ + res.status(400).json({message: 'Image not removed, please try again.'}); + }else{ + res.status(200).json({message: 'Image successfully deleted'}); + } + }); + } + }); +}); + +module.exports = router; diff --git a/routes/user.js b/routes/user.js new file mode 100644 index 0000000..d0dac42 --- /dev/null +++ b/routes/user.js @@ -0,0 +1,196 @@ +const express = require('express'); +const common = require('./common'); +const colors = require('colors'); +const bcrypt = require('bcryptjs'); +const router = express.Router(); + +router.get('/users', common.restrict, (req, res) => { + const db = req.app.db; + db.users.find({}).toArray((err, users) => { + if(err){ + console.info(err.stack); + } + res.render('users', { + title: 'Users', + users: users, + admin: true, + config: common.getConfig(), + isAdmin: req.session.isAdmin, + helpers: req.handlebars.helpers, + session: req.session, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType') + }); + }); +}); + +// edit user +router.get('/user/edit/:id', common.restrict, (req, res) => { + const db = req.app.db; + db.users.findOne({_id: common.getId(req.params.id)}, (err, user) => { + if(err){ + console.info(err.stack); + } + // if the user we want to edit is not the current logged in user and the current user is not + // an admin we render an access denied message + if(user.userEmail !== req.session.user && req.session.isAdmin === 'false'){ + req.session.message = 'Access denied'; + req.session.messageType = 'danger'; + res.redirect('/Users/'); + return; + } + + res.render('user_edit', { + title: 'User edit', + user: user, + admin: true, + session: req.session, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + helpers: req.handlebars.helpers, + config: common.getConfig() + }); + }); +}); + +// users new +router.get('/user/new', common.restrict, (req, res) => { + res.render('user_new', { + title: 'User - New', + admin: true, + session: req.session, + helpers: req.handlebars.helpers, + message: common.clearSessionValue(req.session, 'message'), + messageType: common.clearSessionValue(req.session, 'messageType'), + config: common.getConfig() + }); +}); + +// delete user +router.get('/user/delete/:id', common.restrict, (req, res) => { + const db = req.app.db; + if(req.session.isAdmin === 'true'){ + db.users.remove({_id: common.getId(req.params.id)}, {}, (err, numRemoved) => { + if(err){ + console.info(err.stack); + } + req.session.message = 'User deleted.'; + req.session.messageType = 'success'; + res.redirect('/admin/users'); + }); + }else{ + req.session.message = 'Access denied.'; + req.session.messageType = 'danger'; + res.redirect('/admin/users'); + } +}); + +// update a user +router.post('/user/update', common.restrict, (req, res) => { + const db = req.app.db; + + let isAdmin = req.body.user_admin === 'on' ? 'true' : 'false'; + + // get the user we want to update + db.users.findOne({_id: common.getId(req.body.userId)}, (err, user) => { + if(err){ + console.info(err.stack); + } + // if the user we want to edit is not the current logged in user and the current user is not + // an admin we render an access denied message + if(user.userEmail !== req.session.user && req.session.isAdmin === 'false'){ + req.session.message = 'Access denied'; + req.session.messageType = 'danger'; + res.redirect('/admin/users/'); + return; + } + + // create the update doc + let updateDoc = {}; + updateDoc.isAdmin = isAdmin; + updateDoc.usersName = req.body.usersName; + if(req.body.userPassword){ + updateDoc.userPassword = bcrypt.hashSync(req.body.userPassword); + } + + db.users.update({_id: common.getId(req.body.userId)}, + { + $set: updateDoc + }, {multi: false}, (err, numReplaced) => { + if(err){ + console.error(colors.red('Failed updating user: ' + err)); + req.session.message = 'Failed to update user'; + req.session.messageType = 'danger'; + res.redirect('/admin/user/edit/' + req.body.userId); + }else{ + // show the view + req.session.message = 'User account updated.'; + req.session.messageType = 'success'; + res.redirect('/admin/user/edit/' + req.body.userId); + } + }); + }); +}); + +// insert a user +router.post('/user/insert', common.restrict, (req, res) => { + const db = req.app.db; + + // set the account to admin if using the setup form. Eg: First user account + let urlParts = url.parse(req.header('Referer')); + + let isAdmin = 'false'; + if(urlParts.path === '/admin/setup'){ + isAdmin = 'true'; + } + + let doc = { + usersName: req.body.usersName, + userEmail: req.body.userEmail, + userPassword: bcrypt.hashSync(req.body.userPassword, 10), + isAdmin: isAdmin + }; + + // check for existing user + db.users.findOne({'userEmail': req.body.userEmail}, (err, user) => { + if(user){ + // user already exists with that email address + console.error(colors.red('Failed to insert user, possibly already exists: ' + err)); + req.session.message = 'A user with that email address already exists'; + req.session.messageType = 'danger'; + res.redirect('/admin/user/new'); + return; + } + // email is ok to be used. + db.users.insert(doc, (err, doc) => { + // show the view + if(err){ + if(doc){ + console.error(colors.red('Failed to insert user: ' + err)); + req.session.message = 'User exists'; + req.session.messageType = 'danger'; + res.redirect('/admin/user/edit/' + doc._id); + return; + } + console.error(colors.red('Failed to insert user: ' + err)); + req.session.message = 'New user creation failed'; + req.session.messageType = 'danger'; + res.redirect('/admin/user/new'); + return; + } + req.session.message = 'User account inserted'; + req.session.messageType = 'success'; + + // if from setup we add user to session and redirect to login. + // Otherwise we show users screen + if(urlParts.path === '/admin/setup'){ + req.session.user = req.body.userEmail; + res.redirect('/admin/login'); + return; + } + res.redirect('/admin/users'); + }); + }); +}); + +module.exports = router;