const url = require('url'); const settingsCache = require('../settings/cache'); const urlService = require('../url'); const MembersApi = require('@tryghost/members-api'); const MembersSSR = require('@tryghost/members-ssr'); const common = require('../../lib/common'); const models = require('../../models'); const mail = require('../mail'); const blogIcon = require('../../lib/image/blog-icon'); const doBlock = fn => fn(); function createMember({name, email, password}) { return models.Member.add({ name, email, password }).then((member) => { return member.toJSON(); }); } function updateMember(member, newData) { return models.Member.findOne(member, { require: true }).then(({id}) => { return models.Member.edit(newData, {id}); }).then((member) => { return member.toJSON(); }); } function getMember(data, options = {}) { return models.Member.findOne(data, Object.assign({require: true}, options)).then((model) => { if (!model) { return null; } return model.toJSON(options); }); } function deleteMember(options) { options = options || {}; return models.Member.destroy(options).catch(models.Member.NotFoundError, () => { throw new common.errors.NotFoundError({ message: common.i18n.t('errors.api.resource.resourceNotFound', { resource: 'Member' }) }); }); } function listMembers(options) { return models.Member.findPage(options).then((models) => { return { members: models.data.map(model => model.toJSON(options)), meta: models.meta }; }); } function validateMember({email, password}) { return models.Member.findOne({email}, { require: true }).then((member) => { return member.comparePassword(password).then((res) => { if (!res) { throw new Error('Password is incorrect'); } return member; }); }).then((member) => { return member.toJSON(); }); } function getSubscriptionSettings() { let membersSettings = settingsCache.get('members_subscription_settings'); if (!membersSettings) { membersSettings = { isPaid: false, paymentProcessors: [{ adapter: 'stripe', config: { secret_token: '', public_token: '', product: { name: 'Ghost Subscription' }, plans: [ { name: 'Monthly', currency: 'usd', interval: 'month', amount: '' }, { name: 'Yearly', currency: 'usd', interval: 'year', amount: '' } ] } }] }; } if (!membersSettings.isPaid) { membersSettings.paymentProcessors = []; } return membersSettings; } const siteUrl = urlService.utils.getSiteUrl(); const siteOrigin = doBlock(() => { const {protocol, host} = url.parse(siteUrl); return `${protocol}//${host}`; }); const getApiUrl = ({version, type}) => { const {href} = new url.URL( urlService.utils.getApiPath({version, type}), siteUrl ); return href; }; const contentApiUrl = getApiUrl({version: 'v2', type: 'content'}); const membersApiUrl = getApiUrl({version: 'v2', type: 'members'}); const accessControl = { [siteOrigin]: { [contentApiUrl]: { tokenLength: '20m' }, [membersApiUrl]: { tokenLength: '180d' } }, '*': { tokenLength: '20m' } }; const sendEmail = (function createSendEmail(mailer) { return function sendEmail(member, {token}) { if (!(mailer instanceof mail.GhostMailer)) { mailer = new mail.GhostMailer(); } const message = { to: member.email, subject: 'Reset password', html: ` Hi ${member.name}, To reset your password, click the following link and follow the instructions: ${siteUrl}#reset-password?token=${token} If you didn't request a password change, just ignore this email. ` }; /* eslint-disable */ // @TODO remove this console.log(message.html); /* eslint-enable */ return mailer.send(message).catch((err) => { return Promise.reject(err); }); }; })(); const getSiteConfig = () => { return { title: settingsCache.get('title') ? settingsCache.get('title').replace(/"/g, '\\"') : 'Publication', icon: blogIcon.getIconUrl() }; }; const membersApiInstance = MembersApi({ authConfig: { issuer: membersApiUrl, ssoOrigin: siteOrigin, publicKey: settingsCache.get('members_public_key'), privateKey: settingsCache.get('members_private_key'), sessionSecret: settingsCache.get('members_session_secret'), accessControl }, paymentConfig: { processors: getSubscriptionSettings().paymentProcessors }, siteConfig: getSiteConfig(), createMember, getMember, deleteMember, listMembers, validateMember, updateMember, sendEmail }); const updateSettingFromModel = function updateSettingFromModel(settingModel) { if (!['members_subscription_settings', 'title', 'icon'].includes(settingModel.get('key'))) { return; } membersApiInstance.reconfigureSettings({ paymentConfig: { processors: getSubscriptionSettings().paymentProcessors }, siteConfig: getSiteConfig() }); }; // Bind to events to automatically keep subscription info up-to-date from settings common.events.on('settings.edited', updateSettingFromModel); module.exports = membersApiInstance; module.exports.ssr = MembersSSR({ cookieSecure: urlService.utils.isSSL(siteUrl), cookieKeys: [settingsCache.get('theme_session_secret')], membersApi: membersApiInstance }); module.exports.isPaymentConfigured = function () { return getSubscriptionSettings().paymentProcessors.length !== 0; };