2022-10-18 13:48:45 +03:00
|
|
|
const ObjectID = require('bson-objectid').default;
|
|
|
|
const {BadRequestError} = require('@tryghost/errors');
|
|
|
|
const Tier = require('./Tier');
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {object} ITierRepository
|
|
|
|
* @prop {(id: ObjectID) => Promise<Tier>} getById
|
|
|
|
* @prop {(tier: Tier) => Promise<void>} save
|
|
|
|
* @prop {(options?: {filter?: string}) => Promise<Tier[]>} getAll
|
|
|
|
*/
|
|
|
|
|
2022-10-20 07:39:32 +03:00
|
|
|
/**
|
|
|
|
* @typedef {object} ISlugService
|
|
|
|
* @prop {(input: string) => Promise<string>} generate
|
|
|
|
*/
|
|
|
|
|
2022-10-18 13:48:45 +03:00
|
|
|
/**
|
|
|
|
* @template {Model}
|
|
|
|
* @typedef {object} Page<Model>
|
|
|
|
* @prop {Model[]} data
|
|
|
|
* @prop {object} meta
|
|
|
|
* @prop {object} meta.pagination
|
|
|
|
* @prop {number} meta.pagination.page - The current page
|
|
|
|
* @prop {number} meta.pagination.pages - The total number of pages
|
|
|
|
* @prop {number} meta.pagination.limit - The limit of models per page
|
|
|
|
* @prop {number} meta.pagination.total - The totaL number of models across all pages
|
|
|
|
* @prop {number|null} meta.pagination.prev - The number of the previous page, or null if there isn't one
|
|
|
|
* @prop {number|null} meta.pagination.next - The number of the next page, or null if there isn't one
|
|
|
|
*/
|
|
|
|
|
|
|
|
module.exports = class TiersAPI {
|
|
|
|
/** @type {ITierRepository} */
|
|
|
|
#repository;
|
|
|
|
|
2022-10-20 07:39:32 +03:00
|
|
|
/** @type {ISlugService} */
|
2022-10-18 13:48:45 +03:00
|
|
|
#slugService;
|
|
|
|
|
|
|
|
constructor(deps) {
|
|
|
|
this.#repository = deps.repository;
|
2022-10-20 07:39:32 +03:00
|
|
|
this.#slugService = deps.slugService;
|
2022-10-18 13:48:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {object} [options]
|
|
|
|
* @param {string} [options.filter] - An NQL filter string
|
|
|
|
*
|
|
|
|
* @returns {Promise<Page<Tier>>}
|
|
|
|
*/
|
|
|
|
async browse(options = {}) {
|
|
|
|
const tiers = await this.#repository.getAll(options);
|
|
|
|
|
|
|
|
return {
|
|
|
|
data: tiers,
|
|
|
|
meta: {
|
|
|
|
pagination: {
|
|
|
|
page: 1,
|
|
|
|
pages: 1,
|
|
|
|
limit: tiers.length,
|
|
|
|
total: tiers.length,
|
|
|
|
prev: null,
|
|
|
|
next: null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string} idString
|
|
|
|
*
|
|
|
|
* @returns {Promise<Tier>}
|
|
|
|
*/
|
|
|
|
async read(idString) {
|
|
|
|
const id = ObjectID.createFromHexString(idString);
|
|
|
|
const tier = await this.#repository.getById(id);
|
|
|
|
|
|
|
|
return tier;
|
|
|
|
}
|
|
|
|
|
2022-10-26 09:20:19 +03:00
|
|
|
/**
|
|
|
|
* Fetches the default tier
|
|
|
|
* @param {object} [options]
|
|
|
|
* @returns {Promise<Tier>}
|
|
|
|
*/
|
|
|
|
async readDefaultTier(options = {}) {
|
|
|
|
const [defaultTier] = await this.#repository.getAll({
|
|
|
|
filter: 'type:paid+active:true',
|
|
|
|
limit: 1,
|
|
|
|
...options
|
|
|
|
});
|
|
|
|
|
|
|
|
return defaultTier;
|
|
|
|
}
|
|
|
|
|
2022-10-18 13:48:45 +03:00
|
|
|
/**
|
|
|
|
* @param {string} id
|
|
|
|
* @param {object} data
|
|
|
|
* @returns {Promise<Tier>}
|
|
|
|
*/
|
2022-10-20 07:51:18 +03:00
|
|
|
async edit(idString, data) {
|
|
|
|
const id = ObjectID.createFromHexString(idString);
|
2022-10-18 13:48:45 +03:00
|
|
|
const tier = await this.#repository.getById(id);
|
|
|
|
|
|
|
|
const editableProperties = [
|
|
|
|
'name',
|
|
|
|
'benefits',
|
|
|
|
'description',
|
|
|
|
'visibility',
|
|
|
|
'active',
|
2022-10-20 07:46:28 +03:00
|
|
|
'trialDays',
|
|
|
|
'welcomePageURL'
|
2022-10-18 13:48:45 +03:00
|
|
|
];
|
|
|
|
|
|
|
|
for (const editableProperty of editableProperties) {
|
|
|
|
if (Reflect.has(data, editableProperty)) {
|
|
|
|
tier[editableProperty] = data[editableProperty];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-20 13:25:19 +03:00
|
|
|
tier.updatePricing({
|
|
|
|
currency: data.currency || tier.currency,
|
|
|
|
monthlyPrice: data.monthlyPrice || tier.monthlyPrice,
|
|
|
|
yearlyPrice: data.yearlyPrice || tier.yearlyPrice
|
|
|
|
});
|
|
|
|
|
2022-10-18 13:48:45 +03:00
|
|
|
await this.#repository.save(tier);
|
|
|
|
|
|
|
|
return tier;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {object} data
|
|
|
|
* @returns {Promise<Tier>}
|
|
|
|
*/
|
|
|
|
async add(data) {
|
2022-10-20 07:56:46 +03:00
|
|
|
if (data.type === 'free') {
|
2022-10-18 13:48:45 +03:00
|
|
|
throw new BadRequestError({
|
|
|
|
message: 'Cannot create free Tier'
|
|
|
|
});
|
|
|
|
}
|
2022-10-20 07:39:32 +03:00
|
|
|
|
|
|
|
const slug = await this.#slugService.generate(data.slug || data.name);
|
2022-10-18 13:48:45 +03:00
|
|
|
const tier = await Tier.create({
|
2022-10-20 07:39:32 +03:00
|
|
|
slug,
|
2022-10-18 13:48:45 +03:00
|
|
|
type: 'paid',
|
|
|
|
status: 'active',
|
|
|
|
visibility: data.visibility,
|
|
|
|
name: data.name,
|
|
|
|
description: data.description,
|
|
|
|
benefits: data.benefits,
|
2022-10-20 07:46:28 +03:00
|
|
|
welcomePageURL: data.welcomePageURL,
|
|
|
|
monthlyPrice: data.monthlyPrice,
|
|
|
|
yearlyPrice: data.yearlyPrice,
|
2022-10-18 13:48:45 +03:00
|
|
|
currency: data.currency,
|
2022-10-20 07:46:28 +03:00
|
|
|
trialDays: data.trialDays
|
2022-10-20 07:39:32 +03:00
|
|
|
});
|
2022-10-18 13:48:45 +03:00
|
|
|
|
|
|
|
await this.#repository.save(tier);
|
|
|
|
|
|
|
|
return tier;
|
|
|
|
}
|
|
|
|
};
|