ghost 6.0.5 → 6.0.7

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.
Files changed (48) hide show
  1. package/components/tryghost-i18n-6.0.7.tgz +0 -0
  2. package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +1 -1
  3. package/core/built/admin/assets/admin-x-activitypub/{index-DBxGycCG.mjs → index-B-ckGCDl.mjs} +20083 -16775
  4. package/core/built/admin/assets/admin-x-activitypub/{index-Db9aLAi4.mjs → index-C81KQoIh.mjs} +2 -2
  5. package/core/built/admin/assets/admin-x-settings/{CodeEditorView-B4W7CQcA.mjs → CodeEditorView-BfL5FINN.mjs} +2 -2
  6. package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +1 -1
  7. package/core/built/admin/assets/admin-x-settings/{index-jv9DN3ZO.mjs → index-C_ZS-INP.mjs} +2 -2
  8. package/core/built/admin/assets/admin-x-settings/{index-CuwMM9FM.mjs → index-DiNZ3HQD.mjs} +12 -4
  9. package/core/built/admin/assets/admin-x-settings/{modals-CUGEPPYA.mjs → modals-BEiWgHsk.mjs} +8807 -8799
  10. package/core/built/admin/assets/{chunk.524.0e2bff9b664f925d7af7.js → chunk.524.9f989b3664418d6271a7.js} +6 -6
  11. package/core/built/admin/assets/{chunk.582.a8f6c436bbec6f9ba678.js → chunk.582.2421014e45b977b43b68.js} +8 -8
  12. package/core/built/admin/assets/{ghost-2b02de85a93ec9a5623180b373cece35.js → ghost-182ed60de3f37fff8a40cdd65d3bd2ef.js} +55 -50
  13. package/core/built/admin/assets/{ghost-2c537ee89c36199137eafc1768fd7de8.css → ghost-a7a53bf80dc45c37ae9c174a0d02a882.css} +1 -1
  14. package/core/built/admin/assets/{ghost-dark-ad23efc1d702e3643a8ee90d089df5d6.css → ghost-dark-6e0062029f988d8676e87f22d8e7f4a3.css} +1 -1
  15. package/core/built/admin/assets/posts/posts.js +78821 -77904
  16. package/core/built/admin/assets/stats/stats.js +21689 -21645
  17. package/core/built/admin/index.html +4 -4
  18. package/core/server/data/tinybird/datasources/analytics_events.datasource +3 -2
  19. package/core/server/data/tinybird/datasources/analytics_events_test.datasource +3 -2
  20. package/core/server/data/tinybird/endpoints/api_top_utm_campaigns.pipe +31 -0
  21. package/core/server/data/tinybird/endpoints/api_top_utm_contents.pipe +31 -0
  22. package/core/server/data/tinybird/endpoints/api_top_utm_mediums.pipe +31 -0
  23. package/core/server/data/tinybird/endpoints/api_top_utm_sources.pipe +31 -0
  24. package/core/server/data/tinybird/endpoints/api_top_utm_terms.pipe +31 -0
  25. package/core/server/data/tinybird/scripts/configure-ghost.sh +4 -2
  26. package/core/server/data/tinybird/tests/api_top_utm_campaigns.yaml +108 -0
  27. package/core/server/data/tinybird/tests/api_top_utm_contents.yaml +108 -0
  28. package/core/server/data/tinybird/tests/api_top_utm_mediums.yaml +108 -0
  29. package/core/server/data/tinybird/tests/api_top_utm_sources.yaml +108 -0
  30. package/core/server/data/tinybird/tests/api_top_utm_terms.yaml +108 -0
  31. package/core/server/services/lib/magic-link/MagicLink.js +98 -12
  32. package/core/server/services/members/MembersConfigProvider.js +11 -1
  33. package/core/server/services/members/SingleUseTokenProvider.js +218 -5
  34. package/core/server/services/members/api.js +18 -8
  35. package/core/server/services/members/emails/signin.js +42 -5
  36. package/core/server/services/members/members-api/controllers/RouterController.js +105 -18
  37. package/core/server/services/members/members-api/members-api.js +12 -7
  38. package/core/server/services/members/members-ssr.js +5 -3
  39. package/core/server/services/members/middleware.js +2 -2
  40. package/core/server/services/stats/PostsStatsService.js +26 -9
  41. package/core/server/services/stats/StatsService.js +1 -1
  42. package/core/server/web/members/app.js +12 -3
  43. package/core/shared/config/defaults.json +1 -1
  44. package/core/shared/labs.js +3 -1
  45. package/package.json +9 -9
  46. package/tsconfig.tsbuildinfo +1 -1
  47. package/yarn.lock +213 -323
  48. package/components/tryghost-i18n-6.0.5.tgz +0 -0
@@ -156,12 +156,13 @@ class MembersSSR {
156
156
  * @method _getMemberDataFromToken
157
157
  *
158
158
  * @param {JWT} token
159
+ * @param {string} [otcVerification]
159
160
  *
160
161
  * @returns {Promise<Member>} member
161
162
  */
162
- async _getMemberDataFromToken(token) {
163
+ async _getMemberDataFromToken(token, otcVerification) {
163
164
  const api = await this._getMembersApi();
164
- return api.getMemberDataFromMagicLinkToken(token);
165
+ return api.getMemberDataFromMagicLinkToken(token, otcVerification);
165
166
  }
166
167
 
167
168
  /**
@@ -234,7 +235,8 @@ class MembersSSR {
234
235
  }
235
236
 
236
237
  const token = Array.isArray(query.token) ? query.token[0] : query.token;
237
- const member = await this._getMemberDataFromToken(token);
238
+ const otcVerification = Array.isArray(query.otc_verification) ? query.otc_verification[0] : query.otc_verification;
239
+ const member = await this._getMemberDataFromToken(token, otcVerification);
238
240
 
239
241
  if (!member) {
240
242
  // The member doesn't exist any longer (could be a sign in token for a member that was deleted)
@@ -332,8 +332,8 @@ const createSessionFromMagicLink = async function createSessionFromMagicLink(req
332
332
  // req.query is a plain object, copy it to a URLSearchParams object so we can call toString()
333
333
  const searchParams = new URLSearchParams('');
334
334
  Object.keys(req.query).forEach((param) => {
335
- // don't copy the "token" or "r" params
336
- if (param !== 'token' && param !== 'r') {
335
+ // don't copy the "token", "r", or "otc_verification" params
336
+ if (param !== 'token' && param !== 'r' && param !== 'otc_verification') {
337
337
  searchParams.set(param, req.query[param]);
338
338
  }
339
339
  });
@@ -74,7 +74,7 @@ const {getDateBoundaries, applyDateFilter} = require('./utils/date-utils');
74
74
  /**
75
75
  * @typedef {Object} NewsletterSubscriberStats
76
76
  * @property {number} total - Total current subscriber count
77
- * @property {Array<{date: string, value: number}>} deltas - Daily subscription deltas
77
+ * @property {Array<{date: string, value: number}>} values - Daily subscription cumulative values
78
78
  */
79
79
 
80
80
  class PostsStatsService {
@@ -932,7 +932,7 @@ class PostsStatsService {
932
932
  * @param {string} [options.date_from] - Optional start date filter (YYYY-MM-DD)
933
933
  * @param {string} [options.date_to] - Optional end date filter (YYYY-MM-DD)
934
934
  * @param {string} [options.timezone] - Timezone to use for date interpretation
935
- * @returns {Promise<{data: Array<{total: number, deltas: Array<{date: string, value: number}>}>}>} The newsletter subscriber stats
935
+ * @returns {Promise<{data: Array<{total: number, values: Array<{date: string, value: number}>}>}>} The newsletter subscriber stats with cumulative values
936
936
  */
937
937
  async getNewsletterSubscriberStats(newsletterId, options = {}) {
938
938
  try {
@@ -956,25 +956,42 @@ class PostsStatsService {
956
956
  const totalValue = totalResult[0] ? totalResult[0].total : 0;
957
957
  const total = parseInt(String(totalValue), 10);
958
958
 
959
- // Transform raw database results to properly typed objects
960
- const deltas = [];
959
+ // Transform raw database results (daily changes) to cumulative values
960
+ const values = [];
961
+ let cumulativeTotal = 0;
962
+
963
+ // First pass: collect all daily changes from database
964
+ const dailyChanges = [];
961
965
  for (const row of rawDeltas) {
962
966
  if (row) {
963
967
  // @ts-ignore
964
968
  const dateValue = row.date || '';
965
969
  // @ts-ignore
966
- const deltaValue = row.value || 0;
967
- deltas.push({
970
+ const changeValue = row.value || 0;
971
+ dailyChanges.push({
968
972
  date: String(dateValue),
969
- value: parseInt(String(deltaValue), 10)
973
+ change: parseInt(String(changeValue), 10)
970
974
  });
971
975
  }
972
976
  }
977
+
978
+ // Calculate the starting point by working backwards from the current total
979
+ const totalChange = dailyChanges.reduce((sum, item) => sum + item.change, 0);
980
+ cumulativeTotal = total - totalChange;
981
+
982
+ // Second pass: build cumulative values from daily changes
983
+ for (const dayData of dailyChanges) {
984
+ cumulativeTotal += dayData.change;
985
+ values.push({
986
+ date: dayData.date,
987
+ value: cumulativeTotal
988
+ });
989
+ }
973
990
 
974
991
  return {
975
992
  data: [{
976
993
  total,
977
- deltas
994
+ values
978
995
  }]
979
996
  };
980
997
  } catch (error) {
@@ -982,7 +999,7 @@ class PostsStatsService {
982
999
  return {
983
1000
  data: [{
984
1001
  total: 0,
985
- deltas: []
1002
+ values: []
986
1003
  }]
987
1004
  };
988
1005
  }
@@ -156,7 +156,7 @@ class StatsService {
156
156
 
157
157
  // If no newsletterId is provided, we can't get specific stats
158
158
  if (!newsletterId) {
159
- return {data: [{total: 0, deltas: []}]};
159
+ return {data: [{total: 0, values: []}]};
160
160
  }
161
161
 
162
162
  const result = await this.posts.getNewsletterSubscriberStats(newsletterId, otherOptions);
@@ -41,7 +41,7 @@ module.exports = function setupMembersApp() {
41
41
  // We don't want to add global bodyParser middleware as that interferes with stripe webhook requests on - `/webhooks`.
42
42
 
43
43
  // Manage newsletter subscription via unsubscribe link - these should be authenticated by uuid and hashed key
44
- membersApp.get('/api/member/newsletters',
44
+ membersApp.get('/api/member/newsletters',
45
45
  middleware.authMemberByUuid,
46
46
  middleware.getMemberNewsletters
47
47
  );
@@ -59,7 +59,7 @@ module.exports = function setupMembersApp() {
59
59
  } else {
60
60
  membersApp.get('/api/member', middleware.getMemberData);
61
61
  }
62
-
62
+
63
63
  membersApp.put('/api/member', bodyParser.json({limit: '50mb'}), middleware.updateMemberData);
64
64
  membersApp.post('/api/member/email', bodyParser.json({limit: '50mb'}), (req, res, next) => membersService.api.middleware.updateEmailAddress(req, res, next));
65
65
 
@@ -72,7 +72,6 @@ module.exports = function setupMembersApp() {
72
72
 
73
73
  membersApp.get('/api/integrity-token', middleware.createIntegrityToken);
74
74
 
75
- // NOTE: this is wrapped in a function to ensure we always go via the getter
76
75
  membersApp.post(
77
76
  '/api/send-magic-link',
78
77
  bodyParser.json(),
@@ -81,10 +80,20 @@ module.exports = function setupMembersApp() {
81
80
  shared.middleware.brute.membersAuthEnumeration,
82
81
  // Prevent brute forcing passwords for the same email address
83
82
  shared.middleware.brute.membersAuth,
83
+ // NOTE: this is wrapped in a function to ensure we always go via the getter
84
84
  function lazySendMagicLinkMw(req, res, next) {
85
85
  return membersService.api.middleware.sendMagicLink(req, res, next);
86
86
  }
87
87
  );
88
+ membersApp.post(
89
+ '/api/verify-otc',
90
+ bodyParser.json(),
91
+ middleware.verifyIntegrityToken,
92
+ // NOTE: this is wrapped in a function to ensure we always go via the getter
93
+ function lazyVerifyOTCMw(req, res, next) {
94
+ return membersService.api.middleware.verifyOTC(req, res, next);
95
+ }
96
+ );
88
97
  membersApp.post('/api/create-stripe-checkout-session', function lazyCreateCheckoutSessionMw(req, res, next) {
89
98
  return membersService.api.middleware.createCheckoutSession(req, res, next);
90
99
  });
@@ -212,7 +212,7 @@
212
212
  },
213
213
  "portal": {
214
214
  "url": "https://cdn.jsdelivr.net/ghost/portal@~{version}/umd/portal.min.js",
215
- "version": "2.52"
215
+ "version": "2.53"
216
216
  },
217
217
  "sodoSearch": {
218
218
  "url": "https://cdn.jsdelivr.net/ghost/sodo-search@~{version}/umd/sodo-search.min.js",
@@ -47,7 +47,9 @@ const PRIVATE_FEATURES = [
47
47
  'lexicalIndicators',
48
48
  'contentVisibilityAlpha',
49
49
  'emailCustomization',
50
- 'membersSigninOTC'
50
+ 'membersSigninOTC',
51
+ 'tagsX',
52
+ 'utmTracking'
51
53
  ];
52
54
 
53
55
  module.exports.GA_KEYS = [...GA_FEATURES];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghost",
3
- "version": "6.0.5",
3
+ "version": "6.0.7",
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.97",
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-6.0.5.tgz",
89
+ "@tryghost/i18n": "file:components/tryghost-i18n-6.0.7.tgz",
90
90
  "@tryghost/image-transform": "1.4.6",
91
91
  "@tryghost/job-manager": "1.0.3",
92
92
  "@tryghost/kg-card-factory": "5.1.2",
@@ -181,7 +181,7 @@
181
181
  "knex-migrator": "5.3.2",
182
182
  "leaky-bucket": "2.2.0",
183
183
  "lodash": "4.17.21",
184
- "luxon": "3.7.1",
184
+ "luxon": "3.7.2",
185
185
  "mailgun.js": "10.4.0",
186
186
  "metascraper": "5.45.15",
187
187
  "metascraper-author": "5.45.10",
@@ -232,14 +232,14 @@
232
232
  "@types/bookshelf": "1.2.9",
233
233
  "@types/common-tags": "1.8.4",
234
234
  "@types/jsonwebtoken": "9.0.10",
235
- "@types/node": "22.17.2",
235
+ "@types/node": "22.18.1",
236
236
  "@types/node-jose": "1.1.13",
237
237
  "@types/nodemailer": "6.4.19",
238
238
  "@types/sinon": "17.0.4",
239
239
  "@types/supertest": "6.0.3",
240
240
  "c8": "10.1.3",
241
241
  "cli-progress": "3.12.0",
242
- "cssnano": "7.1.0",
242
+ "cssnano": "7.1.1",
243
243
  "detect-indent": "6.1.0",
244
244
  "detect-newline": "3.1.0",
245
245
  "expect": "29.7.0",
@@ -250,7 +250,7 @@
250
250
  "inquirer": "8.2.7",
251
251
  "jwk-to-pem": "2.0.7",
252
252
  "jwks-rsa": "3.2.0",
253
- "mocha": "11.7.1",
253
+ "mocha": "11.7.2",
254
254
  "mocha-slow-test-reporter": "0.1.2",
255
255
  "mock-knex": "TryGhost/mock-knex#68948e11b0ea4fe63456098dfdc169bea7f62009",
256
256
  "nock": "13.5.6",
@@ -258,13 +258,13 @@
258
258
  "parse-prometheus-text-format": "1.1.1",
259
259
  "postcss": "8.5.6",
260
260
  "postcss-cli": "11.0.1",
261
- "rewire": "6.0.0",
261
+ "rewire": "8.0.0",
262
262
  "should": "13.2.3",
263
263
  "sinon": "18.0.1",
264
264
  "supertest": "6.3.4",
265
265
  "tmp": "0.2.3",
266
266
  "toml": "3.0.0",
267
- "tsx": "4.20.4",
267
+ "tsx": "4.20.5",
268
268
  "typescript": "5.8.3"
269
269
  },
270
270
  "resolutions": {
@@ -273,7 +273,7 @@
273
273
  "jackspeak": "2.3.6",
274
274
  "moment": "2.24.0",
275
275
  "moment-timezone": "0.5.45",
276
- "@tryghost/i18n": "file:components/tryghost-i18n-6.0.5.tgz"
276
+ "@tryghost/i18n": "file:components/tryghost-i18n-6.0.7.tgz"
277
277
  },
278
278
  "nx": {
279
279
  "targets": {