const fs = require('fs'); const yenv = require('yenv'); if(fs.existsSync('./env.yaml')){ process.env = yenv('env.yaml', { strict: false }); } const path = require('path'); const express = require('express'); const logger = require('morgan'); const cookieParser = require('cookie-parser'); const bodyParser = require('body-parser'); const session = require('express-session'); const moment = require('moment'); const _ = require('lodash'); const MongoStore = require('connect-mongodb-session')(session); const numeral = require('numeral'); const helmet = require('helmet'); const colors = require('colors'); const cron = require('node-cron'); const crypto = require('crypto'); const common = require('./lib/common'); const { runIndexing } = require('./lib/indexing'); const { addSchemas } = require('./lib/schema'); const { initDb, getDbUri } = require('./lib/db'); let handlebars = require('express-handlebars'); const i18n = require('i18n'); // Validate our settings schema const Ajv = require('ajv'); const ajv = new Ajv({ useDefaults: true }); // get config const config = common.getConfig(); const baseConfig = ajv.validate(require('./config/settingsSchema'), config); if(baseConfig === false){ console.log(colors.red(`settings.json incorrect: ${ajv.errorsText()}`)); process.exit(2); } // Validate the payment gateway config if(ajv.validate( require(`./config/payment/schema/${config.paymentGateway}`), require(`./config/payment/config/${config.paymentGateway}`)) === false ){ console.log(colors.red(`${config.paymentGateway} config is incorrect: ${ajv.errorsText()}`)); process.exit(2); } // require the routes const index = require('./routes/index'); const admin = require('./routes/admin'); const product = require('./routes/product'); const customer = require('./routes/customer'); const order = require('./routes/order'); const user = require('./routes/user'); // Add the payment route const paymentRoute = require(`./lib/payments/${config.paymentGateway}`); const app = express(); // Language initialize i18n.configure({ locales: config.availableLanguages, defaultLocale: config.defaultLocale, cookie: 'locale', queryParameter: 'lang', directory: `${__dirname}/locales`, directoryPermissions: '755', api: { __: '__', // now req.__ becomes req.__ __n: '__n' // and req.__n can be called as req.__n } }); // view engine setup app.set('views', path.join(__dirname, '/views')); app.engine('hbs', handlebars({ extname: 'hbs', layoutsDir: path.join(__dirname, 'views', 'layouts'), defaultLayout: 'layout.hbs', partialsDir: [path.join(__dirname, 'views')] })); app.set('view engine', 'hbs'); // helpers for the handlebar templating platform handlebars = handlebars.create({ helpers: { // Language helper __: () => { return i18n.__(this, arguments); }, // eslint-disable-line no-undef __n: () => { return i18n.__n(this, arguments); }, // eslint-disable-line no-undef availableLanguages: (block) => { let total = ''; for(const lang of i18n.getLocales()){ total += block.fn(lang); } return total; }, perRowClass: (numProducts) => { if(parseInt(numProducts) === 1){ return 'col-6 col-md-12 product-item'; } if(parseInt(numProducts) === 2){ return 'col-6 col-md-6 product-item'; } if(parseInt(numProducts) === 3){ return 'col-6 col-md-4 product-item'; } if(parseInt(numProducts) === 4){ return 'col-6 col-md-3 product-item'; } return 'col-md-6 product-item'; }, menuMatch: (title, search) => { if(!title || !search){ return ''; } if(title.toLowerCase().startsWith(search.toLowerCase())){ return 'class="navActive"'; } return ''; }, getTheme: (view) => { return `themes/${config.theme}/${view}`; }, formatAmount: (amt) => { if(amt){ return numeral(amt).format('0.00'); } return '0.00'; }, amountNoDecimal: (amt) => { if(amt){ return handlebars.helpers.formatAmount(amt).replace('.', ''); } return handlebars.helpers.formatAmount(amt); }, getStatusColor: (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: (opts) => { if(opts){ return 'true'; } return 'false'; }, currencySymbol: (value) => { if(typeof value === 'undefined' || value === ''){ return '$'; } return value; }, objectLength: (obj) => { if(obj){ return Object.keys(obj).length; } return 0; }, stringify: (obj) => { if(obj){ return JSON.stringify(obj); } return ''; }, checkedState: (state) => { if(state === 'true' || state === true){ return 'checked'; } return ''; }, selectState: (state, value) => { if(state === value){ return 'selected'; } return ''; }, isNull: (value, options) => { if(typeof value === 'undefined' || value === ''){ return options.fn(this); } return options.inverse(this); }, toLower: (value) => { if(value){ return value.toLowerCase(); } return null; }, formatDate: (date, format) => { return moment(date).format(format); }, discountExpiry: (start, end) => { return moment().isBetween(moment(start), moment(end)); }, ifCond: (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: (value, options) => { if(value === 'true' || value === true){ return options.fn(this); } return options.inverse(this); }, paymentMessage: (status) => { if(status === 'Paid'){ return '