ghost 6.0.10 → 6.2.0

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 (63) hide show
  1. package/components/tryghost-i18n-6.2.0.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-wBqnq7A5.mjs → index-DmCoswaX.mjs} +2 -2
  4. package/core/built/admin/assets/admin-x-activitypub/{index-XhNX0QuF.mjs → index-lT95Q15h.mjs} +8212 -8183
  5. package/core/built/admin/assets/admin-x-settings/{CodeEditorView-BDBDWpWl.mjs → CodeEditorView-UxqLGRTu.mjs} +3 -3
  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-o4Q9MNrB.mjs → index-8WxO2QXI.mjs} +3017 -2827
  8. package/core/built/admin/assets/admin-x-settings/{index-DsbJfrQ7.mjs → index-B5r0jdJS.mjs} +95 -95
  9. package/core/built/admin/assets/admin-x-settings/{index-BB7hgOf0.mjs → index-Co907MFn.mjs} +2 -2
  10. package/core/built/admin/assets/admin-x-settings/{index-BgCSf8S1.mjs → index-DD3HKlR3.mjs} +306 -315
  11. package/core/built/admin/assets/admin-x-settings/{modals-CCpr5VWU.mjs → modals-B7j9sxR4.mjs} +8799 -8807
  12. package/core/built/admin/assets/{chunk.397.e5d027e53a68dff31d76.js → chunk.397.d5e25bb9baf088f52499.js} +2 -2
  13. package/core/built/admin/assets/{chunk.524.695215c994f8cbf547d3.js → chunk.524.70595796c7b8c6003a2d.js} +7 -7
  14. package/core/built/admin/assets/{chunk.582.a949b80543caba37906c.js → chunk.582.d9b970b71da671ac1b7b.js} +8 -8
  15. package/core/built/admin/assets/{ghost-9c608430440a10746540adb7d2cd0f31.js → ghost-2066304fd0b166e1c16d397dd73ef7b2.js} +39 -35
  16. package/core/built/admin/assets/ghost-49475952d56ffe89bd47ab9d9c64ada8.css +1 -0
  17. package/core/built/admin/assets/ghost-dark-27877727751b91f03261d449d74e33b9.css +1 -0
  18. package/core/built/admin/assets/posts/posts.js +27400 -27361
  19. package/core/built/admin/assets/stats/stats.js +28701 -28674
  20. package/core/built/admin/index.html +5 -5
  21. package/core/frontend/public/member-attribution.min.js +1 -1
  22. package/core/server/api/endpoints/search-index.js +2 -2
  23. package/core/server/api/endpoints/stats.js +10 -4
  24. package/core/server/data/migrations/utils/schema.js +11 -6
  25. package/core/server/data/migrations/versions/6.1/2025-09-11-00-38-13-add-uuid-column-to-tokens.js +8 -0
  26. package/core/server/data/migrations/versions/6.1/2025-09-11-00-39-08-backfill-tokens-uuid.js +19 -0
  27. package/core/server/data/migrations/versions/6.1/2025-09-11-00-39-36-tokens-drop-nullable-uuid.js +4 -0
  28. package/core/server/data/migrations/versions/6.2/2025-09-30-14-28-09-add-utm-fields.js +24 -0
  29. package/core/server/data/schema/commands.js +21 -6
  30. package/core/server/data/schema/schema.js +25 -0
  31. package/core/server/data/tinybird/datasources/_mv_hits.datasource +7 -4
  32. package/core/server/data/tinybird/endpoints/api_top_utm_campaigns.pipe +2 -8
  33. package/core/server/data/tinybird/endpoints/api_top_utm_contents.pipe +2 -8
  34. package/core/server/data/tinybird/endpoints/api_top_utm_mediums.pipe +2 -8
  35. package/core/server/data/tinybird/endpoints/api_top_utm_sources.pipe +2 -8
  36. package/core/server/data/tinybird/endpoints/api_top_utm_terms.pipe +2 -8
  37. package/core/server/data/tinybird/fixtures/analytics_events.ndjson +11 -11
  38. package/core/server/data/tinybird/pipes/mv_hits.pipe +12 -2
  39. package/core/server/data/tinybird/pipes/mv_session_data.pipe +16 -6
  40. package/core/server/data/tinybird/tests/api_top_utm_campaigns.yaml +35 -34
  41. package/core/server/data/tinybird/tests/api_top_utm_contents.yaml +57 -48
  42. package/core/server/data/tinybird/tests/api_top_utm_mediums.yaml +40 -38
  43. package/core/server/data/tinybird/tests/api_top_utm_sources.yaml +59 -39
  44. package/core/server/data/tinybird/tests/api_top_utm_terms.yaml +55 -48
  45. package/core/server/models/single-use-token.js +1 -0
  46. package/core/server/services/email-service/EmailRenderer.js +1 -0
  47. package/core/server/services/email-service/email-templates/template.hbs +6 -0
  48. package/core/server/services/lib/MailgunClient.js +4 -3
  49. package/core/server/services/lib/magic-link/MagicLink.js +9 -9
  50. package/core/server/services/mail/GhostMailer.js +4 -1
  51. package/core/server/services/members/MembersConfigProvider.js +0 -15
  52. package/core/server/services/members/SingleUseTokenProvider.js +8 -8
  53. package/core/server/services/members/emails/signin.js +4 -4
  54. package/core/server/services/stats/MrrStatsService.js +10 -5
  55. package/core/server/services/stats/StatsService.js +2 -2
  56. package/core/shared/config/defaults.json +1 -1
  57. package/package.json +9 -9
  58. package/tsconfig.tsbuildinfo +1 -1
  59. package/yarn.lock +1076 -495
  60. package/components/tryghost-i18n-6.0.10.tgz +0 -0
  61. package/core/built/admin/assets/ghost-a7a53bf80dc45c37ae9c174a0d02a882.css +0 -1
  62. package/core/built/admin/assets/ghost-dark-6e0062029f988d8676e87f22d8e7f4a3.css +0 -1
  63. /package/core/built/admin/assets/{chunk.397.e5d027e53a68dff31d76.js.LICENSE.txt → chunk.397.d5e25bb9baf088f52499.js.LICENSE.txt} +0 -0
@@ -193,7 +193,7 @@ class SingleUseTokenProvider {
193
193
  }
194
194
 
195
195
  try {
196
- const model = await this.model.findOne({id: otcRef});
196
+ const model = await this.model.findOne({uuid: otcRef});
197
197
 
198
198
  if (!model) {
199
199
  return false;
@@ -208,16 +208,16 @@ class SingleUseTokenProvider {
208
208
  }
209
209
 
210
210
  /**
211
- * @method getIdByToken
212
- * Retrieves the ID associated with a given token.
211
+ * @method getRefByToken
212
+ * Retrieves the ref associated with a given token.
213
213
  *
214
214
  * @param {string} token - The token to look up.
215
- * @returns {Promise<string|null>} The ID if found, or null if not found or on error.
215
+ * @returns {Promise<string|null>} The ref if found, or null if not found or on error.
216
216
  */
217
- async getIdByToken(token) {
217
+ async getRefByToken(token) {
218
218
  try {
219
219
  const model = await this.model.findOne({token});
220
- return model ? model.get('id') : null;
220
+ return model ? model.get('uuid') : null;
221
221
  } catch (err) {
222
222
  return null;
223
223
  }
@@ -232,7 +232,7 @@ class SingleUseTokenProvider {
232
232
  */
233
233
  async getTokenByRef(ref) {
234
234
  try {
235
- const model = await this.model.findOne({id: ref});
235
+ const model = await this.model.findOne({uuid: ref});
236
236
  return model ? model.get('token') : null;
237
237
  } catch (err) {
238
238
  return null;
@@ -300,7 +300,7 @@ class SingleUseTokenProvider {
300
300
  return false;
301
301
  }
302
302
 
303
- const tokenId = await this.getIdByToken(token);
303
+ const tokenId = await this.getRefByToken(token);
304
304
  if (!tokenId) {
305
305
  return false;
306
306
  }
@@ -126,8 +126,8 @@ module.exports = ({t, siteTitle, email, url, otc, accentColor = '#15212A', siteD
126
126
  <td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
127
127
  <p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 20px; color: #15212A; font-weight: bold; line-height: 24px; margin: 0; margin-bottom: 15px;">${t('Hey there,')}</p>
128
128
  ${otc ?
129
- `<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; line-height: 24px; margin-bottom: 24px;">${t(`Welcome back! Here's your code to sign in to {siteTitle}. For your security, it's only valid for 24 hours`, {siteTitle, interpolation: {escapeValue: false}})}:</p>
130
- <table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box; margin-bottom: 32px;">
129
+ `<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; line-height: 24px; margin-bottom: 24px;">${t(`Welcome back! Here's your code to sign in to {siteTitle}`, {siteTitle, interpolation: {escapeValue: false}})}:</p>
130
+ <table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; min-width: 100%; width: 100%; box-sizing: border-box; margin-bottom: 32px;">
131
131
  <tbody>
132
132
  <tr>
133
133
  <td style="padding: 16px; background-color: #F4F5F6; border-radius: 8px; text-align: center; vertical-align: middle;" valign="middle">
@@ -137,11 +137,11 @@ module.exports = ({t, siteTitle, email, url, otc, accentColor = '#15212A', siteD
137
137
  </tbody>
138
138
  </table>
139
139
  <p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; line-height: 24px; margin-bottom: 24px;">${t('Or, skip the code and sign in directly')}:</p>
140
- <table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
140
+ <table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; min-width: 100%; box-sizing: border-box;">
141
141
  <tbody>
142
142
  <tr>
143
143
  <td align="center" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; padding-bottom: 35px;">
144
- <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
144
+ <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; min-width: 100%;">
145
145
  <tbody>
146
146
  <tr>
147
147
  <td align="center" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; background-color: ${accentColor}; border-radius: 5px; text-align: center;"> <a href="${url}" target="_blank" style="display: inline-block; color: #ffffff; background-color: ${accentColor}; border: solid 1px ${accentColor}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: ${accentColor};">${t('Sign in now')}</a> </td>
@@ -34,18 +34,21 @@ class MrrStatsService {
34
34
 
35
35
  /**
36
36
  * Get the MRR deltas for all days (from old to new), grouped by currency (ascending alphabetically)
37
+ * @param {string} [dateFrom] - Start date to fetch deltas from
37
38
  * @returns {Promise<MrrDelta[]>} The deltas sorted from new to old
38
39
  */
39
- async fetchAllDeltas() {
40
+ async fetchAllDeltas(dateFrom) {
40
41
  const knex = this.knex;
41
- const ninetyDaysAgo = moment.utc().subtract(90, 'days').startOf('day').utc().format('YYYY-MM-DD HH:mm:ss');
42
+ const startDate = dateFrom
43
+ ? moment.utc(dateFrom).startOf('day').utc().format('YYYY-MM-DD HH:mm:ss')
44
+ : moment.utc().subtract(90, 'days').startOf('day').utc().format('YYYY-MM-DD HH:mm:ss');
42
45
  const rows = await knex('members_paid_subscription_events')
43
46
  .select('currency')
44
47
  // In SQLite, DATE(created_at) would map to a string value, while DATE(created_at) would map to a JSDate object in MySQL
45
48
  // That is why we need the cast here (to have some consistency)
46
49
  .select(knex.raw('CAST(DATE(created_at) as CHAR) as date'))
47
50
  .select(knex.raw(`SUM(mrr_delta) as delta`))
48
- .where('created_at', '>=', ninetyDaysAgo)
51
+ .where('created_at', '>=', startDate)
49
52
  .groupByRaw('CAST(DATE(created_at) as CHAR), currency');
50
53
  return rows;
51
54
  }
@@ -53,13 +56,15 @@ class MrrStatsService {
53
56
  /**
54
57
  * Returns a list of the MRR history for each day and currency, including the current MRR per currency as meta data.
55
58
  * The respons is in ascending date order, and currencies for the same date are always in ascending order.
59
+ * @param {Object} [options]
60
+ * @param {string} [options.dateFrom] - Start date to fetch history from
56
61
  * @returns {Promise<MrrHistory>}
57
62
  */
58
- async getHistory() {
63
+ async getHistory(options = {}) {
59
64
  // Fetch current total amounts and start counting from there
60
65
  const totals = await this.getCurrentMrr();
61
66
 
62
- const rows = await this.fetchAllDeltas();
67
+ const rows = await this.fetchAllDeltas(options.dateFrom);
63
68
 
64
69
  rows.sort((rowA, rowB) => {
65
70
  const dateA = new Date(rowA.date);
@@ -23,8 +23,8 @@ class StatsService {
23
23
  this.content = deps.content;
24
24
  }
25
25
 
26
- async getMRRHistory() {
27
- return this.mrr.getHistory();
26
+ async getMRRHistory(options = {}) {
27
+ return this.mrr.getHistory(options);
28
28
  }
29
29
 
30
30
  /**
@@ -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.53"
215
+ "version": "2.54"
216
216
  },
217
217
  "sodoSearch": {
218
218
  "url": "https://cdn.jsdelivr.net/ghost/sodo-search@~{version}/umd/sodo-search.min.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghost",
3
- "version": "6.0.10",
3
+ "version": "6.2.0",
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.10.tgz",
89
+ "@tryghost/i18n": "file:components/tryghost-i18n-6.2.0.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",
@@ -196,7 +196,7 @@
196
196
  "moment": "2.24.0",
197
197
  "moment-timezone": "0.5.45",
198
198
  "multer": "2.0.2",
199
- "mysql2": "3.15.0",
199
+ "mysql2": "3.15.1",
200
200
  "nconf": "0.13.0",
201
201
  "node-fetch": "2.7.0",
202
202
  "node-jose": "2.2.0",
@@ -225,16 +225,16 @@
225
225
  },
226
226
  "devDependencies": {
227
227
  "@actions/core": "1.11.1",
228
- "@playwright/test": "1.55.0",
228
+ "@playwright/test": "1.55.1",
229
229
  "@prettier/sync": "0.6.1",
230
230
  "@tryghost/express-test": "0.15.0",
231
231
  "@tryghost/webhook-mock-receiver": "0.2.17",
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.18.6",
235
+ "@types/node": "22.18.8",
236
236
  "@types/node-jose": "1.1.13",
237
- "@types/nodemailer": "6.4.19",
237
+ "@types/nodemailer": "6.4.20",
238
238
  "@types/sinon": "17.0.4",
239
239
  "@types/supertest": "6.0.3",
240
240
  "c8": "10.1.3",
@@ -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.2",
253
+ "mocha": "11.7.3",
254
254
  "mocha-slow-test-reporter": "0.1.2",
255
255
  "mock-knex": "TryGhost/mock-knex#68948e11b0ea4fe63456098dfdc169bea7f62009",
256
256
  "nock": "13.5.6",
@@ -264,7 +264,7 @@
264
264
  "supertest": "6.3.4",
265
265
  "tmp": "0.2.5",
266
266
  "toml": "3.0.0",
267
- "tsx": "4.20.5",
267
+ "tsx": "4.20.6",
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.10.tgz"
276
+ "@tryghost/i18n": "file:components/tryghost-i18n-6.2.0.tgz"
277
277
  },
278
278
  "nx": {
279
279
  "targets": {