2018-11-09 14:42:21 +03:00
|
|
|
const _ = require('lodash');
|
|
|
|
const Promise = require('bluebird');
|
2020-04-30 22:26:12 +03:00
|
|
|
const errors = require('@tryghost/errors');
|
2021-06-15 17:36:27 +03:00
|
|
|
const logging = require('@tryghost/logging');
|
2021-07-07 23:02:55 +03:00
|
|
|
const tpl = require('@tryghost/tpl');
|
|
|
|
|
2021-07-07 23:41:34 +03:00
|
|
|
const settingsCache = require('./settings-cache');
|
|
|
|
const config = require('./config');
|
2019-11-06 10:42:39 +03:00
|
|
|
|
2021-07-07 23:02:55 +03:00
|
|
|
const messages = {
|
|
|
|
errorMessage: 'The \\{\\{{helperName}\\}\\} helper is not available.',
|
|
|
|
errorContext: 'The {flagName} flag must be enabled in labs if you wish to use the \\{\\{{helperName}\\}\\} helper.',
|
|
|
|
errorHelp: 'See {url}'
|
|
|
|
};
|
|
|
|
|
2021-10-22 17:02:16 +03:00
|
|
|
// flags in this list always return `true`, allows quick global enable prior to full flag removal
|
|
|
|
const GA_FEATURES = [
|
2021-12-01 14:50:45 +03:00
|
|
|
'customThemeSettings',
|
2021-12-06 15:13:33 +03:00
|
|
|
'nftCard',
|
2021-12-02 20:49:33 +03:00
|
|
|
'calloutCard',
|
2021-12-08 18:11:31 +03:00
|
|
|
'accordionCard',
|
|
|
|
'richTwitterNewsletters'
|
2021-10-22 17:02:16 +03:00
|
|
|
];
|
|
|
|
|
2021-06-04 18:56:16 +03:00
|
|
|
// NOTE: this allowlist is meant to be used to filter out any unexpected
|
|
|
|
// input for the "labs" setting value
|
2021-06-09 18:30:24 +03:00
|
|
|
const BETA_FEATURES = [
|
2021-06-07 22:22:06 +03:00
|
|
|
'activitypub',
|
2021-09-09 14:27:00 +03:00
|
|
|
'multipleProducts'
|
2021-06-09 18:30:24 +03:00
|
|
|
];
|
|
|
|
|
|
|
|
const ALPHA_FEATURES = [
|
2021-07-21 18:45:05 +03:00
|
|
|
'oauthLogin',
|
2021-11-02 12:55:55 +03:00
|
|
|
'membersActivity',
|
2021-11-03 12:01:37 +03:00
|
|
|
'cardSettingsPanel',
|
2021-11-12 11:24:27 +03:00
|
|
|
'urlCache',
|
2021-11-04 09:48:53 +03:00
|
|
|
'mediaAPI',
|
2021-11-08 10:33:06 +03:00
|
|
|
'filesAPI',
|
2021-11-08 18:42:02 +03:00
|
|
|
'membersAutoLogin',
|
2021-11-19 16:49:57 +03:00
|
|
|
'fileCard',
|
|
|
|
'audioCard',
|
2021-11-22 13:46:28 +03:00
|
|
|
'videoCard',
|
2021-11-24 19:33:47 +03:00
|
|
|
'productCard',
|
2021-12-07 12:16:11 +03:00
|
|
|
'beforeAfterCard',
|
2021-12-09 16:35:29 +03:00
|
|
|
'tweetGridCard',
|
|
|
|
'headerCard'
|
2021-06-04 18:56:16 +03:00
|
|
|
];
|
|
|
|
|
2021-10-22 17:02:16 +03:00
|
|
|
module.exports.GA_KEYS = [...GA_FEATURES];
|
2021-06-09 18:30:24 +03:00
|
|
|
module.exports.WRITABLE_KEYS_ALLOWLIST = [...BETA_FEATURES, ...ALPHA_FEATURES];
|
2021-06-04 18:56:16 +03:00
|
|
|
|
2021-06-07 19:51:37 +03:00
|
|
|
module.exports.getAll = () => {
|
|
|
|
const labs = _.cloneDeep(settingsCache.get('labs')) || {};
|
|
|
|
|
2021-06-09 18:30:24 +03:00
|
|
|
ALPHA_FEATURES.forEach((alphaKey) => {
|
2021-11-19 14:51:19 +03:00
|
|
|
if (labs[alphaKey] && !(config.get('enableDeveloperExperiments') || process.env.NODE_ENV.startsWith('test'))) {
|
2021-06-09 18:30:24 +03:00
|
|
|
delete labs[alphaKey];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-10-22 17:02:16 +03:00
|
|
|
GA_FEATURES.forEach((gaKey) => {
|
|
|
|
labs[gaKey] = true;
|
|
|
|
});
|
|
|
|
|
2021-06-07 19:51:37 +03:00
|
|
|
labs.members = settingsCache.get('members_signup_access') !== 'none';
|
|
|
|
|
|
|
|
return labs;
|
|
|
|
};
|
2015-11-03 22:17:24 +03:00
|
|
|
|
2021-06-18 18:03:58 +03:00
|
|
|
/**
|
|
|
|
* @param {string} flag
|
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
2019-11-06 10:53:53 +03:00
|
|
|
module.exports.isSet = function isSet(flag) {
|
|
|
|
const labsConfig = module.exports.getAll();
|
2019-11-06 10:42:39 +03:00
|
|
|
|
|
|
|
return !!(labsConfig && labsConfig[flag] && labsConfig[flag] === true);
|
2019-02-24 20:37:11 +03:00
|
|
|
};
|
|
|
|
|
2021-07-07 23:02:55 +03:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param {object} options
|
2021-08-09 12:34:42 +03:00
|
|
|
* @param {string} options.flagKey the internal lookup key of the flag e.g. labs.isSet(matchHelper)
|
2021-07-07 23:02:55 +03:00
|
|
|
* @param {string} options.flagName the user-facing name of the flag e.g. Match helper
|
|
|
|
* @param {string} options.helperName Name of the helper to be enabled/disabled
|
|
|
|
* @param {string} [options.errorMessage] Optional replacement error message
|
|
|
|
* @param {string} [options.errorContext] Optional replacement context message
|
|
|
|
* @param {string} [options.errorHelp] Optional replacement help message
|
|
|
|
* @param {string} [options.helpUrl] Url to show in the help message
|
|
|
|
* @param {string} [options.async] is the helper async?
|
|
|
|
* @param {function} callback
|
|
|
|
* @returns {Promise<Handlebars.SafeString>|Handlebars.SafeString}
|
|
|
|
*/
|
2019-11-06 10:53:53 +03:00
|
|
|
module.exports.enabledHelper = function enabledHelper(options, callback) {
|
2019-02-04 20:58:35 +03:00
|
|
|
const errDetails = {};
|
|
|
|
let errString;
|
2017-03-23 22:00:58 +03:00
|
|
|
|
2019-11-06 10:53:53 +03:00
|
|
|
if (module.exports.isSet(options.flagKey) === true) {
|
2017-03-23 22:00:58 +03:00
|
|
|
// helper is active, use the callback
|
|
|
|
return callback();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Else, the helper is not active and we need to handle this as an error
|
2021-10-21 17:07:14 +03:00
|
|
|
errDetails.message = tpl(options.errorMessage || messages.errorMessage, {helperName: options.helperName});
|
2021-07-07 23:02:55 +03:00
|
|
|
errDetails.context = tpl(options.errorContext || messages.errorContext, {
|
2019-02-04 20:58:35 +03:00
|
|
|
helperName: options.helperName,
|
|
|
|
flagName: options.flagName
|
|
|
|
});
|
2021-07-07 23:02:55 +03:00
|
|
|
errDetails.help = tpl(options.errorHelp || messages.errorHelp, {url: options.helpUrl});
|
2017-03-23 22:00:58 +03:00
|
|
|
|
2021-12-01 14:22:14 +03:00
|
|
|
// eslint-disable-next-line no-restricted-syntax
|
2020-04-30 22:26:12 +03:00
|
|
|
logging.error(new errors.DisabledFeatureError(errDetails));
|
2017-03-23 22:00:58 +03:00
|
|
|
|
2021-10-21 11:27:56 +03:00
|
|
|
const {SafeString} = require('express-hbs');
|
2019-07-15 09:18:58 +03:00
|
|
|
errString = new SafeString(`<script>console.error("${_.values(errDetails).join(' ')}");</script>`);
|
2017-03-23 22:00:58 +03:00
|
|
|
|
|
|
|
if (options.async) {
|
2019-07-15 09:18:58 +03:00
|
|
|
return Promise.resolve(errString);
|
2017-03-23 22:00:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return errString;
|
|
|
|
};
|
2021-07-07 23:47:19 +03:00
|
|
|
|
|
|
|
module.exports.enabledMiddleware = flag => (req, res, next) => {
|
2021-08-16 19:05:47 +03:00
|
|
|
if (module.exports.isSet(flag) === true) {
|
2021-07-07 23:47:19 +03:00
|
|
|
return next();
|
|
|
|
} else {
|
|
|
|
return next(new errors.NotFoundError());
|
|
|
|
}
|
|
|
|
};
|