mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
refs https://github.com/TryGhost/Team/issues/1558 - `members_from_address` is no longer configurable in Admin but was still used as a fallback - This change removes the fallback so we default straight to noreply@<domain> when a from-address isn't set for a newsletter
215 lines
6.6 KiB
JavaScript
215 lines
6.6 KiB
JavaScript
const errors = require('@tryghost/errors');
|
|
const logging = require('@tryghost/logging');
|
|
const tpl = require('@tryghost/tpl');
|
|
const {URL} = require('url');
|
|
const crypto = require('crypto');
|
|
const createKeypair = require('keypair');
|
|
|
|
const messages = {
|
|
incorrectKeyType: 'type must be one of "direct" or "connect".'
|
|
};
|
|
|
|
class MembersConfigProvider {
|
|
/**
|
|
* @param {object} options
|
|
* @param {{get: (key: string) => any}} options.settingsCache
|
|
* @param {{get: (key: string) => any}} options.config
|
|
* @param {any} options.urlUtils
|
|
*/
|
|
constructor(options) {
|
|
this._settingsCache = options.settingsCache;
|
|
this._config = options.config;
|
|
this._urlUtils = options.urlUtils;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
_getDomain() {
|
|
const url = this._urlUtils.urlFor('home', true).match(new RegExp('^https?://([^/:?#]+)(?:[/:?#]|$)', 'i'));
|
|
const domain = (url && url[1]) || '';
|
|
if (domain.startsWith('www.')) {
|
|
return domain.replace(/^(www)\.(?=[^/]*\..{2,5})/, '');
|
|
}
|
|
return domain;
|
|
}
|
|
|
|
getEmailFromAddress() {
|
|
// Individual from addresses are set per newsletter - this is the fallback address
|
|
return `noreply@${this._getDomain()}`;
|
|
}
|
|
|
|
getEmailSupportAddress() {
|
|
const supportAddress = this._settingsCache.get('members_support_address') || 'noreply';
|
|
|
|
// Any fromAddress without domain uses site domain, like default setting `noreply`
|
|
if (supportAddress.indexOf('@') < 0) {
|
|
return `${supportAddress}@${this._getDomain()}`;
|
|
}
|
|
return supportAddress;
|
|
}
|
|
|
|
getAuthEmailFromAddress() {
|
|
return this.getEmailSupportAddress() || this.getEmailFromAddress();
|
|
}
|
|
|
|
getPublicPlans() {
|
|
const defaultPriceData = {
|
|
monthly: 0,
|
|
yearly: 0,
|
|
currency: 'USD'
|
|
};
|
|
|
|
try {
|
|
const plans = this._settingsCache.get('stripe_plans') || [];
|
|
|
|
const priceData = plans.reduce((prices, plan) => {
|
|
const numberAmount = 0 + plan.amount;
|
|
const dollarAmount = numberAmount ? Math.round(numberAmount / 100) : 0;
|
|
return Object.assign(prices, {
|
|
[plan.name.toLowerCase()]: dollarAmount
|
|
});
|
|
}, {});
|
|
|
|
priceData.currency = plans[0].currency || 'USD';
|
|
|
|
if (Number.isInteger(priceData.monthly) && Number.isInteger(priceData.yearly)) {
|
|
return priceData;
|
|
}
|
|
|
|
return defaultPriceData;
|
|
} catch (err) {
|
|
return defaultPriceData;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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 = this._settingsCache.get(`stripe_${type === 'connect' ? 'connect_' : ''}secret_key`);
|
|
const publicKey = this._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 = this._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.getActiveStripeKeys() !== null;
|
|
}
|
|
|
|
getStripePaymentConfig() {
|
|
if (!this.isStripeConnected()) {
|
|
return null;
|
|
}
|
|
|
|
const stripeApiKeys = this.getActiveStripeKeys();
|
|
|
|
if (!stripeApiKeys) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
product: {
|
|
name: this._settingsCache.get('stripe_product_name')
|
|
},
|
|
plans: this._settingsCache.get('stripe_plans') || []
|
|
};
|
|
}
|
|
|
|
getAuthSecret() {
|
|
const hexSecret = this._settingsCache.get('members_email_auth_secret');
|
|
if (!hexSecret) {
|
|
logging.warn('Could not find members_email_auth_secret, using dynamically generated secret');
|
|
return crypto.randomBytes(64);
|
|
}
|
|
const secret = Buffer.from(hexSecret, 'hex');
|
|
if (secret.length < 64) {
|
|
logging.warn('members_email_auth_secret not large enough (64 bytes), using dynamically generated secret');
|
|
return crypto.randomBytes(64);
|
|
}
|
|
return secret;
|
|
}
|
|
|
|
getAllowSelfSignup() {
|
|
// 'invite' and 'none' members signup access disables all signup
|
|
if (this._settingsCache.get('members_signup_access') !== 'all') {
|
|
return false;
|
|
}
|
|
|
|
// if stripe is not connected then selected plans mean nothing.
|
|
// disabling signup would be done by switching to "invite only" mode
|
|
if (!this.isStripeConnected()) {
|
|
return true;
|
|
}
|
|
|
|
// self signup must be available for free plan signup to work
|
|
const hasFreePlan = this._settingsCache.get('portal_plans').includes('free');
|
|
if (hasFreePlan) {
|
|
return true;
|
|
}
|
|
|
|
// signup access is enabled but there's no free plan, don't allow self signup
|
|
return false;
|
|
}
|
|
|
|
getTokenConfig() {
|
|
const membersApiUrl = this._urlUtils.urlFor({relativeUrl: '/members/api'}, true);
|
|
|
|
let privateKey = this._settingsCache.get('members_private_key');
|
|
let publicKey = this._settingsCache.get('members_public_key');
|
|
|
|
if (!privateKey || !publicKey) {
|
|
logging.warn('Could not find members_private_key, using dynamically generated keypair');
|
|
const keypair = createKeypair({bits: 1024});
|
|
privateKey = keypair.private;
|
|
publicKey = keypair.public;
|
|
}
|
|
|
|
return {
|
|
issuer: membersApiUrl,
|
|
publicKey,
|
|
privateKey
|
|
};
|
|
}
|
|
|
|
getSigninURL(token, type) {
|
|
const siteUrl = this._urlUtils.urlFor({relativeUrl: '/members/'}, true);
|
|
const signinURL = new URL(siteUrl);
|
|
signinURL.searchParams.set('token', token);
|
|
signinURL.searchParams.set('action', type);
|
|
return signinURL.toString();
|
|
}
|
|
}
|
|
|
|
module.exports = MembersConfigProvider;
|