Refactored members service logging and errors (#10919)

* Installed @tryghost/members-ssr@0.2.1

refs https://github.com/TryGhost/Members/issues/38

This updates allows for dynamic access of the membersApi, which will be
used in future when replacing the membersApi instance with a newly
configured one.

* Set the membersApiInstance logger to use common.logging

refs https://github.com/TryGhost/Members/issues/38

Passes the Ghost logger to the members api, so that we can keep an eye
on errors produced by the api.

* Refactored memberService use to always use getter

refs https://github.com/TryGhost/Members/issues/38

This will allow us to switch out the membersApi and the consumers of it
to have the updated reference by going through a getter.

* Installed @tryghost/members-api@0.3.0

refs https://github.com/TryGhost/Members/issues/38

Adds support for setting the logger

* Uninstalled stripe@7.0.0

refs https://github.com/TryGhost/Members/issues/38

The stripe module is now a dep of members-api, as it should be

* Updated members service to reconfigure settings

refs https://github.com/TryGhost/Members/issues/38

Previously we were unable to stop an invalidly configured members api
instance, now that we create a new instance, we can wait for the ready
or error event and only switch it out then.
This commit is contained in:
Fabien O'Carroll 2019-07-18 15:37:11 +08:00 committed by GitHub
parent d3ca12056f
commit 078060abdc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 103 additions and 77 deletions

View File

@ -1,4 +1,6 @@
const memberUserObject = require('../../services/members').api.members;
// NOTE: We must not cache references to membersService.api
// as it is a getter and may change during runtime.
const membersService = require('../../services/members');
const members = {
docName: 'members',
@ -14,7 +16,7 @@ const members = {
permissions: true,
validation: {},
query(frame) {
return memberUserObject.list(frame.options);
return membersService.api.members.list(frame.options);
}
},
@ -27,7 +29,7 @@ const members = {
validation: {},
permissions: true,
query(frame) {
return memberUserObject.get(frame.data, frame.options);
return membersService.api.members.get(frame.data, frame.options);
}
},
@ -47,7 +49,7 @@ const members = {
permissions: true,
query(frame) {
frame.options.require = true;
return memberUserObject.destroy(frame.options).return(null);
return membersService.api.members.destroy(frame.options).return(null);
}
}
};

View File

@ -23,7 +23,7 @@ function hideMembersOnlyContent(attrs, frame) {
}
const memberHasPlan = !!(frame.original.context.member.plans || []).length;
if (!membersService.api.isPaymentConfigured()) {
if (!membersService.isPaymentConfigured()) {
return PERMIT_CONTENT;
}
if (memberHasPlan) {

View File

@ -2,7 +2,6 @@ const url = require('url');
const settingsCache = require('../settings/cache');
const urlUtils = require('../../lib/url-utils');
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');
@ -182,50 +181,32 @@ const getSiteConfig = () => {
};
};
const membersApiInstance = MembersApi({
authConfig: {
issuer: membersApiUrl,
ssoOrigin: adminOrigin,
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
});
module.exports = createApiInstance;
const updateSettingFromModel = function updateSettingFromModel(settingModel) {
if (!['members_subscription_settings', 'title', 'icon'].includes(settingModel.get('key'))) {
return;
}
membersApiInstance.reconfigureSettings({
function createApiInstance() {
const membersApiInstance = MembersApi({
authConfig: {
issuer: membersApiUrl,
ssoOrigin: adminOrigin,
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()
siteConfig: getSiteConfig(),
createMember,
getMember,
deleteMember,
listMembers,
validateMember,
updateMember,
sendEmail
});
};
// Bind to events to automatically keep subscription info up-to-date from settings
common.events.on('settings.edited', updateSettingFromModel);
membersApiInstance.setLogger(common.logging);
module.exports = membersApiInstance;
module.exports.ssr = MembersSSR({
cookieSecure: urlUtils.isSSL(siteUrl),
cookieKeys: [settingsCache.get('theme_session_secret')],
membersApi: membersApiInstance
});
module.exports.isPaymentConfigured = function () {
return getSubscriptionSettings().paymentProcessors.length !== 0;
};
return membersApiInstance;
}

View File

@ -1,9 +0,0 @@
const {static} = require('express');
const path = require('path');
module.exports = static(
path.join(
require.resolve('@tryghost/members-auth-pages'),
'../dist'
)
);

View File

@ -1,9 +1,60 @@
module.exports = {
get api() {
return require('./api');
const {static} = require('express');
const path = require('path');
const MembersSSR = require('@tryghost/members-ssr');
const createMembersApiInstance = require('./api');
const common = require('../../lib/common');
const urlUtils = require('../../lib/url-utils');
const settingsCache = require('../settings/cache');
let membersApi;
// Bind to events to automatically keep subscription info up-to-date from settings
common.events.on('settings.edited', function updateSettingFromModel(settingModel) {
if (!['members_subscription_settings', 'title', 'icon'].includes(settingModel.get('key'))) {
return;
}
const reconfiguredMembersAPI = createMembersApiInstance();
reconfiguredMembersAPI.bus.on('ready', function () {
membersApi = reconfiguredMembersAPI;
});
reconfiguredMembersAPI.bus.on('error', function (err) {
common.logging.error(err);
});
});
const membersService = {
isPaymentConfigured() {
const settings = settingsCache.get('members_subscription_settings');
return !!settings && settings.isPaid && settings.paymentProcessors.length !== 0;
},
get authPages() {
return require('./authPages');
}
get api() {
if (!membersApi) {
membersApi = createMembersApiInstance();
membersApi.bus.on('error', function (err) {
common.logging.error(err);
});
}
return membersApi;
},
ssr: MembersSSR({
cookieSecure: urlUtils.isSSL(urlUtils.getSiteUrl()),
cookieKeys: [settingsCache.get('theme_session_secret')],
// This is passed as a function so that updates to the instance
// are picked up in the ssr module
membersApi: () => membersApi
}),
authPages: static(
path.join(
require.resolve('@tryghost/members-auth-pages'),
'../dist'
)
)
};
module.exports = membersService;

View File

@ -15,7 +15,8 @@ module.exports = function setupMembersApiApp() {
apiApp.use('/static/auth', membersService.authPages);
// Set up the api endpoints and the gateway
apiApp.use(membersService.api);
// NOTE: this is wrapped in a function to ensure we always go via the getter
apiApp.use((req, res, next) => membersService.api(req, res, next));
// API error handling
apiApp.use(shared.middlewares.errorHandler.resourceNotFound);

View File

@ -93,7 +93,7 @@ module.exports = function setupSiteApp(options = {}) {
// @TODO only loads this stuff if members is enabled
// Set req.member & res.locals.member if a cookie is set
siteApp.post('/members/ssr', shared.middlewares.labs.members, function (req, res) {
membersService.api.ssr.exchangeTokenForSession(req, res).then(() => {
membersService.ssr.exchangeTokenForSession(req, res).then(() => {
res.writeHead(200);
res.end();
}).catch((err) => {
@ -102,7 +102,7 @@ module.exports = function setupSiteApp(options = {}) {
});
});
siteApp.delete('/members/ssr', shared.middlewares.labs.members, function (req, res) {
membersService.api.ssr.deleteSession(req, res).then(() => {
membersService.ssr.deleteSession(req, res).then(() => {
res.writeHead(204);
res.end();
}).catch((err) => {
@ -111,7 +111,7 @@ module.exports = function setupSiteApp(options = {}) {
});
});
siteApp.use(function (req, res, next) {
membersService.api.ssr.getMemberDataFromSession(req, res).then((member) => {
membersService.ssr.getMemberDataFromSession(req, res).then((member) => {
req.member = member;
next();
}).catch(() => {

View File

@ -41,9 +41,9 @@
"dependencies": {
"@nexes/nql": "0.2.2",
"@tryghost/helpers": "1.1.6",
"@tryghost/members-api": "0.2.0",
"@tryghost/members-api": "0.3.0",
"@tryghost/members-auth-pages": "1.1.0",
"@tryghost/members-ssr": "0.1.5",
"@tryghost/members-ssr": "0.2.1",
"@tryghost/members-theme-bindings": "0.2.3",
"@tryghost/social-urls": "0.1.0",
"@tryghost/string": "^0.1.3",
@ -118,7 +118,6 @@
"semver": "6.2.0",
"simple-dom": "0.3.2",
"simple-html-tokenizer": "0.5.7",
"stripe": "^7.0.0",
"uuid": "3.3.2",
"validator": "6.3.0",
"xml": "1.0.1"

View File

@ -149,10 +149,10 @@
dependencies:
"@tryghost/kg-clean-basic-html" "^0.1.1"
"@tryghost/members-api@0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-0.2.0.tgz#1f13dabc39015f8a5447a714dc05162426775027"
integrity sha512-fLTQylyUz16ZL0KJ4kBvWnUsQbq1NkLFXVkQjOEo/xFV1SG0iJBKlkVtPWfyncSHyGBSXAv9TcQymiT+9vr7pQ==
"@tryghost/members-api@0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-0.3.0.tgz#b1fe4c4853237c1af95065f5db2bfb2072a87524"
integrity sha512-YtYjQwFGrN6uPuwWB8x1WqSuLe8c2m5wuPsyBpKXcV69yEqFz4QCyLMoYs4c6Y4lnfUf1lZAwnQSuDgczT46XQ==
dependencies:
bluebird "^3.5.4"
body-parser "^1.19.0"
@ -162,6 +162,7 @@
jsonwebtoken "^8.5.1"
lodash "^4.17.11"
node-jose "^1.1.3"
stripe "^7.4.0"
"@tryghost/members-auth-pages@1.1.0":
version "1.1.0"
@ -203,10 +204,10 @@
ghost-ignition "^3.1.0"
lodash "^4.17.11"
"@tryghost/members-ssr@0.1.5":
version "0.1.5"
resolved "https://registry.yarnpkg.com/@tryghost/members-ssr/-/members-ssr-0.1.5.tgz#531a36233ac4d7e23b2582ec2e48698aba739c00"
integrity sha512-nhceqbnoSZwJJN2xdEBPfRZu2fCDMuDnBLA84h+k8+RGoU+8qxzHDCLR1vgOlt8vVWUAP8HRS1/C4GyhuaUNXQ==
"@tryghost/members-ssr@0.2.1":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@tryghost/members-ssr/-/members-ssr-0.2.1.tgz#156846832caeeaaa831206522dcc1e5b94e1335b"
integrity sha512-pr3IYDymyDm++428LNJn3cfhZmW8j2Qtr/AmyPpmRIDSmpzbEk5wl28Dc+jWpH6ij36DbOwwfVUNZ9SW1Lwi3w==
dependencies:
bluebird "^3.5.3"
concat-stream "^2.0.0"
@ -7413,7 +7414,7 @@ strip-json-comments@2.0.1, strip-json-comments@^2.0.1, strip-json-comments@~2.0.
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
stripe@^7.0.0:
stripe@^7.4.0:
version "7.4.0"
resolved "https://registry.yarnpkg.com/stripe/-/stripe-7.4.0.tgz#d40834f7763ebe775fe944db94bd3f31f291b0fc"
integrity sha512-eurSZJw45MvnV7PjmFHMgJMkCihHgqGHr11OHpFdMh+5CCyYvbVlA5uP5VoVQakhYjSLCObs0dbXtGYhIAMKvw==