mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-30 21:40:39 +03:00
Changed member limit to be DRY & use raw query
- Member limit code was duplicated in 2 places unnecessarily - Also used member api code that fetched members and subscriptions fully hyrated when we only need a count - Using a raw query significantly improves performance here
This commit is contained in:
parent
0c5c7b32b8
commit
92446d85ea
@ -6,8 +6,6 @@ const {mega} = require('../../services/mega');
|
|||||||
const membersService = require('../../services/members');
|
const membersService = require('../../services/members');
|
||||||
const allowedIncludes = ['tags', 'authors', 'authors.roles', 'email'];
|
const allowedIncludes = ['tags', 'authors', 'authors.roles', 'email'];
|
||||||
const unsafeAttrs = ['status', 'authors', 'visibility'];
|
const unsafeAttrs = ['status', 'authors', 'visibility'];
|
||||||
const _ = require('lodash');
|
|
||||||
const config = require('../../../shared/config');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
docName: 'posts',
|
docName: 'posts',
|
||||||
@ -150,23 +148,9 @@ module.exports = {
|
|||||||
unsafeAttrs: unsafeAttrs
|
unsafeAttrs: unsafeAttrs
|
||||||
},
|
},
|
||||||
async query(frame) {
|
async query(frame) {
|
||||||
/**Check host limits for members when send email is true*/
|
/**Check host limits for members when send email is true**/
|
||||||
const membersHostLimit = config.get('host_settings:limits:members');
|
if (frame.options.send_email_when_published) {
|
||||||
if (frame.options.send_email_when_published && membersHostLimit) {
|
await membersService.checkHostLimit();
|
||||||
const allowedMembersLimit = membersHostLimit.max;
|
|
||||||
const hostUpgradeLink = config.get('host_settings:limits').upgrade_url;
|
|
||||||
const knexOptions = _.pick(frame.options, ['transacting', 'forUpdate']);
|
|
||||||
const {members} = await membersService.api.members.list(Object.assign(knexOptions, {filter: 'subscribed:true'}, {limit: 'all'}));
|
|
||||||
if (members.length > allowedMembersLimit) {
|
|
||||||
throw new errors.HostLimitError({
|
|
||||||
message: `Your current plan allows you to send email to up to ${allowedMembersLimit} members, but you currently have ${members.length} members`,
|
|
||||||
help: hostUpgradeLink,
|
|
||||||
errorDetails: {
|
|
||||||
limit: allowedMembersLimit,
|
|
||||||
total: members.length
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let model = await models.Post.edit(frame.data.posts[0], frame.options);
|
let model = await models.Post.edit(frame.data.posts[0], frame.options);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
const debug = require('ghost-ignition').debug('mega');
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const errors = require('@tryghost/errors');
|
const errors = require('@tryghost/errors');
|
||||||
@ -8,7 +9,6 @@ const membersService = require('../members');
|
|||||||
const bulkEmailService = require('../bulk-email');
|
const bulkEmailService = require('../bulk-email');
|
||||||
const models = require('../../models');
|
const models = require('../../models');
|
||||||
const postEmailSerializer = require('./post-email-serializer');
|
const postEmailSerializer = require('./post-email-serializer');
|
||||||
const config = require('../../../shared/config');
|
|
||||||
|
|
||||||
const getEmailData = async (postModel, members = []) => {
|
const getEmailData = async (postModel, members = []) => {
|
||||||
const {emailTmpl, replacements} = await postEmailSerializer.serialize(postModel);
|
const {emailTmpl, replacements} = await postEmailSerializer.serialize(postModel);
|
||||||
@ -81,10 +81,13 @@ const sendTestEmail = async (postModel, toEmails) => {
|
|||||||
const addEmail = async (postModel, options) => {
|
const addEmail = async (postModel, options) => {
|
||||||
const knexOptions = _.pick(options, ['transacting', 'forUpdate']);
|
const knexOptions = _.pick(options, ['transacting', 'forUpdate']);
|
||||||
|
|
||||||
|
// @TODO: improve performance of this members.list call
|
||||||
|
debug('addEmail: retrieving members list');
|
||||||
const {members} = await membersService.api.members.list(Object.assign(knexOptions, {filter: 'subscribed:true'}, {limit: 'all'}));
|
const {members} = await membersService.api.members.list(Object.assign(knexOptions, {filter: 'subscribed:true'}, {limit: 'all'}));
|
||||||
const membersToSendTo = members.filter((member) => {
|
const membersToSendTo = members.filter((member) => {
|
||||||
return membersService.contentGating.checkPostAccess(postModel.toJSON(), member);
|
return membersService.contentGating.checkPostAccess(postModel.toJSON(), member);
|
||||||
});
|
});
|
||||||
|
debug('addEmail: retrieved members list');
|
||||||
|
|
||||||
// NOTE: don't create email object when there's nobody to send the email to
|
// NOTE: don't create email object when there's nobody to send the email to
|
||||||
if (!membersToSendTo.length) {
|
if (!membersToSendTo.length) {
|
||||||
@ -180,24 +183,6 @@ async function handleUnsubscribeRequest(req) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkHostLimitForMembers(members = []) {
|
|
||||||
const membersHostLimit = config.get('host_settings:limits:members');
|
|
||||||
if (membersHostLimit) {
|
|
||||||
const allowedMembersLimit = membersHostLimit.max;
|
|
||||||
const hostUpgradeLink = config.get('host_settings:limits').upgrade_url;
|
|
||||||
if (members.length > allowedMembersLimit) {
|
|
||||||
throw new errors.HostLimitError({
|
|
||||||
message: `Your current plan allows you to send email to up to ${allowedMembersLimit} members, but you currently have ${members.length} members`,
|
|
||||||
help: hostUpgradeLink,
|
|
||||||
errorDetails: {
|
|
||||||
limit: allowedMembersLimit,
|
|
||||||
total: members.length
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function pendingEmailHandler(emailModel, options) {
|
async function pendingEmailHandler(emailModel, options) {
|
||||||
// CASE: do not send email if we import a database
|
// CASE: do not send email if we import a database
|
||||||
// TODO: refactor post.published events to never fire on importing
|
// TODO: refactor post.published events to never fire on importing
|
||||||
@ -210,24 +195,29 @@ async function pendingEmailHandler(emailModel, options) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {members} = await membersService.api.members.list(Object.assign({filter: 'subscribed:true'}, {limit: 'all'}));
|
|
||||||
|
|
||||||
if (!members.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await models.Email.edit({
|
|
||||||
status: 'submitting'
|
|
||||||
}, {
|
|
||||||
id: emailModel.id
|
|
||||||
});
|
|
||||||
|
|
||||||
let meta = [];
|
let meta = [];
|
||||||
let error = null;
|
let error = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check host limit for allowed member count and throw error if over limit
|
// Check host limit for allowed member count and throw error if over limit
|
||||||
checkHostLimitForMembers(members);
|
await membersService.checkHostLimit();
|
||||||
|
|
||||||
|
// No need to fetch list until after we've passed the check
|
||||||
|
// @TODO: improve performance of this members.list call
|
||||||
|
debug('pendingEmailHandler: retrieving members list');
|
||||||
|
const {members} = await membersService.api.members.list(Object.assign({filter: 'subscribed:true'}, {limit: 'all'}));
|
||||||
|
debug('pendingEmailHandler: retrieved members list');
|
||||||
|
|
||||||
|
if (!members.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await models.Email.edit({
|
||||||
|
status: 'submitting'
|
||||||
|
}, {
|
||||||
|
id: emailModel.id
|
||||||
|
});
|
||||||
|
|
||||||
// NOTE: meta can contains an array which can be a mix of successful and error responses
|
// NOTE: meta can contains an array which can be a mix of successful and error responses
|
||||||
// needs filtering and saving objects of {error, batchData} form to separate property
|
// needs filtering and saving objects of {error, batchData} form to separate property
|
||||||
meta = await sendEmail(postModel, members);
|
meta = await sendEmail(postModel, members);
|
||||||
|
@ -58,6 +58,8 @@ events.on('settings.edited', function updateSettingFromModel(settingModel) {
|
|||||||
const membersService = {
|
const membersService = {
|
||||||
contentGating: require('./content-gating'),
|
contentGating: require('./content-gating'),
|
||||||
|
|
||||||
|
checkHostLimit: require('./limit'),
|
||||||
|
|
||||||
config: membersConfig,
|
config: membersConfig,
|
||||||
|
|
||||||
get api() {
|
get api() {
|
||||||
|
32
core/server/services/members/limit.js
Normal file
32
core/server/services/members/limit.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
const config = require('../../../shared/config');
|
||||||
|
const db = require('../../data/db');
|
||||||
|
const errors = require('@tryghost/errors');
|
||||||
|
|
||||||
|
// Get total members direct from DB
|
||||||
|
// @TODO: determine performance difference between this, normal knex, and using the model layer
|
||||||
|
async function getTotalMembers() {
|
||||||
|
const isSQLite = config.get('database:client') === 'sqlite3';
|
||||||
|
const result = await db.knex.raw('SELECT COUNT(id) AS total FROM members');
|
||||||
|
return isSQLite ? result[0].total : result[0][0].total;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async () => {
|
||||||
|
const membersHostLimit = config.get('host_settings:limits:members');
|
||||||
|
if (membersHostLimit) {
|
||||||
|
const allowedMembersLimit = membersHostLimit.max;
|
||||||
|
const hostUpgradeLink = config.get('host_settings:limits').upgrade_url;
|
||||||
|
|
||||||
|
const totalMembers = await getTotalMembers();
|
||||||
|
|
||||||
|
if (totalMembers > allowedMembersLimit) {
|
||||||
|
throw new errors.HostLimitError({
|
||||||
|
message: `Your current plan allows you to send email to up to ${allowedMembersLimit} members, but you currently have ${totalMembers} members`,
|
||||||
|
help: hostUpgradeLink,
|
||||||
|
errorDetails: {
|
||||||
|
limit: allowedMembersLimit,
|
||||||
|
total: totalMembers
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user