Fixed settings saving + add sending of order hooks
parent
7c10c24ca6
commit
10215af9ff
|
@ -51,6 +51,12 @@ Sometimes you might want some default sample/test data. To create this, run `npm
|
||||||
|
|
||||||
There is currently a limited API for certain functions of the app. Using the API can be done by firstly generating an API key via the Admin login. `Admin > My Account > API Key (Generate) button`. Once an API Key is generated it will need to be supplied in a header called `apiKey` to authenticate requests.
|
There is currently a limited API for certain functions of the app. Using the API can be done by firstly generating an API key via the Admin login. `Admin > My Account > API Key (Generate) button`. Once an API Key is generated it will need to be supplied in a header called `apiKey` to authenticate requests.
|
||||||
|
|
||||||
|
## Hooks / Integrations
|
||||||
|
|
||||||
|
On the completion of a order if a `orderHook` URL is configured, expressCart will POST the data to the configured URL. This is handy or IFTTT or Zapier Webhooks where you may want to use the integration methods to retrieve the order details in other systems.
|
||||||
|
|
||||||
|
Example use might be to send all orders to a Google Docs spreadsheet or an accounting package or a packing slip software etc.
|
||||||
|
|
||||||
## Admin
|
## Admin
|
||||||
|
|
||||||
Visit: [http://127.0.0.1:1111/admin](http://127.0.0.1:1111/admin)
|
Visit: [http://127.0.0.1:1111/admin](http://127.0.0.1:1111/admin)
|
||||||
|
|
8
app.js
8
app.js
|
@ -20,15 +20,15 @@ let handlebars = require('express-handlebars');
|
||||||
const Ajv = require('ajv');
|
const Ajv = require('ajv');
|
||||||
const ajv = new Ajv({ useDefaults: true });
|
const ajv = new Ajv({ useDefaults: true });
|
||||||
|
|
||||||
const baseConfig = ajv.validate(require('./config/baseSchema'), require('./config/settings.json'));
|
// get config
|
||||||
|
let config = common.getConfig();
|
||||||
|
|
||||||
|
const baseConfig = ajv.validate(require('./config/baseSchema'), config);
|
||||||
if(baseConfig === false){
|
if(baseConfig === false){
|
||||||
console.log(colors.red(`settings.json incorrect: ${ajv.errorsText()}`));
|
console.log(colors.red(`settings.json incorrect: ${ajv.errorsText()}`));
|
||||||
process.exit(2);
|
process.exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get config
|
|
||||||
let config = common.getConfig();
|
|
||||||
|
|
||||||
// Validate the payment gateway config
|
// Validate the payment gateway config
|
||||||
if(config.paymentGateway === 'paypal'){
|
if(config.paymentGateway === 'paypal'){
|
||||||
const paypalConfig = ajv.validate(require('./config/paypalSchema'), require('./config/paypal.json'));
|
const paypalConfig = ajv.validate(require('./config/paypalSchema'), require('./config/paypal.json'));
|
||||||
|
|
|
@ -101,6 +101,9 @@
|
||||||
"trackStock": {
|
"trackStock": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"orderHook": {
|
||||||
|
"format": "uri-template"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
|
|
@ -2,6 +2,7 @@ const _ = require('lodash');
|
||||||
const uglifycss = require('uglifycss');
|
const uglifycss = require('uglifycss');
|
||||||
const colors = require('colors');
|
const colors = require('colors');
|
||||||
const cheerio = require('cheerio');
|
const cheerio = require('cheerio');
|
||||||
|
const axios = require('axios');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const glob = require('glob');
|
const glob = require('glob');
|
||||||
|
@ -164,16 +165,16 @@ const getImages = (dir, req, res, callback) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getConfigFilename = () => {
|
|
||||||
let filename = path.join(__dirname, '../config', 'settings-local.json');
|
|
||||||
if(fs.existsSync(filename)){
|
|
||||||
return filename;
|
|
||||||
}
|
|
||||||
return path.join(__dirname, '../config', 'settings.json');
|
|
||||||
};
|
|
||||||
|
|
||||||
const getConfig = () => {
|
const getConfig = () => {
|
||||||
let config = JSON.parse(fs.readFileSync(getConfigFilename(), 'utf8'));
|
let config = JSON.parse(fs.readFileSync(path.join(__dirname, '../config', 'settings.json'), 'utf8'));
|
||||||
|
const localConfigFilePath = path.join(__dirname, '../config', 'settings-local.json');
|
||||||
|
|
||||||
|
// Check for local config file and merge with base settings
|
||||||
|
if(fs.existsSync(localConfigFilePath)){
|
||||||
|
const localConfigFile = JSON.parse(fs.readFileSync(localConfigFilePath, 'utf8'));
|
||||||
|
config = Object.assign(config, localConfigFile);
|
||||||
|
}
|
||||||
|
|
||||||
config.customCss = typeof config.customCss !== 'undefined' ? escape.decode(config.customCss) : null;
|
config.customCss = typeof config.customCss !== 'undefined' ? escape.decode(config.customCss) : null;
|
||||||
config.footerHtml = typeof config.footerHtml !== 'undefined' ? escape.decode(config.footerHtml) : null;
|
config.footerHtml = typeof config.footerHtml !== 'undefined' ? escape.decode(config.footerHtml) : null;
|
||||||
config.googleAnalytics = typeof config.googleAnalytics !== 'undefined' ? escape.decode(config.googleAnalytics) : null;
|
config.googleAnalytics = typeof config.googleAnalytics !== 'undefined' ? escape.decode(config.googleAnalytics) : null;
|
||||||
|
@ -272,9 +273,31 @@ const updateConfig = (fields) => {
|
||||||
settingsFile['productsPerPage'] = parseInt(fields['productsPerPage']);
|
settingsFile['productsPerPage'] = parseInt(fields['productsPerPage']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// write file
|
// If we have a local settings file (not git tracked) we loop its settings and save
|
||||||
|
// and changes made to them. All other settings get updated to the base settings file.
|
||||||
|
const localSettingsFile = path.join(__dirname, '../config', 'settings-local.json');
|
||||||
|
if(fs.existsSync(localSettingsFile)){
|
||||||
|
const localSettings = JSON.parse(fs.readFileSync(localSettingsFile));
|
||||||
|
_.forEach(localSettings, (value, key) => {
|
||||||
|
if(fields[key]){
|
||||||
|
localSettings[key] = fields[key];
|
||||||
|
|
||||||
|
// Exists in local so remove from main settings file
|
||||||
|
delete settingsFile[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Save our local settings
|
||||||
|
try{
|
||||||
|
fs.writeFileSync(localSettingsFile, JSON.stringify(localSettings, null, 4));
|
||||||
|
}catch(exception){
|
||||||
|
console.log('Failed to save local settings file', exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write base settings file
|
||||||
|
const baseSettingsFile = path.join(__dirname, '../config', 'settings.json');
|
||||||
try{
|
try{
|
||||||
fs.writeFileSync(getConfigFilename(), JSON.stringify(settingsFile, null, 4));
|
fs.writeFileSync(baseSettingsFile, JSON.stringify(settingsFile, null, 4));
|
||||||
return true;
|
return true;
|
||||||
}catch(exception){
|
}catch(exception){
|
||||||
return false;
|
return false;
|
||||||
|
@ -470,6 +493,20 @@ const getData = (req, page, query) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hooker = (order) => {
|
||||||
|
let config = getConfig();
|
||||||
|
|
||||||
|
return axios.post(config.orderHook, order, { responseType: 'application/json' })
|
||||||
|
.then((response) => {
|
||||||
|
if(response.status === 200){
|
||||||
|
console.info('Successfully called order hook');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log('Error calling hook:', err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
allowedMimeType,
|
allowedMimeType,
|
||||||
fileSizeLimit,
|
fileSizeLimit,
|
||||||
|
@ -483,7 +520,6 @@ module.exports = {
|
||||||
checkDirectorySync,
|
checkDirectorySync,
|
||||||
getThemes,
|
getThemes,
|
||||||
getImages,
|
getImages,
|
||||||
getConfigFilename,
|
|
||||||
getConfig,
|
getConfig,
|
||||||
getPaymentConfig,
|
getPaymentConfig,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
|
@ -496,5 +532,6 @@ module.exports = {
|
||||||
getEmailTemplate,
|
getEmailTemplate,
|
||||||
sendEmail,
|
sendEmail,
|
||||||
getId,
|
getId,
|
||||||
getData
|
getData,
|
||||||
|
hooker
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
const _ = require('lodash');
|
||||||
const common = require('../lib/common');
|
const common = require('../lib/common');
|
||||||
const { restrict, checkAccess } = require('../lib/auth');
|
const { restrict, checkAccess } = require('../lib/auth');
|
||||||
const escape = require('html-entities').AllHtmlEntities;
|
const escape = require('html-entities').AllHtmlEntities;
|
||||||
|
@ -191,7 +192,7 @@ router.post('/admin/createApiKey', restrict, checkAccess, async (req, res) => {
|
||||||
|
|
||||||
// settings update
|
// settings update
|
||||||
router.post('/admin/settings/update', restrict, checkAccess, (req, res) => {
|
router.post('/admin/settings/update', restrict, checkAccess, (req, res) => {
|
||||||
let result = common.updateConfig(req.body);
|
const result = common.updateConfig(req.body);
|
||||||
if(result === true){
|
if(result === true){
|
||||||
res.status(200).json({ message: 'Settings successfully updated' });
|
res.status(200).json({ message: 'Settings successfully updated' });
|
||||||
res.configDirty = true;
|
res.configDirty = true;
|
||||||
|
|
Loading…
Reference in New Issue