0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

Added new member limit & removed old

refs: https://github.com/TryGhost/Team/issues/510

- The current member limit was implemented as a member-specific concept
- The new limit service is much more generic, here we are swapping old for new
- The updated concept here is blocking all publishing, not just email sending, when a site is over its member limit
- To determine that we are publishing a post, we must be in the model layer. The code has been moved to the permissible function which makes sense as this is a permissions error that we are throwing
- I've left the extra check for email retries in, in case there is some loophole here (but we may wish to change it)
This commit is contained in:
Hannah Wolfe 2021-03-04 17:02:56 +00:00
parent e30b9735fa
commit d51fcd072e
6 changed files with 13 additions and 45 deletions

View file

@ -3,7 +3,6 @@ const {i18n} = require('../../lib/common');
const errors = require('@tryghost/errors');
const urlUtils = require('../../../shared/url-utils');
const {mega} = require('../../services/mega');
const membersService = require('../../services/members');
const allowedIncludes = ['tags', 'authors', 'authors.roles', 'email'];
const unsafeAttrs = ['status', 'authors', 'visibility'];
@ -154,11 +153,6 @@ module.exports = {
unsafeAttrs: unsafeAttrs
},
async query(frame) {
/**Check host limits for members when send email is true**/
if ((frame.options.email_recipient_filter && frame.options.email_recipient_filter !== 'none') || frame.options.send_email_when_published) {
await membersService.checkHostLimit();
}
let model;
if (!frame.options.email_recipient_filter && frame.options.send_email_when_published) {
await models.Base.transaction(async (transacting) => {

View file

@ -3,7 +3,6 @@ const {i18n} = require('../../lib/common');
const errors = require('@tryghost/errors');
const urlUtils = require('../../../shared/url-utils');
const {mega} = require('../../services/mega');
const membersService = require('../../services/members');
const allowedIncludes = ['tags', 'authors', 'authors.roles', 'email'];
const unsafeAttrs = ['status', 'authors', 'visibility'];
@ -154,11 +153,6 @@ module.exports = {
unsafeAttrs: unsafeAttrs
},
async query(frame) {
/**Check host limits for members when send email is true**/
if ((frame.options.email_recipient_filter && frame.options.email_recipient_filter !== 'none') || frame.options.send_email_when_published) {
await membersService.checkHostLimit();
}
let model;
if (!frame.options.email_recipient_filter && frame.options.send_email_when_published) {
await models.Base.transaction(async (transacting) => {

View file

@ -10,6 +10,7 @@ const htmlToPlaintext = require('../../shared/html-to-plaintext');
const ghostBookshelf = require('./base');
const config = require('../../shared/config');
const settingsCache = require('../services/settings/cache');
const limitService = require('../services/limits');
const mobiledocLib = require('../lib/mobiledoc');
const relations = require('./relations');
const urlUtils = require('../../shared/url-utils');
@ -1016,7 +1017,7 @@ Post = ghostBookshelf.Model.extend({
},
// NOTE: the `authors` extension is the parent of the post model. It also has a permissible function.
permissible: function permissible(postModel, action, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasApiKeyPermission) {
permissible: async function permissible(postModel, action, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasApiKeyPermission) {
let isContributor;
let isOwner;
let isAdmin;
@ -1048,6 +1049,13 @@ Post = ghostBookshelf.Model.extend({
isAdd = (action === 'add');
isDestroy = (action === 'destroy');
if (limitService.isLimited('members')) {
// You can't publish a post if you're over your member limit
if ((isEdit && isChanging('status') && isDraft()) || (isAdd && isPublished())) {
await limitService.errorIfIsOverLimit('members');
}
}
if (isContributor && isEdit) {
// Only allow contributor edit if status is changing, and the post is a draft post
hasUserPermission = !isChanging('status') && isDraft();

View file

@ -9,6 +9,7 @@ const {events, i18n} = require('../../lib/common');
const logging = require('../../../shared/logging');
const settingsCache = require('../settings/cache');
const membersService = require('../members');
const limitService = require('../limits');
const bulkEmailService = require('../bulk-email');
const jobsService = require('../jobs');
const db = require('../../data/db');
@ -253,7 +254,9 @@ async function sendEmailJob({emailModel, options}) {
try {
// Check host limit for allowed member count and throw error if over limit
// - do this even if it's a retry so that there's no way around the limit
await membersService.checkHostLimit();
if (limitService.isLimited('members')) {
await limitService.errorIfIsOverLimit('members');
}
// Create email batch and recipient rows unless this is a retry and they already exist
const existingBatchCount = await emailModel.related('emailBatches').count('id');

View file

@ -62,8 +62,6 @@ events.on('settings.edited', function updateSettingFromModel(settingModel) {
const membersService = {
contentGating: require('./content-gating'),
checkHostLimit: require('./limit'),
config: membersConfig,
get api() {

View file

@ -1,29 +0,0 @@
const config = require('../../../shared/config');
const models = require('../../models');
const errors = require('@tryghost/errors');
// Get total members direct from DB
async function getTotalMembers() {
return models.Member.count('id');
}
module.exports = async () => {
const membersHostLimit = config.get('hostSettings:limits:members');
if (membersHostLimit) {
const allowedMembersLimit = membersHostLimit.max;
const hostUpgradeLink = config.get('hostSettings:limits').upgrade_url;
const totalMembers = await getTotalMembers();
if (totalMembers > allowedMembersLimit) {
throw new errors.HostLimitError({
message: `Your current plan allows you to have up to ${allowedMembersLimit} members, but you currently have ${totalMembers} members`,
help: hostUpgradeLink,
errorDetails: {
limit: allowedMembersLimit,
total: totalMembers
}
});
}
}
};