ghost 5.71.2 → 5.72.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.
Files changed (156) hide show
  1. package/components/tryghost-adapter-cache-memory-ttl-5.72.1.tgz +0 -0
  2. package/components/tryghost-adapter-cache-redis-5.72.1.tgz +0 -0
  3. package/components/{tryghost-adapter-manager-5.71.2.tgz → tryghost-adapter-manager-5.72.1.tgz} +0 -0
  4. package/components/tryghost-announcement-bar-settings-5.72.1.tgz +0 -0
  5. package/components/tryghost-api-framework-5.72.1.tgz +0 -0
  6. package/components/{tryghost-api-version-compatibility-service-5.71.2.tgz → tryghost-api-version-compatibility-service-5.72.1.tgz} +0 -0
  7. package/components/{tryghost-audience-feedback-5.71.2.tgz → tryghost-audience-feedback-5.72.1.tgz} +0 -0
  8. package/components/tryghost-bookshelf-repository-5.72.1.tgz +0 -0
  9. package/components/tryghost-bootstrap-socket-5.72.1.tgz +0 -0
  10. package/components/tryghost-collections-5.72.1.tgz +0 -0
  11. package/components/{tryghost-constants-5.71.2.tgz → tryghost-constants-5.72.1.tgz} +0 -0
  12. package/components/tryghost-custom-theme-settings-service-5.72.1.tgz +0 -0
  13. package/components/{tryghost-data-generator-5.71.2.tgz → tryghost-data-generator-5.72.1.tgz} +0 -0
  14. package/components/tryghost-domain-events-5.72.1.tgz +0 -0
  15. package/components/tryghost-donations-5.72.1.tgz +0 -0
  16. package/components/tryghost-dynamic-routing-events-5.72.1.tgz +0 -0
  17. package/components/{tryghost-email-analytics-provider-mailgun-5.71.2.tgz → tryghost-email-analytics-provider-mailgun-5.72.1.tgz} +0 -0
  18. package/components/{tryghost-email-analytics-service-5.71.2.tgz → tryghost-email-analytics-service-5.72.1.tgz} +0 -0
  19. package/components/tryghost-email-content-generator-5.72.1.tgz +0 -0
  20. package/components/{tryghost-email-events-5.71.2.tgz → tryghost-email-events-5.72.1.tgz} +0 -0
  21. package/components/tryghost-email-service-5.72.1.tgz +0 -0
  22. package/components/tryghost-email-suppression-list-5.72.1.tgz +0 -0
  23. package/components/{tryghost-event-aware-cache-wrapper-5.71.2.tgz → tryghost-event-aware-cache-wrapper-5.72.1.tgz} +0 -0
  24. package/components/tryghost-express-dynamic-redirects-5.72.1.tgz +0 -0
  25. package/components/{tryghost-external-media-inliner-5.71.2.tgz → tryghost-external-media-inliner-5.72.1.tgz} +0 -0
  26. package/components/tryghost-extract-api-key-5.72.1.tgz +0 -0
  27. package/components/tryghost-html-to-plaintext-5.72.1.tgz +0 -0
  28. package/components/tryghost-i18n-5.72.1.tgz +0 -0
  29. package/components/{tryghost-importer-handler-content-files-5.71.2.tgz → tryghost-importer-handler-content-files-5.72.1.tgz} +0 -0
  30. package/components/tryghost-importer-revue-5.72.1.tgz +0 -0
  31. package/components/{tryghost-in-memory-repository-5.71.2.tgz → tryghost-in-memory-repository-5.72.1.tgz} +0 -0
  32. package/components/{tryghost-job-manager-5.71.2.tgz → tryghost-job-manager-5.72.1.tgz} +0 -0
  33. package/components/tryghost-link-redirects-5.72.1.tgz +0 -0
  34. package/components/tryghost-link-replacer-5.72.1.tgz +0 -0
  35. package/components/{tryghost-link-tracking-5.71.2.tgz → tryghost-link-tracking-5.72.1.tgz} +0 -0
  36. package/components/tryghost-magic-link-5.72.1.tgz +0 -0
  37. package/components/{tryghost-mail-events-5.71.2.tgz → tryghost-mail-events-5.72.1.tgz} +0 -0
  38. package/components/tryghost-mailgun-client-5.72.1.tgz +0 -0
  39. package/components/tryghost-member-attribution-5.72.1.tgz +0 -0
  40. package/components/{tryghost-member-events-5.71.2.tgz → tryghost-member-events-5.72.1.tgz} +0 -0
  41. package/components/tryghost-members-api-5.72.1.tgz +0 -0
  42. package/components/tryghost-members-csv-5.72.1.tgz +0 -0
  43. package/components/{tryghost-members-events-service-5.71.2.tgz → tryghost-members-events-service-5.72.1.tgz} +0 -0
  44. package/components/{tryghost-members-importer-5.71.2.tgz → tryghost-members-importer-5.72.1.tgz} +0 -0
  45. package/components/{tryghost-members-offers-5.71.2.tgz → tryghost-members-offers-5.72.1.tgz} +0 -0
  46. package/components/{tryghost-members-payments-5.71.2.tgz → tryghost-members-payments-5.72.1.tgz} +0 -0
  47. package/components/tryghost-members-ssr-5.72.1.tgz +0 -0
  48. package/components/{tryghost-members-stripe-service-5.71.2.tgz → tryghost-members-stripe-service-5.72.1.tgz} +0 -0
  49. package/components/tryghost-mentions-email-report-5.72.1.tgz +0 -0
  50. package/components/{tryghost-milestones-5.71.2.tgz → tryghost-milestones-5.72.1.tgz} +0 -0
  51. package/components/{tryghost-minifier-5.71.2.tgz → tryghost-minifier-5.72.1.tgz} +0 -0
  52. package/components/{tryghost-model-to-domain-event-interceptor-5.71.2.tgz → tryghost-model-to-domain-event-interceptor-5.72.1.tgz} +0 -0
  53. package/components/tryghost-mw-api-version-mismatch-5.72.1.tgz +0 -0
  54. package/components/tryghost-mw-cache-control-5.72.1.tgz +0 -0
  55. package/components/tryghost-mw-error-handler-5.72.1.tgz +0 -0
  56. package/components/tryghost-mw-session-from-token-5.72.1.tgz +0 -0
  57. package/components/tryghost-mw-update-user-last-seen-5.72.1.tgz +0 -0
  58. package/components/tryghost-mw-version-match-5.72.1.tgz +0 -0
  59. package/components/{tryghost-mw-vhost-5.71.2.tgz → tryghost-mw-vhost-5.72.1.tgz} +0 -0
  60. package/components/{tryghost-nql-filter-expansions-5.71.2.tgz → tryghost-nql-filter-expansions-5.72.1.tgz} +0 -0
  61. package/components/tryghost-oembed-service-5.72.1.tgz +0 -0
  62. package/components/tryghost-package-json-5.72.1.tgz +0 -0
  63. package/components/{tryghost-post-events-5.71.2.tgz → tryghost-post-events-5.72.1.tgz} +0 -0
  64. package/components/{tryghost-post-revisions-5.71.2.tgz → tryghost-post-revisions-5.72.1.tgz} +0 -0
  65. package/components/{tryghost-posts-service-5.71.2.tgz → tryghost-posts-service-5.72.1.tgz} +0 -0
  66. package/components/tryghost-recommendations-5.72.1.tgz +0 -0
  67. package/components/tryghost-referrers-5.72.1.tgz +0 -0
  68. package/components/tryghost-security-5.72.1.tgz +0 -0
  69. package/components/tryghost-session-service-5.72.1.tgz +0 -0
  70. package/components/tryghost-settings-path-manager-5.72.1.tgz +0 -0
  71. package/components/tryghost-slack-notifications-5.72.1.tgz +0 -0
  72. package/components/tryghost-staff-service-5.72.1.tgz +0 -0
  73. package/components/tryghost-stats-service-5.72.1.tgz +0 -0
  74. package/components/tryghost-tiers-5.72.1.tgz +0 -0
  75. package/components/{tryghost-update-check-service-5.71.2.tgz → tryghost-update-check-service-5.72.1.tgz} +0 -0
  76. package/components/{tryghost-verification-trigger-5.71.2.tgz → tryghost-verification-trigger-5.72.1.tgz} +0 -0
  77. package/components/{tryghost-version-notifications-data-service-5.71.2.tgz → tryghost-version-notifications-data-service-5.72.1.tgz} +0 -0
  78. package/components/{tryghost-webmentions-5.71.2.tgz → tryghost-webmentions-5.72.1.tgz} +0 -0
  79. package/core/built/admin/assets/admin-x-settings/{CodeEditorView-cf9b94da.mjs → CodeEditorView-4989db6d.mjs} +71 -71
  80. package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +2 -2
  81. package/core/built/admin/assets/admin-x-settings/{limit-service-c2ca5332.mjs → index-3bbe0fa2.mjs} +6 -6
  82. package/core/built/admin/assets/admin-x-settings/{index-2cbdc6fc.mjs → index-81eb7730.mjs} +4967 -4892
  83. package/core/built/admin/assets/admin-x-settings/{modals-3c8a0931.mjs → modals-02dc5819.mjs} +8470 -8296
  84. package/core/built/admin/assets/{chunk.143.dd3ca5f62fa39254412e.js → chunk.143.ece81e21fe21e88fd1b3.js} +5 -5
  85. package/core/built/admin/assets/{chunk.178.b44accb84bb38c10c7ba.js → chunk.178.edad244e884dcf66cedb.js} +4 -4
  86. package/core/built/admin/assets/{chunk.338.a23271afc734a6de1d3b.js → chunk.611.cbd29e2a43c5b8a0d83e.js} +3031 -3086
  87. package/core/built/admin/assets/{chunk.338.a23271afc734a6de1d3b.js.LICENSE.txt → chunk.611.cbd29e2a43c5b8a0d83e.js.LICENSE.txt} +15 -0
  88. package/core/built/admin/assets/{ghost-a43c6dbe8d871a5929a0c4ccb54d391e.js → ghost-f85471aad8d3f75d7024a9c2bf2f77bd.js} +1284 -1774
  89. package/core/built/admin/assets/koenig-lexical/index.css +1 -1
  90. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +68752 -68327
  91. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +186 -186
  92. package/core/built/admin/assets/{vendor-d4aa86641c6080ce3bc58e6d54252774.js → vendor-71d606e7533962f2cd721075ab56a66a.js} +1526 -2124
  93. package/core/built/admin/index.html +5 -5
  94. package/core/frontend/apps/private-blogging/lib/views/private.hbs +1 -1
  95. package/core/frontend/web/middleware/static-theme.js +1 -1
  96. package/core/server/api/endpoints/recommendations.js +18 -0
  97. package/core/server/data/migrations/utils/schema.js +18 -1
  98. package/core/server/data/migrations/versions/5.72/2023-10-31-11-06-00-members-created-attribution-id-index.js +3 -0
  99. package/core/server/data/migrations/versions/5.72/2023-10-31-11-06-01-members-subscription-created-attribution-id-index.js +3 -0
  100. package/core/server/data/schema/commands.js +56 -0
  101. package/core/server/data/schema/schema.js +2 -2
  102. package/core/server/services/custom-redirects/CustomRedirectsAPI.js +3 -7
  103. package/core/server/services/members/MembersConfigProvider.js +0 -4
  104. package/core/server/services/members/api.js +1 -1
  105. package/core/server/services/members/middleware.js +8 -0
  106. package/core/server/services/recommendations/RecommendationServiceWrapper.js +13 -2
  107. package/core/server/web/api/endpoints/admin/routes.js +1 -0
  108. package/core/shared/labs.js +2 -1
  109. package/core/shared/sentry.js +13 -1
  110. package/package.json +181 -181
  111. package/yarn.lock +977 -1039
  112. package/components/tryghost-adapter-cache-memory-ttl-5.71.2.tgz +0 -0
  113. package/components/tryghost-adapter-cache-redis-5.71.2.tgz +0 -0
  114. package/components/tryghost-announcement-bar-settings-5.71.2.tgz +0 -0
  115. package/components/tryghost-api-framework-5.71.2.tgz +0 -0
  116. package/components/tryghost-bookshelf-repository-5.71.2.tgz +0 -0
  117. package/components/tryghost-bootstrap-socket-5.71.2.tgz +0 -0
  118. package/components/tryghost-collections-5.71.2.tgz +0 -0
  119. package/components/tryghost-custom-theme-settings-service-5.71.2.tgz +0 -0
  120. package/components/tryghost-domain-events-5.71.2.tgz +0 -0
  121. package/components/tryghost-donations-5.71.2.tgz +0 -0
  122. package/components/tryghost-dynamic-routing-events-5.71.2.tgz +0 -0
  123. package/components/tryghost-email-content-generator-5.71.2.tgz +0 -0
  124. package/components/tryghost-email-service-5.71.2.tgz +0 -0
  125. package/components/tryghost-email-suppression-list-5.71.2.tgz +0 -0
  126. package/components/tryghost-express-dynamic-redirects-5.71.2.tgz +0 -0
  127. package/components/tryghost-extract-api-key-5.71.2.tgz +0 -0
  128. package/components/tryghost-html-to-plaintext-5.71.2.tgz +0 -0
  129. package/components/tryghost-i18n-5.71.2.tgz +0 -0
  130. package/components/tryghost-importer-revue-5.71.2.tgz +0 -0
  131. package/components/tryghost-link-redirects-5.71.2.tgz +0 -0
  132. package/components/tryghost-link-replacer-5.71.2.tgz +0 -0
  133. package/components/tryghost-magic-link-5.71.2.tgz +0 -0
  134. package/components/tryghost-mailgun-client-5.71.2.tgz +0 -0
  135. package/components/tryghost-member-attribution-5.71.2.tgz +0 -0
  136. package/components/tryghost-members-api-5.71.2.tgz +0 -0
  137. package/components/tryghost-members-csv-5.71.2.tgz +0 -0
  138. package/components/tryghost-members-ssr-5.71.2.tgz +0 -0
  139. package/components/tryghost-mentions-email-report-5.71.2.tgz +0 -0
  140. package/components/tryghost-mw-api-version-mismatch-5.71.2.tgz +0 -0
  141. package/components/tryghost-mw-cache-control-5.71.2.tgz +0 -0
  142. package/components/tryghost-mw-error-handler-5.71.2.tgz +0 -0
  143. package/components/tryghost-mw-session-from-token-5.71.2.tgz +0 -0
  144. package/components/tryghost-mw-update-user-last-seen-5.71.2.tgz +0 -0
  145. package/components/tryghost-mw-version-match-5.71.2.tgz +0 -0
  146. package/components/tryghost-oembed-service-5.71.2.tgz +0 -0
  147. package/components/tryghost-package-json-5.71.2.tgz +0 -0
  148. package/components/tryghost-recommendations-5.71.2.tgz +0 -0
  149. package/components/tryghost-referrers-5.71.2.tgz +0 -0
  150. package/components/tryghost-security-5.71.2.tgz +0 -0
  151. package/components/tryghost-session-service-5.71.2.tgz +0 -0
  152. package/components/tryghost-settings-path-manager-5.71.2.tgz +0 -0
  153. package/components/tryghost-slack-notifications-5.71.2.tgz +0 -0
  154. package/components/tryghost-staff-service-5.71.2.tgz +0 -0
  155. package/components/tryghost-stats-service-5.71.2.tgz +0 -0
  156. package/components/tryghost-tiers-5.71.2.tgz +0 -0
@@ -8,7 +8,7 @@
8
8
  <title>Ghost Admin</title>
9
9
 
10
10
 
11
- <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22cdnUrl%22%3A%22%22%2C%22editorUrl%22%3A%22%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.71%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22ember-websockets%22%3A%7B%22socketIO%22%3Atrue%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%22f960def3fe%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%22b4a426895e%22%7D" />
11
+ <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22cdnUrl%22%3A%22%22%2C%22editorUrl%22%3A%22%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.72%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22ember-websockets%22%3A%7B%22socketIO%22%3Atrue%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%227bfacb4ae6%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%2258cfaff0c3%22%7D" />
12
12
 
13
13
  <meta name="HandheldFriendly" content="True" />
14
14
  <meta name="MobileOptimized" content="320" />
@@ -56,9 +56,9 @@
56
56
 
57
57
  <div id="ember-basic-dropdown-wormhole"></div>
58
58
 
59
- <script src="assets/vendor-d4aa86641c6080ce3bc58e6d54252774.js"></script>
60
- <script src="assets/chunk.338.a23271afc734a6de1d3b.js"></script>
61
- <script src="assets/chunk.143.dd3ca5f62fa39254412e.js"></script>
62
- <script src="assets/ghost-a43c6dbe8d871a5929a0c4ccb54d391e.js"></script>
59
+ <script src="assets/vendor-71d606e7533962f2cd721075ab56a66a.js"></script>
60
+ <script src="assets/chunk.611.cbd29e2a43c5b8a0d83e.js"></script>
61
+ <script src="assets/chunk.143.ece81e21fe21e88fd1b3.js"></script>
62
+ <script src="assets/ghost-f85471aad8d3f75d7024a9c2bf2f77bd.js"></script>
63
63
  </body>
64
64
  </html>
@@ -5,7 +5,7 @@
5
5
  <meta http-equiv="Content-Type" content="text/html" charset="UTF-8" />
6
6
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
7
7
 
8
- <title>{{@site.title}} - Private Site Access</title>
8
+ <title>{{@site.title}}</title>
9
9
 
10
10
  <meta name="HandheldFriendly" content="True">
11
11
  <meta name="MobileOptimized" content="320">
@@ -45,7 +45,7 @@ function isAllowedFile(file) {
45
45
 
46
46
  const normalizedFilePath = path.normalize(decodedFilePath);
47
47
 
48
- const allowedFiles = ['manifest.json'];
48
+ const allowedFiles = ['manifest.json', 'assetlinks.json'];
49
49
  const allowedPath = '/assets/';
50
50
  const alwaysDeny = ['.hbs'];
51
51
 
@@ -48,6 +48,24 @@ module.exports = {
48
48
  }
49
49
  },
50
50
 
51
+ /**
52
+ * Fetch metadata for a recommendation URL
53
+ */
54
+ check: {
55
+ headers: {
56
+ cacheInvalidate: true
57
+ },
58
+ options: [],
59
+ validation: {},
60
+ permissions: {
61
+ // Everyone who has add permissions, can 'check'
62
+ method: 'add'
63
+ },
64
+ async query(frame) {
65
+ return await recommendations.controller.check(frame);
66
+ }
67
+ },
68
+
51
69
  edit: {
52
70
  headers: {
53
71
  cacheInvalidate: true
@@ -96,6 +96,22 @@ function createSetNullableMigration(table, column, options = {}) {
96
96
  );
97
97
  }
98
98
 
99
+ /**
100
+ * @param {string} table
101
+ * @param {string[]|string} columns One or multiple columns (in case the index should be for multiple columns)
102
+ * @returns {Migration}
103
+ */
104
+ function createAddIndexMigration(table, columns) {
105
+ return createTransactionalMigration(
106
+ async function up(knex) {
107
+ await commands.addIndex(table, columns, knex);
108
+ },
109
+ async function down(knex) {
110
+ await commands.dropIndex(table, columns, knex);
111
+ }
112
+ );
113
+ }
114
+
99
115
  /**
100
116
  * @param {string} table
101
117
  * @param {string} from
@@ -163,7 +179,8 @@ module.exports = {
163
179
  createDropColumnMigration,
164
180
  createSetNullableMigration,
165
181
  createDropNullableMigration,
166
- createRenameColumnMigration
182
+ createRenameColumnMigration,
183
+ createAddIndexMigration
167
184
  };
168
185
 
169
186
  /**
@@ -0,0 +1,3 @@
1
+ const {createAddIndexMigration} = require('../../utils');
2
+
3
+ module.exports = createAddIndexMigration('members_created_events', ['attribution_id']);
@@ -0,0 +1,3 @@
1
+ const {createAddIndexMigration} = require('../../utils');
2
+
3
+ module.exports = createAddIndexMigration('members_subscription_created_events', ['attribution_id']);
@@ -175,6 +175,60 @@ async function renameColumn(tableName, from, to, transaction = db.knex) {
175
175
  });
176
176
  }
177
177
 
178
+ /**
179
+ * Adds an non-unique index to a table over the given columns.
180
+ *
181
+ * @param {string} tableName - name of the table to add indexes to
182
+ * @param {string|string[]} columns - column(s) to add indexes for
183
+ * @param {import('knex').Knex} [transaction] - connection object containing knex reference
184
+ */
185
+ async function addIndex(tableName, columns, transaction = db.knex) {
186
+ try {
187
+ logging.info(`Adding index for '${columns}' in table '${tableName}'`);
188
+
189
+ return await transaction.schema.table(tableName, function (table) {
190
+ table.index(columns);
191
+ });
192
+ } catch (err) {
193
+ if (err.code === 'SQLITE_ERROR') {
194
+ logging.warn(`Index for '${columns}' already exists for table '${tableName}'`);
195
+ return;
196
+ }
197
+ if (err.code === 'ER_DUP_KEYNAME') {
198
+ logging.warn(`Index for '${columns}' already exists for table '${tableName}'`);
199
+ return;
200
+ }
201
+ throw err;
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Drops a non-unique index from a table over the given columns.
207
+ *
208
+ * @param {string} tableName - name of the table to remove indexes from
209
+ * @param {string|string[]} columns - column(s) to remove indexes for
210
+ * @param {import('knex').Knex} [transaction] - connection object containing knex reference
211
+ */
212
+ async function dropIndex(tableName, columns, transaction = db.knex) {
213
+ try {
214
+ logging.info(`Dropping index for '${columns}' in table '${tableName}'`);
215
+
216
+ return await transaction.schema.table(tableName, function (table) {
217
+ table.dropIndex(columns);
218
+ });
219
+ } catch (err) {
220
+ if (err.code === 'SQLITE_ERROR') {
221
+ logging.warn(`Constraint for '${columns}' does not exist for table '${tableName}'`);
222
+ return;
223
+ }
224
+ if (err.code === 'ER_CANT_DROP_FIELD_OR_KEY') {
225
+ logging.warn(`Constraint for '${columns}' does not exist for table '${tableName}'`);
226
+ return;
227
+ }
228
+ throw err;
229
+ }
230
+ }
231
+
178
232
  /**
179
233
  * Adds an unique index to a table over the given columns.
180
234
  *
@@ -535,6 +589,8 @@ module.exports = {
535
589
  getIndexes,
536
590
  addUnique,
537
591
  dropUnique,
592
+ addIndex,
593
+ dropIndex,
538
594
  addPrimaryKey,
539
595
  addForeign,
540
596
  dropForeign,
@@ -523,7 +523,7 @@ module.exports = {
523
523
  id: {type: 'string', maxlength: 24, nullable: false, primary: true},
524
524
  created_at: {type: 'dateTime', nullable: false},
525
525
  member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
526
- attribution_id: {type: 'string', maxlength: 24, nullable: true},
526
+ attribution_id: {type: 'string', maxlength: 24, nullable: true, index: true},
527
527
  attribution_type: {
528
528
  type: 'string', maxlength: 50, nullable: true, validations: {
529
529
  isIn: [['url', 'post', 'page', 'author', 'tag']]
@@ -709,7 +709,7 @@ module.exports = {
709
709
  created_at: {type: 'dateTime', nullable: false},
710
710
  member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
711
711
  subscription_id: {type: 'string', maxlength: 24, nullable: false, references: 'members_stripe_customers_subscriptions.id', cascadeDelete: true},
712
- attribution_id: {type: 'string', maxlength: 24, nullable: true},
712
+ attribution_id: {type: 'string', maxlength: 24, nullable: true, index: true},
713
713
  attribution_type: {
714
714
  type: 'string', maxlength: 50, nullable: true, validations: {
715
715
  isIn: [['url', 'post', 'page', 'author', 'tag']]
@@ -9,7 +9,7 @@ const errors = require('@tryghost/errors');
9
9
  const messages = {
10
10
  jsonParse: 'Could not parse JSON: {context}.',
11
11
  yamlParse: 'Could not parse YAML: {context}.',
12
- yamlPlainString: 'YAML input cannot be a plain string. Check the format of your YAML file.',
12
+ yamlInvalid: 'YAML input is invalid. Check the contents of your YAML file.',
13
13
  redirectsHelp: 'https://ghost.org/docs/themes/routing/#redirects',
14
14
  redirectsRegister: 'Could not register custom redirects.'
15
15
  };
@@ -78,13 +78,9 @@ const parseRedirectsFile = (content, ext) => {
78
78
  });
79
79
  }
80
80
 
81
- // yaml.load passes almost every yaml code.
82
- // Because of that, it's hard to detect if there's an error in the file.
83
- // But one of the obvious errors is the plain string output.
84
- // Here we check if the user made this mistake.
85
- if (typeof configYaml === 'string') {
81
+ if (typeof configYaml !== 'object' || configYaml === null) {
86
82
  throw new errors.BadRequestError({
87
- message: tpl(messages.yamlPlainString),
83
+ message: tpl(messages.yamlInvalid),
88
84
  help: tpl(messages.redirectsHelp)
89
85
  });
90
86
  }
@@ -35,10 +35,6 @@ class MembersConfigProvider {
35
35
  return this._settingsHelpers.getMembersSupportAddress();
36
36
  }
37
37
 
38
- getAuthEmailFromAddress() {
39
- return this.getEmailSupportAddress();
40
- }
41
-
42
38
  /**
43
39
  * @deprecated Use settingsHelpers.isStripeConnected instead
44
40
  */
@@ -47,7 +47,7 @@ function createApiInstance(config) {
47
47
  logging.warn(message.text);
48
48
  }
49
49
  let msg = Object.assign({
50
- from: config.getAuthEmailFromAddress(),
50
+ from: config.getEmailSupportAddress(),
51
51
  subject: 'Signin',
52
52
  forceTextContent: true
53
53
  }, message);
@@ -265,8 +265,16 @@ const createSessionFromMagicLink = async function createSessionFromMagicLink(req
265
265
  const ensureEndsWith = (string, endsWith) => (string.endsWith(endsWith) ? string : string + endsWith);
266
266
  const removeLeadingSlash = string => string.replace(/^\//, '');
267
267
 
268
+ // Add query parameters so the frontend can detect that the signup went fine
269
+
268
270
  const redirectUrl = new URL(removeLeadingSlash(ensureEndsWith(customRedirect, '/')), ensureEndsWith(baseUrl, '/'));
269
271
 
272
+ if (urlUtils.isSiteUrl(redirectUrl)) {
273
+ // Add only for non-external URLs
274
+ redirectUrl.searchParams.set('success', 'true');
275
+ redirectUrl.searchParams.set('action', 'signup');
276
+ }
277
+
270
278
  return res.redirect(redirectUrl.href);
271
279
  }
272
280
  }
@@ -50,6 +50,7 @@ class RecommendationServiceWrapper {
50
50
  const sentry = require('../../../shared/sentry');
51
51
  const settings = require('../settings');
52
52
  const RecommendationEnablerService = require('./RecommendationEnablerService');
53
+
53
54
  const {
54
55
  BookshelfRecommendationRepository,
55
56
  RecommendationService,
@@ -58,7 +59,8 @@ class RecommendationServiceWrapper {
58
59
  BookshelfClickEventRepository,
59
60
  IncomingRecommendationController,
60
61
  IncomingRecommendationService,
61
- IncomingRecommendationEmailRenderer
62
+ IncomingRecommendationEmailRenderer,
63
+ RecommendationMetadataService
62
64
  } = require('@tryghost/recommendations');
63
65
 
64
66
  const mentions = require('../mentions');
@@ -87,13 +89,22 @@ class RecommendationServiceWrapper {
87
89
  sentry
88
90
  });
89
91
 
92
+ const oembedService = require('../oembed');
93
+ const externalRequest = require('../../../server/lib/request-external.js');
94
+
95
+ const recommendationMetadataService = new RecommendationMetadataService({
96
+ oembedService,
97
+ externalRequest
98
+ });
99
+
90
100
  this.service = new RecommendationService({
91
101
  repository: this.repository,
92
102
  recommendationEnablerService,
93
103
  wellknownService,
94
104
  mentionSendingService: mentions.sendingService,
95
105
  clickEventRepository: this.clickEventRepository,
96
- subscribeEventRepository: this.subscribeEventRepository
106
+ subscribeEventRepository: this.subscribeEventRepository,
107
+ recommendationMetadataService
97
108
  });
98
109
 
99
110
  const mail = require('../mail');
@@ -351,6 +351,7 @@ module.exports = function apiRoutes() {
351
351
  router.get('/recommendations', mw.authAdminApi, http(api.recommendations.browse));
352
352
  router.get('/recommendations/:id', mw.authAdminApi, http(api.recommendations.read));
353
353
  router.post('/recommendations', mw.authAdminApi, http(api.recommendations.add));
354
+ router.post('/recommendations/check', mw.authAdminApi, http(api.recommendations.check));
354
355
  router.put('/recommendations/:id', mw.authAdminApi, http(api.recommendations.edit));
355
356
  router.del('/recommendations/:id', mw.authAdminApi, http(api.recommendations.destroy));
356
357
 
@@ -43,7 +43,8 @@ const ALPHA_FEATURES = [
43
43
  'importMemberTier',
44
44
  'lexicalIndicators',
45
45
  'listUnsubscribeHeader',
46
- 'editorEmojiPicker'
46
+ 'editorEmojiPicker',
47
+ 'adminXOffers'
47
48
  ];
48
49
 
49
50
  module.exports.GA_KEYS = [...GA_FEATURES];
@@ -22,13 +22,25 @@ if (sentryConfig && !sentryConfig.disabled) {
22
22
  event.exception.values[0].type = exception.context;
23
23
  }
24
24
 
25
+ // This is a mysql2 error — add some additional context
26
+ if (exception.sql) {
27
+ event.exception.values[0].type = `SQL Error ${exception.errno}: ${exception.sqlErrorCode}`;
28
+ event.exception.values[0].value = exception.sqlMessage;
29
+ event.contexts.mysql = {
30
+ errno: exception.errno,
31
+ code: exception.sqlErrorCode,
32
+ sql: exception.sql,
33
+ message: exception.sqlMessage,
34
+ state: exception.sqlState
35
+ };
36
+ }
37
+
25
38
  // This is a Ghost Error, copy all our extra data to tags
26
39
  event.tags.type = exception.errorType;
27
40
  event.tags.code = exception.code;
28
41
  event.tags.id = exception.id;
29
42
  event.tags.statusCode = exception.statusCode;
30
43
  }
31
-
32
44
  return event;
33
45
  }
34
46
  });