ghost 5.77.0 → 5.79.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 (176) hide show
  1. package/components/tryghost-adapter-cache-memory-ttl-5.79.0.tgz +0 -0
  2. package/components/tryghost-adapter-cache-redis-5.79.0.tgz +0 -0
  3. package/components/tryghost-adapter-manager-5.79.0.tgz +0 -0
  4. package/components/tryghost-announcement-bar-settings-5.79.0.tgz +0 -0
  5. package/components/tryghost-api-framework-5.79.0.tgz +0 -0
  6. package/components/tryghost-api-version-compatibility-service-5.79.0.tgz +0 -0
  7. package/components/tryghost-audience-feedback-5.79.0.tgz +0 -0
  8. package/components/tryghost-bookshelf-repository-5.79.0.tgz +0 -0
  9. package/components/tryghost-bootstrap-socket-5.79.0.tgz +0 -0
  10. package/components/tryghost-collections-5.79.0.tgz +0 -0
  11. package/components/tryghost-constants-5.79.0.tgz +0 -0
  12. package/components/tryghost-custom-theme-settings-service-5.79.0.tgz +0 -0
  13. package/components/{tryghost-data-generator-5.77.0.tgz → tryghost-data-generator-5.79.0.tgz} +0 -0
  14. package/components/tryghost-domain-events-5.79.0.tgz +0 -0
  15. package/components/tryghost-donations-5.79.0.tgz +0 -0
  16. package/components/tryghost-dynamic-routing-events-5.79.0.tgz +0 -0
  17. package/components/{tryghost-email-addresses-5.77.0.tgz → tryghost-email-addresses-5.79.0.tgz} +0 -0
  18. package/components/tryghost-email-analytics-provider-mailgun-5.79.0.tgz +0 -0
  19. package/components/tryghost-email-analytics-service-5.79.0.tgz +0 -0
  20. package/components/tryghost-email-content-generator-5.79.0.tgz +0 -0
  21. package/components/tryghost-email-events-5.79.0.tgz +0 -0
  22. package/components/tryghost-email-service-5.79.0.tgz +0 -0
  23. package/components/tryghost-email-suppression-list-5.79.0.tgz +0 -0
  24. package/components/tryghost-express-dynamic-redirects-5.79.0.tgz +0 -0
  25. package/components/tryghost-external-media-inliner-5.79.0.tgz +0 -0
  26. package/components/tryghost-extract-api-key-5.79.0.tgz +0 -0
  27. package/components/tryghost-html-to-plaintext-5.79.0.tgz +0 -0
  28. package/components/tryghost-i18n-5.79.0.tgz +0 -0
  29. package/components/tryghost-importer-handler-content-files-5.79.0.tgz +0 -0
  30. package/components/{tryghost-importer-revue-5.77.0.tgz → tryghost-importer-revue-5.79.0.tgz} +0 -0
  31. package/components/{tryghost-in-memory-repository-5.77.0.tgz → tryghost-in-memory-repository-5.79.0.tgz} +0 -0
  32. package/components/tryghost-job-manager-5.79.0.tgz +0 -0
  33. package/components/tryghost-link-redirects-5.79.0.tgz +0 -0
  34. package/components/tryghost-link-replacer-5.79.0.tgz +0 -0
  35. package/components/tryghost-link-tracking-5.79.0.tgz +0 -0
  36. package/components/{tryghost-magic-link-5.77.0.tgz → tryghost-magic-link-5.79.0.tgz} +0 -0
  37. package/components/tryghost-mail-events-5.79.0.tgz +0 -0
  38. package/components/tryghost-mailgun-client-5.79.0.tgz +0 -0
  39. package/components/tryghost-member-attribution-5.79.0.tgz +0 -0
  40. package/components/tryghost-member-events-5.79.0.tgz +0 -0
  41. package/components/tryghost-members-api-5.79.0.tgz +0 -0
  42. package/components/tryghost-members-csv-5.79.0.tgz +0 -0
  43. package/components/tryghost-members-events-service-5.79.0.tgz +0 -0
  44. package/components/{tryghost-members-importer-5.77.0.tgz → tryghost-members-importer-5.79.0.tgz} +0 -0
  45. package/components/tryghost-members-offers-5.79.0.tgz +0 -0
  46. package/components/tryghost-members-payments-5.79.0.tgz +0 -0
  47. package/components/tryghost-members-ssr-5.79.0.tgz +0 -0
  48. package/components/{tryghost-members-stripe-service-5.77.0.tgz → tryghost-members-stripe-service-5.79.0.tgz} +0 -0
  49. package/components/tryghost-mentions-email-report-5.79.0.tgz +0 -0
  50. package/components/tryghost-milestones-5.79.0.tgz +0 -0
  51. package/components/{tryghost-minifier-5.77.0.tgz → tryghost-minifier-5.79.0.tgz} +0 -0
  52. package/components/tryghost-model-to-domain-event-interceptor-5.79.0.tgz +0 -0
  53. package/components/tryghost-mw-api-version-mismatch-5.79.0.tgz +0 -0
  54. package/components/tryghost-mw-cache-control-5.79.0.tgz +0 -0
  55. package/components/tryghost-mw-error-handler-5.79.0.tgz +0 -0
  56. package/components/tryghost-mw-session-from-token-5.79.0.tgz +0 -0
  57. package/components/tryghost-mw-update-user-last-seen-5.79.0.tgz +0 -0
  58. package/components/tryghost-mw-version-match-5.79.0.tgz +0 -0
  59. package/components/tryghost-mw-vhost-5.79.0.tgz +0 -0
  60. package/components/tryghost-nql-filter-expansions-5.79.0.tgz +0 -0
  61. package/components/tryghost-oembed-service-5.79.0.tgz +0 -0
  62. package/components/tryghost-package-json-5.79.0.tgz +0 -0
  63. package/components/tryghost-post-events-5.79.0.tgz +0 -0
  64. package/components/tryghost-post-revisions-5.79.0.tgz +0 -0
  65. package/components/tryghost-posts-service-5.79.0.tgz +0 -0
  66. package/components/tryghost-recommendations-5.79.0.tgz +0 -0
  67. package/components/tryghost-referrers-5.79.0.tgz +0 -0
  68. package/components/tryghost-security-5.79.0.tgz +0 -0
  69. package/components/tryghost-session-service-5.79.0.tgz +0 -0
  70. package/components/tryghost-settings-path-manager-5.79.0.tgz +0 -0
  71. package/components/tryghost-slack-notifications-5.79.0.tgz +0 -0
  72. package/components/{tryghost-staff-service-5.77.0.tgz → tryghost-staff-service-5.79.0.tgz} +0 -0
  73. package/components/tryghost-stats-service-5.79.0.tgz +0 -0
  74. package/components/tryghost-tiers-5.79.0.tgz +0 -0
  75. package/components/tryghost-update-check-service-5.79.0.tgz +0 -0
  76. package/components/{tryghost-verification-trigger-5.77.0.tgz → tryghost-verification-trigger-5.79.0.tgz} +0 -0
  77. package/components/tryghost-version-notifications-data-service-5.79.0.tgz +0 -0
  78. package/components/tryghost-webmentions-5.79.0.tgz +0 -0
  79. package/core/boot.js +1 -9
  80. package/core/built/admin/assets/admin-x-demo/admin-x-demo.js +1 -1
  81. package/core/built/admin/assets/admin-x-demo/{index-eee22c84.mjs → index-0722d3aa.mjs} +3 -3
  82. package/core/built/admin/assets/admin-x-demo/{modals-e4b12fba.mjs → modals-969dbbd3.mjs} +2 -2
  83. package/core/built/admin/assets/admin-x-settings/{CodeEditorView-c3b34635.mjs → CodeEditorView-ca5a31fd.mjs} +2 -2
  84. package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +1 -1
  85. package/core/built/admin/assets/admin-x-settings/{index-8808e054.mjs → index-60a33701.mjs} +2 -2
  86. package/core/built/admin/assets/admin-x-settings/{index-99bd65a2.mjs → index-f874baf8.mjs} +5 -4
  87. package/core/built/admin/assets/admin-x-settings/{modals-f491bd44.mjs → modals-7dbf838b.mjs} +3 -3
  88. package/core/built/admin/assets/{chunk.664.99d0951b803d7ec00d8b.js → chunk.160.f064da019dcb493ba212.js} +2737 -2737
  89. package/core/built/admin/assets/chunk.524.fe4143d694c7ab008a78.js +35 -0
  90. package/core/built/admin/assets/{chunk.178.09e01be47e7daa486981.js → chunk.92.812dc089b3ac289f0866.js} +4 -4
  91. package/core/built/admin/assets/{ghost-90accaf45b85c7bc55ca781472d27e8e.js → ghost-74787e16c2e5229de6164ddfe7426882.js} +18 -17
  92. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +5009 -4993
  93. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +79 -79
  94. package/core/built/admin/index.html +4 -4
  95. package/core/server/data/db/ConnectionPoolInstrumentation.js +142 -0
  96. package/core/server/data/db/backup.js +7 -1
  97. package/core/server/data/db/connection.js +6 -0
  98. package/core/server/data/db/index.js +9 -0
  99. package/core/server/data/migrations/versions/5.79/2024-01-30-19-36-44-fix-discrepancy-in-free-tier-visibility.js +43 -0
  100. package/core/server/services/Users.js +6 -2
  101. package/core/shared/config/defaults.json +2 -1
  102. package/core/shared/labs.js +2 -2
  103. package/package.json +150 -151
  104. package/yarn.lock +351 -353
  105. package/components/tryghost-adapter-cache-memory-ttl-5.77.0.tgz +0 -0
  106. package/components/tryghost-adapter-cache-redis-5.77.0.tgz +0 -0
  107. package/components/tryghost-adapter-manager-5.77.0.tgz +0 -0
  108. package/components/tryghost-announcement-bar-settings-5.77.0.tgz +0 -0
  109. package/components/tryghost-api-framework-5.77.0.tgz +0 -0
  110. package/components/tryghost-api-version-compatibility-service-5.77.0.tgz +0 -0
  111. package/components/tryghost-audience-feedback-5.77.0.tgz +0 -0
  112. package/components/tryghost-bookshelf-repository-5.77.0.tgz +0 -0
  113. package/components/tryghost-bootstrap-socket-5.77.0.tgz +0 -0
  114. package/components/tryghost-collections-5.77.0.tgz +0 -0
  115. package/components/tryghost-constants-5.77.0.tgz +0 -0
  116. package/components/tryghost-custom-theme-settings-service-5.77.0.tgz +0 -0
  117. package/components/tryghost-domain-events-5.77.0.tgz +0 -0
  118. package/components/tryghost-donations-5.77.0.tgz +0 -0
  119. package/components/tryghost-dynamic-routing-events-5.77.0.tgz +0 -0
  120. package/components/tryghost-email-analytics-provider-mailgun-5.77.0.tgz +0 -0
  121. package/components/tryghost-email-analytics-service-5.77.0.tgz +0 -0
  122. package/components/tryghost-email-content-generator-5.77.0.tgz +0 -0
  123. package/components/tryghost-email-events-5.77.0.tgz +0 -0
  124. package/components/tryghost-email-service-5.77.0.tgz +0 -0
  125. package/components/tryghost-email-suppression-list-5.77.0.tgz +0 -0
  126. package/components/tryghost-express-dynamic-redirects-5.77.0.tgz +0 -0
  127. package/components/tryghost-external-media-inliner-5.77.0.tgz +0 -0
  128. package/components/tryghost-extract-api-key-5.77.0.tgz +0 -0
  129. package/components/tryghost-html-to-plaintext-5.77.0.tgz +0 -0
  130. package/components/tryghost-i18n-5.77.0.tgz +0 -0
  131. package/components/tryghost-importer-handler-content-files-5.77.0.tgz +0 -0
  132. package/components/tryghost-job-manager-5.77.0.tgz +0 -0
  133. package/components/tryghost-link-redirects-5.77.0.tgz +0 -0
  134. package/components/tryghost-link-replacer-5.77.0.tgz +0 -0
  135. package/components/tryghost-link-tracking-5.77.0.tgz +0 -0
  136. package/components/tryghost-mail-events-5.77.0.tgz +0 -0
  137. package/components/tryghost-mailgun-client-5.77.0.tgz +0 -0
  138. package/components/tryghost-member-attribution-5.77.0.tgz +0 -0
  139. package/components/tryghost-member-events-5.77.0.tgz +0 -0
  140. package/components/tryghost-members-api-5.77.0.tgz +0 -0
  141. package/components/tryghost-members-csv-5.77.0.tgz +0 -0
  142. package/components/tryghost-members-events-service-5.77.0.tgz +0 -0
  143. package/components/tryghost-members-offers-5.77.0.tgz +0 -0
  144. package/components/tryghost-members-payments-5.77.0.tgz +0 -0
  145. package/components/tryghost-members-ssr-5.77.0.tgz +0 -0
  146. package/components/tryghost-mentions-email-report-5.77.0.tgz +0 -0
  147. package/components/tryghost-milestones-5.77.0.tgz +0 -0
  148. package/components/tryghost-model-to-domain-event-interceptor-5.77.0.tgz +0 -0
  149. package/components/tryghost-mw-api-version-mismatch-5.77.0.tgz +0 -0
  150. package/components/tryghost-mw-cache-control-5.77.0.tgz +0 -0
  151. package/components/tryghost-mw-error-handler-5.77.0.tgz +0 -0
  152. package/components/tryghost-mw-session-from-token-5.77.0.tgz +0 -0
  153. package/components/tryghost-mw-update-user-last-seen-5.77.0.tgz +0 -0
  154. package/components/tryghost-mw-version-match-5.77.0.tgz +0 -0
  155. package/components/tryghost-mw-vhost-5.77.0.tgz +0 -0
  156. package/components/tryghost-nql-filter-expansions-5.77.0.tgz +0 -0
  157. package/components/tryghost-oembed-service-5.77.0.tgz +0 -0
  158. package/components/tryghost-package-json-5.77.0.tgz +0 -0
  159. package/components/tryghost-post-events-5.77.0.tgz +0 -0
  160. package/components/tryghost-post-revisions-5.77.0.tgz +0 -0
  161. package/components/tryghost-posts-service-5.77.0.tgz +0 -0
  162. package/components/tryghost-recommendations-5.77.0.tgz +0 -0
  163. package/components/tryghost-referrers-5.77.0.tgz +0 -0
  164. package/components/tryghost-security-5.77.0.tgz +0 -0
  165. package/components/tryghost-session-service-5.77.0.tgz +0 -0
  166. package/components/tryghost-settings-path-manager-5.77.0.tgz +0 -0
  167. package/components/tryghost-slack-notifications-5.77.0.tgz +0 -0
  168. package/components/tryghost-stats-service-5.77.0.tgz +0 -0
  169. package/components/tryghost-tiers-5.77.0.tgz +0 -0
  170. package/components/tryghost-update-check-service-5.77.0.tgz +0 -0
  171. package/components/tryghost-version-notifications-data-service-5.77.0.tgz +0 -0
  172. package/components/tryghost-webmentions-5.77.0.tgz +0 -0
  173. package/core/built/admin/assets/chunk.143.16228699a88898175515.js +0 -35
  174. package/core/server/services/segment/DomainEventsAnalytics.js +0 -109
  175. package/core/server/services/segment/index.js +0 -25
  176. /package/core/built/admin/assets/{chunk.664.99d0951b803d7ec00d8b.js.LICENSE.txt → chunk.160.f064da019dcb493ba212.js.LICENSE.txt} +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.77%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%22a9a58f16a8%22%2C%22adminXDemoFilename%22%3A%22admin-x-demo.js%22%2C%22adminXDemoHash%22%3A%2233d2c60ad0%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%22d07813452e%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.79%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%22f32944c498%22%2C%22adminXDemoFilename%22%3A%22admin-x-demo.js%22%2C%22adminXDemoHash%22%3A%22b6e4cf34df%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%22e10c30afc5%22%7D" />
12
12
 
13
13
  <meta name="HandheldFriendly" content="True" />
14
14
  <meta name="MobileOptimized" content="320" />
@@ -57,8 +57,8 @@
57
57
  <div id="ember-basic-dropdown-wormhole"></div>
58
58
 
59
59
  <script src="assets/vendor-6b83da0c6e6fede335947219c59e1543.js"></script>
60
- <script src="assets/chunk.664.99d0951b803d7ec00d8b.js"></script>
61
- <script src="assets/chunk.143.16228699a88898175515.js"></script>
62
- <script src="assets/ghost-90accaf45b85c7bc55ca781472d27e8e.js"></script>
60
+ <script src="assets/chunk.160.f064da019dcb493ba212.js"></script>
61
+ <script src="assets/chunk.524.fe4143d694c7ab008a78.js"></script>
62
+ <script src="assets/ghost-74787e16c2e5229de6164ddfe7426882.js"></script>
63
63
  </body>
64
64
  </html>
@@ -0,0 +1,142 @@
1
+ class ConnectionPoolInstrumentation {
2
+ constructor({knex, logging, metrics, config}) {
3
+ this.knex = knex;
4
+ this.pool = this.knex.client.pool;
5
+ this.logging = logging;
6
+ this.metrics = metrics;
7
+ this.enabled = config.get('telemetry:connectionPool');
8
+ this.requestStartTimes = {};
9
+ this.createStartTimes = {};
10
+ }
11
+
12
+ // Get the number of pending creates and acquires plus free and used connections
13
+ getPoolMetrics() {
14
+ return {
15
+ numPendingCreates: this.pool.numPendingCreates(),
16
+ numPendingAcquires: this.pool.numPendingAcquires(),
17
+ numFree: this.pool.numFree(),
18
+ numUsed: this.pool.numUsed()
19
+ };
20
+ }
21
+
22
+ handleCreateRequest(eventId) {
23
+ // Track when the request was submitted for calculating wait time
24
+ this.createStartTimes[eventId] = Date.now();
25
+ const poolMetrics = this.getPoolMetrics();
26
+ this.logging.debug(`[ConnectionPool] Creating a connection. EventID: ${eventId} Pending Creates: ${poolMetrics.numPendingCreates} Free: ${poolMetrics.numFree} Used: ${poolMetrics.numUsed}`);
27
+ this.metrics.metric('metrics-connection-pool', {
28
+ event: 'create-request',
29
+ eventId,
30
+ ...poolMetrics
31
+ });
32
+ }
33
+
34
+ handleCreateSuccess(eventId, resource) {
35
+ // Calculate the time it took to create the connection
36
+ const timeToCreate = Date.now() - this.createStartTimes[eventId];
37
+ // Delete the start time so we don't leak memory
38
+ delete this.createStartTimes[eventId];
39
+ this.logging.debug(`[ConnectionPool] Created a connection. EventID: ${eventId} Connection ID: ${resource.connectionId} Knex ID: ${resource.__knexUid} Time to Create: ${timeToCreate}ms`);
40
+ this.metrics.metric('metrics-connection-pool', {
41
+ event: 'create-success',
42
+ eventId,
43
+ connectionId: resource.connectionId,
44
+ knexUid: resource.__knexUid,
45
+ timeToCreate
46
+ });
47
+ }
48
+
49
+ handleCreateFail(eventId, err) {
50
+ // Calculate the time it took to create the connection
51
+ const timeToFail = Date.now() - this.createStartTimes[eventId];
52
+ // Delete the start time so we don't leak memory
53
+ delete this.createStartTimes[eventId];
54
+ const poolMetrics = this.getPoolMetrics();
55
+ this.logging.error(`[ConnectionPool] Failed to create a connection. EventID: ${eventId} Time to Create: ${timeToFail}ms`, err);
56
+ this.metrics.metric('metrics-connection-pool', {
57
+ event: 'create-fail',
58
+ eventId,
59
+ timeToFail,
60
+ ...poolMetrics
61
+ });
62
+ }
63
+
64
+ handleAcquireRequest(eventId) {
65
+ // Track when the request was submitted for calculating wait time
66
+ this.requestStartTimes[eventId] = Date.now();
67
+ const poolMetrics = this.getPoolMetrics();
68
+ this.logging.debug(`[ConnectionPool] Acquiring a connection. EventID: ${eventId} Pending Acquires: ${poolMetrics.numPendingAcquires} Free: ${poolMetrics.numFree} Used: ${poolMetrics.numUsed}`);
69
+ this.metrics.metric('metrics-connection-pool', {
70
+ event: 'acquire-request',
71
+ eventId,
72
+ ...poolMetrics
73
+ });
74
+ }
75
+
76
+ handleAcquireSuccess(eventId, resource) {
77
+ // Calculate the time it took to acquire the connection
78
+ const timeToAcquire = Date.now() - this.requestStartTimes[eventId];
79
+ // Delete the start time so we don't leak memory
80
+ delete this.requestStartTimes[eventId];
81
+ // Track when the connection was acquired for calculating lifetime upon release
82
+ resource.connectionAcquired = Date.now();
83
+ this.logging.debug(`[ConnectionPool] Acquired a connection. EventID: ${eventId} Connection ID: ${resource.connectionId} Knex Id: ${resource.__knexUid} Time to Acquire: ${timeToAcquire}ms`);
84
+ this.metrics.metric('metrics-connection-pool', {
85
+ event: 'acquire-success',
86
+ eventId,
87
+ connectionId: resource.connectionId,
88
+ knexUid: resource.__knexUid,
89
+ timeToAcquire
90
+ });
91
+ }
92
+
93
+ handleAcquireFail(eventId, err) {
94
+ // Calculate the time it took to acquire the connection
95
+ const timeToFail = Date.now() - this.requestStartTimes[eventId];
96
+ // Delete the start time so we don't leak memory
97
+ delete this.requestStartTimes[eventId];
98
+ const poolMetrics = this.getPoolMetrics();
99
+ this.logging.error(`[ConnectionPool] Failed to acquire a connection. EventID: ${eventId} Time to Acquire: ${timeToFail}ms`, err);
100
+ this.metrics.metric('metrics-connection-pool', {
101
+ event: 'acquire-fail',
102
+ eventId,
103
+ timeToFail,
104
+ ...poolMetrics
105
+ });
106
+ }
107
+
108
+ handleRelease(resource) {
109
+ const lifetime = Date.now() - resource.connectionAcquired;
110
+ this.logging.debug(`[ConnectionPool] Released a connection. Connection ID: ${resource.connectionId} Lifetime: ${lifetime}ms`);
111
+ this.metrics.metric('metrics-connection-pool', {
112
+ event: 'release',
113
+ connectionId: resource.connectionId,
114
+ knexUid: resource.__knexUid,
115
+ lifetime
116
+ });
117
+ }
118
+
119
+ instrument() {
120
+ if (this.enabled) {
121
+ // Check to make sure these event listeners haven't already been added
122
+ if (this.pool.emitter.eventNames().length === 0) {
123
+ // Fires when a connection is requested to be created
124
+ this.pool.on('createRequest', eventId => this.handleCreateRequest(eventId));
125
+ // Fires when a connection is successfully created
126
+ this.pool.on('createSuccess', (eventId, resource) => this.handleCreateSuccess(eventId, resource));
127
+ // Fires when a connection fails to be created
128
+ this.pool.on('createFail', (eventId, err) => this.handleCreateFail(eventId, err));
129
+ // Fires when a connection is requested from the pool
130
+ this.pool.on('acquireRequest', eventId => this.handleAcquireRequest(eventId));
131
+ // Fires when a connection is allocated from the pool
132
+ this.pool.on('acquireSuccess', (eventId, resource) => this.handleAcquireSuccess(eventId, resource));
133
+ // Fires when a connection fails to be allocated from the pool
134
+ this.pool.on('acquireFail', (eventId, err) => this.handleAcquireFail(eventId, err));
135
+ // Fires when a connection is released back into the pool
136
+ this.pool.on('release', resource => this.handleRelease(resource));
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ module.exports = ConnectionPoolInstrumentation;
@@ -42,9 +42,15 @@ const readBackup = async (filename) => {
42
42
  * Does an export, and stores this in a local file
43
43
  *
44
44
  * @param {Object} options
45
- * @returns {Promise<String>}
45
+ * @returns {Promise<String> | null}
46
46
  */
47
47
  const backup = async function backup(options = {}) {
48
+ // do not create backup if disabled in config (this is intended for large customers who will OOM node)
49
+ if (config.get('disableJSBackups')) {
50
+ logging.info('Database backup is disabled in Ghost config');
51
+ return null;
52
+ }
53
+
48
54
  logging.info('Creating database backup');
49
55
 
50
56
  const filename = await exporter.fileName(options);
@@ -4,8 +4,10 @@ const os = require('os');
4
4
  const fs = require('fs');
5
5
 
6
6
  const logging = require('@tryghost/logging');
7
+ const metrics = require('@tryghost/metrics');
7
8
  const config = require('../../../shared/config');
8
9
  const errors = require('@tryghost/errors');
10
+ const ConnectionPoolInstrumentation = require('./ConnectionPoolInstrumentation');
9
11
  let knexInstance;
10
12
 
11
13
  // @TODO:
@@ -62,6 +64,10 @@ function configure(dbConfig) {
62
64
 
63
65
  if (!knexInstance && config.get('database') && config.get('database').client) {
64
66
  knexInstance = knex(configure(config.get('database')));
67
+ if (config.get('telemetry:connectionPool')) {
68
+ const instrumentation = new ConnectionPoolInstrumentation({knex: knexInstance, logging, metrics, config});
69
+ instrumentation.instrument();
70
+ }
65
71
  }
66
72
 
67
73
  module.exports = knexInstance;
@@ -1,3 +1,8 @@
1
+ const logging = require('@tryghost/logging');
2
+ const metrics = require('@tryghost/metrics');
3
+ const config = require('../../../shared/config');
4
+ const ConnectionPoolInstrumentation = require('./ConnectionPoolInstrumentation');
5
+
1
6
  let connection;
2
7
 
3
8
  Object.defineProperty(exports, 'knex', {
@@ -5,6 +10,10 @@ Object.defineProperty(exports, 'knex', {
5
10
  configurable: true,
6
11
  get: function get() {
7
12
  connection = connection || require('./connection');
13
+ if (config.get('telemetry:connectionPool')) {
14
+ const instrumentation = new ConnectionPoolInstrumentation({knex: connection, logging, metrics, config});
15
+ instrumentation.instrument();
16
+ }
8
17
  return connection;
9
18
  }
10
19
  });
@@ -0,0 +1,43 @@
1
+ const {createTransactionalMigration} = require('../../utils');
2
+ const logging = require('@tryghost/logging');
3
+
4
+ module.exports = createTransactionalMigration(
5
+ async function up(knex) {
6
+ const portalPlansRaw = await knex('settings').select('value').where('key', 'portal_plans').first();
7
+ const freeTier = await knex('products').select('visibility').where('type', 'free').first();
8
+
9
+ if (!portalPlansRaw || !freeTier) {
10
+ logging.warn('Could not read `portal_plans` setting or `visibility` of the free tier - skipping migration');
11
+ return;
12
+ }
13
+
14
+ let portalPlans;
15
+ try {
16
+ portalPlans = JSON.parse(portalPlansRaw.value);
17
+
18
+ if (!portalPlans || !Array.isArray(portalPlans)) {
19
+ logging.warn('`portal_plans` setting is not valid - skipping migration');
20
+ return;
21
+ }
22
+ } catch (err) {
23
+ logging.warn('Could not parse `portal_plans` setting - skipping migration');
24
+ return;
25
+ }
26
+
27
+ if (portalPlans.includes('free') && freeTier.visibility === 'none') {
28
+ logging.info('`portal_plans` setting contains "free", but free tier is not visible - updating free tier visibility to public');
29
+ await knex('products').update('visibility', 'public').where('type', 'free');
30
+ return;
31
+ }
32
+
33
+ if (!portalPlans.includes('free') && freeTier.visibility === 'public') {
34
+ logging.info('`portal_plans` setting does not contain "free", but free tier is visible - updating free tier visibility to none');
35
+ await knex('products').update('visibility', 'none').where('type', 'free');
36
+ return;
37
+ }
38
+ },
39
+
40
+ async function down() {
41
+ // noop, as this is a data discrepancy fix
42
+ }
43
+ );
@@ -148,9 +148,13 @@ class Users {
148
148
  * @returns
149
149
  */
150
150
  async destroyUser(frameOptions) {
151
+ let filename = null;
151
152
  const backupPath = await this.dbBackup.backup();
152
- const parsedFileName = path.parse(backupPath);
153
- const filename = `${parsedFileName.name}${parsedFileName.ext}`;
153
+
154
+ if (backupPath) {
155
+ const parsedFileName = path.parse(backupPath);
156
+ filename = `${parsedFileName.name}${parsedFileName.ext}`;
157
+ }
154
158
 
155
159
  return this.models.Base.transaction(async (t) => {
156
160
  frameOptions.transacting = t;
@@ -228,5 +228,6 @@
228
228
  },
229
229
  "bulkEmail": {
230
230
  "batchSize": 1000
231
- }
231
+ },
232
+ "disableJSBackups": false
232
233
  }
@@ -22,7 +22,8 @@ const GA_FEATURES = [
22
22
  'signupForm',
23
23
  'recommendations',
24
24
  'listUnsubscribeHeader',
25
- 'filterEmailDisabled'
25
+ 'filterEmailDisabled',
26
+ 'newEmailAddresses'
26
27
  ];
27
28
 
28
29
  // NOTE: this allowlist is meant to be used to filter out any unexpected
@@ -47,7 +48,6 @@ const ALPHA_FEATURES = [
47
48
  // 'adminXOffers',
48
49
  'filterEmailDisabled',
49
50
  'adminXDemo',
50
- 'newEmailAddresses',
51
51
  'portalImprovements'
52
52
  ];
53
53