2021-04-08 07:59:56 +03:00
|
|
|
import LimitService from '@tryghost/limit-service';
|
|
|
|
import RSVP from 'rsvp';
|
|
|
|
import Service, {inject as service} from '@ember/service';
|
|
|
|
import {bind} from '@ember/runloop';
|
|
|
|
|
|
|
|
class LimitError {
|
|
|
|
constructor({errorType, errorDetails, message}) {
|
|
|
|
this.errorType = errorType;
|
|
|
|
this.errorDetails = errorDetails;
|
|
|
|
this.message = message;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class IncorrectUsageError extends LimitError {
|
|
|
|
constructor(options) {
|
|
|
|
super(Object.assign({errorType: 'IncorrectUsageError'}, options));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class HostLimitError extends LimitError {
|
|
|
|
constructor(options) {
|
|
|
|
super(Object.assign({errorType: 'HostLimitError'}, options));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default class LimitsService extends Service {
|
|
|
|
@service config;
|
|
|
|
@service store;
|
|
|
|
@service membersStats;
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
super(...arguments);
|
|
|
|
|
|
|
|
let limits = this.config.get('hostSettings.limits');
|
|
|
|
|
2021-04-09 12:16:37 +03:00
|
|
|
this.limiter = new LimitService();
|
2021-04-08 07:59:56 +03:00
|
|
|
|
2021-04-09 12:16:37 +03:00
|
|
|
if (!limits) {
|
|
|
|
return;
|
|
|
|
}
|
2021-04-08 07:59:56 +03:00
|
|
|
|
2021-04-09 12:16:37 +03:00
|
|
|
let helpLink;
|
2021-04-08 07:59:56 +03:00
|
|
|
|
2021-04-09 12:16:37 +03:00
|
|
|
if (this.config.get('hostSettings.billing.enabled')
|
|
|
|
&& this.config.get('hostSettings.billing.enabled') === true
|
|
|
|
&& this.config.get('hostSettings.billing.url')) {
|
|
|
|
helpLink = this.config.get('hostSettings.billing.url');
|
|
|
|
} else {
|
|
|
|
helpLink = 'https://ghost.org/help/';
|
2021-04-08 07:59:56 +03:00
|
|
|
}
|
2021-04-09 12:16:37 +03:00
|
|
|
|
2022-08-03 14:21:16 +03:00
|
|
|
this.limiter.loadLimits({
|
2021-04-09 12:16:37 +03:00
|
|
|
limits: this.decorateWithCountQueries(limits),
|
|
|
|
helpLink,
|
|
|
|
errors: {
|
|
|
|
HostLimitError,
|
|
|
|
IncorrectUsageError
|
|
|
|
}
|
|
|
|
});
|
2021-04-08 07:59:56 +03:00
|
|
|
}
|
|
|
|
|
2021-04-20 20:15:33 +03:00
|
|
|
async checkWouldGoOverLimit(limitName, metadata = {}) {
|
|
|
|
return this.limiter.checkWouldGoOverLimit(limitName, metadata);
|
|
|
|
}
|
|
|
|
|
2021-04-08 07:59:56 +03:00
|
|
|
decorateWithCountQueries(limits) {
|
|
|
|
if (limits.staff) {
|
|
|
|
limits.staff.currentCountQuery = bind(this, this.getStaffUsersCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (limits.members) {
|
|
|
|
limits.members.currentCountQuery = bind(this, this.getMembersCount);
|
|
|
|
}
|
|
|
|
|
2022-05-11 13:07:13 +03:00
|
|
|
if (limits.newsletters) {
|
|
|
|
limits.newsletters.currentCountQuery = bind(this, this.getNewslettersCount);
|
|
|
|
}
|
|
|
|
|
2021-04-08 07:59:56 +03:00
|
|
|
return limits;
|
|
|
|
}
|
|
|
|
|
|
|
|
async getStaffUsersCount() {
|
|
|
|
return RSVP.hash({
|
|
|
|
users: this.store.findAll('user', {reload: true}),
|
2021-04-28 17:07:18 +03:00
|
|
|
invites: this.store.findAll('invite', {reload: true}),
|
|
|
|
roles: this.store.findAll('role', {reload: true}) // NOTE: roles have to be fetched as they are not always loaded with invites
|
2021-04-08 07:59:56 +03:00
|
|
|
}).then((data) => {
|
2021-04-08 12:10:59 +03:00
|
|
|
const staffUsers = data.users.filter(u => u.get('status') !== 'inactive' && u.role.get('name') !== 'Contributor');
|
|
|
|
const staffInvites = data.invites.filter(i => i.role.get('name') !== 'Contributor');
|
2021-04-08 11:07:24 +03:00
|
|
|
|
2021-04-08 12:10:59 +03:00
|
|
|
return staffUsers.length + staffInvites.length;
|
2021-04-08 07:59:56 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async getMembersCount() {
|
|
|
|
const counts = await this.membersStats.fetchCounts();
|
|
|
|
|
|
|
|
return counts.total;
|
|
|
|
}
|
2022-05-11 13:07:13 +03:00
|
|
|
|
|
|
|
async getNewslettersCount() {
|
|
|
|
const activeNewsletters = await this.store.query('newsletter', {filter: 'status:active', limit: 'all'});
|
|
|
|
return activeNewsletters.length;
|
|
|
|
}
|
2021-04-08 07:59:56 +03:00
|
|
|
}
|