Fixed settings saving + add sending of order hooks

master
Mark Moffat 2019-06-15 15:35:31 +09:30
parent 7c10c24ca6
commit 10215af9ff
5 changed files with 65 additions and 18 deletions

View File

@ -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.
## 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
Visit: [http://127.0.0.1:1111/admin](http://127.0.0.1:1111/admin)

8
app.js
View File

@ -20,15 +20,15 @@ let handlebars = require('express-handlebars');
const Ajv = require('ajv');
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){
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'));

View File

@ -101,6 +101,9 @@
"trackStock": {
"type": "boolean",
"default": false
},
"orderHook": {
"format": "uri-template"
}
},
"required": [

View File

@ -2,6 +2,7 @@ const _ = require('lodash');
const uglifycss = require('uglifycss');
const colors = require('colors');
const cheerio = require('cheerio');
const axios = require('axios');
const fs = require('fs');
const path = require('path');
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 = () => {
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.footerHtml = typeof config.footerHtml !== 'undefined' ? escape.decode(config.footerHtml) : null;
config.googleAnalytics = typeof config.googleAnalytics !== 'undefined' ? escape.decode(config.googleAnalytics) : null;
@ -272,9 +273,31 @@ const updateConfig = (fields) => {
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{
fs.writeFileSync(getConfigFilename(), JSON.stringify(settingsFile, null, 4));
fs.writeFileSync(baseSettingsFile, JSON.stringify(settingsFile, null, 4));
return true;
}catch(exception){
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 = {
allowedMimeType,
fileSizeLimit,
@ -483,7 +520,6 @@ module.exports = {
checkDirectorySync,
getThemes,
getImages,
getConfigFilename,
getConfig,
getPaymentConfig,
updateConfig,
@ -496,5 +532,6 @@ module.exports = {
getEmailTemplate,
sendEmail,
getId,
getData
getData,
hooker
};

View File

@ -1,4 +1,5 @@
const express = require('express');
const _ = require('lodash');
const common = require('../lib/common');
const { restrict, checkAccess } = require('../lib/auth');
const escape = require('html-entities').AllHtmlEntities;
@ -191,7 +192,7 @@ router.post('/admin/createApiKey', restrict, checkAccess, async (req, res) => {
// settings update
router.post('/admin/settings/update', restrict, checkAccess, (req, res) => {
let result = common.updateConfig(req.body);
const result = common.updateConfig(req.body);
if(result === true){
res.status(200).json({ message: 'Settings successfully updated' });
res.configDirty = true;