ghost 5.127.0 → 5.127.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/tryghost-i18n-5.127.1.tgz +0 -0
- package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +2 -2
- package/core/built/admin/assets/admin-x-activitypub/{index-9vyYGTFg.mjs → index-BhzxZm-7.mjs} +8319 -8130
- package/core/built/admin/assets/admin-x-activitypub/{index-DsFs5lK0.mjs → index-BqFrbO6E.mjs} +2 -2
- package/core/built/admin/assets/{chunk.524.26236112ccb590199030.js → chunk.524.e6b0293eefcd2d23848f.js} +6 -6
- package/core/built/admin/assets/{chunk.582.8f3427a3fdd4dc0c14b4.js → chunk.582.dbda4c7f49cd241a0767.js} +8 -8
- package/core/built/admin/assets/{ghost-f22a17dd4f8592f0bf6ae28d6d454d9b.js → ghost-71ab6cab4367841fc79ac68cd664b0de.js} +214 -259
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +95521 -87956
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +1008 -126
- package/core/built/admin/assets/posts/posts.js +630 -733
- package/core/built/admin/assets/stats/stats.js +31295 -31199
- package/core/built/admin/index.html +3 -3
- package/core/server/api/endpoints/stats.js +40 -30
- package/core/server/services/email-service/email-templates/partials/styles.hbs +0 -8
- package/core/server/services/koenig/node-renderers/call-to-action-renderer.js +1 -1
- package/core/server/services/koenig/render-partials/email-button.js +5 -1
- package/core/server/services/stats/PostsStatsService.js +352 -91
- package/core/server/services/stats/ReferrersStatsService.js +215 -71
- package/core/server/services/stats/StatsService.js +9 -13
- package/core/server/web/api/endpoints/admin/routes.js +1 -1
- package/package.json +7 -7
- package/yarn.lock +57 -30
- package/components/tryghost-i18n-5.127.0.tgz +0 -0
|
@@ -1,5 +1,121 @@
|
|
|
1
1
|
const moment = require('moment');
|
|
2
2
|
|
|
3
|
+
// Source normalization mapping - consolidated from frontend apps
|
|
4
|
+
const SOURCE_NORMALIZATION_MAP = new Map([
|
|
5
|
+
// Social Media Consolidation
|
|
6
|
+
['facebook', 'Facebook'],
|
|
7
|
+
['www.facebook.com', 'Facebook'],
|
|
8
|
+
['l.facebook.com', 'Facebook'],
|
|
9
|
+
['lm.facebook.com', 'Facebook'],
|
|
10
|
+
['m.facebook.com', 'Facebook'],
|
|
11
|
+
['twitter', 'Twitter'],
|
|
12
|
+
['x.com', 'Twitter'],
|
|
13
|
+
['com.twitter.android', 'Twitter'],
|
|
14
|
+
['go.bsky.app', 'Bluesky'],
|
|
15
|
+
['bsky', 'Bluesky'],
|
|
16
|
+
['bsky.app', 'Bluesky'],
|
|
17
|
+
['linkedin', 'LinkedIn'],
|
|
18
|
+
['www.linkedin.com', 'LinkedIn'],
|
|
19
|
+
['linkedin.com', 'LinkedIn'],
|
|
20
|
+
['instagram', 'Instagram'],
|
|
21
|
+
['www.instagram.com', 'Instagram'],
|
|
22
|
+
['instagram.com', 'Instagram'],
|
|
23
|
+
['youtube', 'YouTube'],
|
|
24
|
+
['www.youtube.com', 'YouTube'],
|
|
25
|
+
['youtube.com', 'YouTube'],
|
|
26
|
+
['m.youtube.com', 'YouTube'],
|
|
27
|
+
['threads', 'Threads'],
|
|
28
|
+
['www.threads.net', 'Threads'],
|
|
29
|
+
['threads.net', 'Threads'],
|
|
30
|
+
['tiktok', 'TikTok'],
|
|
31
|
+
['www.tiktok.com', 'TikTok'],
|
|
32
|
+
['tiktok.com', 'TikTok'],
|
|
33
|
+
['pinterest', 'Pinterest'],
|
|
34
|
+
['www.pinterest.com', 'Pinterest'],
|
|
35
|
+
['pinterest.com', 'Pinterest'],
|
|
36
|
+
['reddit', 'Reddit'],
|
|
37
|
+
['www.reddit.com', 'Reddit'],
|
|
38
|
+
['reddit.com', 'Reddit'],
|
|
39
|
+
['whatsapp', 'WhatsApp'],
|
|
40
|
+
['whatsapp.com', 'WhatsApp'],
|
|
41
|
+
['www.whatsapp.com', 'WhatsApp'],
|
|
42
|
+
['telegram', 'Telegram'],
|
|
43
|
+
['telegram.org', 'Telegram'],
|
|
44
|
+
['www.telegram.org', 'Telegram'],
|
|
45
|
+
['t.me', 'Telegram'],
|
|
46
|
+
['news.ycombinator.com', 'Hacker News'],
|
|
47
|
+
['substack', 'Substack'],
|
|
48
|
+
['substack.com', 'Substack'],
|
|
49
|
+
['www.substack.com', 'Substack'],
|
|
50
|
+
['medium', 'Medium'],
|
|
51
|
+
['medium.com', 'Medium'],
|
|
52
|
+
['www.medium.com', 'Medium'],
|
|
53
|
+
|
|
54
|
+
// Search Engines
|
|
55
|
+
['google', 'Google'],
|
|
56
|
+
['www.google.com', 'Google'],
|
|
57
|
+
['google.com', 'Google'],
|
|
58
|
+
['bing', 'Bing'],
|
|
59
|
+
['www.bing.com', 'Bing'],
|
|
60
|
+
['bing.com', 'Bing'],
|
|
61
|
+
['yahoo', 'Yahoo'],
|
|
62
|
+
['www.yahoo.com', 'Yahoo'],
|
|
63
|
+
['yahoo.com', 'Yahoo'],
|
|
64
|
+
['search.yahoo.com', 'Yahoo'],
|
|
65
|
+
['duckduckgo', 'DuckDuckGo'],
|
|
66
|
+
['duckduckgo.com', 'DuckDuckGo'],
|
|
67
|
+
['www.duckduckgo.com', 'DuckDuckGo'],
|
|
68
|
+
['search.brave.com', 'Brave Search'],
|
|
69
|
+
['yandex', 'Yandex'],
|
|
70
|
+
['yandex.com', 'Yandex'],
|
|
71
|
+
['www.yandex.com', 'Yandex'],
|
|
72
|
+
['baidu', 'Baidu'],
|
|
73
|
+
['baidu.com', 'Baidu'],
|
|
74
|
+
['www.baidu.com', 'Baidu'],
|
|
75
|
+
['ecosia', 'Ecosia'],
|
|
76
|
+
['www.ecosia.org', 'Ecosia'],
|
|
77
|
+
['ecosia.org', 'Ecosia'],
|
|
78
|
+
|
|
79
|
+
// Email Platforms
|
|
80
|
+
['gmail', 'Gmail'],
|
|
81
|
+
['mail.google.com', 'Gmail'],
|
|
82
|
+
['gmail.com', 'Gmail'],
|
|
83
|
+
['outlook', 'Outlook'],
|
|
84
|
+
['outlook.live.com', 'Outlook'],
|
|
85
|
+
['outlook.com', 'Outlook'],
|
|
86
|
+
['hotmail.com', 'Outlook'],
|
|
87
|
+
['mail.yahoo.com', 'Yahoo Mail'],
|
|
88
|
+
['ymail.com', 'Yahoo Mail'],
|
|
89
|
+
['icloud.com', 'Apple Mail'],
|
|
90
|
+
['me.com', 'Apple Mail'],
|
|
91
|
+
['mac.com', 'Apple Mail'],
|
|
92
|
+
|
|
93
|
+
// News Aggregators
|
|
94
|
+
['news.google.com', 'Google News'],
|
|
95
|
+
['apple.news', 'Apple News'],
|
|
96
|
+
['flipboard', 'Flipboard'],
|
|
97
|
+
['flipboard.com', 'Flipboard'],
|
|
98
|
+
['www.flipboard.com', 'Flipboard'],
|
|
99
|
+
['smartnews', 'SmartNews'],
|
|
100
|
+
['smartnews.com', 'SmartNews'],
|
|
101
|
+
['www.smartnews.com', 'SmartNews']
|
|
102
|
+
]);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Normalize source names to consistent display names
|
|
106
|
+
* @param {string|null} source - Raw source string from referrer data
|
|
107
|
+
* @returns {string} Normalized source name or 'Direct' for empty/null sources
|
|
108
|
+
*/
|
|
109
|
+
function normalizeSource(source) {
|
|
110
|
+
if (!source || source === '') {
|
|
111
|
+
return 'Direct';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Case-insensitive lookup
|
|
115
|
+
const lowerSource = source.toLowerCase();
|
|
116
|
+
return SOURCE_NORMALIZATION_MAP.get(lowerSource) || source;
|
|
117
|
+
}
|
|
118
|
+
|
|
3
119
|
class ReferrersStatsService {
|
|
4
120
|
/**
|
|
5
121
|
* @param {object} deps
|
|
@@ -34,21 +150,25 @@ class ReferrersStatsService {
|
|
|
34
150
|
|
|
35
151
|
const map = new Map();
|
|
36
152
|
for (const row of signupRows) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
153
|
+
const normalizedSource = normalizeSource(row.referrer_source);
|
|
154
|
+
const existing = map.get(normalizedSource) || {
|
|
155
|
+
source: normalizedSource,
|
|
156
|
+
signups: 0,
|
|
40
157
|
paid_conversions: 0
|
|
41
|
-
}
|
|
158
|
+
};
|
|
159
|
+
existing.signups += row.total;
|
|
160
|
+
map.set(normalizedSource, existing);
|
|
42
161
|
}
|
|
43
162
|
|
|
44
163
|
for (const row of conversionRows) {
|
|
45
|
-
const
|
|
46
|
-
|
|
164
|
+
const normalizedSource = normalizeSource(row.referrer_source);
|
|
165
|
+
const existing = map.get(normalizedSource) || {
|
|
166
|
+
source: normalizedSource,
|
|
47
167
|
signups: 0,
|
|
48
168
|
paid_conversions: 0
|
|
49
169
|
};
|
|
50
|
-
existing.paid_conversions
|
|
51
|
-
map.set(
|
|
170
|
+
existing.paid_conversions += row.total;
|
|
171
|
+
map.set(normalizedSource, existing);
|
|
52
172
|
}
|
|
53
173
|
|
|
54
174
|
return [...map.values()].sort((a, b) => b.paid_conversions - a.paid_conversions);
|
|
@@ -96,69 +216,6 @@ class ReferrersStatsService {
|
|
|
96
216
|
};
|
|
97
217
|
}
|
|
98
218
|
|
|
99
|
-
/**
|
|
100
|
-
* Return a list of all the attribution sources with date range filtering, including MRR data
|
|
101
|
-
* @param {string} startDate - Start date in YYYY-MM-DD format
|
|
102
|
-
* @param {string} endDate - End date in YYYY-MM-DD format
|
|
103
|
-
* @returns {Promise<{data: AttributionCountStatWithMrr[], meta: {}}>}
|
|
104
|
-
*/
|
|
105
|
-
async getReferrersHistoryWithRange(startDate, endDate) {
|
|
106
|
-
const paidConversionEntries = await this.fetchPaidConversionSourcesWithRange(startDate, endDate);
|
|
107
|
-
const signupEntries = await this.fetchSignupSourcesWithRange(startDate, endDate);
|
|
108
|
-
const mrrEntries = await this.fetchMrrSourcesWithRange(startDate, endDate);
|
|
109
|
-
|
|
110
|
-
const allEntries = signupEntries.map((entry) => {
|
|
111
|
-
return {
|
|
112
|
-
...entry,
|
|
113
|
-
paid_conversions: 0,
|
|
114
|
-
mrr: 0,
|
|
115
|
-
date: moment(entry.date).format('YYYY-MM-DD')
|
|
116
|
-
};
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
paidConversionEntries.forEach((entry) => {
|
|
120
|
-
const entryDate = moment(entry.date).format('YYYY-MM-DD');
|
|
121
|
-
const existingEntry = allEntries.find(e => e.source === entry.source && e.date === entryDate);
|
|
122
|
-
|
|
123
|
-
if (existingEntry) {
|
|
124
|
-
existingEntry.paid_conversions = entry.paid_conversions;
|
|
125
|
-
} else {
|
|
126
|
-
allEntries.push({
|
|
127
|
-
...entry,
|
|
128
|
-
signups: 0,
|
|
129
|
-
mrr: 0,
|
|
130
|
-
date: entryDate
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
mrrEntries.forEach((entry) => {
|
|
136
|
-
const entryDate = moment(entry.date).format('YYYY-MM-DD');
|
|
137
|
-
const existingEntry = allEntries.find(e => e.source === entry.source && e.date === entryDate);
|
|
138
|
-
|
|
139
|
-
if (existingEntry) {
|
|
140
|
-
existingEntry.mrr = entry.mrr;
|
|
141
|
-
} else {
|
|
142
|
-
allEntries.push({
|
|
143
|
-
...entry,
|
|
144
|
-
signups: 0,
|
|
145
|
-
paid_conversions: 0,
|
|
146
|
-
date: entryDate
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// sort allEntries in date ascending format
|
|
152
|
-
allEntries.sort((a, b) => {
|
|
153
|
-
return moment(a.date).diff(moment(b.date));
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
return {
|
|
157
|
-
data: allEntries,
|
|
158
|
-
meta: {}
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
219
|
/**
|
|
163
220
|
* @returns {Promise<PaidConversionsCountStatDate[]>}
|
|
164
221
|
**/
|
|
@@ -268,9 +325,96 @@ class ReferrersStatsService {
|
|
|
268
325
|
|
|
269
326
|
return rows;
|
|
270
327
|
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Return aggregated attribution sources for a date range, grouped by source only (not by date)
|
|
331
|
+
* This is used for "Top Sources" tables that need server-side sorting
|
|
332
|
+
* @param {string} startDate - Start date in YYYY-MM-DD format
|
|
333
|
+
* @param {string} endDate - End date in YYYY-MM-DD format
|
|
334
|
+
* @param {string} [orderBy='signups desc'] - Sort order: 'signups desc', 'paid_conversions desc', 'mrr desc', 'source desc'
|
|
335
|
+
* @param {number} [limit=50] - Maximum number of sources to return
|
|
336
|
+
* @returns {Promise<{data: AttributionCountStatWithMrr[], meta: {}}>}
|
|
337
|
+
*/
|
|
338
|
+
async getTopSourcesWithRange(startDate, endDate, orderBy = 'signups desc', limit = 50) {
|
|
339
|
+
const paidConversionEntries = await this.fetchPaidConversionSourcesWithRange(startDate, endDate);
|
|
340
|
+
const signupEntries = await this.fetchSignupSourcesWithRange(startDate, endDate);
|
|
341
|
+
const mrrEntries = await this.fetchMrrSourcesWithRange(startDate, endDate);
|
|
342
|
+
|
|
343
|
+
// Aggregate by source (not by date + source)
|
|
344
|
+
const sourceMap = new Map();
|
|
345
|
+
|
|
346
|
+
// Add signup data
|
|
347
|
+
signupEntries.forEach((entry) => {
|
|
348
|
+
const source = normalizeSource(entry.source);
|
|
349
|
+
const existing = sourceMap.get(source) || {source, signups: 0, paid_conversions: 0, mrr: 0};
|
|
350
|
+
existing.signups += entry.signups;
|
|
351
|
+
sourceMap.set(source, existing);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// Add paid conversion data
|
|
355
|
+
paidConversionEntries.forEach((entry) => {
|
|
356
|
+
const source = normalizeSource(entry.source);
|
|
357
|
+
const existing = sourceMap.get(source) || {source, signups: 0, paid_conversions: 0, mrr: 0};
|
|
358
|
+
existing.paid_conversions += entry.paid_conversions;
|
|
359
|
+
sourceMap.set(source, existing);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Add MRR data
|
|
363
|
+
mrrEntries.forEach((entry) => {
|
|
364
|
+
const source = normalizeSource(entry.source);
|
|
365
|
+
const existing = sourceMap.get(source) || {source, signups: 0, paid_conversions: 0, mrr: 0};
|
|
366
|
+
existing.mrr += entry.mrr;
|
|
367
|
+
sourceMap.set(source, existing);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Convert to array and sort
|
|
371
|
+
let results = Array.from(sourceMap.values());
|
|
372
|
+
|
|
373
|
+
// Apply sorting - only allow descending sorts for sources
|
|
374
|
+
const [field] = orderBy.split(' ');
|
|
375
|
+
|
|
376
|
+
results.sort((a, b) => {
|
|
377
|
+
let valueA; let valueB;
|
|
378
|
+
|
|
379
|
+
switch (field) {
|
|
380
|
+
case 'signups':
|
|
381
|
+
valueA = a.signups;
|
|
382
|
+
valueB = b.signups;
|
|
383
|
+
break;
|
|
384
|
+
case 'paid_conversions':
|
|
385
|
+
valueA = a.paid_conversions;
|
|
386
|
+
valueB = b.paid_conversions;
|
|
387
|
+
break;
|
|
388
|
+
case 'mrr':
|
|
389
|
+
valueA = a.mrr;
|
|
390
|
+
valueB = b.mrr;
|
|
391
|
+
break;
|
|
392
|
+
case 'source':
|
|
393
|
+
valueA = a.source.toLowerCase();
|
|
394
|
+
valueB = b.source.toLowerCase();
|
|
395
|
+
break;
|
|
396
|
+
default:
|
|
397
|
+
return 0;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Always sort in descending order (highest to lowest)
|
|
401
|
+
return valueA < valueB ? 1 : valueA > valueB ? -1 : 0;
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// Apply limit
|
|
405
|
+
if (limit && limit > 0) {
|
|
406
|
+
results = results.slice(0, limit);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
data: results,
|
|
411
|
+
meta: {}
|
|
412
|
+
};
|
|
413
|
+
}
|
|
271
414
|
}
|
|
272
415
|
|
|
273
416
|
module.exports = ReferrersStatsService;
|
|
417
|
+
module.exports.normalizeSource = normalizeSource;
|
|
274
418
|
|
|
275
419
|
/**
|
|
276
420
|
* @typedef AttributionCountStat
|
|
@@ -53,14 +53,6 @@ class StatsService {
|
|
|
53
53
|
return this.referrers.getReferrersHistory();
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
/**
|
|
57
|
-
* @param {string} startDate - Start date in YYYY-MM-DD format
|
|
58
|
-
* @param {string} endDate - End date in YYYY-MM-DD format
|
|
59
|
-
*/
|
|
60
|
-
async getReferrersHistoryWithRange(startDate, endDate) {
|
|
61
|
-
return this.referrers.getReferrersHistoryWithRange(startDate, endDate);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
56
|
/**
|
|
65
57
|
* @param {string} postId
|
|
66
58
|
*/
|
|
@@ -87,9 +79,9 @@ class StatsService {
|
|
|
87
79
|
}
|
|
88
80
|
|
|
89
81
|
/**
|
|
90
|
-
* Get top posts by attribution metrics
|
|
82
|
+
* Get top posts by attribution metrics (includes all content that drove conversions)
|
|
91
83
|
* @param {import('./PostsStatsService').TopPostsOptions} options
|
|
92
|
-
* @returns {Promise<{data: import('./PostsStatsService').
|
|
84
|
+
* @returns {Promise<{data: import('./PostsStatsService').AttributionResult[]}>}
|
|
93
85
|
*/
|
|
94
86
|
async getTopPosts(options = {}) {
|
|
95
87
|
// Return the original { data: results } structure
|
|
@@ -133,7 +125,7 @@ class StatsService {
|
|
|
133
125
|
* @param {number} [options.limit=20] - Max number of results to return
|
|
134
126
|
* @param {string} [options.date_from] - Start date filter in YYYY-MM-DD format
|
|
135
127
|
* @param {string} [options.date_to] - End date filter in YYYY-MM-DD format
|
|
136
|
-
* @returns {Promise<{data:
|
|
128
|
+
* @returns {Promise<{data: import('./PostsStatsService').NewsletterStatResult[]}>}
|
|
137
129
|
*/
|
|
138
130
|
async getNewsletterStats(options = {}) {
|
|
139
131
|
// Extract newsletter_id from options
|
|
@@ -156,7 +148,7 @@ class StatsService {
|
|
|
156
148
|
* @param {string} [options.newsletter_id] - ID of the specific newsletter to get stats for
|
|
157
149
|
* @param {string} [options.date_from] - Start date filter in YYYY-MM-DD format
|
|
158
150
|
* @param {string} [options.date_to] - End date filter in YYYY-MM-DD format
|
|
159
|
-
* @returns {Promise<{data:
|
|
151
|
+
* @returns {Promise<{data: import('./PostsStatsService').NewsletterSubscriberStats[]}>}
|
|
160
152
|
*/
|
|
161
153
|
async getNewsletterSubscriberStats(options = {}) {
|
|
162
154
|
// Extract newsletter_id from options
|
|
@@ -202,7 +194,7 @@ class StatsService {
|
|
|
202
194
|
* @param {number} [options.limit=20] - Max number of results to return
|
|
203
195
|
* @param {string} [options.date_from] - Start date filter in YYYY-MM-DD format
|
|
204
196
|
* @param {string} [options.date_to] - End date filter in YYYY-MM-DD format
|
|
205
|
-
* @returns {Promise<{data:
|
|
197
|
+
* @returns {Promise<{data: import('./PostsStatsService').NewsletterStatResult[]}>}
|
|
206
198
|
*/
|
|
207
199
|
async getNewsletterBasicStats(options = {}) {
|
|
208
200
|
// Extract newsletter_id from options
|
|
@@ -239,6 +231,10 @@ class StatsService {
|
|
|
239
231
|
return result;
|
|
240
232
|
}
|
|
241
233
|
|
|
234
|
+
async getTopSourcesWithRange(startDate, endDate, orderBy, limit) {
|
|
235
|
+
return this.referrers.getTopSourcesWithRange(startDate, endDate, orderBy, limit);
|
|
236
|
+
}
|
|
237
|
+
|
|
242
238
|
/**
|
|
243
239
|
* @param {object} deps
|
|
244
240
|
*
|
|
@@ -155,7 +155,6 @@ module.exports = function apiRoutes() {
|
|
|
155
155
|
router.get('/stats/subscriptions', mw.authAdminApi, http(api.stats.subscriptions));
|
|
156
156
|
router.get('/stats/referrers/posts/:id', mw.authAdminApi, http(api.stats.postReferrers));
|
|
157
157
|
router.get('/stats/referrers', mw.authAdminApi, http(api.stats.referrersHistory));
|
|
158
|
-
router.get('/stats/referrers-growth', mw.authAdminApi, http(api.stats.referrersHistoryWithRange));
|
|
159
158
|
if (labs.isSet('trafficAnalytics')) {
|
|
160
159
|
router.get('/stats/posts/:id/stats', mw.authAdminApi, http(api.stats.postStats));
|
|
161
160
|
router.get('/stats/top-posts', mw.authAdminApi, http(api.stats.topPosts));
|
|
@@ -167,6 +166,7 @@ module.exports = function apiRoutes() {
|
|
|
167
166
|
router.get('/stats/subscriber-count', mw.authAdminApi, http(api.stats.subscriberCount));
|
|
168
167
|
router.get('/stats/posts/:id/top-referrers', mw.authAdminApi, http(api.stats.postReferrersAlpha));
|
|
169
168
|
router.get('/stats/posts/:id/growth', mw.authAdminApi, http(api.stats.postGrowthStats));
|
|
169
|
+
router.get('/stats/top-sources-growth', mw.authAdminApi, http(api.stats.topSourcesGrowth));
|
|
170
170
|
router.post('/stats/posts-visitor-counts', mw.authAdminApi, http(api.stats.postsVisitorCounts));
|
|
171
171
|
router.post('/stats/posts-member-counts', mw.authAdminApi, http(api.stats.postsMemberCounts));
|
|
172
172
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ghost",
|
|
3
|
-
"version": "5.127.
|
|
3
|
+
"version": "5.127.1",
|
|
4
4
|
"description": "The professional publishing platform",
|
|
5
5
|
"author": "Ghost Foundation",
|
|
6
6
|
"homepage": "https://ghost.org",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"@tryghost/helpers": "1.1.95",
|
|
87
87
|
"@tryghost/html-to-plaintext": "1.0.4",
|
|
88
88
|
"@tryghost/http-cache-utils": "0.1.20",
|
|
89
|
-
"@tryghost/i18n": "file:components/tryghost-i18n-5.127.
|
|
89
|
+
"@tryghost/i18n": "file:components/tryghost-i18n-5.127.1.tgz",
|
|
90
90
|
"@tryghost/image-transform": "1.4.5",
|
|
91
91
|
"@tryghost/job-manager": "1.0.1",
|
|
92
92
|
"@tryghost/kg-card-factory": "5.1.2",
|
|
@@ -94,10 +94,10 @@
|
|
|
94
94
|
"@tryghost/kg-converters": "1.1.7",
|
|
95
95
|
"@tryghost/kg-default-atoms": "5.1.1",
|
|
96
96
|
"@tryghost/kg-default-cards": "10.1.5",
|
|
97
|
-
"@tryghost/kg-default-nodes": "2.0.
|
|
98
|
-
"@tryghost/kg-default-transforms": "1.2.
|
|
99
|
-
"@tryghost/kg-html-to-lexical": "1.2.
|
|
100
|
-
"@tryghost/kg-lexical-html-renderer": "1.3.
|
|
97
|
+
"@tryghost/kg-default-nodes": "2.0.1",
|
|
98
|
+
"@tryghost/kg-default-transforms": "1.2.24",
|
|
99
|
+
"@tryghost/kg-html-to-lexical": "1.2.24",
|
|
100
|
+
"@tryghost/kg-lexical-html-renderer": "1.3.24",
|
|
101
101
|
"@tryghost/kg-markdown-html-renderer": "7.1.3",
|
|
102
102
|
"@tryghost/kg-mobiledoc-html-renderer": "7.1.3",
|
|
103
103
|
"@tryghost/limit-service": "1.2.18",
|
|
@@ -276,7 +276,7 @@
|
|
|
276
276
|
"jackspeak": "2.3.6",
|
|
277
277
|
"moment": "2.24.0",
|
|
278
278
|
"moment-timezone": "0.5.45",
|
|
279
|
-
"@tryghost/i18n": "file:components/tryghost-i18n-5.127.
|
|
279
|
+
"@tryghost/i18n": "file:components/tryghost-i18n-5.127.1.tgz"
|
|
280
280
|
},
|
|
281
281
|
"nx": {
|
|
282
282
|
"targets": {
|
package/yarn.lock
CHANGED
|
@@ -8031,34 +8031,39 @@
|
|
|
8031
8031
|
lodash "^4.17.21"
|
|
8032
8032
|
luxon "^3.5.0"
|
|
8033
8033
|
|
|
8034
|
-
"@tryghost/kg-default-nodes@2.0.
|
|
8035
|
-
version "2.0.
|
|
8036
|
-
resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-2.0.
|
|
8037
|
-
integrity sha512-
|
|
8034
|
+
"@tryghost/kg-default-nodes@2.0.1":
|
|
8035
|
+
version "2.0.1"
|
|
8036
|
+
resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-2.0.1.tgz#987d7708a8f7395b934d4d5e849a594484b87150"
|
|
8037
|
+
integrity sha512-DPO1bvnrnIh7xlHxtnICCbs88qGKeslT0S3MowOtiJ0FG6xRdl1+J0h5JitS38NjQi+HZxSDW75hYdmWvVQfCg==
|
|
8038
8038
|
dependencies:
|
|
8039
8039
|
"@lexical/clipboard" "0.13.1"
|
|
8040
8040
|
"@lexical/rich-text" "0.13.1"
|
|
8041
8041
|
"@lexical/selection" "0.13.1"
|
|
8042
8042
|
"@lexical/utils" "0.13.1"
|
|
8043
8043
|
"@tryghost/kg-clean-basic-html" "4.2.7"
|
|
8044
|
+
"@tryghost/kg-markdown-html-renderer" "7.1.2"
|
|
8045
|
+
clsx "2.1.1"
|
|
8046
|
+
html-minifier "^4.0.0"
|
|
8044
8047
|
jsdom "^24.1.1"
|
|
8045
8048
|
lexical "0.13.1"
|
|
8049
|
+
lodash "^4.17.21"
|
|
8050
|
+
luxon "^3.5.0"
|
|
8046
8051
|
|
|
8047
|
-
"@tryghost/kg-default-transforms@1.2.
|
|
8048
|
-
version "1.2.
|
|
8049
|
-
resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.2.
|
|
8050
|
-
integrity sha512-
|
|
8052
|
+
"@tryghost/kg-default-transforms@1.2.24":
|
|
8053
|
+
version "1.2.24"
|
|
8054
|
+
resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.2.24.tgz#f539b1b9763d81fc649bc31b9d7798a314b677f8"
|
|
8055
|
+
integrity sha512-AgoA7nUqAtsVJiROdA1FXvoNr92cNxEDSY4ndPd7nqXP1K2NHX5hqNYb6C2m2vds7lJKA6aXEFJPs+ddpgWS5w==
|
|
8051
8056
|
dependencies:
|
|
8052
8057
|
"@lexical/list" "0.13.1"
|
|
8053
8058
|
"@lexical/rich-text" "0.13.1"
|
|
8054
8059
|
"@lexical/utils" "0.13.1"
|
|
8055
|
-
"@tryghost/kg-default-nodes" "2.0.
|
|
8060
|
+
"@tryghost/kg-default-nodes" "2.0.1"
|
|
8056
8061
|
lexical "0.13.1"
|
|
8057
8062
|
|
|
8058
|
-
"@tryghost/kg-html-to-lexical@1.2.
|
|
8059
|
-
version "1.2.
|
|
8060
|
-
resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.2.
|
|
8061
|
-
integrity sha512-
|
|
8063
|
+
"@tryghost/kg-html-to-lexical@1.2.24":
|
|
8064
|
+
version "1.2.24"
|
|
8065
|
+
resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.2.24.tgz#a51c27cb2fc2a77b3bca840ddc036f80bd22162e"
|
|
8066
|
+
integrity sha512-8XH3a9UiaSK3qludJj6IRJKaPvk9oFBFKSl9PZnnvraP/fVZBiBIERP4MmwYUMHrqAieA1SQx+ueESES+8xSCw==
|
|
8062
8067
|
dependencies:
|
|
8063
8068
|
"@lexical/clipboard" "0.13.1"
|
|
8064
8069
|
"@lexical/headless" "0.13.1"
|
|
@@ -8066,15 +8071,15 @@
|
|
|
8066
8071
|
"@lexical/link" "0.13.1"
|
|
8067
8072
|
"@lexical/list" "0.13.1"
|
|
8068
8073
|
"@lexical/rich-text" "0.13.1"
|
|
8069
|
-
"@tryghost/kg-default-nodes" "2.0.
|
|
8070
|
-
"@tryghost/kg-default-transforms" "1.2.
|
|
8074
|
+
"@tryghost/kg-default-nodes" "2.0.1"
|
|
8075
|
+
"@tryghost/kg-default-transforms" "1.2.24"
|
|
8071
8076
|
jsdom "^24.1.1"
|
|
8072
8077
|
lexical "0.13.1"
|
|
8073
8078
|
|
|
8074
|
-
"@tryghost/kg-lexical-html-renderer@1.3.
|
|
8075
|
-
version "1.3.
|
|
8076
|
-
resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.3.
|
|
8077
|
-
integrity sha512-
|
|
8079
|
+
"@tryghost/kg-lexical-html-renderer@1.3.24":
|
|
8080
|
+
version "1.3.24"
|
|
8081
|
+
resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.3.24.tgz#458b9adc66ac9d7853f25b93b258ffbba8412581"
|
|
8082
|
+
integrity sha512-Cuc4TL9itKhzcpM8FwQdxwQZQeb4Xe164jfJyygmsrkRg7mAwv7iRWmiHVL656nNaIyNFp4nplJxkHEXnLW5FQ==
|
|
8078
8083
|
dependencies:
|
|
8079
8084
|
"@lexical/clipboard" "0.13.1"
|
|
8080
8085
|
"@lexical/code" "0.13.1"
|
|
@@ -8082,11 +8087,26 @@
|
|
|
8082
8087
|
"@lexical/link" "0.13.1"
|
|
8083
8088
|
"@lexical/list" "0.13.1"
|
|
8084
8089
|
"@lexical/rich-text" "0.13.1"
|
|
8085
|
-
"@tryghost/kg-default-nodes" "2.0.
|
|
8086
|
-
"@tryghost/kg-default-transforms" "1.2.
|
|
8090
|
+
"@tryghost/kg-default-nodes" "2.0.1"
|
|
8091
|
+
"@tryghost/kg-default-transforms" "1.2.24"
|
|
8087
8092
|
jsdom "^24.1.1"
|
|
8088
8093
|
lexical "0.13.1"
|
|
8089
8094
|
|
|
8095
|
+
"@tryghost/kg-markdown-html-renderer@7.1.2":
|
|
8096
|
+
version "7.1.2"
|
|
8097
|
+
resolved "https://registry.yarnpkg.com/@tryghost/kg-markdown-html-renderer/-/kg-markdown-html-renderer-7.1.2.tgz#f896c70acdc8ab9e25174343dd68646d631a06f3"
|
|
8098
|
+
integrity sha512-7/im5iBkBcGvTy+ju5CKChsix+RBsjimLLnHC6agnhr5NzWhSPNzhV57J0msqihbwboDEFr5UPXb6XfiKJvBHw==
|
|
8099
|
+
dependencies:
|
|
8100
|
+
"@tryghost/kg-utils" "1.0.31"
|
|
8101
|
+
markdown-it "^14.0.0"
|
|
8102
|
+
markdown-it-footnote "^4.0.0"
|
|
8103
|
+
markdown-it-image-lazy-loading "^2.0.0"
|
|
8104
|
+
markdown-it-lazy-headers "^0.1.3"
|
|
8105
|
+
markdown-it-mark "^4.0.0"
|
|
8106
|
+
markdown-it-sub "^2.0.0"
|
|
8107
|
+
markdown-it-sup "^2.0.0"
|
|
8108
|
+
semver "^7.7.0"
|
|
8109
|
+
|
|
8090
8110
|
"@tryghost/kg-markdown-html-renderer@7.1.3":
|
|
8091
8111
|
version "7.1.3"
|
|
8092
8112
|
resolved "https://registry.yarnpkg.com/@tryghost/kg-markdown-html-renderer/-/kg-markdown-html-renderer-7.1.3.tgz#f00370d0991cb519b62cd8d39421f1d5c5e19b63"
|
|
@@ -8118,10 +8138,17 @@
|
|
|
8118
8138
|
dependencies:
|
|
8119
8139
|
"@tryghost/kg-clean-basic-html" "4.2.7"
|
|
8120
8140
|
|
|
8121
|
-
"@tryghost/kg-unsplash-selector@0.3.
|
|
8122
|
-
version "0.3.
|
|
8123
|
-
resolved "https://registry.yarnpkg.com/@tryghost/kg-unsplash-selector/-/kg-unsplash-selector-0.3.
|
|
8124
|
-
integrity sha512-
|
|
8141
|
+
"@tryghost/kg-unsplash-selector@0.3.6":
|
|
8142
|
+
version "0.3.6"
|
|
8143
|
+
resolved "https://registry.yarnpkg.com/@tryghost/kg-unsplash-selector/-/kg-unsplash-selector-0.3.6.tgz#7b573600efce885e3a7ba8382dfff16c409e3c49"
|
|
8144
|
+
integrity sha512-tjCa9ynS2ODtCCFsOetKMS5Ov3BqvtgVJHdBqLlk7cJBt1ZBJDI7IDuKcqkwOhDm15SHQHbG1lNPHGG2wRCGGg==
|
|
8145
|
+
|
|
8146
|
+
"@tryghost/kg-utils@1.0.31":
|
|
8147
|
+
version "1.0.31"
|
|
8148
|
+
resolved "https://registry.yarnpkg.com/@tryghost/kg-utils/-/kg-utils-1.0.31.tgz#00e29cec01a16c53bc12e9d0d7f2153e58fde5d4"
|
|
8149
|
+
integrity sha512-TJRTu3XCxfqclueDjs8cVi3p9o5lYrIIkA6abNl4EAHvT9FtR6nY0FQgEgDwtDDjpaWq+VKcDyo48abvAG456g==
|
|
8150
|
+
dependencies:
|
|
8151
|
+
semver "^7.7.0"
|
|
8125
8152
|
|
|
8126
8153
|
"@tryghost/kg-utils@1.0.32":
|
|
8127
8154
|
version "1.0.32"
|
|
@@ -8130,10 +8157,10 @@
|
|
|
8130
8157
|
dependencies:
|
|
8131
8158
|
semver "^7.7.0"
|
|
8132
8159
|
|
|
8133
|
-
"@tryghost/koenig-lexical@1.7.
|
|
8134
|
-
version "1.7.
|
|
8135
|
-
resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.7.
|
|
8136
|
-
integrity sha512-
|
|
8160
|
+
"@tryghost/koenig-lexical@1.7.2":
|
|
8161
|
+
version "1.7.2"
|
|
8162
|
+
resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.7.2.tgz#03de94b698b1071f17a1a8a053fe060ed9c4f785"
|
|
8163
|
+
integrity sha512-GqE03JX6277X9slx1WoXblw1Sd2mGW1zWhvKpifGbs/ePR4oaBtpaq3OpNmMvWqfAaE3hXnHxYPy8J9gs/HnvQ==
|
|
8137
8164
|
|
|
8138
8165
|
"@tryghost/limit-service@1.2.18":
|
|
8139
8166
|
version "1.2.18"
|
|
@@ -19790,7 +19817,7 @@ html-escaper@^2.0.0:
|
|
|
19790
19817
|
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
|
|
19791
19818
|
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
|
|
19792
19819
|
|
|
19793
|
-
html-minifier@4.0.0:
|
|
19820
|
+
html-minifier@4.0.0, html-minifier@^4.0.0:
|
|
19794
19821
|
version "4.0.0"
|
|
19795
19822
|
resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-4.0.0.tgz#cca9aad8bce1175e02e17a8c33e46d8988889f56"
|
|
19796
19823
|
integrity sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==
|
|
Binary file
|