expressCart/routes/index.js

603 lines
22 KiB
JavaScript
Raw Normal View History

2018-01-07 04:55:48 +10:00
const express = require('express');
const router = express.Router();
const colors = require('colors');
2018-02-04 01:23:59 +10:00
const async = require('async');
2018-01-07 04:55:48 +10:00
const _ = require('lodash');
const common = require('../lib/common');
2018-01-07 04:55:48 +10:00
2018-02-05 23:20:44 +10:00
// These is the customer facing routes
router.get('/payment/:orderId', async (req, res, next) => {
2018-01-07 05:35:49 +10:00
let db = req.app.db;
2018-02-23 03:41:24 +10:00
let config = req.app.config;
2018-01-07 04:55:48 +10:00
// render the payment complete message
2019-02-09 14:07:02 +10:00
db.orders.findOne({_id: common.getId(req.params.orderId)}, async (err, order) => {
2018-01-07 04:55:48 +10:00
if(err){
console.info(err.stack);
}
2019-02-09 14:07:02 +10:00
// If stock management is turned on payment approved update stock level
if(config.trackStock && req.session.paymentApproved){
order.orderProducts.forEach(async (product) => {
const dbProduct = await db.products.findOne({_id: common.getId(product.productId)});
let newStockLevel = dbProduct.productStock - product.quantity;
if(newStockLevel < 1){
newStockLevel = 0;
}
// Update product stock
await db.products.update({
_id: common.getId(product.productId)
}, {
$set: {
productStock: newStockLevel
}
}, {multi: false});
});
}
2018-01-15 07:11:22 +10:00
res.render(`${config.themeViews}payment_complete`, {
2018-01-07 04:55:48 +10:00
title: 'Payment complete',
2018-02-23 03:41:24 +10:00
config: req.app.config,
2018-01-07 04:55:48 +10:00
session: req.session,
pageCloseBtn: common.showCartCloseBtn('payment'),
2019-02-09 14:07:02 +10:00
result: order,
2018-01-07 04:55:48 +10:00
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.sortMenu(await common.getMenu(db))
2018-01-07 04:55:48 +10:00
});
});
});
router.get('/checkout', async (req, res, next) => {
2018-02-23 03:41:24 +10:00
let config = req.app.config;
2018-01-07 04:55:48 +10:00
// if there is no items in the cart then render a failure
if(!req.session.cart){
req.session.message = 'The are no items in your cart. Please add some items before checking out';
req.session.messageType = 'danger';
res.redirect('/');
return;
}
// render the checkout
2018-01-15 07:11:22 +10:00
res.render(`${config.themeViews}checkout`, {
2018-01-07 04:55:48 +10:00
title: 'Checkout',
2018-02-23 03:41:24 +10:00
config: req.app.config,
2018-01-07 04:55:48 +10:00
session: req.session,
pageCloseBtn: common.showCartCloseBtn('checkout'),
checkout: 'hidden',
page: 'checkout',
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
showFooter: 'showFooter'
2018-01-07 04:55:48 +10:00
});
});
router.get('/pay', async (req, res, next) => {
2018-02-23 03:41:24 +10:00
const config = req.app.config;
2018-01-07 04:55:48 +10:00
// if there is no items in the cart then render a failure
if(!req.session.cart){
req.session.message = 'The are no items in your cart. Please add some items before checking out';
req.session.messageType = 'danger';
res.redirect('/checkout');
return;
}
// render the payment page
2018-01-15 07:11:22 +10:00
res.render(`${config.themeViews}pay`, {
2018-01-07 04:55:48 +10:00
title: 'Pay',
2018-02-23 03:41:24 +10:00
config: req.app.config,
2018-01-07 04:55:48 +10:00
paymentConfig: common.getPaymentConfig(),
pageCloseBtn: common.showCartCloseBtn('pay'),
session: req.session,
paymentPage: true,
page: 'pay',
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
showFooter: 'showFooter'
2018-01-07 04:55:48 +10:00
});
});
router.get('/cartPartial', (req, res) => {
2018-02-23 03:41:24 +10:00
const config = req.app.config;
2018-02-14 06:21:22 +10:00
res.render(`${config.themeViews}cart`, {
2018-01-07 04:55:48 +10:00
pageCloseBtn: common.showCartCloseBtn(req.query.path),
page: req.query.path,
layout: false,
helpers: req.handlebars.helpers,
2018-02-23 03:41:24 +10:00
config: req.app.config,
2018-01-07 04:55:48 +10:00
session: req.session
});
});
// show an individual product
router.get('/product/:id', (req, res) => {
2018-01-07 05:35:49 +10:00
let db = req.app.db;
2018-02-23 03:41:24 +10:00
let config = req.app.config;
2018-01-07 04:55:48 +10:00
2018-01-07 05:35:49 +10:00
db.products.findOne({$or: [{_id: common.getId(req.params.id)}, {productPermalink: req.params.id}]}, (err, result) => {
2018-01-07 04:55:48 +10:00
// render 404 if page is not published
if(err){
res.render('error', {title: 'Not found', message: 'Product not found', helpers: req.handlebars.helpers, config});
2018-01-07 04:55:48 +10:00
}
if(err || result == null || result.productPublished === 'false'){
res.render('error', {title: 'Not found', message: 'Product not found', helpers: req.handlebars.helpers, config});
2018-01-07 04:55:48 +10:00
}else{
2018-01-07 05:35:49 +10:00
let productOptions = {};
2018-01-07 04:55:48 +10:00
if(result.productOptions){
productOptions = JSON.parse(result.productOptions);
}
2018-02-23 03:41:24 +10:00
// If JSON query param return json instead
if(req.query.json === 'true'){
res.status(200).json(result);
return;
}
2018-01-07 04:55:48 +10:00
// show the view
common.getImages(result._id, req, res, async (images) => {
2018-01-15 07:11:22 +10:00
res.render(`${config.themeViews}product`, {
2018-01-07 04:55:48 +10:00
title: result.productTitle,
result: result,
productOptions: productOptions,
images: images,
productDescription: result.productDescription,
metaDescription: config.cartTitle + ' - ' + result.productTitle,
pageCloseBtn: common.showCartCloseBtn('product'),
config: config,
session: req.session,
pageUrl: config.baseUrl + req.originalUrl,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.sortMenu(await common.getMenu(db))
2018-01-07 04:55:48 +10:00
});
});
}
});
});
2018-02-04 01:23:59 +10:00
// Updates a single product quantity
router.post('/product/updatecart', (req, res, next) => {
const db = req.app.db;
2019-02-09 20:46:41 +10:00
const config = req.app.config;
2018-02-04 01:23:59 +10:00
let cartItems = JSON.parse(req.body.items);
let hasError = false;
2019-02-09 20:46:41 +10:00
let stockError = false;
2018-01-07 04:55:48 +10:00
2018-02-04 01:23:59 +10:00
async.eachSeries(cartItems, (cartItem, callback) => {
let productQuantity = cartItem.itemQuantity ? cartItem.itemQuantity : 1;
if(cartItem.itemQuantity === 0){
// quantity equals zero so we remove the item
req.session.cart.splice(cartItem.cartIndex, 1);
callback(null);
}else{
db.products.findOne({_id: common.getId(cartItem.productId)}, (err, product) => {
if(err){
console.error(colors.red('Error updating cart', err));
}
if(product){
2019-02-09 20:46:41 +10:00
// If stock management on check there is sufficient stock for this product
if(config.trackStock){
if(productQuantity > product.productStock){
hasError = true;
stockError = true;
callback(null);
return;
}
}
2018-02-04 01:23:59 +10:00
let productPrice = parseFloat(product.productPrice).toFixed(2);
if(req.session.cart[cartItem.cartIndex]){
req.session.cart[cartItem.cartIndex].quantity = productQuantity;
req.session.cart[cartItem.cartIndex].totalItemPrice = productPrice * productQuantity;
callback(null);
}
}else{
hasError = true;
callback(null);
}
2018-01-07 04:55:48 +10:00
});
2018-02-04 01:23:59 +10:00
}
}, () => {
// update total cart amount
common.updateTotalCartAmount(req, res);
// show response
if(hasError === false){
res.status(200).json({message: 'Cart successfully updated', totalCartItems: Object.keys(req.session.cart).length});
2018-01-07 04:55:48 +10:00
}else{
2019-02-09 20:46:41 +10:00
if(stockError){
res.status(400).json({message: 'There is insufficient stock of this product.', totalCartItems: Object.keys(req.session.cart).length});
}else{
res.status(400).json({message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length});
}
2018-01-07 04:55:48 +10:00
}
});
});
2018-02-04 01:23:59 +10:00
// Remove single product from cart
router.post('/product/removefromcart', (req, res, next) => {
// remove item from cart
async.each(req.session.cart, (item, callback) => {
if(item){
if(item.productId === req.body.cart_index){
2018-02-04 05:14:13 +10:00
req.session.cart = _.pull(req.session.cart, item);
2018-02-04 01:23:59 +10:00
}
2018-01-07 04:55:48 +10:00
}
2018-02-04 01:23:59 +10:00
callback();
}, () => {
// update total cart amount
common.updateTotalCartAmount(req, res);
res.status(200).json({message: 'Product successfully removed', totalCartItems: Object.keys(req.session.cart).length});
2018-01-07 04:55:48 +10:00
});
});
2018-02-04 01:23:59 +10:00
// Totally empty the cart
router.post('/product/emptycart', (req, res, next) => {
delete req.session.cart;
delete req.session.orderId;
2018-01-07 04:55:48 +10:00
2018-02-04 01:23:59 +10:00
// update total cart amount
common.updateTotalCartAmount(req, res);
res.status(200).json({message: 'Cart successfully emptied', totalCartItems: 0});
});
// Add item to cart
router.post('/product/addtocart', (req, res, next) => {
const db = req.app.db;
2019-02-09 14:07:02 +10:00
const config = req.app.config;
2018-02-04 01:23:59 +10:00
let productQuantity = req.body.productQuantity ? parseInt(req.body.productQuantity) : 1;
const productComment = req.body.productComment ? req.body.productComment : null;
2018-02-04 01:23:59 +10:00
2018-05-21 23:36:12 +10:00
// Don't allow negative quantity
if(productQuantity < 0){
productQuantity = 1;
}
2018-02-04 01:23:59 +10:00
// setup cart object if it doesn't exist
if(!req.session.cart){
req.session.cart = [];
}
// Get the item from the DB
db.products.findOne({_id: common.getId(req.body.productId)}, (err, product) => {
2018-01-07 04:55:48 +10:00
if(err){
2018-02-04 01:23:59 +10:00
console.error(colors.red('Error adding to cart', err));
2018-02-23 03:41:24 +10:00
return res.status(400).json({message: 'Error updating cart. Please try again.'});
2018-01-07 04:55:48 +10:00
}
2018-02-23 03:41:24 +10:00
// No product found
if(!product){
return res.status(400).json({message: 'Error updating cart. Please try again.'});
}
2018-02-04 01:23:59 +10:00
2019-02-09 14:07:02 +10:00
// If stock management on check there is sufficient stock for this product
if(config.trackStock){
if(productQuantity > product.productStock){
return res.status(400).json({message: 'There is insufficient stock of this product.'});
}
}
2018-02-23 03:41:24 +10:00
let productPrice = parseFloat(product.productPrice).toFixed(2);
2018-02-04 01:23:59 +10:00
2018-02-23 03:41:24 +10:00
// Doc used to test if existing in the cart with the options. If not found, we add new.
let options = {};
if(req.body.productOptions){
options = JSON.parse(req.body.productOptions);
}
let findDoc = {
productId: req.body.productId,
options: options
};
// if exists we add to the existing value
let cartIndex = _.findIndex(req.session.cart, findDoc);
if(cartIndex > -1){
req.session.cart[cartIndex].quantity = parseInt(req.session.cart[cartIndex].quantity) + productQuantity;
req.session.cart[cartIndex].totalItemPrice = productPrice * parseInt(req.session.cart[cartIndex].quantity);
}else{
// Doesnt exist so we add to the cart session
req.session.cartTotalItems = req.session.cartTotalItems + productQuantity;
// new product deets
let productObj = {};
productObj.productId = req.body.productId;
productObj.title = product.productTitle;
productObj.quantity = productQuantity;
productObj.totalItemPrice = productPrice * productQuantity;
productObj.options = options;
productObj.productImage = product.productImage;
productObj.productComment = productComment;
2018-02-23 03:41:24 +10:00
if(product.productPermalink){
productObj.link = product.productPermalink;
}else{
productObj.link = product._id;
2018-01-07 04:55:48 +10:00
}
2018-02-04 01:23:59 +10:00
2018-02-23 03:41:24 +10:00
// merge into the current cart
req.session.cart.push(productObj);
2018-01-07 04:55:48 +10:00
}
2018-02-23 03:41:24 +10:00
// update total cart amount
common.updateTotalCartAmount(req, res);
// update how many products in the shopping cart
req.session.cartTotalItems = req.session.cart.reduce((a, b) => +a + +b.quantity, 0);
return res.status(200).json({message: 'Cart successfully updated', totalCartItems: req.session.cartTotalItems});
2018-01-07 04:55:48 +10:00
});
});
// search products
2018-01-07 05:35:49 +10:00
router.get('/search/:searchTerm/:pageNum?', (req, res) => {
let db = req.app.db;
2018-01-07 05:35:49 +10:00
let searchTerm = req.params.searchTerm;
let productsIndex = req.app.productsIndex;
2018-02-23 03:41:24 +10:00
let config = req.app.config;
2018-01-07 05:35:49 +10:00
let numberProducts = config.productsPerPage ? config.productsPerPage : 6;
let lunrIdArray = [];
productsIndex.search(searchTerm).forEach((id) => {
2018-01-07 22:14:17 +10:00
lunrIdArray.push(common.getId(id.ref));
2018-01-07 04:55:48 +10:00
});
2018-01-07 05:35:49 +10:00
let pageNum = 1;
2018-01-07 04:55:48 +10:00
if(req.params.pageNum){
pageNum = req.params.pageNum;
}
2018-01-15 07:11:22 +10:00
Promise.all([
common.getData(req, pageNum, {_id: {$in: lunrIdArray}}),
common.getMenu(db)
])
.then(([results, menu]) => {
2018-02-23 03:41:24 +10:00
// If JSON query param return json instead
if(req.query.json === 'true'){
res.status(200).json(results.data);
return;
}
2018-01-15 07:11:22 +10:00
res.render(`${config.themeViews}index`, {
2018-01-07 04:55:48 +10:00
title: 'Results',
results: results.data,
filtered: true,
session: req.session,
2018-02-23 03:41:24 +10:00
metaDescription: req.app.config.cartTitle + ' - Search term: ' + searchTerm,
2018-01-07 04:55:48 +10:00
searchTerm: searchTerm,
pageCloseBtn: common.showCartCloseBtn('search'),
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
productsPerPage: numberProducts,
totalProductCount: results.totalProducts,
pageNum: pageNum,
paginateUrl: 'search',
config: config,
2018-01-15 07:11:22 +10:00
menu: common.sortMenu(menu),
2018-01-07 04:55:48 +10:00
helpers: req.handlebars.helpers,
showFooter: 'showFooter'
});
2018-01-15 07:11:22 +10:00
})
.catch((err) => {
console.error(colors.red('Error searching for products', err));
2018-01-07 04:55:48 +10:00
});
});
// search products
2018-01-07 05:35:49 +10:00
router.get('/category/:cat/:pageNum?', (req, res) => {
let db = req.app.db;
2018-01-07 05:35:49 +10:00
let searchTerm = req.params.cat;
let productsIndex = req.app.productsIndex;
2018-02-23 03:41:24 +10:00
let config = req.app.config;
2018-01-07 05:35:49 +10:00
let numberProducts = config.productsPerPage ? config.productsPerPage : 6;
let lunrIdArray = [];
productsIndex.search(searchTerm).forEach((id) => {
lunrIdArray.push(common.getId(id.ref));
2018-01-07 04:55:48 +10:00
});
2018-01-07 05:35:49 +10:00
let pageNum = 1;
2018-01-07 04:55:48 +10:00
if(req.params.pageNum){
pageNum = req.params.pageNum;
}
2018-01-15 07:11:22 +10:00
Promise.all([
common.getData(req, pageNum, {_id: {$in: lunrIdArray}}),
common.getMenu(db)
])
.then(([results, menu]) => {
const sortedMenu = common.sortMenu(menu);
2018-01-07 04:55:48 +10:00
2018-02-23 03:41:24 +10:00
// If JSON query param return json instead
if(req.query.json === 'true'){
res.status(200).json(results.data);
return;
}
2018-01-15 07:11:22 +10:00
res.render(`${config.themeViews}index`, {
2018-01-07 04:55:48 +10:00
title: 'Category',
results: results.data,
filtered: true,
session: req.session,
searchTerm: searchTerm,
2018-02-23 03:41:24 +10:00
metaDescription: req.app.config.cartTitle + ' - Category: ' + searchTerm,
2018-01-07 04:55:48 +10:00
pageCloseBtn: common.showCartCloseBtn('category'),
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
productsPerPage: numberProducts,
totalProductCount: results.totalProducts,
pageNum: pageNum,
2018-01-15 07:11:22 +10:00
menuLink: _.find(sortedMenu.items, (obj) => { return obj.link === searchTerm; }),
2018-01-07 04:55:48 +10:00
paginateUrl: 'category',
config: config,
2018-01-15 07:11:22 +10:00
menu: sortedMenu,
2018-01-07 04:55:48 +10:00
helpers: req.handlebars.helpers,
showFooter: 'showFooter'
});
2018-01-15 07:11:22 +10:00
})
.catch((err) => {
console.error(colors.red('Error getting products for category', err));
2018-01-07 04:55:48 +10:00
});
});
// return sitemap
2018-01-07 05:35:49 +10:00
router.get('/sitemap.xml', (req, res, next) => {
let sm = require('sitemap');
2018-02-23 03:41:24 +10:00
let config = req.app.config;
2018-01-07 04:55:48 +10:00
2018-01-07 05:35:49 +10:00
common.addSitemapProducts(req, res, (err, products) => {
2018-01-07 04:55:48 +10:00
if(err){
console.error(colors.red('Error generating sitemap.xml', err));
}
2018-01-07 05:35:49 +10:00
let sitemap = sm.createSitemap(
2018-01-07 04:55:48 +10:00
{
hostname: config.baseUrl,
cacheTime: 600000,
urls: [
{url: '/', changefreq: 'weekly', priority: 1.0}
]
});
2018-01-07 05:35:49 +10:00
let currentUrls = sitemap.urls;
let mergedUrls = currentUrls.concat(products);
2018-01-07 04:55:48 +10:00
sitemap.urls = mergedUrls;
// render the sitemap
2018-01-07 05:35:49 +10:00
sitemap.toXML((err, xml) => {
2018-01-07 04:55:48 +10:00
if(err){
return res.status(500).end();
}
res.header('Content-Type', 'application/xml');
res.send(xml);
return true;
});
});
});
2018-01-07 05:35:49 +10:00
router.get('/page/:pageNum', (req, res, next) => {
let db = req.app.db;
2018-02-23 03:41:24 +10:00
let config = req.app.config;
2018-01-07 05:35:49 +10:00
let numberProducts = config.productsPerPage ? config.productsPerPage : 6;
2018-01-07 04:55:48 +10:00
2018-01-15 07:11:22 +10:00
Promise.all([
common.getData(req, req.params.pageNum),
common.getMenu(db)
])
.then(([results, menu]) => {
2018-02-23 03:41:24 +10:00
// If JSON query param return json instead
if(req.query.json === 'true'){
res.status(200).json(results.data);
return;
}
2018-01-15 07:11:22 +10:00
res.render(`${config.themeViews}index`, {
2018-01-07 04:55:48 +10:00
title: 'Shop',
results: results.data,
session: req.session,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
2018-02-23 03:41:24 +10:00
metaDescription: req.app.config.cartTitle + ' - Products page: ' + req.params.pageNum,
2018-01-07 04:55:48 +10:00
pageCloseBtn: common.showCartCloseBtn('page'),
2018-02-23 03:41:24 +10:00
config: req.app.config,
2018-01-07 04:55:48 +10:00
productsPerPage: numberProducts,
totalProductCount: results.totalProducts,
pageNum: req.params.pageNum,
paginateUrl: 'page',
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
2018-01-15 07:11:22 +10:00
menu: common.sortMenu(menu)
2018-01-07 04:55:48 +10:00
});
2018-01-15 07:11:22 +10:00
})
.catch((err) => {
console.error(colors.red('Error getting products for page', err));
2018-01-07 04:55:48 +10:00
});
});
2018-01-15 07:11:22 +10:00
// The main entry point of the shop
2018-01-07 05:35:49 +10:00
router.get('/:page?', (req, res, next) => {
let db = req.app.db;
2018-02-23 03:41:24 +10:00
let config = req.app.config;
2018-01-07 05:35:49 +10:00
let numberProducts = config.productsPerPage ? config.productsPerPage : 6;
2018-01-07 04:55:48 +10:00
// if no page is specified, just render page 1 of the cart
if(!req.params.page){
2018-01-15 07:11:22 +10:00
Promise.all([
common.getData(req, 1, {}),
common.getMenu(db)
])
.then(([results, menu]) => {
2018-02-23 03:41:24 +10:00
// If JSON query param return json instead
if(req.query.json === 'true'){
res.status(200).json(results.data);
return;
}
2018-01-15 07:11:22 +10:00
res.render(`${config.themeViews}index`, {
title: `${config.cartTitle} - Shop`,
2018-01-07 04:55:48 +10:00
theme: config.theme,
results: results.data,
session: req.session,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
pageCloseBtn: common.showCartCloseBtn('page'),
2018-02-23 03:41:24 +10:00
config: req.app.config,
2018-01-07 04:55:48 +10:00
productsPerPage: numberProducts,
totalProductCount: results.totalProducts,
pageNum: 1,
paginateUrl: 'page',
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
2018-01-15 07:11:22 +10:00
menu: common.sortMenu(menu)
2018-01-07 04:55:48 +10:00
});
2018-01-15 07:11:22 +10:00
})
.catch((err) => {
console.error(colors.red('Error getting products for page', err));
2018-01-07 04:55:48 +10:00
});
}else{
if(req.params.page === 'admin'){
next();
return;
}
// lets look for a page
db.pages.findOne({pageSlug: req.params.page, pageEnabled: 'true'}, async (err, page) => {
2018-01-07 04:55:48 +10:00
if(err){
console.error(colors.red('Error getting page', err));
}
// if we have a page lets render it, else throw 404
if(page){
2018-01-15 07:11:22 +10:00
res.render(`${config.themeViews}page`, {
2018-01-07 04:55:48 +10:00
title: page.pageName,
page: page,
2018-02-12 05:47:26 +10:00
searchTerm: req.params.page,
2018-01-07 04:55:48 +10:00
session: req.session,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
pageCloseBtn: common.showCartCloseBtn('page'),
2018-02-23 03:41:24 +10:00
config: req.app.config,
metaDescription: req.app.config.cartTitle + ' - ' + page,
2018-01-07 04:55:48 +10:00
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.sortMenu(await common.getMenu(db))
2018-01-07 04:55:48 +10:00
});
}else{
res.status(404).render('error', {
title: '404 Error - Page not found',
2018-02-23 03:41:24 +10:00
config: req.app.config,
2018-01-07 04:55:48 +10:00
message: '404 Error - Page not found',
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.sortMenu(await common.getMenu(db))
});
2018-01-07 04:55:48 +10:00
}
});
}
});
module.exports = router;