2018-01-07 04:55:48 +10:00
|
|
|
const express = require('express');
|
|
|
|
const path = require('path');
|
|
|
|
const logger = require('morgan');
|
|
|
|
const cookieParser = require('cookie-parser');
|
|
|
|
const bodyParser = require('body-parser');
|
|
|
|
const session = require('express-session');
|
|
|
|
const moment = require('moment');
|
|
|
|
const MongoStore = require('connect-mongodb-session')(session);
|
|
|
|
const MongoClient = require('mongodb').MongoClient;
|
|
|
|
const numeral = require('numeral');
|
|
|
|
const helmet = require('helmet');
|
|
|
|
const colors = require('colors');
|
2018-02-06 04:20:30 +10:00
|
|
|
const common = require('./lib/common');
|
2018-01-11 06:20:36 +10:00
|
|
|
const mongodbUri = require('mongodb-uri');
|
2018-01-07 04:55:48 +10:00
|
|
|
let handlebars = require('express-handlebars');
|
|
|
|
|
2018-01-11 06:20:36 +10:00
|
|
|
// Validate our settings schema
|
|
|
|
const Ajv = require('ajv');
|
|
|
|
const ajv = new Ajv({useDefaults: true});
|
|
|
|
|
|
|
|
const baseConfig = ajv.validate(require('./config/baseSchema'), require('./config/settings.json'));
|
|
|
|
if(baseConfig === false){
|
|
|
|
console.log(colors.red(`settings.json incorrect: ${ajv.errorsText()}`));
|
|
|
|
process.exit(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// get config
|
|
|
|
let config = common.getConfig();
|
|
|
|
|
|
|
|
// Validate the payment gateway config
|
|
|
|
if(config.paymentGateway === 'paypal'){
|
|
|
|
const paypalConfig = ajv.validate(require('./config/paypalSchema'), require('./config/paypal.json'));
|
|
|
|
if(paypalConfig === false){
|
|
|
|
console.log(colors.red(`PayPal config is incorrect: ${ajv.errorsText()}`));
|
|
|
|
process.exit(2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(config.paymentGateway === 'stripe'){
|
|
|
|
const stripeConfig = ajv.validate(require('./config/stripeSchema'), require('./config/stripe.json'));
|
|
|
|
if(stripeConfig === false){
|
2018-02-06 08:04:42 +10:00
|
|
|
console.log(colors.red(`Stripe config is incorrect: ${ajv.errorsText()}`));
|
2018-01-11 06:20:36 +10:00
|
|
|
process.exit(2);
|
|
|
|
}
|
|
|
|
}
|
2018-02-05 07:39:42 +10:00
|
|
|
if(config.paymentGateway === 'authorizenet'){
|
|
|
|
const authorizenetConfig = ajv.validate(require('./config/authorizenetSchema'), require('./config/authorizenet.json'));
|
|
|
|
if(authorizenetConfig === false){
|
|
|
|
console.log(colors.red(`Authorizenet config is incorrect: ${ajv.errorsText()}`));
|
|
|
|
process.exit(2);
|
|
|
|
}
|
|
|
|
}
|
2018-01-11 06:20:36 +10:00
|
|
|
|
2018-01-07 04:55:48 +10:00
|
|
|
// require the routes
|
|
|
|
const index = require('./routes/index');
|
|
|
|
const admin = require('./routes/admin');
|
2018-02-05 23:20:53 +10:00
|
|
|
const product = require('./routes/product');
|
2018-02-03 23:26:09 +10:00
|
|
|
const customer = require('./routes/customer');
|
2018-02-05 23:20:53 +10:00
|
|
|
const order = require('./routes/order');
|
|
|
|
const user = require('./routes/user');
|
2018-01-08 04:34:20 +10:00
|
|
|
const paypal = require('./routes/payments/paypal');
|
|
|
|
const stripe = require('./routes/payments/stripe');
|
2018-02-05 07:39:42 +10:00
|
|
|
const authorizenet = require('./routes/payments/authorizenet');
|
2018-01-07 04:55:48 +10:00
|
|
|
|
|
|
|
const app = express();
|
|
|
|
|
|
|
|
// view engine setup
|
|
|
|
app.set('views', path.join(__dirname, '/views'));
|
2018-02-12 05:47:26 +10:00
|
|
|
app.engine('hbs', handlebars({
|
|
|
|
extname: 'hbs',
|
|
|
|
layoutsDir: path.join(__dirname, 'views', 'layouts'),
|
|
|
|
defaultLayout: 'layout.hbs',
|
|
|
|
partialsDir: [ path.join(__dirname, 'views') ]
|
|
|
|
}));
|
2018-01-07 04:55:48 +10:00
|
|
|
app.set('view engine', 'hbs');
|
|
|
|
|
|
|
|
// helpers for the handlebar templating platform
|
|
|
|
handlebars = handlebars.create({
|
|
|
|
helpers: {
|
|
|
|
perRowClass: function(numProducts){
|
|
|
|
if(parseInt(numProducts) === 1){
|
2018-02-14 06:21:45 +10:00
|
|
|
return'col-md-12 col-xl-12 col m12 xl12 product-item';
|
2018-01-07 04:55:48 +10:00
|
|
|
}
|
|
|
|
if(parseInt(numProducts) === 2){
|
2018-02-14 06:21:45 +10:00
|
|
|
return'col-md-6 col-xl-6 col m6 xl6 product-item';
|
2018-01-07 04:55:48 +10:00
|
|
|
}
|
|
|
|
if(parseInt(numProducts) === 3){
|
2018-02-14 06:21:45 +10:00
|
|
|
return'col-md-4 col-xl-4 col m4 xl4 product-item';
|
2018-01-07 04:55:48 +10:00
|
|
|
}
|
|
|
|
if(parseInt(numProducts) === 4){
|
2018-02-14 06:21:45 +10:00
|
|
|
return'col-md-3 col-xl-3 col m3 xl3 product-item';
|
2018-01-07 04:55:48 +10:00
|
|
|
}
|
|
|
|
|
2018-02-14 06:21:45 +10:00
|
|
|
return'col-md-6 col-xl-6 col m6 xl6 product-item';
|
|
|
|
},
|
2018-02-17 23:30:28 +10:00
|
|
|
menuMatch: function(title, search){
|
|
|
|
if(!title || !search){
|
|
|
|
return'';
|
|
|
|
}
|
|
|
|
if(title.toLowerCase().startsWith(search.toLowerCase())){
|
|
|
|
return'class="navActive"';
|
|
|
|
}
|
|
|
|
return'';
|
|
|
|
},
|
2018-02-14 06:21:45 +10:00
|
|
|
getTheme: function(view){
|
|
|
|
return`themes/${config.theme}/${view}`;
|
2018-01-07 04:55:48 +10:00
|
|
|
},
|
|
|
|
formatAmount: function(amt){
|
|
|
|
if(amt){
|
|
|
|
return numeral(amt).format('0.00');
|
|
|
|
}
|
|
|
|
return'0.00';
|
|
|
|
},
|
|
|
|
amountNoDecimal: function(amt){
|
|
|
|
if(amt){
|
|
|
|
return handlebars.helpers.formatAmount(amt).replace('.', '');
|
|
|
|
}
|
|
|
|
return handlebars.helpers.formatAmount(amt);
|
|
|
|
},
|
|
|
|
getStatusColor: function (status){
|
|
|
|
switch(status){
|
|
|
|
case'Paid':
|
|
|
|
return'success';
|
|
|
|
case'Approved':
|
|
|
|
return'success';
|
|
|
|
case'Approved - Processing':
|
|
|
|
return'success';
|
|
|
|
case'Failed':
|
|
|
|
return'danger';
|
|
|
|
case'Completed':
|
|
|
|
return'success';
|
|
|
|
case'Shipped':
|
|
|
|
return'success';
|
|
|
|
case'Pending':
|
|
|
|
return'warning';
|
|
|
|
default:
|
|
|
|
return'danger';
|
|
|
|
}
|
|
|
|
},
|
|
|
|
checkProductOptions: function (opts){
|
|
|
|
if(opts){
|
|
|
|
return'true';
|
|
|
|
}
|
|
|
|
return'false';
|
|
|
|
},
|
|
|
|
currencySymbol: function(value){
|
|
|
|
if(typeof value === 'undefined' || value === ''){
|
|
|
|
return'$';
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
},
|
|
|
|
objectLength: function(obj){
|
|
|
|
if(obj){
|
|
|
|
return Object.keys(obj).length;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
},
|
|
|
|
checkedState: function (state){
|
|
|
|
if(state === 'true' || state === true){
|
|
|
|
return'checked';
|
|
|
|
}
|
|
|
|
return'';
|
|
|
|
},
|
|
|
|
selectState: function (state, value){
|
|
|
|
if(state === value){
|
|
|
|
return'selected';
|
|
|
|
}
|
|
|
|
return'';
|
|
|
|
},
|
|
|
|
isNull: function (value, options){
|
|
|
|
if(typeof value === 'undefined' || value === ''){
|
|
|
|
return options.fn(this);
|
|
|
|
}
|
|
|
|
return options.inverse(this);
|
|
|
|
},
|
2018-02-12 05:47:26 +10:00
|
|
|
toLower: function (value){
|
|
|
|
if(value){
|
|
|
|
return value.toLowerCase();
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
2018-01-07 04:55:48 +10:00
|
|
|
formatDate: function (date, format){
|
|
|
|
return moment(date).format(format);
|
|
|
|
},
|
|
|
|
ifCond: function (v1, operator, v2, options){
|
|
|
|
switch(operator){
|
|
|
|
case'==':
|
|
|
|
return(v1 === v2) ? options.fn(this) : options.inverse(this);
|
|
|
|
case'!=':
|
|
|
|
return(v1 !== v2) ? options.fn(this) : options.inverse(this);
|
|
|
|
case'===':
|
|
|
|
return(v1 === v2) ? options.fn(this) : options.inverse(this);
|
|
|
|
case'<':
|
|
|
|
return(v1 < v2) ? options.fn(this) : options.inverse(this);
|
|
|
|
case'<=':
|
|
|
|
return(v1 <= v2) ? options.fn(this) : options.inverse(this);
|
|
|
|
case'>':
|
|
|
|
return(v1 > v2) ? options.fn(this) : options.inverse(this);
|
|
|
|
case'>=':
|
|
|
|
return(v1 >= v2) ? options.fn(this) : options.inverse(this);
|
|
|
|
case'&&':
|
|
|
|
return(v1 && v2) ? options.fn(this) : options.inverse(this);
|
|
|
|
case'||':
|
|
|
|
return(v1 || v2) ? options.fn(this) : options.inverse(this);
|
|
|
|
default:
|
|
|
|
return options.inverse(this);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
isAnAdmin: function (value, options){
|
2018-02-04 00:08:51 +10:00
|
|
|
if(value === 'true' || value === true){
|
2018-01-07 04:55:48 +10:00
|
|
|
return options.fn(this);
|
|
|
|
}
|
|
|
|
return options.inverse(this);
|
2018-06-22 08:21:14 +10:00
|
|
|
},
|
|
|
|
cartTotalItems: function(cart){
|
|
|
|
if(cart) {
|
|
|
|
return cart.reduce((a, b) => +a + +b.quantity, 0);
|
|
|
|
}
|
|
|
|
return 0;
|
2018-01-07 04:55:48 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-01-15 07:11:22 +10:00
|
|
|
// session store
|
2018-01-07 04:55:48 +10:00
|
|
|
let store = new MongoStore({
|
|
|
|
uri: config.databaseConnectionString,
|
|
|
|
collection: 'sessions'
|
|
|
|
});
|
|
|
|
|
|
|
|
app.enable('trust proxy');
|
|
|
|
app.use(helmet());
|
|
|
|
app.set('port', process.env.PORT || 1111);
|
|
|
|
app.use(logger('dev'));
|
|
|
|
app.use(bodyParser.json());
|
|
|
|
app.use(bodyParser.urlencoded({extended: false}));
|
|
|
|
app.use(cookieParser('5TOCyfH3HuszKGzFZntk'));
|
|
|
|
app.use(session({
|
|
|
|
resave: true,
|
|
|
|
saveUninitialized: true,
|
|
|
|
secret: 'pAgGxo8Hzg7PFlv1HpO8Eg0Y6xtP7zYx',
|
|
|
|
cookie: {
|
|
|
|
path: '/',
|
|
|
|
httpOnly: true,
|
|
|
|
maxAge: 3600000 * 24
|
|
|
|
},
|
|
|
|
store: store
|
|
|
|
}));
|
|
|
|
|
|
|
|
// serving static content
|
|
|
|
app.use(express.static(path.join(__dirname, 'public')));
|
2018-02-12 05:47:26 +10:00
|
|
|
app.use(express.static(path.join(__dirname, 'views', 'themes')));
|
2018-01-07 04:55:48 +10:00
|
|
|
|
|
|
|
// Make stuff accessible to our router
|
|
|
|
app.use((req, res, next) => {
|
|
|
|
req.handlebars = handlebars;
|
|
|
|
next();
|
|
|
|
});
|
|
|
|
|
2018-05-21 23:36:12 +10:00
|
|
|
// Ran on all routes
|
|
|
|
app.use((req, res, next) => {
|
|
|
|
res.setHeader('Cache-Control', 'no-cache, no-store');
|
|
|
|
next();
|
|
|
|
});
|
|
|
|
|
2018-01-07 04:55:48 +10:00
|
|
|
// setup the routes
|
|
|
|
app.use('/', index);
|
2018-02-03 23:26:09 +10:00
|
|
|
app.use('/', customer);
|
2018-02-05 23:20:53 +10:00
|
|
|
app.use('/', product);
|
|
|
|
app.use('/', order);
|
|
|
|
app.use('/', user);
|
2018-02-06 05:51:04 +10:00
|
|
|
app.use('/', admin);
|
2018-01-07 04:55:48 +10:00
|
|
|
app.use('/paypal', paypal);
|
|
|
|
app.use('/stripe', stripe);
|
2018-02-05 07:39:42 +10:00
|
|
|
app.use('/authorizenet', authorizenet);
|
2018-01-07 04:55:48 +10:00
|
|
|
|
|
|
|
// catch 404 and forward to error handler
|
|
|
|
app.use((req, res, next) => {
|
|
|
|
let err = new Error('Not Found');
|
|
|
|
err.status = 404;
|
|
|
|
next(err);
|
|
|
|
});
|
|
|
|
|
|
|
|
// error handlers
|
|
|
|
|
|
|
|
// development error handler
|
|
|
|
// will print stacktrace
|
|
|
|
if(app.get('env') === 'development'){
|
|
|
|
app.use((err, req, res, next) => {
|
|
|
|
console.error(colors.red(err.stack));
|
|
|
|
res.status(err.status || 500);
|
|
|
|
res.render('error', {
|
|
|
|
message: err.message,
|
|
|
|
error: err,
|
|
|
|
helpers: handlebars.helpers
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// production error handler
|
|
|
|
// no stacktraces leaked to user
|
|
|
|
app.use((err, req, res, next) => {
|
|
|
|
console.error(colors.red(err.stack));
|
|
|
|
res.status(err.status || 500);
|
|
|
|
res.render('error', {
|
|
|
|
message: err.message,
|
|
|
|
error: {},
|
|
|
|
helpers: handlebars.helpers
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-01-15 03:02:10 +10:00
|
|
|
// Nodejs version check
|
2018-02-23 05:16:55 +10:00
|
|
|
const nodeVersionMajor = parseInt(process.version.split('.')[0].replace('v', ''));
|
|
|
|
if(nodeVersionMajor < 7){
|
|
|
|
console.log(colors.red(`Please use Node.js version 7.x or above. Current version: ${nodeVersionMajor}`));
|
2018-01-15 03:02:10 +10:00
|
|
|
process.exit(2);
|
|
|
|
}
|
|
|
|
|
2018-01-07 04:55:48 +10:00
|
|
|
app.on('uncaughtException', (err) => {
|
|
|
|
console.error(colors.red(err.stack));
|
2018-01-15 03:02:10 +10:00
|
|
|
process.exit(2);
|
2018-01-07 04:55:48 +10:00
|
|
|
});
|
|
|
|
|
|
|
|
MongoClient.connect(config.databaseConnectionString, {}, (err, client) => {
|
|
|
|
// On connection error we display then exit
|
|
|
|
if(err){
|
|
|
|
console.log(colors.red('Error connecting to MongoDB: ' + err));
|
2018-01-15 03:02:10 +10:00
|
|
|
process.exit(2);
|
2018-01-07 04:55:48 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
// select DB
|
2018-01-11 06:20:36 +10:00
|
|
|
const dbUriObj = mongodbUri.parse(config.databaseConnectionString);
|
2018-02-23 03:41:24 +10:00
|
|
|
let db;
|
|
|
|
// if in testing, set the testing DB
|
|
|
|
if(process.env.NODE_ENV === 'test'){
|
|
|
|
db = client.db('testingdb');
|
|
|
|
}else{
|
|
|
|
db = client.db(dbUriObj.database);
|
|
|
|
}
|
2018-01-07 04:55:48 +10:00
|
|
|
|
|
|
|
// setup the collections
|
|
|
|
db.users = db.collection('users');
|
|
|
|
db.products = db.collection('products');
|
|
|
|
db.orders = db.collection('orders');
|
|
|
|
db.pages = db.collection('pages');
|
2018-01-15 03:02:10 +10:00
|
|
|
db.menu = db.collection('menu');
|
2018-01-22 07:20:33 +10:00
|
|
|
db.customers = db.collection('customers');
|
2018-01-07 04:55:48 +10:00
|
|
|
|
|
|
|
// add db to app for routes
|
2018-02-23 03:41:24 +10:00
|
|
|
app.dbClient = client;
|
2018-01-07 04:55:48 +10:00
|
|
|
app.db = db;
|
2018-02-23 03:41:24 +10:00
|
|
|
app.config = config;
|
|
|
|
app.port = app.get('port');
|
2018-01-07 04:55:48 +10:00
|
|
|
|
2018-02-11 22:02:47 +10:00
|
|
|
// run indexing
|
2018-01-07 06:07:10 +10:00
|
|
|
common.runIndexing(app)
|
2018-01-15 03:02:10 +10:00
|
|
|
.then(app.listen(app.get('port')))
|
2018-01-07 06:07:10 +10:00
|
|
|
.then(() => {
|
|
|
|
// lift the app
|
2018-02-23 03:41:24 +10:00
|
|
|
app.emit('appStarted');
|
2018-01-15 03:02:10 +10:00
|
|
|
console.log(colors.green('expressCart running on host: http://localhost:' + app.get('port')));
|
2018-01-07 06:07:10 +10:00
|
|
|
})
|
2018-02-23 03:41:24 +10:00
|
|
|
.catch((err) => {
|
2018-01-07 06:07:10 +10:00
|
|
|
console.error(colors.red('Error setting up indexes:' + err));
|
2018-01-15 03:02:10 +10:00
|
|
|
process.exit(2);
|
2018-01-07 04:55:48 +10:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
module.exports = app;
|