ghost 4.18.0 → 4.20.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 (237) hide show
  1. package/.eslintrc.js +9 -8
  2. package/Gruntfile.js +1 -1
  3. package/PRIVACY.md +3 -0
  4. package/content/adapters/README.md +2 -2
  5. package/core/boot.js +17 -12
  6. package/core/bridge.js +9 -1
  7. package/core/built/assets/{chunk.3.b80d3e1e6b8556aaff3c.js → chunk.3.777d43e2ce954ba8b2f5.js} +25 -25
  8. package/core/built/assets/codemirror/{codemirror-21a09582262987037db73b152fb35f7c.js → codemirror-d25c379b87ec8b33d54ac7149bc0b6ae.js} +14 -14
  9. package/core/built/assets/ghost-dark-20e2892d4f30d0d1183c9ac725ea37d0.css +1 -0
  10. package/core/built/assets/{ghost.min-88d647a008a5b1dd678a89ae1e55c038.js → ghost.min-26e427944e719b616b8dc7fbb3bbd2f9.js} +709 -422
  11. package/core/built/assets/ghost.min-57e46fd3b1145ecf2cbd185a13611f3b.css +1 -0
  12. package/core/built/assets/icons/arrow-left-small.svg +0 -4
  13. package/core/built/assets/icons/paintbrush.svg +10 -1
  14. package/core/built/assets/icons/post.svg +3 -1
  15. package/core/built/assets/img/footer-marketplace-bg-572b6c6486a7e26316954d599eaa9f30.png +0 -0
  16. package/core/built/assets/img/marketing/offers-1-f2e1b653c4d5bb90eea9d7a2862530f9.jpg +0 -0
  17. package/core/built/assets/img/marketing/offers-2-28a225d34cc39d133748431536961d00.jpg +0 -0
  18. package/core/built/assets/img/marketing/offers-3-2094c91ab21a16c37fbe6ec16c140160.jpg +0 -0
  19. package/core/built/assets/img/themes/Alto-f4db5af43ca9771c7ac1f754de3ddf2f.png +0 -0
  20. package/core/built/assets/img/themes/Bulletin-57d45b992ff0e26e0acdce7ed4cccd67.png +0 -0
  21. package/core/built/assets/img/themes/Casper-c7e784d7188cc5d7f097d9b6c97b0263.jpg +0 -0
  22. package/core/built/assets/img/themes/Dawn-be81aa8c8caae8fcfb5d5fbec823fdcc.png +0 -0
  23. package/core/built/assets/img/themes/Digest-d3467ac22a290e1ad3a543014758286e.png +0 -0
  24. package/core/built/assets/img/themes/Dope-6f8e0bbc199ce4af9a60859e9e6a74ad.png +0 -0
  25. package/core/built/assets/img/themes/Ease-9c279ea6cec3c0f1823f81c9dd24b116.png +0 -0
  26. package/core/built/assets/img/themes/Edge-0258906309e11fd075a1d9880aa09b20.png +0 -0
  27. package/core/built/assets/img/themes/Edition-d8f508e93bc24bdf2716ae6f8b3d44f8.png +0 -0
  28. package/core/built/assets/img/themes/Editorial-a25a4a34c04dedd858bd5e05ef388b1c.jpg +0 -0
  29. package/core/built/assets/img/themes/Journal-accf0031bbae0919900a049061e65a04.png +0 -0
  30. package/core/built/assets/img/themes/London-3f07efcee9e5bfb9a33827064eb77e70.jpg +0 -0
  31. package/core/built/assets/img/themes/Massively-06edf00108429f7fb8e65f190fba34fe.jpg +0 -0
  32. package/core/built/assets/img/themes/Ruby-11a53c62015612f4b3aca8f503121225.png +0 -0
  33. package/core/built/assets/img/themes/Wave-86e8044c2d76cb57a9030e4c24ac9520.png +0 -0
  34. package/core/built/assets/simplemde/{simplemde-232f69d126310434489071a1891e6d8b.js → simplemde-3ffc0ec9e9fecf29b9a499db678c9e65.js} +14 -14
  35. package/core/built/assets/{vendor.min-7dc7cf9c92175ebfb9cea95c120ee8a7.js → vendor.min-af502ac4142871500fc424f6a5a254ec.js} +2206 -1859
  36. package/core/frontend/apps/amp/lib/router.js +1 -1
  37. package/core/frontend/helpers/match.js +17 -23
  38. package/core/frontend/meta/author-url.js +1 -1
  39. package/core/frontend/meta/url.js +1 -1
  40. package/core/{server → frontend}/public/favicon.ico +0 -0
  41. package/core/{server → frontend}/public/ghost.css +0 -0
  42. package/core/{server → frontend}/public/ghost.min.css +0 -0
  43. package/core/{server → frontend}/public/robots.txt +0 -0
  44. package/core/{server → frontend}/public/sitemap.xsl +0 -0
  45. package/core/frontend/services/proxy.js +1 -1
  46. package/core/frontend/services/rendering.js +1 -1
  47. package/core/frontend/services/routing/CollectionRouter.js +3 -49
  48. package/core/frontend/services/routing/ParentRouter.js +1 -4
  49. package/core/frontend/services/routing/StaticPagesRouter.js +3 -5
  50. package/core/frontend/services/routing/StaticRoutesRouter.js +4 -6
  51. package/core/frontend/services/routing/TaxonomyRouter.js +4 -5
  52. package/core/frontend/services/routing/controllers/collection.js +2 -2
  53. package/core/frontend/services/routing/controllers/email-post.js +2 -2
  54. package/core/frontend/services/routing/controllers/entry.js +2 -2
  55. package/core/frontend/services/routing/controllers/preview.js +2 -2
  56. package/core/frontend/services/routing/index.js +6 -12
  57. package/core/frontend/services/routing/registry.js +13 -0
  58. package/core/frontend/services/routing/router-manager.js +185 -0
  59. package/core/frontend/services/rss/generate-feed.js +2 -2
  60. package/core/frontend/services/theme-engine/i18n/i18n.js +267 -28
  61. package/core/frontend/services/theme-engine/i18n/index.js +1 -1
  62. package/core/frontend/services/theme-engine/i18n/theme-i18n.js +73 -0
  63. package/core/frontend/web/index.js +1 -0
  64. package/core/{server/web/site → frontend/web}/middleware/handle-image-sizes.js +4 -4
  65. package/core/{server/web/site → frontend/web}/middleware/index.js +0 -0
  66. package/core/{server/web/site → frontend/web}/middleware/redirect-ghost-to-admin.js +3 -3
  67. package/core/{server/web/site → frontend/web}/middleware/serve-favicon.js +6 -6
  68. package/core/{server/web/site → frontend/web}/middleware/serve-public-file.js +2 -2
  69. package/core/{server/web/site → frontend/web}/middleware/static-theme.js +3 -3
  70. package/core/frontend/web/routes.js +13 -0
  71. package/core/{server/web/site/app.js → frontend/web/site.js} +12 -16
  72. package/core/server/adapters/storage/LocalFileStorage.js +35 -39
  73. package/core/server/adapters/storage/index.js +12 -2
  74. package/core/server/api/canary/custom-theme-settings.js +2 -2
  75. package/core/server/api/canary/images.js +1 -1
  76. package/core/server/api/canary/oembed.js +2 -2
  77. package/core/server/api/canary/offers.js +29 -1
  78. package/core/server/api/canary/posts-public.js +6 -2
  79. package/core/server/api/canary/products.js +6 -2
  80. package/core/server/api/canary/tags-public.js +6 -2
  81. package/core/server/api/canary/users.js +9 -4
  82. package/core/server/api/canary/utils/serializers/output/custom-theme-settings.js +2 -2
  83. package/core/server/api/canary/utils/serializers/output/notifications.js +1 -0
  84. package/core/server/api/canary/utils/serializers/output/settings.js +2 -3
  85. package/core/server/api/canary/utils/serializers/output/utils/url.js +1 -1
  86. package/core/server/api/canary/utils/validators/input/oembed.js +4 -1
  87. package/core/server/api/canary/utils/validators/input/passwordreset.js +8 -3
  88. package/core/server/api/canary/utils/validators/input/settings.js +5 -4
  89. package/core/server/api/canary/utils/validators/input/setup.js +6 -2
  90. package/core/server/api/canary/utils/validators/input/users.js +6 -2
  91. package/core/server/api/canary/utils/validators/input/webhooks.js +8 -3
  92. package/core/server/api/v2/images.js +1 -1
  93. package/core/server/api/v2/utils/serializers/output/authentication.js +9 -4
  94. package/core/server/api/v2/utils/serializers/output/notifications.js +1 -0
  95. package/core/server/api/v2/utils/serializers/output/users.js +5 -3
  96. package/core/server/api/v2/utils/serializers/output/utils/url.js +1 -1
  97. package/core/server/api/v2/utils/validators/input/images.js +11 -6
  98. package/core/server/api/v2/utils/validators/input/invitations.js +14 -6
  99. package/core/server/api/v2/utils/validators/input/invites.js +6 -2
  100. package/core/server/api/v2/utils/validators/input/oembed.js +6 -2
  101. package/core/server/api/v2/utils/validators/input/passwordreset.js +8 -3
  102. package/core/server/api/v2/utils/validators/input/settings.js +10 -4
  103. package/core/server/api/v2/utils/validators/input/setup.js +6 -2
  104. package/core/server/api/v2/utils/validators/input/users.js +5 -2
  105. package/core/server/api/v3/authentication.js +6 -2
  106. package/core/server/api/v3/authors-public.js +6 -2
  107. package/core/server/api/v3/email.js +9 -4
  108. package/core/server/api/v3/images.js +1 -1
  109. package/core/server/api/v3/integrations.js +7 -3
  110. package/core/server/api/v3/invites.js +6 -3
  111. package/core/server/api/v3/labels.js +10 -5
  112. package/core/server/api/v3/memberSigninUrls.js +5 -2
  113. package/core/server/api/v3/oembed.js +2 -2
  114. package/core/server/api/v3/pages-public.js +5 -2
  115. package/core/server/api/v3/pages.js +6 -3
  116. package/core/server/api/v3/posts-public.js +5 -3
  117. package/core/server/api/v3/posts.js +7 -3
  118. package/core/server/api/v3/preview.js +5 -3
  119. package/core/server/api/v3/session.js +7 -3
  120. package/core/server/api/v3/settings.js +8 -3
  121. package/core/server/api/v3/slugs.js +5 -4
  122. package/core/server/api/v3/utils/serializers/output/authentication.js +10 -4
  123. package/core/server/api/v3/utils/serializers/output/notifications.js +1 -0
  124. package/core/server/api/v3/utils/serializers/output/settings.js +2 -3
  125. package/core/server/api/v3/utils/serializers/output/users.js +6 -2
  126. package/core/server/api/v3/utils/serializers/output/utils/url.js +1 -1
  127. package/core/server/api/v3/utils/validators/input/images.js +12 -7
  128. package/core/server/api/v3/utils/validators/input/invitations.js +14 -6
  129. package/core/server/api/v3/utils/validators/input/invites.js +6 -2
  130. package/core/server/api/v3/utils/validators/input/oembed.js +6 -2
  131. package/core/server/api/v3/utils/validators/input/passwordreset.js +8 -3
  132. package/core/server/api/v3/utils/validators/input/settings.js +5 -4
  133. package/core/server/api/v3/utils/validators/input/setup.js +6 -2
  134. package/core/server/api/v3/utils/validators/input/users.js +6 -2
  135. package/core/server/api/v3/utils/validators/input/webhooks.js +8 -3
  136. package/core/server/data/exporter/table-lists.js +2 -1
  137. package/core/server/data/importer/handlers/image.js +1 -1
  138. package/core/server/data/importer/importers/image.js +1 -1
  139. package/core/server/data/migrations/init/1-create-tables.js +7 -8
  140. package/core/server/data/migrations/init/2-create-fixtures.js +8 -8
  141. package/core/server/data/migrations/versions/4.19/01-add-active-column-to-offers.js +7 -0
  142. package/core/server/data/migrations/versions/4.19/02-add-offer-redemptions-table.js +8 -0
  143. package/core/server/data/migrations/versions/4.20/01-remove-offer-redemptions-table.js +19 -0
  144. package/core/server/data/migrations/versions/4.20/02-remove-offers-table.js +30 -0
  145. package/core/server/data/migrations/versions/4.20/03-add-offers-table.js +21 -0
  146. package/core/server/data/migrations/versions/4.20/04-add-offer-redemptions-table.js +9 -0
  147. package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +41 -0
  148. package/core/server/data/schema/fixtures/utils.js +150 -143
  149. package/core/server/data/schema/schema.js +15 -3
  150. package/core/server/frontend/ghost.min.css +1 -0
  151. package/core/server/lib/image/blog-icon.js +10 -10
  152. package/core/server/lib/image/image-size.js +5 -5
  153. package/core/server/lib/image/image-utils.js +4 -4
  154. package/core/server/lib/image/index.js +1 -2
  155. package/core/server/lib/mobiledoc.js +3 -2
  156. package/core/server/models/action.js +7 -4
  157. package/core/server/models/base/plugins/overrides.js +19 -6
  158. package/core/server/models/custom-theme-setting.js +56 -1
  159. package/core/server/models/index.js +4 -45
  160. package/core/server/models/member.js +5 -0
  161. package/core/server/models/offer-redemption.js +10 -0
  162. package/core/server/models/user.js +2 -1
  163. package/core/server/overrides.js +6 -2
  164. package/core/server/run-update-check.js +0 -3
  165. package/core/server/services/adapter-manager/config.js +1 -0
  166. package/core/server/services/adapter-manager/index.js +9 -5
  167. package/core/server/services/adapter-manager/options-resolver.js +18 -0
  168. package/core/server/services/bulk-email/bulk-email-processor.js +6 -2
  169. package/core/server/services/bulk-email/mailgun.js +1 -1
  170. package/core/server/services/custom-theme-settings.js +10 -4
  171. package/core/server/services/invites/index.js +0 -2
  172. package/core/server/services/invites/invites.js +5 -5
  173. package/core/server/services/mail/GhostMailer.js +18 -10
  174. package/core/server/services/mega/mega.js +3 -3
  175. package/core/server/services/mega/post-email-serializer.js +2 -2
  176. package/core/server/services/members/api.js +3 -4
  177. package/core/server/services/members/emails/signin.js +1 -1
  178. package/core/server/services/members/emails/signup.js +1 -1
  179. package/core/server/services/members/emails/subscribe.js +1 -1
  180. package/core/server/services/members/middleware.js +10 -0
  181. package/core/server/services/members/service.js +2 -1
  182. package/core/server/services/notifications/index.js +1 -1
  183. package/core/server/services/notifications/notifications.js +40 -35
  184. package/core/server/services/oembed.js +4 -9
  185. package/core/server/services/offers/service.js +16 -6
  186. package/core/server/services/permissions/public.js +6 -2
  187. package/core/server/services/route-settings/route-settings.js +1 -1
  188. package/core/server/services/settings/index.js +3 -1
  189. package/core/server/services/settings/settings-bread-service.js +42 -20
  190. package/core/server/services/slack.js +1 -1
  191. package/core/server/services/themes/activate.js +2 -2
  192. package/core/server/services/themes/activation-bridge.js +6 -6
  193. package/core/server/services/themes/storage.js +1 -1
  194. package/core/{frontend → server}/services/url/Queue.js +0 -0
  195. package/core/{frontend → server}/services/url/Resource.js +0 -0
  196. package/core/{frontend → server}/services/url/Resources.js +2 -2
  197. package/core/{frontend → server}/services/url/UrlGenerator.js +14 -14
  198. package/core/{frontend → server}/services/url/UrlService.js +12 -15
  199. package/core/{frontend → server}/services/url/Urls.js +1 -1
  200. package/core/{frontend → server}/services/url/configs/canary.js +0 -0
  201. package/core/{frontend → server}/services/url/configs/v2.js +0 -0
  202. package/core/{frontend → server}/services/url/configs/v3.js +0 -0
  203. package/core/{frontend → server}/services/url/configs/v4.js +0 -0
  204. package/core/{frontend → server}/services/url/index.js +0 -0
  205. package/core/server/services/xmlrpc.js +1 -1
  206. package/core/server/update-check.js +3 -3
  207. package/core/server/web/admin/controller.js +11 -0
  208. package/core/server/web/admin/views/default-prod.html +4 -4
  209. package/core/server/web/admin/views/default.html +4 -4
  210. package/core/server/web/api/app.js +8 -9
  211. package/core/server/web/members/app.js +1 -0
  212. package/core/server/web/oauth/app.js +4 -2
  213. package/core/server/web/parent/backend.js +3 -3
  214. package/core/server/web/parent/frontend.js +2 -2
  215. package/core/server/web/shared/middlewares/api/spam-prevention.js +0 -2
  216. package/core/server/web/shared/middlewares/custom-redirects.js +0 -8
  217. package/core/server/web/shared/middlewares/maintenance.js +1 -1
  218. package/core/server/web/well-known.js +10 -10
  219. package/core/shared/config/defaults.json +2 -2
  220. package/core/shared/config/overrides.json +1 -1
  221. package/core/shared/express.js +10 -0
  222. package/core/shared/html-to-plaintext.js +2 -2
  223. package/core/shared/labs.js +14 -6
  224. package/loggingrc.js +10 -0
  225. package/package.json +60 -57
  226. package/yarn.lock +1317 -914
  227. package/core/built/assets/ghost-dark-13627f10941a7dbb2b12e1d41dc51c34.css +0 -1
  228. package/core/built/assets/ghost.min-d9cbfb4eb2db8915fcd2bf2416218616.css +0 -1
  229. package/core/built/assets/img/themes/London-68501c8ab797de7f2851cf9ea0a28e26.jpg +0 -0
  230. package/core/frontend/services/routing/bootstrap.js +0 -114
  231. package/core/server/public/404-ghost.png +0 -0
  232. package/core/server/public/404-ghost@2x.png +0 -0
  233. package/core/server/web/site/index.js +0 -1
  234. package/core/server/web/site/routes.js +0 -9
  235. package/core/shared/i18n/i18n.js +0 -312
  236. package/core/shared/i18n/index.js +0 -6
  237. package/core/shared/i18n/translations/en.json +0 -675
@@ -9,7 +9,7 @@ const Resources = require('./Resources');
9
9
  const urlUtils = require('../../../shared/url-utils');
10
10
 
11
11
  // This listens to services.themes.api.changed, routing events, and it's own queue events
12
- const events = require('../../../server/lib/common/events');
12
+ const events = require('../../lib/common/events');
13
13
 
14
14
  /**
15
15
  * The url service class holds all instances in a centralized place.
@@ -35,9 +35,6 @@ class UrlService {
35
35
  * @private
36
36
  */
37
37
  _listeners() {
38
- this._onRouterAddedListener = this._onRouterAddedType.bind(this);
39
- events.on('router.created', this._onRouterAddedListener);
40
-
41
38
  this._onThemeChangedListener = this._onThemeChangedListener.bind(this);
42
39
  events.on('services.themes.api.changed', this._onThemeChangedListener);
43
40
 
@@ -77,22 +74,23 @@ class UrlService {
77
74
  /**
78
75
  * @description Router was created, connect it with a url generator.
79
76
  * @param {ExpressRouter} router
80
- * @private
81
77
  */
82
- _onRouterAddedType(router) {
83
- // CASE: there are router types which do not generate resource urls
84
- // e.g. static route router
85
- // we are listening on the general `router.created` event - every router throws this event
86
- if (!router || !router.getPermalinks()) {
87
- return;
88
- }
89
-
90
- debug('router.created');
78
+ onRouterAddedType(router) {
79
+ debug('Registering route: ', router.name);
91
80
 
92
81
  let urlGenerator = new UrlGenerator(router, this.queue, this.resources, this.urls, this.urlGenerators.length);
93
82
  this.urlGenerators.push(urlGenerator);
94
83
  }
95
84
 
85
+ /**
86
+ * @description Router update handler - regenerates it's resources
87
+ * @param {ExpressRouter} router
88
+ */
89
+ onRouterUpdated(router) {
90
+ const generator = this.urlGenerators.find(g => g.router.id === router.id);
91
+ generator.regenerateResources();
92
+ }
93
+
96
94
  /**
97
95
  * @description If the API version in the theme config changes, we have to reset urls and resources.
98
96
  * @private
@@ -307,7 +305,6 @@ class UrlService {
307
305
  if (!options.keepListeners) {
308
306
  this._onQueueStartedListener && this.queue.removeListener('started', this._onQueueStartedListener);
309
307
  this._onQueueEndedListener && this.queue.removeListener('ended', this._onQueueEndedListener);
310
- this._onRouterAddedListener && events.removeListener('router.created', this._onRouterAddedListener);
311
308
  this._onThemeChangedListener && events.removeListener('services.themes.api.changed', this._onThemeChangedListener);
312
309
  }
313
310
  }
@@ -5,7 +5,7 @@ const logging = require('@tryghost/logging');
5
5
  const errors = require('@tryghost/errors');
6
6
 
7
7
  // This emits its own url added/removed events
8
- const events = require('../../../server/lib/common/events');
8
+ const events = require('../../lib/common/events');
9
9
 
10
10
  /**
11
11
  * This class keeps track of all urls in the system.
File without changes
@@ -1,7 +1,7 @@
1
1
  const _ = require('lodash');
2
2
  const xml = require('xml');
3
3
  const config = require('../../shared/config');
4
- const urlService = require('../../frontend/services/url');
4
+ const urlService = require('./url');
5
5
  const errors = require('@tryghost/errors');
6
6
  const tpl = require('@tryghost/tpl');
7
7
  const logging = require('@tryghost/logging');
@@ -1,7 +1,6 @@
1
1
  const _ = require('lodash');
2
2
 
3
3
  const api = require('./api');
4
- const GhostMailer = require('./services/mail').GhostMailer;
5
4
  const config = require('../shared/config');
6
5
  const urlUtils = require('./../shared/url-utils');
7
6
  const jobsService = require('./services/jobs');
@@ -10,8 +9,6 @@ const request = require('@tryghost/request');
10
9
  const ghostVersion = require('@tryghost/version');
11
10
  const UpdateCheckService = require('@tryghost/update-check-service');
12
11
 
13
- const ghostMailer = new GhostMailer();
14
-
15
12
  /**
16
13
  * Initializes and triggers update check
17
14
  *
@@ -25,6 +22,9 @@ module.exports = async () => {
25
22
  return;
26
23
  }
27
24
 
25
+ const {GhostMailer} = require('./services/mail');
26
+ const ghostMailer = new GhostMailer();
27
+
28
28
  const updateChecker = new UpdateCheckService({
29
29
  api: {
30
30
  settings: {
@@ -1,5 +1,7 @@
1
1
  const debug = require('@tryghost/debug')('web:admin:controller');
2
2
  const path = require('path');
3
+ const fs = require('fs');
4
+ const crypto = require('crypto');
3
5
  const config = require('../../../shared/config');
4
6
  const updateCheck = require('../../update-check');
5
7
 
@@ -21,6 +23,15 @@ module.exports = function adminController(req, res) {
21
23
  const templatePath = path.resolve(config.get('paths').adminViews, defaultTemplate);
22
24
  const headers = {};
23
25
 
26
+ // Generate our own ETag header
27
+ // `sendFile` by default uses filesize+lastmod date to generate an etag.
28
+ // That doesn't work for admin templates because the filesize doesn't change between versions
29
+ // and `npm pack` sets a fixed lastmod date for every file meaning the default etag never changes
30
+ const fileBuffer = fs.readFileSync(templatePath);
31
+ const hashSum = crypto.createHash('md5');
32
+ hashSum.update(fileBuffer);
33
+ headers.ETag = hashSum.digest('hex');
34
+
24
35
  if (config.get('adminFrameProtection')) {
25
36
  headers['X-Frame-Options'] = 'sameorigin';
26
37
  }
@@ -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%22rootURL%22%3A%22%2F%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%224.18%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22emberKeyboard%22%3A%7B%22disableInputsInitializer%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%7D" />
11
+ <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%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%224.20%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22emberKeyboard%22%3A%7B%22disableInputsInitializer%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%7D" />
12
12
 
13
13
  <meta name="HandheldFriendly" content="True" />
14
14
  <meta name="MobileOptimized" content="320" />
@@ -41,7 +41,7 @@
41
41
 
42
42
 
43
43
  <link rel="stylesheet" href="assets/vendor.min-987af30228885bce50f05c4723fe6f53.css">
44
- <link rel="stylesheet" href="assets/ghost.min-d9cbfb4eb2db8915fcd2bf2416218616.css" title="light">
44
+ <link rel="stylesheet" href="assets/ghost.min-57e46fd3b1145ecf2cbd185a13611f3b.css" title="light">
45
45
 
46
46
 
47
47
 
@@ -59,8 +59,8 @@
59
59
  <div id="ember-basic-dropdown-wormhole"></div>
60
60
 
61
61
 
62
- <script src="assets/vendor.min-7dc7cf9c92175ebfb9cea95c120ee8a7.js"></script>
63
- <script src="assets/ghost.min-88d647a008a5b1dd678a89ae1e55c038.js"></script>
62
+ <script src="assets/vendor.min-af502ac4142871500fc424f6a5a254ec.js"></script>
63
+ <script src="assets/ghost.min-26e427944e719b616b8dc7fbb3bbd2f9.js"></script>
64
64
 
65
65
  </body>
66
66
  </html>
@@ -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%22rootURL%22%3A%22%2F%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%224.18%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22emberKeyboard%22%3A%7B%22disableInputsInitializer%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%7D" />
11
+ <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%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%224.20%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22emberKeyboard%22%3A%7B%22disableInputsInitializer%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%7D" />
12
12
 
13
13
  <meta name="HandheldFriendly" content="True" />
14
14
  <meta name="MobileOptimized" content="320" />
@@ -41,7 +41,7 @@
41
41
 
42
42
 
43
43
  <link rel="stylesheet" href="assets/vendor.min-987af30228885bce50f05c4723fe6f53.css">
44
- <link rel="stylesheet" href="assets/ghost.min-d9cbfb4eb2db8915fcd2bf2416218616.css" title="light">
44
+ <link rel="stylesheet" href="assets/ghost.min-57e46fd3b1145ecf2cbd185a13611f3b.css" title="light">
45
45
 
46
46
 
47
47
 
@@ -59,8 +59,8 @@
59
59
  <div id="ember-basic-dropdown-wormhole"></div>
60
60
 
61
61
 
62
- <script src="assets/vendor.min-7dc7cf9c92175ebfb9cea95c120ee8a7.js"></script>
63
- <script src="assets/ghost.min-88d647a008a5b1dd678a89ae1e55c038.js"></script>
62
+ <script src="assets/vendor.min-af502ac4142871500fc424f6a5a254ec.js"></script>
63
+ <script src="assets/ghost.min-26e427944e719b616b8dc7fbb3bbd2f9.js"></script>
64
64
 
65
65
  </body>
66
66
  </html>
@@ -12,18 +12,17 @@ module.exports = function setupApiApp() {
12
12
  apiApp.use(require('./testmode')());
13
13
  }
14
14
 
15
- // Mount different API versions
16
- apiApp.use(urlUtils.getVersionPath({version: 'v2', type: 'content'}), require('./v2/content/app')());
17
- apiApp.use(urlUtils.getVersionPath({version: 'v2', type: 'admin'}), require('./v2/admin/app')());
15
+ apiApp.lazyUse(urlUtils.getVersionPath({version: 'v2', type: 'content'}), require('./v2/content/app'));
16
+ apiApp.lazyUse(urlUtils.getVersionPath({version: 'v2', type: 'admin'}), require('./v2/admin/app'));
18
17
 
19
- apiApp.use(urlUtils.getVersionPath({version: 'v3', type: 'content'}), require('./v3/content/app')());
20
- apiApp.use(urlUtils.getVersionPath({version: 'v3', type: 'admin'}), require('./v3/admin/app')());
18
+ apiApp.lazyUse(urlUtils.getVersionPath({version: 'v3', type: 'content'}), require('./v3/content/app'));
19
+ apiApp.lazyUse(urlUtils.getVersionPath({version: 'v3', type: 'admin'}), require('./v3/admin/app'));
21
20
 
22
- apiApp.use(urlUtils.getVersionPath({version: 'v4', type: 'content'}), require('./canary/content/app')());
23
- apiApp.use(urlUtils.getVersionPath({version: 'v4', type: 'admin'}), require('./canary/admin/app')());
21
+ apiApp.lazyUse(urlUtils.getVersionPath({version: 'v4', type: 'content'}), require('./canary/content/app'));
22
+ apiApp.lazyUse(urlUtils.getVersionPath({version: 'v4', type: 'admin'}), require('./canary/admin/app'));
24
23
 
25
- apiApp.use(urlUtils.getVersionPath({version: 'canary', type: 'content'}), require('./canary/content/app')());
26
- apiApp.use(urlUtils.getVersionPath({version: 'canary', type: 'admin'}), require('./canary/admin/app')());
24
+ apiApp.lazyUse(urlUtils.getVersionPath({version: 'canary', type: 'content'}), require('./canary/content/app'));
25
+ apiApp.lazyUse(urlUtils.getVersionPath({version: 'canary', type: 'admin'}), require('./canary/admin/app'));
27
26
 
28
27
  // Error handling for requests to non-existent API versions
29
28
  apiApp.use(errorHandler.resourceNotFound);
@@ -37,6 +37,7 @@ module.exports = function setupMembersApp() {
37
37
  membersApp.put('/api/member', bodyParser.json({limit: '1mb'}), middleware.updateMemberData);
38
38
  membersApp.post('/api/member/email', bodyParser.json({limit: '1mb'}), (req, res) => membersService.api.middleware.updateEmailAddress(req, res));
39
39
  membersApp.get('/api/session', middleware.getIdentityToken);
40
+ membersApp.get('/api/offers/:id', labs.enabledMiddleware('offers'), middleware.getOfferData);
40
41
  membersApp.delete('/api/session', middleware.deleteSession);
41
42
  membersApp.get('/api/site', middleware.getMemberSiteData);
42
43
 
@@ -1,7 +1,5 @@
1
1
  const debug = require('@tryghost/debug')('web:oauth:app');
2
2
  const {URL} = require('url');
3
- const passport = require('passport');
4
- const GoogleStrategy = require('passport-google-oauth20').Strategy;
5
3
  const express = require('../../../shared/express');
6
4
  const urlUtils = require('../../../shared/url-utils');
7
5
  const shared = require('../shared');
@@ -36,6 +34,10 @@ module.exports = function setupOAuthApp() {
36
34
  */
37
35
  function googleOAuthMiddleware(clientId, secret) {
38
36
  return (req, res, next) => {
37
+ // Lazy-required to save boot time
38
+ const passport = require('passport');
39
+ const GoogleStrategy = require('passport-google-oauth20').Strategy;
40
+
39
41
  const adminURL = urlUtils.urlFor('admin', true);
40
42
 
41
43
  //Create the callback url to be sent to Google
@@ -10,9 +10,9 @@ module.exports = () => {
10
10
  // BACKEND
11
11
  // Wrap the admin and API apps into a single express app for use with vhost
12
12
  const backendApp = express('backend');
13
- backendApp.use('/ghost/api', require('../api')());
14
- backendApp.use('/ghost/oauth', require('../oauth')());
15
- backendApp.use('/ghost/.well-known', require('../well-known')());
13
+ backendApp.lazyUse('/ghost/api', require('../api'));
14
+ backendApp.lazyUse('/ghost/oauth', require('../oauth'));
15
+ backendApp.lazyUse('/ghost/.well-known', require('../well-known'));
16
16
  backendApp.use('/ghost', require('../../services/auth/session').createSessionFromToken, require('../admin')());
17
17
 
18
18
  return backendApp;
@@ -18,8 +18,8 @@ module.exports = (options) => {
18
18
  // otherwise we serve assets/pages with http. This can cause mixed content warnings in the admin client.
19
19
  frontendApp.use(shared.middlewares.urlRedirects.frontendSSLRedirect);
20
20
 
21
- frontendApp.use('/members', require('../members')());
22
- frontendApp.use('/', require('../site')(options));
21
+ frontendApp.lazyUse('/members', require('../members'));
22
+ frontendApp.use('/', require('../../../frontend/web')(options));
23
23
 
24
24
  return frontendApp;
25
25
  };
@@ -107,7 +107,6 @@ const globalReset = () => {
107
107
  extend({
108
108
  attachResetToRequest: false,
109
109
  failCallback(req, res, next, nextValidRequestDate) {
110
- // TODO use i18n again
111
110
  return next(new errors.TooManyRequestsError({
112
111
  message: `Too many attempts try again in ${moment(nextValidRequestDate).fromNow(true)}`,
113
112
  context: tpl(messages.forgottenPasswordIp.error,
@@ -143,7 +142,6 @@ const userLogin = () => {
143
142
  failCallback(req, res, next, nextValidRequestDate) {
144
143
  return next(new errors.TooManyRequestsError({
145
144
  message: `Too many sign-in attempts try again in ${moment(nextValidRequestDate).fromNow(true)}`,
146
- // TODO add more options to i18n
147
145
  context: tpl(messages.tooManySigninAttempts.context),
148
146
  help: tpl(messages.tooManySigninAttempts.context)
149
147
  }));
@@ -7,7 +7,6 @@ const urlUtils = require('../../../../shared/url-utils');
7
7
  const errors = require('@tryghost/errors');
8
8
  const logging = require('@tryghost/logging');
9
9
  const redirectsService = require('../../../services/redirects');
10
- const labsService = require('../../../../shared/labs');
11
10
 
12
11
  const messages = {
13
12
  customRedirectsRegistrationFailure: 'Could not register custom redirects.'
@@ -27,13 +26,6 @@ let customRedirectsRouter;
27
26
  _private.registerRoutes = (router, redirects) => {
28
27
  debug('redirects loading');
29
28
 
30
- if (labsService.isSet('offers')) {
31
- redirects.unshift({
32
- from: '/zimo50',
33
- to: '/#/portal/offers/abcdefuckoff'
34
- });
35
- }
36
-
37
29
  redirects.forEach((redirect) => {
38
30
  /**
39
31
  * Detect case insensitive modifier when regex is enclosed by
@@ -1,7 +1,7 @@
1
1
  const errors = require('@tryghost/errors');
2
2
  const config = require('../../../../shared/config');
3
3
  const tpl = require('@tryghost/tpl');
4
- const urlService = require('../../../../frontend/services/url');
4
+ const urlService = require('../../../services/url');
5
5
 
6
6
  const messages = {
7
7
  maintenance: 'Site is currently undergoing maintenance, please wait a moment then retry.',
@@ -1,19 +1,19 @@
1
1
  const express = require('../../shared/express');
2
2
  const settings = require('../../shared/settings-cache');
3
- const jose = require('node-jose');
4
-
5
- const dangerousPrivateKey = settings.get('ghost_private_key');
6
- const keyStore = jose.JWK.createKeyStore();
7
- const keyStoreReady = keyStore.add(dangerousPrivateKey, 'pem');
8
-
9
- const getSafePublicJWKS = async () => {
10
- await keyStoreReady;
11
- return keyStore.toJSON();
12
- };
13
3
 
14
4
  module.exports = function setupWellKnownApp() {
15
5
  const wellKnownApp = express('well-known');
16
6
 
7
+ const jose = require('node-jose');
8
+ const dangerousPrivateKey = settings.get('ghost_private_key');
9
+ const keyStore = jose.JWK.createKeyStore();
10
+ const keyStoreReady = keyStore.add(dangerousPrivateKey, 'pem');
11
+
12
+ const getSafePublicJWKS = async () => {
13
+ await keyStoreReady;
14
+ return keyStore.toJSON();
15
+ };
16
+
17
17
  wellKnownApp.get('/jwks.json', async (req, res) => {
18
18
  const jwks = await getSafePublicJWKS();
19
19
  res.json(jwks);
@@ -121,7 +121,7 @@
121
121
  "emailAnalytics": true
122
122
  },
123
123
  "portal": {
124
- "url": "https://unpkg.com/@tryghost/portal@~1.11.0/umd/portal.min.js",
125
- "version": "1.11"
124
+ "url": "https://unpkg.com/@tryghost/portal@~1.12.0/umd/portal.min.js",
125
+ "version": "1.12"
126
126
  }
127
127
  }
@@ -10,7 +10,7 @@
10
10
  "internalAppPath": "core/frontend/apps/",
11
11
  "internalAdaptersPath": "core/server/adapters/",
12
12
  "migrationPath": "core/server/data/migrations",
13
- "publicFilePath": "core/server/public"
13
+ "publicFilePath": "core/frontend/public"
14
14
  },
15
15
  "apps": {
16
16
  "internal": [
@@ -1,7 +1,10 @@
1
1
  const debug = require('@tryghost/debug')('shared:express');
2
2
  const express = require('express');
3
+ const {createLazyRouter} = require('express-lazy-router');
3
4
  const sentry = require('./sentry');
4
5
 
6
+ const lazyLoad = createLazyRouter();
7
+
5
8
  module.exports = (name) => {
6
9
  debug('new app start', name);
7
10
  const app = express();
@@ -14,6 +17,13 @@ module.exports = (name) => {
14
17
  // Sentry must be our first error handler. Mounting it here means all custom error handlers will come after
15
18
  app.use(sentry.errorHandler);
16
19
 
20
+ app.lazyUse = function lazyUse(mountPath, requireFn) {
21
+ app.use(mountPath, lazyLoad(() => {
22
+ debug(`lazy-loading on ${mountPath}`);
23
+ return Promise.resolve(requireFn());
24
+ }));
25
+ };
26
+
17
27
  debug('new app end', name);
18
28
  return app;
19
29
  };
@@ -1,6 +1,6 @@
1
- const htmlToText = require('html-to-text');
2
-
3
1
  module.exports = function htmlToPlaintext(html) {
2
+ const htmlToText = require('html-to-text');
3
+
4
4
  return htmlToText.fromString(html, {
5
5
  wordwrap: 80,
6
6
  ignoreImage: true,
@@ -1,6 +1,5 @@
1
1
  const _ = require('lodash');
2
2
  const Promise = require('bluebird');
3
- const SafeString = require('express-hbs').SafeString;
4
3
  const errors = require('@tryghost/errors');
5
4
  const logging = require('@tryghost/logging');
6
5
  const tpl = require('@tryghost/tpl');
@@ -14,21 +13,25 @@ const messages = {
14
13
  errorHelp: 'See {url}'
15
14
  };
16
15
 
16
+ // flags in this list always return `true`, allows quick global enable prior to full flag removal
17
+ const GA_FEATURES = [
18
+ 'customThemeSettings',
19
+ 'offers'
20
+ ];
21
+
17
22
  // NOTE: this allowlist is meant to be used to filter out any unexpected
18
23
  // input for the "labs" setting value
19
24
  const BETA_FEATURES = [
20
25
  'activitypub',
21
- 'matchHelper',
22
26
  'multipleProducts'
23
27
  ];
24
28
 
25
29
  const ALPHA_FEATURES = [
26
30
  'oauthLogin',
27
- 'customThemeSettings',
28
- 'membersActivity',
29
- 'offers'
31
+ 'membersActivity'
30
32
  ];
31
33
 
34
+ module.exports.GA_KEYS = [...GA_FEATURES];
32
35
  module.exports.WRITABLE_KEYS_ALLOWLIST = [...BETA_FEATURES, ...ALPHA_FEATURES];
33
36
 
34
37
  module.exports.getAll = () => {
@@ -40,6 +43,10 @@ module.exports.getAll = () => {
40
43
  }
41
44
  });
42
45
 
46
+ GA_FEATURES.forEach((gaKey) => {
47
+ labs[gaKey] = true;
48
+ });
49
+
43
50
  labs.members = settingsCache.get('members_signup_access') !== 'none';
44
51
 
45
52
  return labs;
@@ -79,7 +86,7 @@ module.exports.enabledHelper = function enabledHelper(options, callback) {
79
86
  }
80
87
 
81
88
  // Else, the helper is not active and we need to handle this as an error
82
- errDetails.message = tpl(options.errorMessage || messages.errorMessage, {helperName: options.helperName}),
89
+ errDetails.message = tpl(options.errorMessage || messages.errorMessage, {helperName: options.helperName});
83
90
  errDetails.context = tpl(options.errorContext || messages.errorContext, {
84
91
  helperName: options.helperName,
85
92
  flagName: options.flagName
@@ -88,6 +95,7 @@ module.exports.enabledHelper = function enabledHelper(options, callback) {
88
95
 
89
96
  logging.error(new errors.DisabledFeatureError(errDetails));
90
97
 
98
+ const {SafeString} = require('express-hbs');
91
99
  errString = new SafeString(`<script>console.error("${_.values(errDetails).join(' ')}");</script>`);
92
100
 
93
101
  if (options.async) {
package/loggingrc.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const config = require('./core/shared/config');
2
+ const ghostVersion = require('@tryghost/version');
2
3
 
3
4
  module.exports = {
4
5
  name: config.get('logging:name'),
@@ -8,6 +9,15 @@ module.exports = {
8
9
  mode: config.get('logging:mode'),
9
10
  level: config.get('logging:level'),
10
11
  transports: config.get('logging:transports'),
12
+ metrics: {
13
+ transports: config.get('logging:metrics:transports'),
14
+ metadata: {
15
+ // Undefined if unavailable
16
+ siteId: config.get('hostSettings:siteId'),
17
+ domain: config.get('url'),
18
+ version: ghostVersion.safe
19
+ }
20
+ },
11
21
  gelf: config.get('logging:gelf'),
12
22
  loggly: config.get('logging:loggly'),
13
23
  elasticsearch: config.get('logging:elasticsearch'),