0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00
ghost/core/server/services/settings/settings-service.js
Rishabh Garg f4066067e4
Extended public settings to include portal settings (#14801)
refs https://github.com/TryGhost/Team/issues/1599

- adds `portal_*` settings to public settings endpoint
- adds calculated `firstpromoter_account` setting for public settings endpoint
- also adds Ghost `version` information
2022-05-12 19:54:45 +05:30

184 lines
5.9 KiB
JavaScript

/**
* Settings Lib
* A collection of utilities for handling settings including a cache
*/
const errors = require('@tryghost/errors');
const tpl = require('@tryghost/tpl');
const events = require('../../lib/common/events');
const models = require('../../models');
const labs = require('../../../shared/labs');
const config = require('../../../shared/config');
const SettingsCache = require('../../../shared/settings-cache');
const SettingsBREADService = require('./settings-bread-service');
const {obfuscatedSetting, isSecretSetting, hideValueIfSecret} = require('./settings-utils');
const ObjectId = require('bson-objectid');
const messages = {
incorrectKeyType: 'type must be one of "direct" or "connect".'
};
/**
* @returns {SettingsBREADService} instance of the PostsService
*/
const getSettingsBREADServiceInstance = () => {
return new SettingsBREADService({
SettingsModel: models.Settings,
settingsCache: SettingsCache,
labsService: labs
});
};
class CalculatedField {
constructor({key, type, group, fn, dependents}) {
this.key = key;
this.type = type;
this.group = group;
this.fn = fn;
this.dependents = dependents;
}
getSetting() {
return {
key: this.key,
type: this.type,
group: this.group,
value: this.fn(),
// @TODO: remove this hack
id: ObjectId().toHexString(),
created_at: new Date().toISOString().replace(/\d{3}Z$/, '000Z'),
updated_at: new Date().toISOString().replace(/\d{3}Z$/, '000Z')
};
}
}
module.exports = {
/**
* Initialize the cache, used in boot and in testing
*/
async init() {
const settingsCollection = await models.Settings.populateDefaults();
SettingsCache.init(events, settingsCollection, this.getCalculatedFields());
},
/**
* Restore the cache, used during e2e testing only
*/
reset() {
SettingsCache.reset(events);
},
isMembersEnabled() {
return SettingsCache.get('members_signup_access') !== 'none';
},
isMembersInviteOnly() {
return SettingsCache.get('members_signup_access') === 'invite';
},
/**
* @param {'direct' | 'connect'} type - The "type" of keys to fetch from settings
* @returns {{publicKey: string, secretKey: string} | null}
*/
getStripeKeys(type) {
if (type !== 'direct' && type !== 'connect') {
throw new errors.IncorrectUsageError({message: tpl(messages.incorrectKeyType)});
}
const secretKey = SettingsCache.get(`stripe_${type === 'connect' ? 'connect_' : ''}secret_key`);
const publicKey = SettingsCache.get(`stripe_${type === 'connect' ? 'connect_' : ''}publishable_key`);
if (!secretKey || !publicKey) {
return null;
}
return {
secretKey,
publicKey
};
},
/**
* @returns {{publicKey: string, secretKey: string} | null}
*/
getActiveStripeKeys() {
const stripeDirect = config.get('stripeDirect');
if (stripeDirect) {
return this.getStripeKeys('direct');
}
const connectKeys = this.getStripeKeys('connect');
if (!connectKeys) {
return this.getStripeKeys('direct');
}
return connectKeys;
},
isStripeConnected() {
return this.isMembersEnabled() && this.getActiveStripeKeys() !== null;
},
getFirstpromoterId() {
if (!SettingsCache.get('firstpromoter')) {
return null;
}
return SettingsCache.get('firstpromoter_id');
},
/**
*
*/
getCalculatedFields() {
const fields = [];
fields.push(new CalculatedField({key: 'members_enabled', type: 'boolean', group: 'members', fn: this.isMembersEnabled.bind(this), dependents: ['members_signup_access']}));
fields.push(new CalculatedField({key: 'members_invite_only', type: 'boolean', group: 'members', fn: this.isMembersInviteOnly.bind(this), dependents: ['members_signup_access']}));
fields.push(new CalculatedField({key: 'paid_members_enabled', type: 'boolean', group: 'members', fn: this.isStripeConnected.bind(this), dependents: ['members_signup_access', 'stripe_secret_key', 'stripe_publishable_key', 'stripe_connect_secret_key', 'stripe_connect_publishable_key']}));
fields.push(new CalculatedField({key: 'firstpromoter_account', type: 'string', group: 'firstpromoter', fn: this.getFirstpromoterId.bind(this), dependents: ['firstpromoter', 'firstpromoter_id']}));
return fields;
},
/**
* Handles synchronization of routes.yaml hash loaded in the frontend with
* the value stored in the settings table.
* getRoutesHash is a function to allow keeping "frontend" decoupled from settings
*
* @param {function} getRoutesHash function fetching currently loaded routes file hash
*/
async syncRoutesHash(getRoutesHash) {
const currentRoutesHash = await getRoutesHash();
if (SettingsCache.get('routes_hash') !== currentRoutesHash) {
return await models.Settings.edit([{
key: 'routes_hash',
value: currentRoutesHash
}], {context: {internal: true}});
}
},
/**
* Handles email setting synchronization when email has been verified per instance
*
* @param {boolean} configValue current email verification value from local config
*/
async syncEmailSettings(configValue) {
const isEmailDisabled = SettingsCache.get('email_verification_required');
if (configValue === true && isEmailDisabled) {
return await models.Settings.edit([{
key: 'email_verification_required',
value: false
}], {context: {internal: true}});
}
},
obfuscatedSetting,
isSecretSetting,
hideValueIfSecret,
getSettingsBREADServiceInstance
};