From 4533e23993379e834eb00bae56872ae006e77a21 Mon Sep 17 00:00:00 2001 From: Mark Moffat Date: Mon, 5 Feb 2018 20:51:04 +0100 Subject: [PATCH] Added permission validation to non admin users --- app.js | 2 +- lib/common.js | 52 ++++- public/javascripts/expressCart.js | 8 +- public/javascripts/expressCart.min.js | 2 +- routes/admin.js | 56 +++-- routes/order.js | 2 +- routes/product.js | 20 +- views/partials/menu.hbs | 4 + views/settings.hbs | 294 ++++++++++++++------------ 9 files changed, 249 insertions(+), 191 deletions(-) diff --git a/app.js b/app.js index 6bf0452..38a7e93 100644 --- a/app.js +++ b/app.js @@ -235,7 +235,7 @@ app.use('/', customer); app.use('/', product); app.use('/', order); app.use('/', user); -app.use('/admin', admin); +app.use('/', admin); app.use('/paypal', paypal); app.use('/stripe', stripe); app.use('/authorizenet', authorizenet); diff --git a/lib/common.js b/lib/common.js index e42d68b..1ace6ea 100644 --- a/lib/common.js +++ b/lib/common.js @@ -11,7 +11,35 @@ const nodemailer = require('nodemailer'); const escape = require('html-entities').AllHtmlEntities; let ObjectId = require('mongodb').ObjectID; +const restrictedRoutes = [ + {route: '/admin/product/new', response: 'redirect'}, + {route: '/admin/product/insert', response: 'redirect'}, + {route: '/admin/product/edit/:id', response: 'redirect'}, + {route: '/admin/product/update', response: 'redirect'}, + {route: '/admin/product/delete/:id', response: 'redirect'}, + {route: '/admin/product/published_state', response: 'json'}, + {route: '/admin/product/setasmainimage', response: 'json'}, + {route: '/admin/product/deleteimage', response: 'json'}, + {route: '/admin/order/statusupdate', response: 'json'}, + {route: '/admin/settings/update', response: 'json'}, + {route: '/admin/settings/option/remove', response: 'json'}, + {route: '/admin/settings/pages/new', response: 'redirect'}, + {route: '/admin/settings/pages/edit/:page', response: 'redirect'}, + {route: '/admin/settings/pages/update', response: 'json'}, + {route: '/admin/settings/pages/delete/:page', response: 'redirect'}, + {route: '/admin/settings/menu/new', response: 'redirect'}, + {route: '/admin/settings/menu/update', response: 'redirect'}, + {route: '/admin/settings/menu/delete/:menuid', response: 'redirect'}, + {route: '/admin/settings/menu/save_order', response: 'json'}, + {route: '/admin/file/upload', response: 'redirect'}, + {route: '/admin/file/delete', response: 'json'} +]; + // common functions +exports.restrict = (req, res, next) => { + exports.checkLogin(req, res, next); +}; + exports.checkLogin = (req, res, next) => { // if not protecting we check for public pages and don't checkLogin if(req.session.needsSetup === true){ @@ -26,6 +54,26 @@ exports.checkLogin = (req, res, next) => { res.redirect('/admin/login'); }; +// Middleware to check for admin access for certain route +exports.checkAccess = (req, res, next) => { + const routeCheck = _.find(restrictedRoutes, {'route': req.route.path}); + + // If the user is not an admin and route is restricted, show message and redirect to /admin + if(req.session.isAdmin === 'false' && routeCheck){ + if(routeCheck.response === 'redirect'){ + req.session.message = 'Unauthorised. Please refer to administrator.'; + req.session.messageType = 'danger'; + res.redirect('/admin'); + return; + } + if(routeCheck.response === 'json'){ + res.status(400).json({message: 'Unauthorised. Please refer to administrator.'}); + } + }else{ + next(); + } +}; + exports.showCartCloseBtn = (page) => { let showCartCloseButton = true; if(page === 'checkout' || page === 'pay'){ @@ -64,10 +112,6 @@ exports.addSitemapProducts = (req, res, cb) => { }); }; -exports.restrict = (req, res, next) => { - exports.checkLogin(req, res, next); -}; - exports.clearSessionValue = (session, sessionVar) => { let temp; if(session){ diff --git a/public/javascripts/expressCart.js b/public/javascripts/expressCart.js index 8beb048..bf75594 100644 --- a/public/javascripts/expressCart.js +++ b/public/javascripts/expressCart.js @@ -50,7 +50,7 @@ $(document).ready(function (){ showNotification(msg, 'success'); }) .fail(function(msg){ - showNotification(msg.responseText, 'danger'); + showNotification(msg.responseJSON.message, 'danger'); }); }); @@ -118,10 +118,10 @@ $(document).ready(function (){ data: {id: this.id, state: this.checked} }) .done(function(msg){ - showNotification(msg, 'success'); + showNotification(msg.message, 'success'); }) .fail(function(msg){ - showNotification(msg.responseText, 'danger'); + showNotification(msg.responseJSON.message, 'danger'); }); }); @@ -540,7 +540,7 @@ $(document).ready(function (){ showNotification(msg, 'success'); }) .fail(function(msg){ - showNotification(msg.responseText, 'danger'); + showNotification(msg.responseJSON.message, 'danger'); }); }else{ showNotification('Please enter a permalink to validate', 'danger'); diff --git a/public/javascripts/expressCart.min.js b/public/javascripts/expressCart.min.js index c0c2e11..bfe68ba 100644 --- a/public/javascripts/expressCart.min.js +++ b/public/javascripts/expressCart.min.js @@ -1 +1 @@ -function deleteFromCart(t){$.ajax({method:"POST",url:"/product/removefromcart",data:{cart_index:t.attr("data-id")}}).done(function(e){$("#cart-count").text(e.totalCartItems),0===e.totalCartItems?($(t).closest(".cart-row").hide("slow",function(){$(t).closest(".cart-row").remove()}),$(".cart-contents-shipping").hide("slow",function(){$(".cart-contents-shipping").remove()}),showNotification(e.message,"success"),setTimeout(function(){window.location="/"},3700)):($(t).closest(".cart-row").hide("slow",function(){$(t).closest(".cart-row").remove()}),showNotification(e.message,"success"))}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}function slugify(t){return $.trim(t).replace(/[^a-z0-9-æøå]/gi,"-").replace(/-+/g,"-").replace(/^-|-$/g,"").replace(/æ/gi,"ae").replace(/ø/gi,"oe").replace(/å/gi,"a").toLowerCase()}function cartUpdate(t){$(t).val()<1?deleteFromCart($(t)):""!==$(t).val()&&updateCart()}function updateCart(){var t=[];$(".cart-product-quantity").each(function(){var e={cartIndex:$(this).attr("id"),itemQuantity:$(this).val(),productId:$(this).attr("data-id")};$(this).val()<0?deleteFromCart($(this)):t.push(e)}),$.ajax({method:"POST",url:"/product/updatecart",data:{items:JSON.stringify(t)}}).done(function(t){updateCartDiv(),$("#cart-count").text(t.totalCartItems)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}function updateCartDiv(){var t=window.location.pathname.split("/").length>0?window.location.pathname.split("/")[1]:"";$.ajax({method:"GET",url:"/cartPartial",data:{path:t}}).done(function(t){$("#cart").html(t)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}function getSelectedOptions(){var t={};return $(".product-opt").each(function(){t[$(this).attr("name")]=$(this).val()}),t}function showNotification(t,e,a){a=a||!1,$("#notify_message").removeClass(),$("#notify_message").addClass("alert-"+e),$("#notify_message").html(t),$("#notify_message").slideDown(600).delay(2500).slideUp(600,function(){!0===a&&location.reload()})}$(document).ready(function(){if($(window).width()<768&&($(".menu-side").on("click",function(t){t.preventDefault(),$('.menu-side li:not(".active")').slideToggle()}),$('.menu-side li:not(".active")').hide(),$(".menu-side>.active").html(''),$(".menu-side>.active").addClass("menu-side-mobile"),0===$("#navbar ul li").length&&$("#navbar").hide(),$("#offcanvasClose").hide()),$(".shipping-form input").each(function(t){$(this).wrap("
");var e=$(this).attr("placeholder");$(this).after('")}),$(".shipping-form input").on("focus",function(){$(this).next().addClass("floatLabel"),$(this).next().removeClass("hidden")}),$(".shipping-form input").on("blur",function(){""===$(this).val()&&($(this).next().addClass("hidden"),$(this).next().removeClass("floatLabel"))}),$(".menu-btn").on("click",function(t){t.preventDefault()}),$("#sendTestEmail").on("click",function(t){t.preventDefault(),$.ajax({method:"POST",url:"/admin/testEmail"}).done(function(t){showNotification(t,"success")}).fail(function(t){showNotification(t.responseText,"danger")})}),$("#footerHtml").length){var t=window.CodeMirror.fromTextArea(document.getElementById("footerHtml"),{mode:"xml",tabMode:"indent",theme:"flatly",lineNumbers:!0,htmlMode:!0,fixedGutter:!1});t.setValue(t.getValue())}if($("#googleAnalytics").length&&window.CodeMirror.fromTextArea(document.getElementById("googleAnalytics"),{mode:"xml",tabMode:"indent",theme:"flatly",lineNumbers:!0,htmlMode:!0,fixedGutter:!1}),$("#customCss").length){var e=window.CodeMirror.fromTextArea(document.getElementById("customCss"),{mode:"text/css",tabMode:"indent",theme:"flatly",lineNumbers:!0}),a=window.cssbeautify(e.getValue(),{indent:" ",autosemicolon:!0});e.setValue(a)}if($("table").each(function(){$(this).addClass("table table-hover")}),$("#frmProductTags").tokenfield(),$(document).on("click",".dashboard_list",function(t){window.document.location=$(this).attr("href")}).hover(function(){$(this).toggleClass("hover")}),$(".product-title").dotdotdot({ellipsis:"..."}),$('input[class="published_state"]').change(function(){$.ajax({method:"POST",url:"/admin/product/published_state",data:{id:this.id,state:this.checked}}).done(function(t){showNotification(t,"success")}).fail(function(t){showNotification(t.responseText,"danger")})}),$(document).on("click",".btn-qty-minus",function(t){var e=$(t.target).parent().parent().find(".cart-product-quantity");$(e).val(parseInt(e.val())-1),cartUpdate(e)}),$(document).on("click",".btn-qty-add",function(t){var e=$(t.target).parent().parent().find(".cart-product-quantity");$(e).val(parseInt(e.val())+1),cartUpdate(e)}),$(document).on("click",".orderFilterByStatus",function(t){t.preventDefault(),window.location="/admin/orders/bystatus/"+$("#orderStatusFilter").val()}),$("#pager").length){var o=$("#pageNum").val(),i=$("#productsPerPage").val(),n=$("#totalProductCount").val(),r=$("#paginateUrl").val(),s=$("#searchTerm").val();""!==s&&(s+="/");var c="/"+r+"/"+s+"{{number}}",d=Math.ceil(n/i);parseInt(n)>parseInt(i)&&$("#pager").bootpag({total:d,page:o,maxVisible:5,href:c})}if($(document).on("click","#btnPageUpdate",function(t){t.preventDefault(),$.ajax({method:"POST",url:"/admin/settings/pages/update",data:{page_id:$("#page_id").val(),pageName:$("#pageName").val(),pageSlug:$("#pageSlug").val(),pageEnabled:$("#pageEnabled").is(":checked"),pageContent:$("#pageContent").val()}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(document).on("click",".product_opt_remove",function(t){t.preventDefault();var e=$(this).closest("li").find(".opt-name").html();$.ajax({method:"POST",url:"/admin/settings/option/remove/",data:{productId:$("#frmProductId").val(),optName:e}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(document).on("click","#product_opt_add",function(t){t.preventDefault();var e=$("#product_optName").val(),a=$("#product_optLabel").val(),o=$("#product_optType").val(),i=$("#product_optOptions").val(),n={};""!==$("#productOptJson").val()&&(n=JSON.parse($("#productOptJson").val()));var r='
  • ';r+='
    ',r+='
    '+e+"
    ",r+='
    '+a+"
    ",r+='
    '+o+"
    ",r+='
    '+i+"
    ",r+='
    ',r+='',r+="
  • ",$("#product_opt_wrapper").append(r),n[e]={optName:e,optLabel:a,optType:o,optOptions:$.grep(i.split(","),function(t){return 0===t||t})},$("#productOptJson").val(JSON.stringify(n)),$("#product_optName").val(""),$("#product_optLabel").val(""),$("#product_optOptions").val("")}),$("#stripeButton").validator().on("click",function(t){(t.preventDefault(),0===$("#shipping-form").validator("validate").has(".has-error").length)&&window.StripeCheckout.configure({key:$("#stripeButton").data("key"),image:$("#stripeButton").data("image"),locale:"auto",token:function(t){$("#shipping-form").append(''),$("#shipping-form").submit()}}).open({name:$("#stripeButton").data("name"),description:$("#stripeButton").data("description"),zipCode:$("#stripeButton").data("zipCode"),amount:$("#stripeButton").data("amount"),currency:$("#stripeButton").data("currency")})}),$("#settingsForm").validator().on("submit",function(t){t.isDefaultPrevented()||(t.preventDefault(),$("#footerHtml_input").val($(".CodeMirror")[0].CodeMirror.getValue()),$("#googleAnalytics_input").val($(".CodeMirror")[1].CodeMirror.getValue()),$("#customCss_input").val($(".CodeMirror")[2].CodeMirror.getValue()),$.ajax({method:"POST",url:"/admin/settings/update",data:$("#settingsForm").serialize()}).done(function(t){showNotification(t.message,"success")}).fail(function(t){showNotification(t.responseJSON.message,"danger")}))}),$("#customerLogout").on("click",function(t){$.ajax({method:"POST",url:"/customer/logout",data:{}}).done(function(t){location.reload()})}),$("#createCustomerAccount").validator().on("click",function(t){t.preventDefault(),0===$("#shipping-form").validator("validate").has(".has-error").length&&$.ajax({method:"POST",url:"/customer/create",data:{email:$("#shipEmail").val(),firstName:$("#shipFirstname").val(),lastName:$("#shipLastname").val(),address1:$("#shipAddr1").val(),address2:$("#shipAddr2").val(),country:$("#shipCountry").val(),state:$("#shipState").val(),postcode:$("#shipPostcode").val(),phone:$("#shipPhoneNumber").val(),password:$("#newCustomerPassword").val()}}).done(function(t){location.reload()}).fail(function(t){showNotification(t.responseJSON.err,"danger")})}),$("#customerLogin").on("click",function(t){t.isDefaultPrevented()||(t.preventDefault(),$.ajax({method:"POST",url:"/customer/login_action",data:{loginEmail:$("#customerLoginEmail").val(),loginPassword:$("#customerLoginPassword").val()}}).done(function(t){var e=t.customer;$("#shipEmail").val(e.email),$("#shipFirstname").val(e.firstName),$("#shipLastname").val(e.lastName),$("#shipAddr1").val(e.address1),$("#shipAddr2").val(e.address2),$("#shipCountry").val(e.country),$("#shipState").val(e.state),$("#shipPostcode").val(e.postcode),$("#shipPhoneNumber").val(e.phone),location.reload()}).fail(function(t){showNotification(t.responseJSON.err,"danger")})),t.preventDefault()}),$(document).on("click",".image-next",function(t){var e=$(".thumbnail-image"),a=0,o=0;$(".thumbnail-image").each(function(){$("#product-title-image").attr("src")===$(this).attr("src")&&(o=a+1===e.length||a+1<0?0:a+1),a++}),$("#product-title-image").attr("src",$(e).eq(o).attr("src"))}),$(document).on("click",".image-prev",function(t){var e=$(".thumbnail-image"),a=0,o=0;$(".thumbnail-image").each(function(){$("#product-title-image").attr("src")===$(this).attr("src")&&(o=a-1===e.length||a-1<0?e.length-1:a-1),a++}),$("#product-title-image").attr("src",$(e).eq(o).attr("src"))}),$(document).on("click","#orderStatusUpdate",function(t){$.ajax({method:"POST",url:"/admin/order/statusupdate",data:{order_id:$("#order_id").val(),status:$("#orderStatus").val()}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(document).on("click",".product-add-to-cart",function(t){var e=getSelectedOptions();$.ajax({method:"POST",url:"/product/addtocart",data:{productId:$("#productId").val(),productQuantity:$("#product_quantity").val(),productOptions:JSON.stringify(e)}}).done(function(t){$("#cart-count").text(t.totalCartItems),updateCartDiv(),showNotification(t.message,"success")}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(".cart-product-quantity").on("input",function(){cartUpdate()}),$(document).on("click",".pushy-link",function(t){$("body").removeClass("pushy-open-right")}),$(document).on("click",".add-to-cart",function(t){var e="/product/"+$(this).attr("data-id");$(this).attr("data-link")&&(e="/product/"+$(this).attr("data-link")),"true"===$(this).attr("data-has-options")?window.location=e:$.ajax({method:"POST",url:"/product/addtocart",data:{productId:$(this).attr("data-id")}}).done(function(t){$("#cart-count").text(t.totalCartItems),updateCartDiv(),showNotification(t.message,"success")}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(document).on("click","#empty-cart",function(t){$.ajax({method:"POST",url:"/product/emptycart"}).done(function(t){$("#cart-count").text(t.totalCartItems),updateCartDiv(),showNotification(t.message,"success",!0)})}),$(".qty-btn-minus").on("click",function(){$(this).parent().siblings("input").val(parseInt($(this).parent().siblings("input").val())-1)}),$(".qty-btn-plus").on("click",function(){$(this).parent().siblings("input").val(parseInt($(this).parent().siblings("input").val())+1)}),$(".thumbnail-image").on("click",function(){$("#product-title-image").attr("src",$(this).attr("src"))}),$(".set-as-main-image").on("click",function(){$.ajax({method:"POST",url:"/admin/product/setasmainimage",data:{product_id:$("#frmProductId").val(),productImage:$(this).attr("data-id")}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(".btn-delete-image").on("click",function(){$.ajax({method:"POST",url:"/admin/product/deleteimage",data:{product_id:$("#frmProductId").val(),productImage:$(this).attr("data-id")}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(document).on("click","#validate_permalink",function(t){""!==$("#frmProductPermalink").val()?$.ajax({method:"POST",url:"/admin/api/validate_permalink",data:{permalink:$("#frmProductPermalink").val(),docId:$("#frmProductId").val()}}).done(function(t){showNotification(t,"success")}).fail(function(t){showNotification(t.responseText,"danger")}):showNotification("Please enter a permalink to validate","danger")}),$(document).on("click","#btn_product_filter",function(t){""!==$("#product_filter").val()?window.location.href="/admin/products/filter/"+$("#product_filter").val():showNotification("Please enter a keyword to filter","danger")}),$(document).on("click","#btn_order_filter",function(t){""!==$("#order_filter").val()?window.location.href="/admin/orders/filter/"+$("#order_filter").val():showNotification("Please enter a keyword to filter","danger")}),$(document).on("click","#btn_customer_filter",function(t){""!==$("#customer_filter").val()?window.location.href="/admin/customers/filter/"+$("#customer_filter").val():showNotification("Please enter a keyword to filter","danger")}),$(document).on("click","#btn_search_reset",function(t){window.location.replace("/")}),$(document).on("click","#btn_search",function(t){t.preventDefault(),""===$("#frm_search").val().trim()?showNotification("Please enter a search value","danger"):window.location.href="/search/"+$("#frm_search").val()}),$(document).on("click","#frm_edit_product_save",function(t){""===$("#frmProductPermalink").val()&&""!==$("#frmProductTitle").val()&&$("#frmProductPermalink").val(slugify($("#frmProductTitle").val()))}),""!==$("#input_notify_message").val()){var l=$("#input_notify_message").val(),u=$("#input_notify_messageType").val();$("#input_notify_message").val(""),$("#input_notify_messageType").val(""),showNotification(l,u,!1)}}); \ No newline at end of file +function deleteFromCart(t){$.ajax({method:"POST",url:"/product/removefromcart",data:{cart_index:t.attr("data-id")}}).done(function(e){$("#cart-count").text(e.totalCartItems),0===e.totalCartItems?($(t).closest(".cart-row").hide("slow",function(){$(t).closest(".cart-row").remove()}),$(".cart-contents-shipping").hide("slow",function(){$(".cart-contents-shipping").remove()}),showNotification(e.message,"success"),setTimeout(function(){window.location="/"},3700)):($(t).closest(".cart-row").hide("slow",function(){$(t).closest(".cart-row").remove()}),showNotification(e.message,"success"))}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}function slugify(t){return $.trim(t).replace(/[^a-z0-9-æøå]/gi,"-").replace(/-+/g,"-").replace(/^-|-$/g,"").replace(/æ/gi,"ae").replace(/ø/gi,"oe").replace(/å/gi,"a").toLowerCase()}function cartUpdate(t){$(t).val()<1?deleteFromCart($(t)):""!==$(t).val()&&updateCart()}function updateCart(){var t=[];$(".cart-product-quantity").each(function(){var e={cartIndex:$(this).attr("id"),itemQuantity:$(this).val(),productId:$(this).attr("data-id")};$(this).val()<0?deleteFromCart($(this)):t.push(e)}),$.ajax({method:"POST",url:"/product/updatecart",data:{items:JSON.stringify(t)}}).done(function(t){updateCartDiv(),$("#cart-count").text(t.totalCartItems)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}function updateCartDiv(){var t=window.location.pathname.split("/").length>0?window.location.pathname.split("/")[1]:"";$.ajax({method:"GET",url:"/cartPartial",data:{path:t}}).done(function(t){$("#cart").html(t)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}function getSelectedOptions(){var t={};return $(".product-opt").each(function(){t[$(this).attr("name")]=$(this).val()}),t}function showNotification(t,e,a){a=a||!1,$("#notify_message").removeClass(),$("#notify_message").addClass("alert-"+e),$("#notify_message").html(t),$("#notify_message").slideDown(600).delay(2500).slideUp(600,function(){!0===a&&location.reload()})}$(document).ready(function(){if($(window).width()<768&&($(".menu-side").on("click",function(t){t.preventDefault(),$('.menu-side li:not(".active")').slideToggle()}),$('.menu-side li:not(".active")').hide(),$(".menu-side>.active").html(''),$(".menu-side>.active").addClass("menu-side-mobile"),0===$("#navbar ul li").length&&$("#navbar").hide(),$("#offcanvasClose").hide()),$(".shipping-form input").each(function(t){$(this).wrap("
    ");var e=$(this).attr("placeholder");$(this).after('")}),$(".shipping-form input").on("focus",function(){$(this).next().addClass("floatLabel"),$(this).next().removeClass("hidden")}),$(".shipping-form input").on("blur",function(){""===$(this).val()&&($(this).next().addClass("hidden"),$(this).next().removeClass("floatLabel"))}),$(".menu-btn").on("click",function(t){t.preventDefault()}),$("#sendTestEmail").on("click",function(t){t.preventDefault(),$.ajax({method:"POST",url:"/admin/testEmail"}).done(function(t){showNotification(t,"success")}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$("#footerHtml").length){var t=window.CodeMirror.fromTextArea(document.getElementById("footerHtml"),{mode:"xml",tabMode:"indent",theme:"flatly",lineNumbers:!0,htmlMode:!0,fixedGutter:!1});t.setValue(t.getValue())}if($("#googleAnalytics").length&&window.CodeMirror.fromTextArea(document.getElementById("googleAnalytics"),{mode:"xml",tabMode:"indent",theme:"flatly",lineNumbers:!0,htmlMode:!0,fixedGutter:!1}),$("#customCss").length){var e=window.CodeMirror.fromTextArea(document.getElementById("customCss"),{mode:"text/css",tabMode:"indent",theme:"flatly",lineNumbers:!0}),a=window.cssbeautify(e.getValue(),{indent:" ",autosemicolon:!0});e.setValue(a)}if($("table").each(function(){$(this).addClass("table table-hover")}),$("#frmProductTags").tokenfield(),$(document).on("click",".dashboard_list",function(t){window.document.location=$(this).attr("href")}).hover(function(){$(this).toggleClass("hover")}),$(".product-title").dotdotdot({ellipsis:"..."}),$('input[class="published_state"]').change(function(){$.ajax({method:"POST",url:"/admin/product/published_state",data:{id:this.id,state:this.checked}}).done(function(t){showNotification(t.message,"success")}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(document).on("click",".btn-qty-minus",function(t){var e=$(t.target).parent().parent().find(".cart-product-quantity");$(e).val(parseInt(e.val())-1),cartUpdate(e)}),$(document).on("click",".btn-qty-add",function(t){var e=$(t.target).parent().parent().find(".cart-product-quantity");$(e).val(parseInt(e.val())+1),cartUpdate(e)}),$(document).on("click",".orderFilterByStatus",function(t){t.preventDefault(),window.location="/admin/orders/bystatus/"+$("#orderStatusFilter").val()}),$("#pager").length){var o=$("#pageNum").val(),i=$("#productsPerPage").val(),n=$("#totalProductCount").val(),r=$("#paginateUrl").val(),s=$("#searchTerm").val();""!==s&&(s+="/");var c="/"+r+"/"+s+"{{number}}",d=Math.ceil(n/i);parseInt(n)>parseInt(i)&&$("#pager").bootpag({total:d,page:o,maxVisible:5,href:c})}if($(document).on("click","#btnPageUpdate",function(t){t.preventDefault(),$.ajax({method:"POST",url:"/admin/settings/pages/update",data:{page_id:$("#page_id").val(),pageName:$("#pageName").val(),pageSlug:$("#pageSlug").val(),pageEnabled:$("#pageEnabled").is(":checked"),pageContent:$("#pageContent").val()}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(document).on("click",".product_opt_remove",function(t){t.preventDefault();var e=$(this).closest("li").find(".opt-name").html();$.ajax({method:"POST",url:"/admin/settings/option/remove/",data:{productId:$("#frmProductId").val(),optName:e}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(document).on("click","#product_opt_add",function(t){t.preventDefault();var e=$("#product_optName").val(),a=$("#product_optLabel").val(),o=$("#product_optType").val(),i=$("#product_optOptions").val(),n={};""!==$("#productOptJson").val()&&(n=JSON.parse($("#productOptJson").val()));var r='
  • ';r+='
    ',r+='
    '+e+"
    ",r+='
    '+a+"
    ",r+='
    '+o+"
    ",r+='
    '+i+"
    ",r+='
    ',r+='',r+="
  • ",$("#product_opt_wrapper").append(r),n[e]={optName:e,optLabel:a,optType:o,optOptions:$.grep(i.split(","),function(t){return 0===t||t})},$("#productOptJson").val(JSON.stringify(n)),$("#product_optName").val(""),$("#product_optLabel").val(""),$("#product_optOptions").val("")}),$("#stripeButton").validator().on("click",function(t){(t.preventDefault(),0===$("#shipping-form").validator("validate").has(".has-error").length)&&window.StripeCheckout.configure({key:$("#stripeButton").data("key"),image:$("#stripeButton").data("image"),locale:"auto",token:function(t){$("#shipping-form").append(''),$("#shipping-form").submit()}}).open({name:$("#stripeButton").data("name"),description:$("#stripeButton").data("description"),zipCode:$("#stripeButton").data("zipCode"),amount:$("#stripeButton").data("amount"),currency:$("#stripeButton").data("currency")})}),$("#settingsForm").validator().on("submit",function(t){t.isDefaultPrevented()||(t.preventDefault(),$("#footerHtml_input").val($(".CodeMirror")[0].CodeMirror.getValue()),$("#googleAnalytics_input").val($(".CodeMirror")[1].CodeMirror.getValue()),$("#customCss_input").val($(".CodeMirror")[2].CodeMirror.getValue()),$.ajax({method:"POST",url:"/admin/settings/update",data:$("#settingsForm").serialize()}).done(function(t){showNotification(t.message,"success")}).fail(function(t){showNotification(t.responseJSON.message,"danger")}))}),$("#customerLogout").on("click",function(t){$.ajax({method:"POST",url:"/customer/logout",data:{}}).done(function(t){location.reload()})}),$("#createCustomerAccount").validator().on("click",function(t){t.preventDefault(),0===$("#shipping-form").validator("validate").has(".has-error").length&&$.ajax({method:"POST",url:"/customer/create",data:{email:$("#shipEmail").val(),firstName:$("#shipFirstname").val(),lastName:$("#shipLastname").val(),address1:$("#shipAddr1").val(),address2:$("#shipAddr2").val(),country:$("#shipCountry").val(),state:$("#shipState").val(),postcode:$("#shipPostcode").val(),phone:$("#shipPhoneNumber").val(),password:$("#newCustomerPassword").val()}}).done(function(t){location.reload()}).fail(function(t){showNotification(t.responseJSON.err,"danger")})}),$("#customerLogin").on("click",function(t){t.isDefaultPrevented()||(t.preventDefault(),$.ajax({method:"POST",url:"/customer/login_action",data:{loginEmail:$("#customerLoginEmail").val(),loginPassword:$("#customerLoginPassword").val()}}).done(function(t){var e=t.customer;$("#shipEmail").val(e.email),$("#shipFirstname").val(e.firstName),$("#shipLastname").val(e.lastName),$("#shipAddr1").val(e.address1),$("#shipAddr2").val(e.address2),$("#shipCountry").val(e.country),$("#shipState").val(e.state),$("#shipPostcode").val(e.postcode),$("#shipPhoneNumber").val(e.phone),location.reload()}).fail(function(t){showNotification(t.responseJSON.err,"danger")})),t.preventDefault()}),$(document).on("click",".image-next",function(t){var e=$(".thumbnail-image"),a=0,o=0;$(".thumbnail-image").each(function(){$("#product-title-image").attr("src")===$(this).attr("src")&&(o=a+1===e.length||a+1<0?0:a+1),a++}),$("#product-title-image").attr("src",$(e).eq(o).attr("src"))}),$(document).on("click",".image-prev",function(t){var e=$(".thumbnail-image"),a=0,o=0;$(".thumbnail-image").each(function(){$("#product-title-image").attr("src")===$(this).attr("src")&&(o=a-1===e.length||a-1<0?e.length-1:a-1),a++}),$("#product-title-image").attr("src",$(e).eq(o).attr("src"))}),$(document).on("click","#orderStatusUpdate",function(t){$.ajax({method:"POST",url:"/admin/order/statusupdate",data:{order_id:$("#order_id").val(),status:$("#orderStatus").val()}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(document).on("click",".product-add-to-cart",function(t){var e=getSelectedOptions();$.ajax({method:"POST",url:"/product/addtocart",data:{productId:$("#productId").val(),productQuantity:$("#product_quantity").val(),productOptions:JSON.stringify(e)}}).done(function(t){$("#cart-count").text(t.totalCartItems),updateCartDiv(),showNotification(t.message,"success")}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(".cart-product-quantity").on("input",function(){cartUpdate()}),$(document).on("click",".pushy-link",function(t){$("body").removeClass("pushy-open-right")}),$(document).on("click",".add-to-cart",function(t){var e="/product/"+$(this).attr("data-id");$(this).attr("data-link")&&(e="/product/"+$(this).attr("data-link")),"true"===$(this).attr("data-has-options")?window.location=e:$.ajax({method:"POST",url:"/product/addtocart",data:{productId:$(this).attr("data-id")}}).done(function(t){$("#cart-count").text(t.totalCartItems),updateCartDiv(),showNotification(t.message,"success")}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(document).on("click","#empty-cart",function(t){$.ajax({method:"POST",url:"/product/emptycart"}).done(function(t){$("#cart-count").text(t.totalCartItems),updateCartDiv(),showNotification(t.message,"success",!0)})}),$(".qty-btn-minus").on("click",function(){$(this).parent().siblings("input").val(parseInt($(this).parent().siblings("input").val())-1)}),$(".qty-btn-plus").on("click",function(){$(this).parent().siblings("input").val(parseInt($(this).parent().siblings("input").val())+1)}),$(".thumbnail-image").on("click",function(){$("#product-title-image").attr("src",$(this).attr("src"))}),$(".set-as-main-image").on("click",function(){$.ajax({method:"POST",url:"/admin/product/setasmainimage",data:{product_id:$("#frmProductId").val(),productImage:$(this).attr("data-id")}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(".btn-delete-image").on("click",function(){$.ajax({method:"POST",url:"/admin/product/deleteimage",data:{product_id:$("#frmProductId").val(),productImage:$(this).attr("data-id")}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(document).on("click","#validate_permalink",function(t){""!==$("#frmProductPermalink").val()?$.ajax({method:"POST",url:"/admin/api/validate_permalink",data:{permalink:$("#frmProductPermalink").val(),docId:$("#frmProductId").val()}}).done(function(t){showNotification(t,"success")}).fail(function(t){showNotification(t.responseJSON.message,"danger")}):showNotification("Please enter a permalink to validate","danger")}),$(document).on("click","#btn_product_filter",function(t){""!==$("#product_filter").val()?window.location.href="/admin/products/filter/"+$("#product_filter").val():showNotification("Please enter a keyword to filter","danger")}),$(document).on("click","#btn_order_filter",function(t){""!==$("#order_filter").val()?window.location.href="/admin/orders/filter/"+$("#order_filter").val():showNotification("Please enter a keyword to filter","danger")}),$(document).on("click","#btn_customer_filter",function(t){""!==$("#customer_filter").val()?window.location.href="/admin/customers/filter/"+$("#customer_filter").val():showNotification("Please enter a keyword to filter","danger")}),$(document).on("click","#btn_search_reset",function(t){window.location.replace("/")}),$(document).on("click","#btn_search",function(t){t.preventDefault(),""===$("#frm_search").val().trim()?showNotification("Please enter a search value","danger"):window.location.href="/search/"+$("#frm_search").val()}),$(document).on("click","#frm_edit_product_save",function(t){""===$("#frmProductPermalink").val()&&""!==$("#frmProductTitle").val()&&$("#frmProductPermalink").val(slugify($("#frmProductTitle").val()))}),""!==$("#input_notify_message").val()){var l=$("#input_notify_message").val(),u=$("#input_notify_messageType").val();$("#input_notify_message").val(""),$("#input_notify_messageType").val(""),showNotification(l,u,!1)}}); \ No newline at end of file diff --git a/routes/admin.js b/routes/admin.js index 9430578..4b7c8a1 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -10,12 +10,12 @@ const glob = require('glob'); const router = express.Router(); // Admin section -router.get('/', common.restrict, (req, res, next) => { +router.get('/admin', common.restrict, (req, res, next) => { res.redirect('/admin/orders'); }); // logout -router.get('/logout', (req, res) => { +router.get('/admin/logout', (req, res) => { req.session.user = null; req.session.message = null; req.session.messageType = null; @@ -23,7 +23,7 @@ router.get('/logout', (req, res) => { }); // login form -router.get('/login', (req, res) => { +router.get('/admin/login', (req, res) => { let db = req.app.db; db.users.count({}, (err, userCount) => { @@ -54,7 +54,7 @@ router.get('/login', (req, res) => { }); // login the user and check the password -router.post('/login_action', (req, res) => { +router.post('/admin/login_action', (req, res) => { let db = req.app.db; db.users.findOne({userEmail: req.body.email}, (err, user) => { @@ -92,7 +92,7 @@ router.post('/login_action', (req, res) => { }); // setup form is shown when there are no users setup in the DB -router.get('/setup', (req, res) => { +router.get('/admin/setup', (req, res) => { let db = req.app.db; db.users.count({}, (err, userCount) => { @@ -119,7 +119,7 @@ router.get('/setup', (req, res) => { }); // insert a user -router.post('/setup_action', (req, res) => { +router.post('/admin/setup_action', (req, res) => { const db = req.app.db; let doc = { @@ -156,7 +156,7 @@ router.post('/setup_action', (req, res) => { }); // settings update -router.get('/settings', common.restrict, (req, res) => { +router.get('/admin/settings', common.restrict, (req, res) => { res.render('settings', { title: 'Cart settings', session: req.session, @@ -172,7 +172,7 @@ router.get('/settings', common.restrict, (req, res) => { }); // settings update -router.post('/settings/update', common.restrict, (req, res) => { +router.post('/admin/settings/update', common.restrict, common.checkAccess, (req, res) => { let result = common.updateConfig(req.body); if(result === true){ res.status(200).json({message: 'Settings successfully updated'}); @@ -182,7 +182,7 @@ router.post('/settings/update', common.restrict, (req, res) => { }); // settings update -router.post('/settings/option/remove', common.restrict, (req, res) => { +router.post('/admin/settings/option/remove', common.restrict, common.checkAccess, (req, res) => { const db = req.app.db; db.products.findOne({_id: common.getId(req.body.productId)}, (err, product) => { if(err){ @@ -209,7 +209,7 @@ router.post('/settings/option/remove', common.restrict, (req, res) => { }); // settings update -router.get('/settings/menu', common.restrict, async (req, res) => { +router.get('/admin/settings/menu', common.restrict, async (req, res) => { const db = req.app.db; res.render('settings_menu', { title: 'Cart menu', @@ -224,7 +224,7 @@ router.get('/settings/menu', common.restrict, async (req, res) => { }); // settings page list -router.get('/settings/pages', common.restrict, (req, res) => { +router.get('/admin/settings/pages', common.restrict, (req, res) => { const db = req.app.db; db.pages.find({}).toArray(async (err, pages) => { if(err){ @@ -246,7 +246,7 @@ router.get('/settings/pages', common.restrict, (req, res) => { }); // settings pages new -router.get('/settings/pages/new', common.restrict, async (req, res) => { +router.get('/admin/settings/pages/new', common.restrict, common.checkAccess, async (req, res) => { const db = req.app.db; res.render('settings_page_edit', { @@ -263,7 +263,7 @@ router.get('/settings/pages/new', common.restrict, async (req, res) => { }); // settings pages editor -router.get('/settings/pages/edit/:page', common.restrict, (req, res) => { +router.get('/admin/settings/pages/edit/:page', common.restrict, common.checkAccess, (req, res) => { const db = req.app.db; db.pages.findOne({_id: common.getId(req.params.page)}, async (err, page) => { if(err){ @@ -299,7 +299,7 @@ router.get('/settings/pages/edit/:page', common.restrict, (req, res) => { }); // settings update page -router.post('/settings/pages/update', common.restrict, (req, res) => { +router.post('/admin/settings/pages/update', common.restrict, common.checkAccess, (req, res) => { const db = req.app.db; let doc = { @@ -339,7 +339,7 @@ router.post('/settings/pages/update', common.restrict, (req, res) => { }); // settings delete page -router.get('/settings/pages/delete/:page', common.restrict, (req, res) => { +router.get('/admin/settings/pages/delete/:page', common.restrict, common.checkAccess, (req, res) => { const db = req.app.db; db.pages.remove({_id: common.getId(req.params.page)}, {}, (err, numRemoved) => { if(err){ @@ -355,7 +355,7 @@ router.get('/settings/pages/delete/:page', common.restrict, (req, res) => { }); // new menu item -router.post('/settings/menu/new', common.restrict, (req, res) => { +router.post('/admin/settings/menu/new', common.restrict, common.checkAccess, (req, res) => { let result = common.newMenu(req, res); if(result === false){ req.session.message = 'Failed creating menu.'; @@ -365,7 +365,7 @@ router.post('/settings/menu/new', common.restrict, (req, res) => { }); // update existing menu item -router.post('/settings/menu/update', common.restrict, (req, res) => { +router.post('/admin/settings/menu/update', common.restrict, common.checkAccess, (req, res) => { let result = common.updateMenu(req, res); if(result === false){ req.session.message = 'Failed updating menu.'; @@ -375,7 +375,7 @@ router.post('/settings/menu/update', common.restrict, (req, res) => { }); // delete menu item -router.get('/settings/menu/delete/:menuid', common.restrict, (req, res) => { +router.get('/admin/settings/menu/delete/:menuid', common.restrict, common.checkAccess, (req, res) => { let result = common.deleteMenu(req, res, req.params.menuid); if(result === false){ req.session.message = 'Failed deleting menu.'; @@ -385,7 +385,7 @@ router.get('/settings/menu/delete/:menuid', common.restrict, (req, res) => { }); // We call this via a Ajax call to save the order from the sortable list -router.post('/settings/menu/save_order', common.restrict, (req, res) => { +router.post('/admin/settings/menu/save_order', common.restrict, common.checkAccess, (req, res) => { let result = common.orderMenu(req, res); if(result === false){ res.status(400).json({message: 'Failed saving menu order'}); @@ -395,7 +395,7 @@ router.post('/settings/menu/save_order', common.restrict, (req, res) => { }); // validate the permalink -router.post('/api/validate_permalink', (req, res) => { +router.post('/admin/api/validate_permalink', (req, res) => { // if doc id is provided it checks for permalink in any products other that one provided, // else it just checks for any products with that permalink const db = req.app.db; @@ -412,18 +412,16 @@ router.post('/api/validate_permalink', (req, res) => { console.info(err.stack); } if(products > 0){ - res.writeHead(400, {'Content-Type': 'application/text'}); - res.end('Permalink already exists'); + res.status(400).json({message: 'Permalink already exists'}); }else{ - res.writeHead(200, {'Content-Type': 'application/text'}); - res.end('Permalink validated successfully'); + res.status(200).json({message: 'Permalink validated successfully'}); } }); }); // upload the file let upload = multer({dest: 'public/uploads/'}); -router.post('/file/upload', common.restrict, upload.single('upload_file'), (req, res, next) => { +router.post('/admin/file/upload', common.restrict, common.checkAccess, upload.single('upload_file'), (req, res, next) => { const db = req.app.db; if(req.file){ @@ -479,15 +477,15 @@ router.post('/file/upload', common.restrict, upload.single('upload_file'), (req, }); // delete a file via ajax request -router.post('/testEmail', common.restrict, (req, res) => { +router.post('/admin/testEmail', common.restrict, (req, res) => { let config = common.getConfig(); // TODO: Should fix this to properly handle result common.sendEmail(config.emailAddress, 'expressCart test email', 'Your email settings are working'); - res.status(200).json('Test email sent'); + res.status(200).json({message: 'Test email sent'}); }); // delete a file via ajax request -router.post('/file/delete', common.restrict, (req, res) => { +router.post('/admin/file/delete', common.restrict, common.checkAccess, (req, res) => { req.session.message = null; req.session.messageType = null; @@ -503,7 +501,7 @@ router.post('/file/delete', common.restrict, (req, res) => { }); }); -router.get('/files', common.restrict, (req, res) => { +router.get('/admin/files', common.restrict, (req, res) => { // loop files in /public/uploads/ glob('public/uploads/**', {nosort: true}, (er, files) => { // sort array diff --git a/routes/order.js b/routes/order.js index cb2c352..e25af1f 100644 --- a/routes/order.js +++ b/routes/order.js @@ -131,7 +131,7 @@ router.get('/admin/order/delete/:id', common.restrict, (req, res) => { }); // update order status -router.post('/admin/order/statusupdate', common.restrict, (req, res) => { +router.post('/admin/order/statusupdate', common.restrict, common.checkAccess, (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){ diff --git a/routes/product.js b/routes/product.js index aa840e9..11a3e08 100644 --- a/routes/product.js +++ b/routes/product.js @@ -74,7 +74,7 @@ router.get('/admin/product/new', common.restrict, common.checkAccess, (req, res) }); // insert new product form action -router.post('/admin/product/insert', common.restrict, (req, res) => { +router.post('/admin/product/insert', common.restrict, common.checkAccess, (req, res) => { const db = req.app.db; let doc = { @@ -145,7 +145,7 @@ router.post('/admin/product/insert', common.restrict, (req, res) => { }); // render the editor -router.get('/admin/product/edit/:id', common.restrict, (req, res) => { +router.get('/admin/product/edit/:id', common.restrict, common.checkAccess, (req, res) => { const db = req.app.db; common.getImages(req.params.id, req, res, (images) => { @@ -176,7 +176,7 @@ router.get('/admin/product/edit/:id', common.restrict, (req, res) => { }); // Update an existing product form action -router.post('/admin/product/update', common.restrict, (req, res) => { +router.post('/admin/product/update', common.restrict, common.checkAccess, (req, res) => { const db = req.app.db; db.products.findOne({_id: common.getId(req.body.frmProductId)}, (err, product) => { @@ -256,7 +256,7 @@ router.post('/admin/product/update', common.restrict, (req, res) => { }); // delete product -router.get('/admin/product/delete/:id', common.restrict, (req, res) => { +router.get('/admin/product/delete/:id', common.restrict, common.checkAccess, (req, res) => { const db = req.app.db; // remove the article @@ -283,23 +283,21 @@ router.get('/admin/product/delete/:id', common.restrict, (req, res) => { }); // update the published state based on an ajax call from the frontend -router.post('/admin/product/published_state', common.restrict, (req, res) => { +router.post('/admin/product/published_state', common.restrict, common.checkAccess, (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'); + res.status(400).json('Published state not updated'); }else{ - res.writeHead(200, {'Content-Type': 'application/text'}); - res.end('Published state updated'); + res.status(200).json('Published state updated'); } }); }); // set as main product image -router.post('/admin/product/setasmainimage', common.restrict, (req, res) => { +router.post('/admin/product/setasmainimage', common.restrict, common.checkAccess, (req, res) => { const db = req.app.db; // update the productImage to the db @@ -313,7 +311,7 @@ router.post('/admin/product/setasmainimage', common.restrict, (req, res) => { }); // deletes a product image -router.post('/admin/product/deleteimage', common.restrict, (req, res) => { +router.post('/admin/product/deleteimage', common.restrict, common.checkAccess, (req, res) => { const db = req.app.db; // get the productImage from the db diff --git a/views/partials/menu.hbs b/views/partials/menu.hbs index 36b4c82..1d0e3fd 100644 --- a/views/partials/menu.hbs +++ b/views/partials/menu.hbs @@ -2,15 +2,19 @@

    Menu