Ghost/ghost/admin/app/services/limit.js
Kevin Ansfield 7b443d4b63 Removed need for .get() with config service
no issue

The `config` service has been a source of confusion when writing with modern Ember patterns because it's use of the deprecated `ProxyMixin` forced all property access/setting to go via `.get()` and `.set()` whereas the rest of the system has mostly (there are a few other uses of ProxyObjects remaining) eliminated the use of the non-native get/set methods.

- removed use of `ProxyMixin` in the `config` service by grabbing the API response after fetching and using `Object.defineProperty()` to add native getters/setters that pass through to a tracked object holding the API response data. Ember's autotracking automatically works across the native getters/setters so we can then use the service as if it was any other native object
- updated all code to use `config.{attrName}` directly for getting/setting instead of `.get()` and `.set()`
- removed unnecessary async around `config.availableTimezones` which wasn't making any async calls
2022-10-07 16:14:57 +01:00

106 lines
3.0 KiB
JavaScript

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.hostSettings?.limits;
this.limiter = new LimitService();
if (!limits) {
return;
}
let helpLink;
if (this.config.hostSettings?.billing?.enabled === true
&& this.config.hostSettings?.billing?.url
) {
helpLink = this.config.hostSettings.billing?.url;
} else {
helpLink = 'https://ghost.org/help/';
}
this.limiter.loadLimits({
limits: this.decorateWithCountQueries(limits),
helpLink,
errors: {
HostLimitError,
IncorrectUsageError
}
});
}
async checkWouldGoOverLimit(limitName, metadata = {}) {
return this.limiter.checkWouldGoOverLimit(limitName, metadata);
}
decorateWithCountQueries(limits) {
if (limits.staff) {
limits.staff.currentCountQuery = bind(this, this.getStaffUsersCount);
}
if (limits.members) {
limits.members.currentCountQuery = bind(this, this.getMembersCount);
}
if (limits.newsletters) {
limits.newsletters.currentCountQuery = bind(this, this.getNewslettersCount);
}
return limits;
}
async getStaffUsersCount() {
return RSVP.hash({
users: this.store.findAll('user', {reload: true}),
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
}).then((data) => {
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');
return staffUsers.length + staffInvites.length;
});
}
async getMembersCount() {
const counts = await this.membersStats.fetchCounts();
return counts.total;
}
async getNewslettersCount() {
const activeNewsletters = await this.store.query('newsletter', {filter: 'status:active', limit: 'all'});
return activeNewsletters.length;
}
}