UNPKG

ghost

Version:

The professional publishing platform

278 lines (245 loc) 10.1 kB
const MRRService = require('./MrrStatsService'); const MembersService = require('./MembersStatsService'); const SubscriptionStatsService = require('./SubscriptionStatsService'); const ReferrersStatsService = require('./ReferrersStatsService'); const PostsStatsService = require('./PostsStatsService'); const ContentStatsService = require('./ContentStatsService'); class StatsService { /** * @param {object} deps * @param {MRRService} deps.mrr * @param {MembersService} deps.members * @param {SubscriptionStatsService} deps.subscriptions * @param {ReferrersStatsService} deps.referrers * @param {PostsStatsService} deps.posts * @param {ContentStatsService} deps.content **/ constructor(deps) { this.mrr = deps.mrr; this.members = deps.members; this.subscriptions = deps.subscriptions; this.referrers = deps.referrers; this.posts = deps.posts; this.content = deps.content; } async getMRRHistory() { return this.mrr.getHistory(); } /** * @param {Object} [options] * @param {string} [options.dateFrom] - Start date in YYYY-MM-DD format * @param {string} [options.endDate] - End date in YYYY-MM-DD format */ async getMemberCountHistory(options = {}) { // Map dateFrom to startDate for backwards compatibility const mappedOptions = { ...options, startDate: options.dateFrom }; delete mappedOptions.dateFrom; return this.members.getCountHistory(mappedOptions); } async getSubscriptionCountHistory() { return this.subscriptions.getSubscriptionHistory(); } async getReferrersHistory() { return this.referrers.getReferrersHistory(); } /** * @param {string} postId */ async getPostReferrers(postId) { return { data: await this.referrers.getForPost(postId), meta: {} }; } /** * @param {string} postId */ async getReferrersForPost(postId, options) { const result = await this.posts.getReferrersForPost(postId, options); return result; } /** * @param {Object} options */ async getTopContent(options = {}) { return await this.content.getTopContent(options); } /** * Get top posts by attribution metrics (includes all content that drove conversions) * @param {import('./PostsStatsService').TopPostsOptions} options * @returns {Promise<{data: import('./PostsStatsService').AttributionResult[]}>} */ async getTopPosts(options = {}) { // Return the original { data: results } structure const result = await this.posts.getTopPosts(options); return result; } /** * Get top posts by views * @param {Object} options * @param {string} options.date_from - Start date in YYYY-MM-DD format * @param {string} options.date_to - End date in YYYY-MM-DD format * @param {string} options.timezone - Timezone to use for date interpretation * @param {number} [options.limit=5] - Maximum number of posts to return * @returns {Promise<{data: import('./PostsStatsService').TopPostResult[]}>} */ async getTopPostsViews(options) { const result = await this.posts.getTopPostsViews(options); return result; } /** * @param {string} postId */ async getGrowthStatsForPost(postId) { return await this.posts.getGrowthStatsForPost(postId); } /** * @param {string[]} postIds */ async getPostsMemberCounts(postIds) { return await this.posts.getPostsMemberCounts(postIds); } /** * Get newsletter stats for sent posts * @param {Object} options * @param {string} [options.newsletter_id] - ID of the specific newsletter to get stats for * @param {string} [options.order='published_at desc'] - Order field and direction * @param {number} [options.limit=20] - Max number of results to return * @param {string} [options.date_from] - Start date filter in YYYY-MM-DD format * @param {string} [options.date_to] - End date filter in YYYY-MM-DD format * @returns {Promise<{data: import('./PostsStatsService').NewsletterStatResult[]}>} */ async getNewsletterStats(options = {}) { // Extract newsletter_id from options const {newsletter_id: newsletterId, ...otherOptions} = options; // If no newsletterId is provided, we can't get specific stats if (!newsletterId) { return {data: []}; } // Return newsletter stats for the specific newsletter const result = await this.posts.getNewsletterStats(newsletterId, otherOptions); return result; } /** * Get newsletter subscriber statistics including total count and daily deltas * * @param {Object} options * @param {string} [options.newsletter_id] - ID of the specific newsletter to get stats for * @param {string} [options.date_from] - Start date filter in YYYY-MM-DD format * @param {string} [options.date_to] - End date filter in YYYY-MM-DD format * @returns {Promise<{data: import('./PostsStatsService').NewsletterSubscriberStats[]}>} */ async getNewsletterSubscriberStats(options = {}) { // Extract newsletter_id from options const {newsletter_id: newsletterId, ...otherOptions} = options; // If no newsletterId is provided, we can't get specific stats if (!newsletterId) { return {data: [{total: 0, deltas: []}]}; } const result = await this.posts.getNewsletterSubscriberStats(newsletterId, otherOptions); return result; } /** * Get stats for a specific post by ID * @param {string} postId - The post ID to get stats for * @returns {Promise<{data: Object}>} */ async getPostStats(postId) { return await this.posts.getPostStats(postId); } /** * Get visitor counts for multiple posts * @param {string[]} postUuids - Array of post UUIDs * @returns {Promise<{data: Object}>} Visitor counts mapped by post UUID */ async getPostsVisitorCounts(postUuids) { const visitorCounts = await this.posts.getPostsVisitorCounts(postUuids); return { data: { visitor_counts: visitorCounts } }; } /** * Get newsletter basic stats for sent posts (without click data) * @param {Object} options * @param {string} [options.newsletter_id] - ID of the specific newsletter to get stats for * @param {string} [options.order='published_at desc'] - Order field and direction * @param {number} [options.limit=20] - Max number of results to return * @param {string} [options.date_from] - Start date filter in YYYY-MM-DD format * @param {string} [options.date_to] - End date filter in YYYY-MM-DD format * @returns {Promise<{data: import('./PostsStatsService').NewsletterStatResult[]}>} */ async getNewsletterBasicStats(options = {}) { // Extract newsletter_id from options const {newsletter_id: newsletterId, ...otherOptions} = options; // If no newsletterId is provided, we can't get specific stats if (!newsletterId) { return {data: []}; } // Return newsletter basic stats for the specific newsletter const result = await this.posts.getNewsletterBasicStats(newsletterId, otherOptions); return result; } /** * Get newsletter click stats for specific posts * @param {Object} options * @param {string} [options.newsletter_id] - ID of the specific newsletter to get stats for * @param {string} [options.post_ids] - Comma-separated string of post IDs to get click data for * @returns {Promise<{data: Object[]}>} */ async getNewsletterClickStats(options = {}) { // Extract newsletter_id and post_ids from options const {newsletter_id: newsletterId, post_ids: postIds} = options; // If no newsletterId is provided, we can't get specific stats if (!newsletterId) { return {data: []}; } // Return newsletter click stats for the specific newsletter and posts const result = await this.posts.getNewsletterClickStats(newsletterId, postIds); return result; } async getTopSourcesWithRange(startDate, endDate, orderBy, limit) { return this.referrers.getTopSourcesWithRange(startDate, endDate, orderBy, limit); } /** * @param {object} deps * * @returns {StatsService} **/ static create(deps) { // Create the Tinybird client if config exists let tinybirdClient = null; const config = deps.config || require('../../../shared/config'); const request = deps.request || require('../../lib/request-external'); const settingsCache = deps.settingsCache || require('../../../shared/settings-cache'); if (settingsCache.get('web_analytics_enabled')) { // TODO: move the tinybird client to the tinybird service const TinybirdServiceWrapper = require('../tinybird'); TinybirdServiceWrapper.init(); tinybirdClient = require('./utils/tinybird').create({ config, request, settingsCache, tinybirdService: TinybirdServiceWrapper.instance }); } // Add the Tinybird client to the dependencies const depsWithTinybird = { ...deps, tinybirdClient }; return new StatsService({ mrr: new MRRService(deps), members: new MembersService(deps), subscriptions: new SubscriptionStatsService(deps), referrers: new ReferrersStatsService(deps), posts: new PostsStatsService(depsWithTinybird), content: new ContentStatsService(depsWithTinybird) }); } } module.exports = StatsService;