mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-23 11:55:01 +03:00
🐛 Fixed sending emails from email domain that includes www subdomain (#15348)
fixes https://github.com/TryGhost/Team/issues/1855 fixes https://github.com/TryGhost/Team/issues/1866 This commit moves all duplicate methods to get the support email address to a single location. Also methods to get the default email domain are moved. For the location, I initially wanted to put it at the settings service. But that service doesn't feel like the right place. Instead I created a new settings helpers service. This service takes the settingsCache, urlUtils and config and calculates some special 'calculated' settings based on those: - Support email methods - Stripe (active) keys / stripe connected (also removed some duplicate code that calculated the keys in a couple of places) - All the calculated settings are moved to the settings helpers I'm not 100% confident in whether this is the right place to put the helpers. Suggestions are welcome.
This commit is contained in:
parent
51ddc39fa7
commit
2e85ae98be
@ -5,12 +5,13 @@ const htmlToPlaintext = require('@tryghost/html-to-plaintext');
|
||||
const postEmailSerializer = require('../mega/post-email-serializer');
|
||||
|
||||
class CommentsServiceEmails {
|
||||
constructor({config, logging, models, mailer, settingsCache, urlService, urlUtils}) {
|
||||
constructor({config, logging, models, mailer, settingsCache, settingsHelpers, urlService, urlUtils}) {
|
||||
this.config = config;
|
||||
this.logging = logging;
|
||||
this.models = models;
|
||||
this.mailer = mailer;
|
||||
this.settingsCache = settingsCache;
|
||||
this.settingsHelpers = settingsHelpers;
|
||||
this.urlService = urlService;
|
||||
this.urlUtils = urlUtils;
|
||||
|
||||
@ -166,25 +167,8 @@ class CommentsServiceEmails {
|
||||
return siteDomain;
|
||||
}
|
||||
|
||||
get membersAddress() {
|
||||
// TODO: get from address of default newsletter?
|
||||
return `noreply@${this.siteDomain}`;
|
||||
}
|
||||
|
||||
// TODO: duplicated from services/members/config - exrtact to settings?
|
||||
get supportAddress() {
|
||||
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.siteDomain}`;
|
||||
}
|
||||
|
||||
return supportAddress;
|
||||
}
|
||||
|
||||
get notificationFromAddress() {
|
||||
return this.supportAddress || this.membersAddress;
|
||||
return this.settingsHelpers.getMembersSupportAddress();
|
||||
}
|
||||
|
||||
extractInitials(name = '') {
|
||||
|
@ -14,6 +14,7 @@ class CommentsServiceWrapper {
|
||||
const urlUtils = require('../../../shared/url-utils');
|
||||
const membersService = require('../members');
|
||||
const db = require('../../data/db');
|
||||
const settingsHelpers = require('../settings-helpers');
|
||||
|
||||
this.api = new CommentsService({
|
||||
config,
|
||||
@ -21,6 +22,7 @@ class CommentsServiceWrapper {
|
||||
models,
|
||||
mailer,
|
||||
settingsCache,
|
||||
settingsHelpers,
|
||||
urlService,
|
||||
urlUtils,
|
||||
contentGating: membersService.contentGating
|
||||
|
@ -15,7 +15,7 @@ const messages = {
|
||||
};
|
||||
|
||||
class CommentsService {
|
||||
constructor({config, logging, models, mailer, settingsCache, urlService, urlUtils, contentGating}) {
|
||||
constructor({config, logging, models, mailer, settingsCache, settingsHelpers, urlService, urlUtils, contentGating}) {
|
||||
/** @private */
|
||||
this.models = models;
|
||||
|
||||
@ -33,6 +33,7 @@ class CommentsService {
|
||||
models,
|
||||
mailer,
|
||||
settingsCache,
|
||||
settingsHelpers,
|
||||
urlService,
|
||||
urlUtils
|
||||
});
|
||||
|
@ -1,101 +1,40 @@
|
||||
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 {{getDefaultEmailDomain(): string, getMembersSupportAddress(): string, isStripeConnected(): boolean}} options.settingsHelpers
|
||||
* @param {any} options.urlUtils
|
||||
*/
|
||||
constructor(options) {
|
||||
this._settingsCache = options.settingsCache;
|
||||
this._config = options.config;
|
||||
this._urlUtils = options.urlUtils;
|
||||
constructor({settingsCache, settingsHelpers, urlUtils}) {
|
||||
this._settingsCache = settingsCache;
|
||||
this._settingsHelpers = settingsHelpers;
|
||||
this._urlUtils = 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;
|
||||
get defaultEmailDomain() {
|
||||
return this._settingsHelpers.getDefaultEmailDomain();
|
||||
}
|
||||
|
||||
getEmailFromAddress() {
|
||||
// Individual from addresses are set per newsletter - this is the fallback address
|
||||
return `noreply@${this._getDomain()}`;
|
||||
return `noreply@${this.defaultEmailDomain}`;
|
||||
}
|
||||
|
||||
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;
|
||||
return this._settingsHelpers.getMembersSupportAddress();
|
||||
}
|
||||
|
||||
getAuthEmailFromAddress() {
|
||||
return this.getEmailSupportAddress() || this.getEmailFromAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
return this.getEmailSupportAddress();
|
||||
}
|
||||
|
||||
isStripeConnected() {
|
||||
return this.getActiveStripeKeys() !== null;
|
||||
return this._settingsHelpers.isStripeConnected();
|
||||
}
|
||||
|
||||
getAuthSecret() {
|
||||
|
@ -20,6 +20,7 @@ const VerificationTrigger = require('@tryghost/verification-trigger');
|
||||
const DomainEvents = require('@tryghost/domain-events');
|
||||
const {LastSeenAtUpdater} = require('@tryghost/members-events-service');
|
||||
const DatabaseInfo = require('@tryghost/database-info');
|
||||
const settingsHelpers = require('../settings-helpers');
|
||||
|
||||
const messages = {
|
||||
noLiveKeysInDevelopment: 'Cannot use live stripe keys in development. Please restart in production mode.',
|
||||
@ -30,7 +31,7 @@ const messages = {
|
||||
const ghostMailer = new GhostMailer();
|
||||
|
||||
const membersConfig = new MembersConfigProvider({
|
||||
config,
|
||||
settingsHelpers,
|
||||
settingsCache,
|
||||
urlUtils
|
||||
});
|
||||
|
@ -0,0 +1,6 @@
|
||||
const settingsCache = require('../../../shared/settings-cache');
|
||||
const urlUtils = require('../../../shared/url-utils');
|
||||
const config = require('../../../shared/config');
|
||||
const SettingsHelpers = require('./settings-helpers');
|
||||
|
||||
module.exports = new SettingsHelpers({settingsCache, urlUtils, config});
|
@ -0,0 +1,99 @@
|
||||
const tpl = require('@tryghost/tpl');
|
||||
const errors = require('@tryghost/errors');
|
||||
|
||||
const messages = {
|
||||
incorrectKeyType: 'type must be one of "direct" or "connect".'
|
||||
};
|
||||
|
||||
class SettingsHelpers {
|
||||
constructor({settingsCache, urlUtils, config}) {
|
||||
this.settingsCache = settingsCache;
|
||||
this.urlUtils = urlUtils;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
isMembersEnabled() {
|
||||
return this.settingsCache.get('members_signup_access') !== 'none';
|
||||
}
|
||||
|
||||
isMembersInviteOnly() {
|
||||
return this.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 = 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;
|
||||
}
|
||||
|
||||
arePaidMembersEnabled() {
|
||||
return this.isMembersEnabled() && this.isStripeConnected();
|
||||
}
|
||||
|
||||
getFirstpromoterId() {
|
||||
if (!this.settingsCache.get('firstpromoter')) {
|
||||
return null;
|
||||
}
|
||||
return this.settingsCache.get('firstpromoter_id');
|
||||
}
|
||||
|
||||
getDefaultEmailDomain() {
|
||||
const url = this.urlUtils.urlFor('home', true).match(new RegExp('^https?://([^/:?#]+)(?:[/:?#]|$)', 'i'));
|
||||
const domain = (url && url[1]) || '';
|
||||
if (domain.startsWith('www.')) {
|
||||
return domain.substring('www.'.length);
|
||||
}
|
||||
return domain;
|
||||
}
|
||||
|
||||
getMembersSupportAddress() {
|
||||
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.getDefaultEmailDomain()}`;
|
||||
}
|
||||
return supportAddress;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SettingsHelpers;
|
@ -2,13 +2,9 @@
|
||||
* 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 adapterManager = require('../adapter-manager');
|
||||
const SettingsCache = require('../../../shared/settings-cache');
|
||||
const SettingsBREADService = require('./settings-bread-service');
|
||||
@ -18,10 +14,7 @@ const SingleUseTokenProvider = require('../members/SingleUseTokenProvider');
|
||||
const urlUtils = require('../../../shared/url-utils');
|
||||
|
||||
const ObjectId = require('bson-objectid');
|
||||
|
||||
const messages = {
|
||||
incorrectKeyType: 'type must be one of "direct" or "connect".'
|
||||
};
|
||||
const settingsHelpers = require('../settings-helpers');
|
||||
|
||||
const MAGIC_LINK_TOKEN_VALIDITY = 24 * 60 * 60 * 1000;
|
||||
|
||||
@ -79,76 +72,16 @@ module.exports = {
|
||||
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;
|
||||
},
|
||||
|
||||
arePaidMembersEnabled() {
|
||||
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.arePaidMembersEnabled.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']}));
|
||||
fields.push(new CalculatedField({key: 'members_enabled', type: 'boolean', group: 'members', fn: settingsHelpers.isMembersEnabled.bind(settingsHelpers), dependents: ['members_signup_access']}));
|
||||
fields.push(new CalculatedField({key: 'members_invite_only', type: 'boolean', group: 'members', fn: settingsHelpers.isMembersInviteOnly.bind(settingsHelpers), dependents: ['members_signup_access']}));
|
||||
fields.push(new CalculatedField({key: 'paid_members_enabled', type: 'boolean', group: 'members', fn: settingsHelpers.arePaidMembersEnabled.bind(settingsHelpers), 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: settingsHelpers.getFirstpromoterId.bind(settingsHelpers), dependents: ['firstpromoter', 'firstpromoter_id']}));
|
||||
|
||||
return fields;
|
||||
},
|
||||
|
@ -2,22 +2,20 @@ class StaffServiceWrapper {
|
||||
init() {
|
||||
const StaffService = require('@tryghost/staff-service');
|
||||
|
||||
const config = require('../../../shared/config');
|
||||
const logging = require('@tryghost/logging');
|
||||
const models = require('../../models');
|
||||
const {GhostMailer} = require('../mail');
|
||||
const mailer = new GhostMailer();
|
||||
const settingsCache = require('../../../shared/settings-cache');
|
||||
const urlService = require('../url');
|
||||
const urlUtils = require('../../../shared/url-utils');
|
||||
const settingsHelpers = require('../settings-helpers');
|
||||
|
||||
this.api = new StaffService({
|
||||
config,
|
||||
logging,
|
||||
models,
|
||||
mailer,
|
||||
settingsHelpers,
|
||||
settingsCache,
|
||||
urlService,
|
||||
urlUtils
|
||||
});
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ const messages = {
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
getConfig(settings, config, urlUtils) {
|
||||
getConfig({config, urlUtils, settingsHelpers}) {
|
||||
/**
|
||||
* @returns {StripeURLConfig}
|
||||
*/
|
||||
@ -41,43 +41,7 @@ module.exports = {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {'direct' | 'connect'} type - The "type" of keys to fetch from settings
|
||||
* @returns {{publicKey: string, secretKey: string} | null}
|
||||
*/
|
||||
function getStripeKeys(type) {
|
||||
const secretKey = settings.get(`stripe_${type === 'connect' ? 'connect_' : ''}secret_key`);
|
||||
const publicKey = settings.get(`stripe_${type === 'connect' ? 'connect_' : ''}publishable_key`);
|
||||
|
||||
if (!secretKey || !publicKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
secretKey,
|
||||
publicKey
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {{publicKey: string, secretKey: string} | null}
|
||||
*/
|
||||
function getActiveStripeKeys() {
|
||||
const stripeDirect = config.get('stripeDirect');
|
||||
|
||||
if (stripeDirect) {
|
||||
return getStripeKeys('direct');
|
||||
}
|
||||
|
||||
const connectKeys = getStripeKeys('connect');
|
||||
|
||||
if (!connectKeys) {
|
||||
return getStripeKeys('direct');
|
||||
}
|
||||
|
||||
return connectKeys;
|
||||
}
|
||||
const keys = getActiveStripeKeys();
|
||||
const keys = settingsHelpers.getActiveStripeKeys();
|
||||
if (!keys) {
|
||||
return null;
|
||||
}
|
||||
|
@ -8,9 +8,10 @@ const urlUtils = require('../../../shared/url-utils');
|
||||
const events = require('../../lib/common/events');
|
||||
const models = require('../../models');
|
||||
const {getConfig} = require('./config');
|
||||
const settingsHelpers = require('../settings-helpers');
|
||||
|
||||
async function configureApi() {
|
||||
const cfg = getConfig(settings, config, urlUtils);
|
||||
const cfg = getConfig({settingsHelpers, config, urlUtils});
|
||||
if (cfg) {
|
||||
cfg.testEnv = process.env.NODE_ENV.startsWith('test');
|
||||
await module.exports.configure(cfg);
|
||||
|
@ -1854,7 +1854,7 @@ Object {
|
||||
|
||||
exports[`Comments API when commenting enabled for all when authenticated Can fetch counts 1: [body] 1`] = `
|
||||
Object {
|
||||
"618ba1ffbe2896088840a6df": 13,
|
||||
"618ba1ffbe2896088840a6df": 15,
|
||||
"618ba1ffbe2896088840a6e1": 0,
|
||||
"618ba1ffbe2896088840a6e3": 0,
|
||||
}
|
||||
@ -2270,6 +2270,86 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Comments API when commenting enabled for all when authenticated Can reply to a comment with custom support email 1: [body] 1`] = `
|
||||
Object {
|
||||
"comments": Array [
|
||||
Object {
|
||||
"count": Object {
|
||||
"likes": Any<Number>,
|
||||
"replies": 0,
|
||||
},
|
||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||
"edited_at": null,
|
||||
"html": "This is a reply",
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"liked": Any<Boolean>,
|
||||
"member": Object {
|
||||
"avatar_image": null,
|
||||
"bio": null,
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"name": null,
|
||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||
},
|
||||
"replies": Array [],
|
||||
"status": "published",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Comments API when commenting enabled for all when authenticated Can reply to a comment with custom support email 2: [headers] 1`] = `
|
||||
Object {
|
||||
"access-control-allow-origin": "*",
|
||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||
"content-length": "342",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
"location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//,
|
||||
"vary": "Accept-Encoding",
|
||||
"x-powered-by": "Express",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Comments API when commenting enabled for all when authenticated Can reply to a comment with www domain 1: [body] 1`] = `
|
||||
Object {
|
||||
"comments": Array [
|
||||
Object {
|
||||
"count": Object {
|
||||
"likes": Any<Number>,
|
||||
"replies": 0,
|
||||
},
|
||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||
"edited_at": null,
|
||||
"html": "This is a reply",
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"liked": Any<Boolean>,
|
||||
"member": Object {
|
||||
"avatar_image": null,
|
||||
"bio": null,
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"name": null,
|
||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||
},
|
||||
"replies": Array [],
|
||||
"status": "published",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Comments API when commenting enabled for all when authenticated Can reply to a comment with www domain 2: [headers] 1`] = `
|
||||
Object {
|
||||
"access-control-allow-origin": "*",
|
||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||
"content-length": "342",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
"location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//,
|
||||
"vary": "Accept-Encoding",
|
||||
"x-powered-by": "Express",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Comments API when commenting enabled for all when authenticated Can reply to your own comment 1: [body] 1`] = `
|
||||
Object {
|
||||
"comments": Array [
|
||||
@ -2319,6 +2399,53 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Comments API when commenting enabled for all when authenticated Can request last page of replies 1: [body] 1`] = `
|
||||
Object {
|
||||
"comments": Array [
|
||||
Object {
|
||||
"count": Object {
|
||||
"likes": Any<Number>,
|
||||
},
|
||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||
"edited_at": null,
|
||||
"html": "This is a reply",
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"liked": Any<Boolean>,
|
||||
"member": Object {
|
||||
"avatar_image": null,
|
||||
"bio": null,
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"name": null,
|
||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||
},
|
||||
"status": "published",
|
||||
},
|
||||
],
|
||||
"meta": Object {
|
||||
"pagination": Object {
|
||||
"limit": 3,
|
||||
"next": null,
|
||||
"page": 3,
|
||||
"pages": 3,
|
||||
"prev": 2,
|
||||
"total": 7,
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Comments API when commenting enabled for all when authenticated Can request last page of replies 2: [headers] 1`] = `
|
||||
Object {
|
||||
"access-control-allow-origin": "*",
|
||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||
"content-length": "401",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
"vary": "Accept-Encoding",
|
||||
"x-powered-by": "Express",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Comments API when commenting enabled for all when authenticated Can request second page of replies 1: [body] 1`] = `
|
||||
Object {
|
||||
"comments": Array [
|
||||
@ -2477,6 +2604,42 @@ Object {
|
||||
},
|
||||
"status": "published",
|
||||
},
|
||||
Object {
|
||||
"count": Object {
|
||||
"likes": Any<Number>,
|
||||
},
|
||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||
"edited_at": null,
|
||||
"html": "This is a reply",
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"liked": Any<Boolean>,
|
||||
"member": Object {
|
||||
"avatar_image": null,
|
||||
"bio": null,
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"name": null,
|
||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||
},
|
||||
"status": "published",
|
||||
},
|
||||
Object {
|
||||
"count": Object {
|
||||
"likes": Any<Number>,
|
||||
},
|
||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||
"edited_at": null,
|
||||
"html": "This is a reply",
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"liked": Any<Boolean>,
|
||||
"member": Object {
|
||||
"avatar_image": null,
|
||||
"bio": null,
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"name": null,
|
||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||
},
|
||||
"status": "published",
|
||||
},
|
||||
],
|
||||
"meta": Object {
|
||||
"pagination": Object {
|
||||
@ -2485,7 +2648,7 @@ Object {
|
||||
"page": 1,
|
||||
"pages": 1,
|
||||
"prev": null,
|
||||
"total": 5,
|
||||
"total": 7,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -2495,7 +2658,7 @@ exports[`Comments API when commenting enabled for all when authenticated Can ret
|
||||
Object {
|
||||
"access-control-allow-origin": "*",
|
||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||
"content-length": "1629",
|
||||
"content-length": "2235",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
"vary": "Accept-Encoding",
|
||||
|
@ -1,11 +1,12 @@
|
||||
const assert = require('assert');
|
||||
const {agentProvider, mockManager, fixtureManager, matchers} = require('../../utils/e2e-framework');
|
||||
const {agentProvider, mockManager, fixtureManager, matchers, configUtils} = require('../../utils/e2e-framework');
|
||||
const {anyEtag, anyObjectId, anyLocationFor, anyISODateTime, anyErrorId, anyUuid, anyNumber, anyBoolean} = matchers;
|
||||
const should = require('should');
|
||||
const models = require('../../../core/server/models');
|
||||
const moment = require('moment-timezone');
|
||||
const settingsCache = require('../../../core/shared/settings-cache');
|
||||
const sinon = require('sinon');
|
||||
const settingsService = require('../../../core/server/services/settings/settings-service');
|
||||
|
||||
let membersAgent, membersAgent2, postId, postTitle, commentId;
|
||||
|
||||
@ -98,7 +99,7 @@ async function testCanCommentOnPost(member) {
|
||||
should.notEqual(member.get('last_commented_at'), null, 'The member should have a `last_commented_at` property after posting a comment.');
|
||||
}
|
||||
|
||||
async function testCanReply(member) {
|
||||
async function testCanReply(member, emailMatchers = {}) {
|
||||
const date = new Date(0);
|
||||
await models.Member.edit({last_seen_at: date, last_commented_at: date}, {id: member.get('id')});
|
||||
|
||||
@ -125,6 +126,7 @@ async function testCanReply(member) {
|
||||
});
|
||||
|
||||
mockManager.assert.sentEmail({
|
||||
...emailMatchers,
|
||||
subject: '↪️ New reply to your comment on Ghost',
|
||||
to: fixtureManager.get('members', 0).email
|
||||
});
|
||||
@ -195,6 +197,7 @@ describe('Comments API', function () {
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
configUtils.restore();
|
||||
mockManager.restore();
|
||||
});
|
||||
|
||||
@ -210,7 +213,7 @@ describe('Comments API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
afterEach(async function () {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
@ -245,13 +248,15 @@ describe('Comments API', function () {
|
||||
});
|
||||
|
||||
describe('when authenticated', function () {
|
||||
let getStub;
|
||||
|
||||
before(async function () {
|
||||
await membersAgent.loginAs('member@example.com');
|
||||
member = await models.Member.findOne({email: 'member@example.com'}, {require: true});
|
||||
await membersAgent2.loginAs('member2@example.com');
|
||||
});
|
||||
beforeEach(function () {
|
||||
const getStub = sinon.stub(settingsCache, 'get');
|
||||
getStub = sinon.stub(settingsCache, 'get');
|
||||
getStub.callsFake((key, options) => {
|
||||
if (key === 'comments_enabled') {
|
||||
return 'all';
|
||||
@ -323,7 +328,7 @@ describe('Comments API', function () {
|
||||
it('Can reply to a comment', async function () {
|
||||
await testCanReply(member);
|
||||
});
|
||||
|
||||
|
||||
let testReplyId;
|
||||
it('Limits returned replies to 3', async function () {
|
||||
const parentId = fixtureManager.get('comments', 0).id;
|
||||
@ -378,6 +383,26 @@ describe('Comments API', function () {
|
||||
body.comments[0].count.replies.should.eql(5);
|
||||
});
|
||||
});
|
||||
|
||||
it('Can reply to a comment with www domain', async function () {
|
||||
// Test that the www. is stripped from the default
|
||||
configUtils.set('url', 'http://www.domain.example/');
|
||||
await testCanReply(member, {from: 'noreply@domain.example'});
|
||||
});
|
||||
|
||||
it('Can reply to a comment with custom support email', async function () {
|
||||
// Test that the www. is stripped from the default
|
||||
getStub.callsFake((key, options) => {
|
||||
if (key === 'members_support_address') {
|
||||
return 'support@example.com';
|
||||
}
|
||||
if (key === 'comments_enabled') {
|
||||
return 'all';
|
||||
}
|
||||
return getStub.wrappedMethod.call(settingsCache, key, options);
|
||||
});
|
||||
await testCanReply(member, {from: 'support@example.com'});
|
||||
});
|
||||
|
||||
it('Can like a comment', async function () {
|
||||
// Check not liked
|
||||
@ -472,11 +497,11 @@ describe('Comments API', function () {
|
||||
etag: anyEtag
|
||||
})
|
||||
.matchBodySnapshot({
|
||||
comments: new Array(5).fill(commentMatcher)
|
||||
comments: new Array(7).fill(commentMatcher)
|
||||
})
|
||||
.expect(({body}) => {
|
||||
should(body.comments[0].count.replies).be.undefined();
|
||||
should(body.meta.pagination.total).eql(5);
|
||||
should(body.meta.pagination.total).eql(7);
|
||||
should(body.meta.pagination.next).eql(null);
|
||||
|
||||
// Check liked + likes working for replies too
|
||||
@ -486,22 +511,22 @@ describe('Comments API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Can request second page of replies', async function () {
|
||||
it('Can request last page of replies', async function () {
|
||||
const parentId = fixtureManager.get('comments', 0).id;
|
||||
|
||||
// Check initial status: two replies before test
|
||||
await membersAgent
|
||||
.get(`/api/comments/${parentId}/replies/?page=2&limit=3`)
|
||||
.get(`/api/comments/${parentId}/replies/?page=3&limit=3`)
|
||||
.expectStatus(200)
|
||||
.matchHeaderSnapshot({
|
||||
etag: anyEtag
|
||||
})
|
||||
.matchBodySnapshot({
|
||||
comments: new Array(2).fill(commentMatcher)
|
||||
comments: new Array(1).fill(commentMatcher)
|
||||
})
|
||||
.expect(({body}) => {
|
||||
should(body.comments[0].count.replies).be.undefined();
|
||||
should(body.meta.pagination.total).eql(5);
|
||||
should(body.meta.pagination.total).eql(7);
|
||||
should(body.meta.pagination.next).eql(null);
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,93 @@
|
||||
const should = require('should');
|
||||
const sinon = require('sinon');
|
||||
const configUtils = require('../../../../utils/configUtils');
|
||||
const SettingsHelpers = require('../../../../../core/server/services/settings-helpers/settings-helpers');
|
||||
|
||||
function createSettingsMock({setDirect, setConnect}) {
|
||||
const getStub = sinon.stub();
|
||||
|
||||
getStub.withArgs('members_signup_access').returns('all');
|
||||
getStub.withArgs('stripe_secret_key').returns(setDirect ? 'direct_secret' : null);
|
||||
getStub.withArgs('stripe_publishable_key').returns(setDirect ? 'direct_publishable' : null);
|
||||
getStub.withArgs('stripe_plans').returns([{
|
||||
name: 'Monthly',
|
||||
currency: 'usd',
|
||||
interval: 'month',
|
||||
amount: 1000
|
||||
}, {
|
||||
name: 'Yearly',
|
||||
currency: 'usd',
|
||||
interval: 'year',
|
||||
amount: 10000
|
||||
}]);
|
||||
|
||||
getStub.withArgs('stripe_connect_secret_key').returns(setConnect ? 'connect_secret' : null);
|
||||
getStub.withArgs('stripe_connect_publishable_key').returns(setConnect ? 'connect_publishable' : null);
|
||||
getStub.withArgs('stripe_connect_livemode').returns(true);
|
||||
getStub.withArgs('stripe_connect_display_name').returns('Test');
|
||||
getStub.withArgs('stripe_connect_account_id').returns('ac_XXXXXXXXXXXXX');
|
||||
|
||||
return {
|
||||
get: getStub
|
||||
};
|
||||
}
|
||||
|
||||
describe('Settings Helpers - getActiveStripeKeys', function () {
|
||||
beforeEach(function () {
|
||||
configUtils.set({
|
||||
url: 'http://domain.tld/subdir',
|
||||
admin: {url: 'http://sub.domain.tld'}
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
configUtils.restore();
|
||||
});
|
||||
|
||||
it('Uses direct keys when stripeDirect is true, regardles of which keys exist', function () {
|
||||
const fakeSettings = createSettingsMock({setDirect: true, setConnect: true});
|
||||
configUtils.set({
|
||||
stripeDirect: true
|
||||
});
|
||||
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils: {}});
|
||||
const keys = settingsHelpers.getActiveStripeKeys();
|
||||
|
||||
should.equal(keys.publicKey, 'direct_publishable');
|
||||
should.equal(keys.secretKey, 'direct_secret');
|
||||
});
|
||||
|
||||
it('Does not use connect keys if stripeDirect is true, and the direct keys do not exist', function () {
|
||||
const fakeSettings = createSettingsMock({setDirect: false, setConnect: true});
|
||||
configUtils.set({
|
||||
stripeDirect: true
|
||||
});
|
||||
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils: {}});
|
||||
const keys = settingsHelpers.getActiveStripeKeys();
|
||||
|
||||
should.equal(keys, null);
|
||||
});
|
||||
|
||||
it('Uses connect keys when stripeDirect is false, and the connect keys exist', function () {
|
||||
const fakeSettings = createSettingsMock({setDirect: true, setConnect: true});
|
||||
configUtils.set({
|
||||
stripeDirect: false
|
||||
});
|
||||
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils: {}});
|
||||
const keys = settingsHelpers.getActiveStripeKeys();
|
||||
|
||||
should.equal(keys.publicKey, 'connect_publishable');
|
||||
should.equal(keys.secretKey, 'connect_secret');
|
||||
});
|
||||
|
||||
it('Uses direct keys when stripeDirect is false, but the connect keys do not exist', function () {
|
||||
const fakeSettings = createSettingsMock({setDirect: true, setConnect: false});
|
||||
configUtils.set({
|
||||
stripeDirect: false
|
||||
});
|
||||
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils: {}});
|
||||
const keys = settingsHelpers.getActiveStripeKeys();
|
||||
|
||||
should.equal(keys.publicKey, 'direct_publishable');
|
||||
should.equal(keys.secretKey, 'direct_secret');
|
||||
});
|
||||
});
|
@ -6,37 +6,12 @@ const configUtils = require('../../../../utils/configUtils');
|
||||
|
||||
const {getConfig} = require('../../../../../core/server/services/stripe/config');
|
||||
|
||||
/**
|
||||
* @param {object} options
|
||||
* @param {boolean} options.setDirect - Whether the "direct" keys should be set
|
||||
* @param {boolean} options.setConnect - Whether the connect_integration keys should be set
|
||||
*/
|
||||
function createSettingsMock({setDirect, setConnect}) {
|
||||
const getStub = sinon.stub();
|
||||
|
||||
getStub.withArgs('members_signup_access').returns('all');
|
||||
getStub.withArgs('stripe_secret_key').returns(setDirect ? 'direct_secret' : null);
|
||||
getStub.withArgs('stripe_publishable_key').returns(setDirect ? 'direct_publishable' : null);
|
||||
getStub.withArgs('stripe_plans').returns([{
|
||||
name: 'Monthly',
|
||||
currency: 'usd',
|
||||
interval: 'month',
|
||||
amount: 1000
|
||||
}, {
|
||||
name: 'Yearly',
|
||||
currency: 'usd',
|
||||
interval: 'year',
|
||||
amount: 10000
|
||||
}]);
|
||||
|
||||
getStub.withArgs('stripe_connect_secret_key').returns(setConnect ? 'connect_secret' : null);
|
||||
getStub.withArgs('stripe_connect_publishable_key').returns(setConnect ? 'connect_publishable' : null);
|
||||
getStub.withArgs('stripe_connect_livemode').returns(true);
|
||||
getStub.withArgs('stripe_connect_display_name').returns('Test');
|
||||
getStub.withArgs('stripe_connect_account_id').returns('ac_XXXXXXXXXXXXX');
|
||||
|
||||
function createSettingsHelpersMock() {
|
||||
return {
|
||||
get: getStub
|
||||
getActiveStripeKeys: sinon.stub().returns({
|
||||
secretKey: 'direct_secret',
|
||||
publicKey: 'direct_publishable'
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
@ -71,67 +46,35 @@ describe('Stripe - config', function () {
|
||||
configUtils.restore();
|
||||
});
|
||||
|
||||
it('Uses direct keys when stripeDirect is true, regardles of which keys exist', function () {
|
||||
const fakeSettings = createSettingsMock({setDirect: true, setConnect: true});
|
||||
configUtils.set({
|
||||
stripeDirect: true
|
||||
});
|
||||
const fakeUrlUtils = createUrlUtilsMock();
|
||||
|
||||
const config = getConfig(fakeSettings, configUtils.config, fakeUrlUtils);
|
||||
|
||||
should.equal(config.publicKey, 'direct_publishable');
|
||||
should.equal(config.secretKey, 'direct_secret');
|
||||
});
|
||||
|
||||
it('Does not use connect keys if stripeDirect is true, and the direct keys do not exist', function () {
|
||||
const fakeSettings = createSettingsMock({setDirect: false, setConnect: true});
|
||||
configUtils.set({
|
||||
stripeDirect: true
|
||||
});
|
||||
const fakeUrlUtils = createUrlUtilsMock();
|
||||
|
||||
const config = getConfig(fakeSettings, configUtils.config, fakeUrlUtils);
|
||||
|
||||
should.equal(config, null);
|
||||
});
|
||||
|
||||
it('Uses connect keys when stripeDirect is false, and the connect keys exist', function () {
|
||||
const fakeSettings = createSettingsMock({setDirect: true, setConnect: true});
|
||||
configUtils.set({
|
||||
stripeDirect: false
|
||||
});
|
||||
const fakeUrlUtils = createUrlUtilsMock();
|
||||
|
||||
const config = getConfig(fakeSettings, configUtils.config, fakeUrlUtils);
|
||||
|
||||
should.equal(config.publicKey, 'connect_publishable');
|
||||
should.equal(config.secretKey, 'connect_secret');
|
||||
});
|
||||
|
||||
it('Uses direct keys when stripeDirect is false, but the connect keys do not exist', function () {
|
||||
const fakeSettings = createSettingsMock({setDirect: true, setConnect: false});
|
||||
configUtils.set({
|
||||
stripeDirect: false
|
||||
});
|
||||
const fakeUrlUtils = createUrlUtilsMock();
|
||||
|
||||
const config = getConfig(fakeSettings, configUtils.config, fakeUrlUtils);
|
||||
|
||||
should.equal(config.publicKey, 'direct_publishable');
|
||||
should.equal(config.secretKey, 'direct_secret');
|
||||
});
|
||||
|
||||
it('Includes the subdirectory in the webhookHandlerUrl', function () {
|
||||
it('Returns null if Stripe not connected', function () {
|
||||
configUtils.set({
|
||||
stripeDirect: false,
|
||||
url: 'http://site.com/subdir'
|
||||
});
|
||||
const fakeSettings = createSettingsMock({setDirect: true, setConnect: false});
|
||||
const settingsHelpers = {
|
||||
getActiveStripeKeys: sinon.stub().returns(null)
|
||||
};
|
||||
const config = getConfig({settingsHelpers, config: configUtils.config, urlUtils: {}});
|
||||
|
||||
should.equal(config, null);
|
||||
});
|
||||
|
||||
it('Includes the subdirectory in the webhookHandlerUrl', function () {
|
||||
configUtils.set({
|
||||
url: 'http://site.com/subdir'
|
||||
});
|
||||
const settingsHelpers = createSettingsHelpersMock();
|
||||
const fakeUrlUtils = createUrlUtilsMock();
|
||||
|
||||
const config = getConfig(fakeSettings, configUtils.config, fakeUrlUtils);
|
||||
|
||||
const config = getConfig({settingsHelpers, config: configUtils.config, urlUtils: fakeUrlUtils});
|
||||
|
||||
should.equal(config.secretKey, 'direct_secret');
|
||||
should.equal(config.publicKey, 'direct_publishable');
|
||||
should.equal(config.webhookHandlerUrl, 'http://site.com/subdir/members/webhooks/stripe/');
|
||||
|
||||
should.exist(config.checkoutSessionSuccessUrl);
|
||||
should.exist(config.checkoutSessionCancelUrl);
|
||||
should.exist(config.checkoutSetupSessionSuccessUrl);
|
||||
should.exist(config.checkoutSetupSessionCancelUrl);
|
||||
});
|
||||
});
|
||||
|
@ -75,6 +75,8 @@ const sentEmail = (matchers) => {
|
||||
|
||||
let spyCall = mocks.mail.getCall(emailCount);
|
||||
|
||||
assert.notEqual(spyCall, null, 'Expected at least ' + (emailCount + 1) + ' emails sent.');
|
||||
|
||||
// We increment here so that the messaging has an index of 1, whilst getting the call has an index of 0
|
||||
emailCount += 1;
|
||||
|
||||
|
@ -4,10 +4,11 @@ const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
|
||||
class StaffServiceEmails {
|
||||
constructor({logging, models, mailer, settingsCache, urlUtils}) {
|
||||
constructor({logging, models, mailer, settingsHelpers, settingsCache, urlUtils}) {
|
||||
this.logging = logging;
|
||||
this.models = models;
|
||||
this.mailer = mailer;
|
||||
this.settingsHelpers = settingsHelpers;
|
||||
this.settingsCache = settingsCache;
|
||||
this.urlUtils = urlUtils;
|
||||
|
||||
@ -248,29 +249,17 @@ class StaffServiceEmails {
|
||||
return siteDomain;
|
||||
}
|
||||
|
||||
get defaultEmailDomain() {
|
||||
return this.settingsHelpers.getDefaultEmailDomain();
|
||||
}
|
||||
|
||||
get membersAddress() {
|
||||
// TODO: get from address of default newsletter?
|
||||
return `noreply@${this.siteDomain}`;
|
||||
return `noreply@${this.defaultEmailDomain}`;
|
||||
}
|
||||
|
||||
get fromEmailAddress() {
|
||||
return `ghost@${this.siteDomain}`;
|
||||
}
|
||||
|
||||
// TODO: duplicated from services/members/config - exrtact to settings?
|
||||
get supportAddress() {
|
||||
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.siteDomain}`;
|
||||
}
|
||||
|
||||
return supportAddress;
|
||||
}
|
||||
|
||||
get notificationFromAddress() {
|
||||
return this.supportAddress || this.membersAddress;
|
||||
return `ghost@${this.defaultEmailDomain}`;
|
||||
}
|
||||
|
||||
extractInitials(name = '') {
|
||||
|
@ -1,5 +1,5 @@
|
||||
class StaffService {
|
||||
constructor({logging, models, mailer, settingsCache, urlUtils}) {
|
||||
constructor({logging, models, mailer, settingsCache, settingsHelpers, urlUtils}) {
|
||||
this.logging = logging;
|
||||
|
||||
/** @private */
|
||||
@ -13,6 +13,7 @@ class StaffService {
|
||||
logging,
|
||||
models,
|
||||
mailer,
|
||||
settingsHelpers,
|
||||
settingsCache,
|
||||
urlUtils
|
||||
});
|
||||
|
@ -113,6 +113,36 @@ describe('StaffService', function () {
|
||||
forUpdate: true
|
||||
};
|
||||
let stubs;
|
||||
|
||||
const settingsCache = {
|
||||
get: (setting) => {
|
||||
if (setting === 'title') {
|
||||
return 'Ghost Site';
|
||||
} else if (setting === 'accent_color') {
|
||||
return '#ffffff';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const urlUtils = {
|
||||
getSiteUrl: () => {
|
||||
return 'https://ghost.example';
|
||||
},
|
||||
urlJoin: (adminUrl,hash,path) => {
|
||||
return `${adminUrl}/${hash}${path}`;
|
||||
},
|
||||
urlFor: () => {
|
||||
return 'https://admin.ghost.example';
|
||||
}
|
||||
};
|
||||
|
||||
const settingsHelpers = {
|
||||
getDefaultEmailDomain: () => {
|
||||
return 'ghost.example';
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
mailStub = sinon.stub().resolves();
|
||||
getEmailAlertUsersStub = sinon.stub().resolves([{
|
||||
@ -132,27 +162,9 @@ describe('StaffService', function () {
|
||||
mailer: {
|
||||
send: mailStub
|
||||
},
|
||||
settingsCache: {
|
||||
get: (setting) => {
|
||||
if (setting === 'title') {
|
||||
return 'Ghost Site';
|
||||
} else if (setting === 'accent_color') {
|
||||
return '#ffffff';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
urlUtils: {
|
||||
getSiteUrl: () => {
|
||||
return 'https://ghost.example';
|
||||
},
|
||||
urlJoin: (adminUrl,hash,path) => {
|
||||
return `${adminUrl}/${hash}${path}`;
|
||||
},
|
||||
urlFor: () => {
|
||||
return 'https://admin.ghost.example';
|
||||
}
|
||||
}
|
||||
settingsCache,
|
||||
urlUtils,
|
||||
settingsHelpers
|
||||
});
|
||||
stubs = {mailStub, getEmailAlertUsersStub};
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user