ghost 4.22.3 → 4.25.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 (230) hide show
  1. package/.c8rc.json +24 -0
  2. package/.eslintrc.js +39 -0
  3. package/Gruntfile.js +0 -1
  4. package/content/public/README.md +3 -0
  5. package/content/themes/casper/assets/built/casper.js +1 -1
  6. package/content/themes/casper/assets/built/casper.js.map +1 -1
  7. package/content/themes/casper/assets/built/global.css +1 -1
  8. package/content/themes/casper/assets/built/global.css.map +1 -1
  9. package/content/themes/casper/assets/built/screen.css +1 -1
  10. package/content/themes/casper/assets/built/screen.css.map +1 -1
  11. package/content/themes/casper/assets/css/global.css +6 -1
  12. package/content/themes/casper/assets/css/screen.css +32 -216
  13. package/content/themes/casper/default.hbs +2 -2
  14. package/content/themes/casper/package.json +3 -2
  15. package/content/themes/casper/post.hbs +1 -1
  16. package/content/themes/casper/yarn.lock +173 -123
  17. package/core/app.js +12 -1
  18. package/core/boot.js +47 -28
  19. package/core/bridge.js +10 -10
  20. package/core/built/assets/{chunk.3.324fd0cc598c73650219.js → chunk.3.8f95b516d88ff4eec64c.js} +18 -18
  21. package/core/built/assets/ghost-dark-d690e732e17ffc794e2e59c1467ca282.css +1 -0
  22. package/core/built/assets/ghost.min-043bb7480a0810109b130f13b2a4235e.css +1 -0
  23. package/core/built/assets/{ghost.min-7da921f6c6cac3fe10da1ba104575440.js → ghost.min-bc72f685c1c9adc9885925c1412435a5.js} +563 -605
  24. package/core/built/assets/icons/audio-upload.svg +8 -0
  25. package/core/built/assets/icons/powered-by-tenor.svg +35 -0
  26. package/core/built/assets/icons/tenor.svg +7 -0
  27. package/core/built/assets/{vendor.min-413f887176a041e6dbf88214ca9a7481.js → vendor.min-d1234c632a54502777c34e50752fa3fc.js} +4622 -3631
  28. package/core/frontend/apps/amp/lib/helpers/amp_content.js +2 -2
  29. package/core/frontend/apps/amp/lib/views/amp.hbs +112 -0
  30. package/core/frontend/apps/private-blogging/index.js +1 -1
  31. package/core/frontend/apps/private-blogging/lib/router.js +1 -1
  32. package/core/frontend/services/apps/index.js +1 -1
  33. package/core/frontend/services/apps/loader.js +3 -3
  34. package/core/frontend/services/card-assets/index.js +0 -12
  35. package/core/frontend/services/card-assets/service.js +29 -28
  36. package/core/frontend/services/helpers/handlebars.js +1 -1
  37. package/core/frontend/services/routing/CollectionRouter.js +4 -5
  38. package/core/frontend/services/routing/EmailRouter.js +1 -1
  39. package/core/frontend/services/routing/ParentRouter.js +0 -8
  40. package/core/frontend/services/routing/PreviewRouter.js +1 -1
  41. package/core/frontend/services/routing/StaticPagesRouter.js +1 -1
  42. package/core/frontend/services/routing/StaticRoutesRouter.js +4 -4
  43. package/core/frontend/services/routing/TaxonomyRouter.js +3 -3
  44. package/core/frontend/services/routing/{middlewares → middleware}/index.js +0 -0
  45. package/core/frontend/services/routing/{middlewares → middleware}/page-param.js +0 -0
  46. package/core/frontend/services/routing/router-manager.js +7 -2
  47. package/core/frontend/services/rss/generate-feed.js +2 -1
  48. package/core/frontend/services/theme-engine/middleware/ensure-active-theme.js +34 -0
  49. package/core/frontend/services/theme-engine/middleware/index.js +6 -0
  50. package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +116 -0
  51. package/core/frontend/services/theme-engine/middleware/update-local-template-data.js +9 -0
  52. package/core/frontend/services/theme-engine/middleware/update-local-template-options.js +57 -0
  53. package/core/frontend/src/cards/css/bookmark.css +72 -47
  54. package/core/frontend/src/cards/css/button.css +4 -0
  55. package/core/frontend/src/cards/css/callout.css +40 -3
  56. package/core/frontend/src/cards/css/gallery.css +15 -10
  57. package/core/frontend/src/cards/css/nft.css +20 -11
  58. package/core/frontend/src/cards/css/toggle.css +58 -0
  59. package/core/frontend/src/cards/js/toggle.js +16 -0
  60. package/core/frontend/web/middleware/error-handler.js +93 -0
  61. package/core/frontend/web/middleware/handle-image-sizes.js +3 -6
  62. package/core/frontend/web/middleware/index.js +1 -0
  63. package/core/frontend/web/middleware/serve-public-file.js +39 -16
  64. package/core/frontend/web/site.js +11 -14
  65. package/core/server/adapters/scheduling/SchedulingDefault.js +2 -2
  66. package/core/server/adapters/storage/LocalStorageBase.js +2 -2
  67. package/core/server/api/canary/authentication.js +1 -1
  68. package/core/server/api/canary/db.js +2 -2
  69. package/core/server/api/canary/media.js +3 -2
  70. package/core/server/api/canary/oembed.js +16 -1
  71. package/core/server/api/canary/session.js +1 -1
  72. package/core/server/api/canary/slugs.js +1 -1
  73. package/core/server/api/canary/utils/permissions.js +2 -2
  74. package/core/server/api/canary/utils/serializers/output/config.js +2 -6
  75. package/core/server/api/v2/authentication.js +1 -1
  76. package/core/server/api/v2/db.js +2 -2
  77. package/core/server/api/v2/session.js +1 -1
  78. package/core/server/api/v2/slugs.js +1 -1
  79. package/core/server/api/v2/utils/permissions.js +2 -2
  80. package/core/server/api/v3/authentication.js +1 -1
  81. package/core/server/api/v3/db.js +2 -2
  82. package/core/server/api/v3/session.js +1 -1
  83. package/core/server/api/v3/slugs.js +1 -1
  84. package/core/server/api/v3/utils/permissions.js +2 -2
  85. package/core/server/data/db/connection.js +7 -0
  86. package/core/server/data/db/state-manager.js +4 -4
  87. package/core/server/data/exporter/export-filename.js +1 -1
  88. package/core/server/data/importer/handlers/json.js +1 -1
  89. package/core/server/data/importer/import-manager.js +1 -1
  90. package/core/server/data/importer/importers/data/base.js +1 -1
  91. package/core/server/data/importer/importers/data/data-importer.js +3 -3
  92. package/core/server/data/migrations/init/2-create-fixtures.js +3 -20
  93. package/core/server/data/migrations/utils.js +2 -2
  94. package/core/server/data/migrations/versions/1.21/1-add-contributor-role.js +5 -5
  95. package/core/server/data/migrations/versions/1.25/1-update-koenig-beta-html.js +1 -0
  96. package/core/server/data/migrations/versions/2.15/2-insert-zapier-integration.js +3 -3
  97. package/core/server/data/migrations/versions/2.2/3-insert-admin-integration-role.js +5 -5
  98. package/core/server/data/migrations/versions/2.27/1-insert-ghost-db-backup-role.js +5 -6
  99. package/core/server/data/migrations/versions/2.27/2-insert-db-backup-integration.js +3 -4
  100. package/core/server/data/migrations/versions/2.28/3-insert-ghost-scheduler-role.js +7 -7
  101. package/core/server/data/migrations/versions/2.28/4-insert-scheduler-integration.js +3 -3
  102. package/core/server/data/migrations/versions/3.1/08-add-uuid-values-to-members.js +1 -0
  103. package/core/server/data/migrations/versions/3.22/02-settings-key-renames.js +2 -0
  104. package/core/server/data/migrations/versions/3.22/05-migrate-members-subscription-settings.js +3 -0
  105. package/core/server/data/migrations/versions/3.22/06-migrate-stripe-connect-settings.js +2 -0
  106. package/core/server/data/migrations/versions/3.23/01-migrate-bulk-email-settings.js +1 -0
  107. package/core/server/data/migrations/versions/3.29/01-remove-duplicate-subscriptions.js +2 -0
  108. package/core/server/data/migrations/versions/3.29/02-remove-duplicate-customers.js +2 -0
  109. package/core/server/data/migrations/versions/3.38/04-populate-recipient-filter-column.js +2 -0
  110. package/core/server/data/migrations/versions/4.0/01-update-mobiledoc.js +2 -0
  111. package/core/server/data/migrations/versions/4.0/03-populate-status-column-for-members.js +4 -0
  112. package/core/server/data/migrations/versions/4.0/06-populate-members-subscribe-events-table.js +1 -0
  113. package/core/server/data/migrations/versions/4.0/17-populate-members-status-events-table.js +1 -0
  114. package/core/server/data/migrations/versions/4.0/18-transform-urls-absolute-to-transform-ready.js +5 -0
  115. package/core/server/data/migrations/versions/4.0/22-solve-orphaned-webhooks.js +1 -0
  116. package/core/server/data/migrations/versions/4.0/23-regenerate-posts-html.js +1 -0
  117. package/core/server/data/migrations/versions/4.0/25-populate-members-paid-subscription-events-table.js +2 -1
  118. package/core/server/data/migrations/versions/4.12/02-fix-member-statuses.js +1 -0
  119. package/core/server/data/migrations/versions/4.14/01-fix-comped-member-statuses.js +3 -0
  120. package/core/server/data/migrations/versions/4.14/02-fix-free-members-status-events.js +1 -0
  121. package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +2 -0
  122. package/core/server/data/migrations/versions/4.23/01-truncate-offer-names.js +59 -0
  123. package/core/server/data/migrations/versions/4.3/04-attach-members-to-product.js +1 -0
  124. package/core/server/data/migrations/versions/4.4/01-restore-free-members-signup-setting-from-backup.js +1 -0
  125. package/core/server/data/migrations/versions/4.6/01-remove-comped-status.js +1 -0
  126. package/core/server/data/migrations/versions/4.8/04-migrate-show-newsletter-header-setting.js +1 -0
  127. package/core/server/data/migrations/versions/4.9/05-fix-missed-mobiledoc-url-transforms.js +1 -0
  128. package/core/server/data/migrations/versions/4.9/06-add-comped-status.js +1 -0
  129. package/core/server/data/migrations/versions/4.9/07-update-comped-members-status-events.js +1 -0
  130. package/core/server/data/schema/commands.js +2 -2
  131. package/core/server/data/schema/fixtures/fixture-manager.js +340 -0
  132. package/core/server/data/schema/fixtures/index.js +8 -2
  133. package/core/server/ghost-server.js +2 -2
  134. package/core/server/lib/image/image-size.js +2 -2
  135. package/core/server/models/base/listeners.js +2 -2
  136. package/core/server/models/member-email-change-event.js +2 -2
  137. package/core/server/models/member-login-event.js +2 -2
  138. package/core/server/models/member-paid-subscription-event.js +3 -3
  139. package/core/server/models/member-payment-event.js +3 -3
  140. package/core/server/models/member-product-event.js +6 -6
  141. package/core/server/models/member-status-event.js +5 -3
  142. package/core/server/models/member-subscribe-event.js +9 -3
  143. package/core/server/models/relations/authors.js +1 -1
  144. package/core/server/models/settings.js +1 -1
  145. package/core/server/services/auth/passwordreset.js +1 -1
  146. package/core/server/services/auth/setup.js +1 -1
  147. package/core/server/services/email-analytics/jobs/index.js +1 -1
  148. package/core/server/services/mega/mega.js +6 -4
  149. package/core/server/services/mega/post-email-serializer.js +5 -1
  150. package/core/server/services/mega/segment-parser.js +1 -2
  151. package/core/server/services/mega/template.js +52 -37
  152. package/core/server/services/members/api.js +22 -0
  153. package/core/server/services/members/config.js +1 -1
  154. package/core/server/services/members/emails/signup-paid.js +168 -0
  155. package/core/server/services/members/service.js +6 -2
  156. package/core/server/services/members/stripe-connect.js +4 -2
  157. package/core/server/services/nft-oembed.js +13 -22
  158. package/core/server/services/oembed.js +28 -24
  159. package/core/server/services/permissions/can-this.js +1 -1
  160. package/core/server/services/public-config/config.js +1 -1
  161. package/core/server/services/redirects/api.js +20 -25
  162. package/core/server/services/redirects/index.js +18 -10
  163. package/core/server/services/redirects/utils.js +14 -0
  164. package/core/server/services/redirects/validation.js +10 -0
  165. package/core/server/services/route-settings/default-settings-manager.js +1 -1
  166. package/core/server/services/route-settings/index.js +40 -17
  167. package/core/server/services/route-settings/route-settings.js +120 -115
  168. package/core/server/services/route-settings/settings-loader.js +18 -36
  169. package/core/server/services/route-settings/yaml-parser.js +1 -1
  170. package/core/server/services/slack.js +1 -1
  171. package/core/server/services/themes/activation-bridge.js +3 -3
  172. package/core/server/services/themes/storage.js +2 -2
  173. package/core/server/services/twitter-embed.js +80 -0
  174. package/core/server/services/url/LocalFileCache.js +75 -0
  175. package/core/server/services/url/Resources.js +8 -2
  176. package/core/server/services/url/UrlGenerator.js +23 -20
  177. package/core/server/services/url/UrlService.js +75 -63
  178. package/core/server/services/url/index.js +17 -3
  179. package/core/server/services/xmlrpc.js +2 -2
  180. package/core/server/web/admin/app.js +7 -10
  181. package/core/server/web/admin/controller.js +35 -12
  182. package/core/server/web/admin/middleware/redirect-admin-urls.js +15 -0
  183. package/core/server/web/admin/views/default-prod.html +4 -4
  184. package/core/server/web/admin/views/default.html +4 -4
  185. package/core/server/web/api/app.js +1 -1
  186. package/core/server/web/api/canary/admin/app.js +3 -6
  187. package/core/server/web/api/canary/admin/middleware.js +7 -7
  188. package/core/server/web/api/canary/admin/routes.js +5 -5
  189. package/core/server/web/api/canary/content/app.js +3 -6
  190. package/core/server/web/api/canary/content/middleware.js +3 -3
  191. package/core/server/web/api/v2/admin/app.js +3 -6
  192. package/core/server/web/api/v2/admin/middleware.js +7 -7
  193. package/core/server/web/api/v2/admin/routes.js +5 -5
  194. package/core/server/web/api/v2/content/app.js +3 -6
  195. package/core/server/web/api/v2/content/middleware.js +3 -3
  196. package/core/server/web/api/v3/admin/app.js +3 -6
  197. package/core/server/web/api/v3/admin/middleware.js +7 -7
  198. package/core/server/web/api/v3/admin/routes.js +5 -5
  199. package/core/server/web/api/v3/content/app.js +3 -6
  200. package/core/server/web/api/v3/content/middleware.js +3 -3
  201. package/core/server/web/members/app.js +6 -9
  202. package/core/server/web/oauth/app.js +0 -4
  203. package/core/server/web/parent/app.js +17 -9
  204. package/core/server/web/parent/frontend.js +1 -1
  205. package/core/server/web/shared/index.js +2 -2
  206. package/core/server/web/shared/{middlewares → middleware}/api/index.js +0 -0
  207. package/core/server/web/shared/{middlewares → middleware}/api/spam-prevention.js +0 -0
  208. package/core/server/web/shared/{middlewares → middleware}/brute.js +0 -0
  209. package/core/server/web/shared/{middlewares → middleware}/cache-control.js +0 -0
  210. package/core/server/web/shared/middleware/error-handler.js +224 -0
  211. package/core/server/web/shared/{middlewares → middleware}/index.js +0 -4
  212. package/core/server/web/shared/{middlewares → middleware}/pretty-urls.js +0 -0
  213. package/core/server/web/shared/{middlewares → middleware}/uncapitalise.js +0 -0
  214. package/core/server/web/shared/{middlewares → middleware}/url-redirects.js +0 -0
  215. package/core/shared/config/defaults.json +13 -1
  216. package/core/shared/config/helpers.js +42 -0
  217. package/core/shared/config/loader.js +1 -1
  218. package/core/shared/labs.js +9 -5
  219. package/core/shared/sentry.js +1 -1
  220. package/loggingrc.js +19 -20
  221. package/package.json +38 -37
  222. package/yarn.lock +1064 -892
  223. package/content/themes/casper/assets/js/gallery-card.js +0 -24
  224. package/core/built/assets/ghost-dark-39fb496d051565531062d7e047d1c0b1.css +0 -1
  225. package/core/built/assets/ghost.min-4207edfc1ae0a3f9f6505ca00d20b0c0.css +0 -1
  226. package/core/frontend/services/theme-engine/middleware.js +0 -209
  227. package/core/server/data/schema/fixtures/utils.js +0 -321
  228. package/core/server/web/parent/vhost-utils.js +0 -39
  229. package/core/server/web/shared/middlewares/error-handler.js +0 -329
  230. package/core/server/web/shared/middlewares/maintenance.js +0 -25
@@ -11,20 +11,41 @@ const messages = {
11
11
  fileNotFound: 'File not found'
12
12
  };
13
13
 
14
- function createPublicFileMiddleware(file, type, maxAge) {
15
- let content;
16
- const publicFilePath = config.get('paths').publicFilePath;
17
- const filePath = file.match(/^public/) ? path.join(publicFilePath, file.replace(/^public/, '')) : path.join(publicFilePath, file);
14
+ /**
15
+ * If this request has a ?v= param, make sure the cache has the same key
16
+ *
17
+ * @param {Object} req
18
+ * @param {Object} cache
19
+ * @returns {boolean}
20
+ */
21
+ function matchCacheKey(req, cache) {
22
+ if (req.query && req.query.v && cache && cache.key) {
23
+ return req.query.v === cache.key;
24
+ }
25
+
26
+ return true;
27
+ }
28
+
29
+ function createPublicFileMiddleware(location, file, mime, maxAge) {
30
+ let cache;
31
+ // These files are provided by Ghost, and therefore live inside of the core folder
32
+ const staticFilePath = config.get('paths').publicFilePath;
33
+ // These files are built on the fly, and must be saved in the content folder
34
+ const builtFilePath = config.getContentPath('public');
35
+
36
+ let locationPath = location === 'static' ? staticFilePath : builtFilePath;
37
+
38
+ const filePath = file.match(/^public/) ? path.join(locationPath, file.replace(/^public/, '')) : path.join(locationPath, file);
18
39
  const blogRegex = /(\{\{blog-url\}\})/g;
19
40
 
20
41
  return function servePublicFileMiddleware(req, res, next) {
21
- if (content) {
22
- res.writeHead(200, content.headers);
23
- return res.end(content.body);
42
+ if (cache && matchCacheKey(req, cache)) {
43
+ res.writeHead(200, cache.headers);
44
+ return res.end(cache.body);
24
45
  }
25
46
 
26
47
  // send image files directly and let express handle content-length, etag, etc
27
- if (type.match(/^image/)) {
48
+ if (mime.match(/^image/)) {
28
49
  return res.sendFile(filePath, (err) => {
29
50
  if (err && err.status === 404) {
30
51
  // ensure we're triggering basic asset 404 and not a templated 404
@@ -57,29 +78,31 @@ function createPublicFileMiddleware(file, type, maxAge) {
57
78
 
58
79
  let str = buf.toString();
59
80
 
60
- if (type === 'text/xsl' || type === 'text/plain' || type === 'application/javascript') {
81
+ if (mime === 'text/xsl' || mime === 'text/plain' || mime === 'application/javascript') {
61
82
  str = str.replace(blogRegex, urlUtils.urlFor('home', true).replace(/\/$/, ''));
62
83
  }
63
84
 
64
- content = {
85
+ cache = {
65
86
  headers: {
66
- 'Content-Type': type,
87
+ 'Content-Type': mime,
67
88
  'Content-Length': Buffer.from(str).length,
68
89
  ETag: `"${crypto.createHash('md5').update(str, 'utf8').digest('hex')}"`,
69
90
  'Cache-Control': `public, max-age=${maxAge}`
70
91
  },
71
- body: str
92
+ body: str,
93
+ key: req.query && req.query.v ? req.query.v : null
72
94
  };
73
- res.writeHead(200, content.headers);
74
- res.end(content.body);
95
+
96
+ res.writeHead(200, cache.headers);
97
+ res.end(cache.body);
75
98
  });
76
99
  };
77
100
  }
78
101
 
79
102
  // ### servePublicFile Middleware
80
103
  // Handles requests to robots.txt and favicon.ico (and caches them)
81
- function servePublicFile(file, type, maxAge) {
82
- const publicFileMiddleware = createPublicFileMiddleware(file, type, maxAge);
104
+ function servePublicFile(location, file, type, maxAge) {
105
+ const publicFileMiddleware = createPublicFileMiddleware(location, file, type, maxAge);
83
106
 
84
107
  return function servePublicFileMiddleware(req, res, next) {
85
108
  if (req.path === '/' + file) {
@@ -104,15 +104,15 @@ module.exports = function setupSiteApp(options = {}) {
104
104
  siteApp.use(mw.serveFavicon());
105
105
 
106
106
  // Serve sitemap.xsl file
107
- siteApp.use(mw.servePublicFile('sitemap.xsl', 'text/xsl', constants.ONE_DAY_S));
107
+ siteApp.use(mw.servePublicFile('static', 'sitemap.xsl', 'text/xsl', constants.ONE_DAY_S));
108
108
 
109
109
  // Serve stylesheets for default templates
110
- siteApp.use(mw.servePublicFile('public/ghost.css', 'text/css', constants.ONE_HOUR_S));
111
- siteApp.use(mw.servePublicFile('public/ghost.min.css', 'text/css', constants.ONE_YEAR_S));
110
+ siteApp.use(mw.servePublicFile('static', 'public/ghost.css', 'text/css', constants.ONE_HOUR_S));
111
+ siteApp.use(mw.servePublicFile('static', 'public/ghost.min.css', 'text/css', constants.ONE_YEAR_S));
112
112
 
113
113
  // Card assets
114
- siteApp.use(mw.servePublicFile('public/cards.min.css', 'text/css', constants.ONE_YEAR_S));
115
- siteApp.use(mw.servePublicFile('public/cards.min.js', 'text/js', constants.ONE_YEAR_S));
114
+ siteApp.use(mw.servePublicFile('built', 'public/cards.min.css', 'text/css', constants.ONE_YEAR_S));
115
+ siteApp.use(mw.servePublicFile('built', 'public/cards.min.js', 'text/js', constants.ONE_YEAR_S));
116
116
 
117
117
  // Serve blog images using the storage adapter
118
118
  siteApp.use(STATIC_IMAGE_URL_PREFIX, mw.handleImageSizes, storage.getStorage('images').serve());
@@ -147,26 +147,23 @@ module.exports = function setupSiteApp(options = {}) {
147
147
  debug('Themes done');
148
148
 
149
149
  // Serve robots.txt if not found in theme
150
- siteApp.use(mw.servePublicFile('robots.txt', 'text/plain', constants.ONE_HOUR_S));
150
+ siteApp.use(mw.servePublicFile('static', 'robots.txt', 'text/plain', constants.ONE_HOUR_S));
151
151
 
152
152
  // site map - this should probably be refactored to be an internal app
153
153
  sitemapHandler(siteApp);
154
154
  debug('Internal apps done');
155
155
 
156
- // send 503 error page in case of maintenance
157
- siteApp.use(shared.middlewares.maintenance);
158
-
159
156
  // Add in all trailing slashes & remove uppercase
160
157
  // must happen AFTER asset loading and BEFORE routing
161
- siteApp.use(shared.middlewares.prettyUrls);
158
+ siteApp.use(shared.middleware.prettyUrls);
162
159
 
163
160
  // ### Caching
164
161
  siteApp.use(function (req, res, next) {
165
162
  // Site frontend is cacheable UNLESS request made by a member or blog is in private mode
166
163
  if (req.member || res.isPrivateBlog) {
167
- return shared.middlewares.cacheControl('private')(req, res, next);
164
+ return shared.middleware.cacheControl('private')(req, res, next);
168
165
  } else {
169
- return shared.middlewares.cacheControl('public', {maxAge: config.get('caching:frontend:maxAge')})(req, res, next);
166
+ return shared.middleware.cacheControl('public', {maxAge: config.get('caching:frontend:maxAge')})(req, res, next);
170
167
  }
171
168
  });
172
169
 
@@ -179,7 +176,7 @@ module.exports = function setupSiteApp(options = {}) {
179
176
  siteApp.use(SiteRouter);
180
177
 
181
178
  // ### Error handlers
182
- siteApp.use(shared.middlewares.errorHandler.pageNotFound);
179
+ siteApp.use(shared.middleware.errorHandler.pageNotFound);
183
180
  config.get('apps:internal').forEach((appName) => {
184
181
  const app = require(path.join(config.get('paths').internalAppPath, appName));
185
182
 
@@ -187,7 +184,7 @@ module.exports = function setupSiteApp(options = {}) {
187
184
  app.setupErrorHandling(siteApp);
188
185
  }
189
186
  });
190
- siteApp.use(shared.middlewares.errorHandler.handleThemeResponse);
187
+ siteApp.use(mw.errorHandler.handleThemeResponse);
191
188
 
192
189
  debug('Site setup end');
193
190
 
@@ -307,7 +307,7 @@ SchedulingDefault.prototype._pingUrl = function (object) {
307
307
  this._pingUrl(object);
308
308
  }, this.retryTimeoutInMs);
309
309
 
310
- logging.error(new errors.GhostError({
310
+ logging.error(new errors.InternalServerError({
311
311
  err,
312
312
  context: 'Retrying...',
313
313
  level: 'normal'
@@ -316,7 +316,7 @@ SchedulingDefault.prototype._pingUrl = function (object) {
316
316
  return;
317
317
  }
318
318
 
319
- logging.error(new errors.GhostError({
319
+ logging.error(new errors.InternalServerError({
320
320
  err,
321
321
  level: 'critical'
322
322
  }));
@@ -147,7 +147,7 @@ class LocalStorageBase extends StorageBase {
147
147
  return next(new errors.NoPermissionError({err: err}));
148
148
  }
149
149
 
150
- return next(new errors.GhostError({err: err}));
150
+ return next(new errors.InternalServerError({err: err}));
151
151
  }
152
152
 
153
153
  next();
@@ -196,7 +196,7 @@ class LocalStorageBase extends StorageBase {
196
196
  return reject(new errors.NoPermissionError({err: err}));
197
197
  }
198
198
 
199
- return reject(new errors.GhostError({
199
+ return reject(new errors.InternalServerError({
200
200
  err: err,
201
201
  message: tpl(this.errorMessages.cannotRead, {file: options.path})
202
202
  }));
@@ -163,7 +163,7 @@ module.exports = {
163
163
  options = Object.assign(options, {context: {internal: true}});
164
164
  return auth.passwordreset.doReset(options, tokenParts, api.settings)
165
165
  .then((params) => {
166
- web.shared.middlewares.api.spamPrevention.userLogin().reset(frame.options.ip, `${tokenParts.email}login`);
166
+ web.shared.middleware.api.spamPrevention.userLogin().reset(frame.options.ip, `${tokenParts.email}login`);
167
167
  return params;
168
168
  });
169
169
  });
@@ -62,7 +62,7 @@ module.exports = {
62
62
  return Promise.resolve()
63
63
  .then(() => exporter.doExport({include: frame.options.withRelated}))
64
64
  .catch((err) => {
65
- return Promise.reject(new errors.GhostError({err: err}));
65
+ return Promise.reject(new errors.InternalServerError({err: err}));
66
66
  });
67
67
  }
68
68
  },
@@ -124,7 +124,7 @@ module.exports = {
124
124
  }, {concurrency: 100});
125
125
  })
126
126
  .catch((err) => {
127
- throw new errors.GhostError({
127
+ throw new errors.InternalServerError({
128
128
  err: err
129
129
  });
130
130
  });
@@ -23,8 +23,9 @@ module.exports = {
23
23
 
24
24
  uploadThumbnail: {
25
25
  permissions: false,
26
- options: [
27
- 'url'
26
+ data: [
27
+ 'url',
28
+ 'ref'
28
29
  ],
29
30
  async query(frame) {
30
31
  const mediaStorage = storage.getStorage('media');
@@ -3,9 +3,24 @@ const externalRequest = require('../../lib/request-external');
3
3
 
4
4
  const OEmbed = require('../../services/oembed');
5
5
  const oembed = new OEmbed({config, externalRequest});
6
+
6
7
  const NFT = require('../../services/nft-oembed');
7
- const nft = new NFT();
8
+ const nft = new NFT({
9
+ config: {
10
+ apiKey: config.get('opensea').privateReadOnlyApiKey
11
+ }
12
+ });
13
+
14
+ const Twitter = require('../../services/twitter-embed');
15
+ const twitter = new Twitter({
16
+ config: {
17
+ bearerToken: config.get('twitter').privateReadOnlyToken
18
+ },
19
+ logging: require('@tryghost/logging')
20
+ });
21
+
8
22
  oembed.registerProvider(nft);
23
+ oembed.registerProvider(twitter);
9
24
 
10
25
  module.exports = {
11
26
  docName: 'oembed',
@@ -42,7 +42,7 @@ const session = {
42
42
  });
43
43
  });
44
44
  }).catch(async (err) => {
45
- if (!errors.utils.isIgnitionError(err)) {
45
+ if (!errors.utils.isGhostError(err)) {
46
46
  throw new errors.UnauthorizedError({
47
47
  message: tpl(messages.accessDenied),
48
48
  err
@@ -43,7 +43,7 @@ module.exports = {
43
43
  return models.Base.Model.generateSlug(allowedTypes[frame.options.type], frame.data.name, {status: 'all'})
44
44
  .then((slug) => {
45
45
  if (!slug) {
46
- return Promise.reject(new errors.GhostError({
46
+ return Promise.reject(new errors.InternalServerError({
47
47
  message: tpl(messages.couldNotGenerateSlug)
48
48
  }));
49
49
  }
@@ -66,11 +66,11 @@ const nonePublicAuth = (apiConfig, frame) => {
66
66
  return Promise.reject(err);
67
67
  }
68
68
 
69
- if (errors.utils.isIgnitionError(err)) {
69
+ if (errors.utils.isGhostError(err)) {
70
70
  return Promise.reject(err);
71
71
  }
72
72
 
73
- return Promise.reject(new errors.GhostError({
73
+ return Promise.reject(new errors.InternalServerError({
74
74
  err: err
75
75
  }));
76
76
  });
@@ -1,5 +1,4 @@
1
1
  const _ = require('lodash');
2
- const labs = require('../../../../../../shared/labs');
3
2
  const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:config');
4
3
 
5
4
  module.exports = {
@@ -18,13 +17,10 @@ module.exports = {
18
17
  'stripeDirect',
19
18
  'mailgunIsConfigured',
20
19
  'emailAnalytics',
21
- 'hostSettings'
20
+ 'hostSettings',
21
+ 'tenor'
22
22
  ];
23
23
 
24
- if (labs.isSet('gifsCard')) {
25
- keys.push('tenorApiKey');
26
- }
27
-
28
24
  frame.response = {
29
25
  config: _.pick(data, keys)
30
26
  };
@@ -145,7 +145,7 @@ module.exports = {
145
145
  options = Object.assign(options, {context: {internal: true}});
146
146
  return auth.passwordreset.doReset(options, tokenParts, api.settings)
147
147
  .then((params) => {
148
- web.shared.middlewares.api.spamPrevention.userLogin().reset(frame.options.ip, `${tokenParts.email}login`);
148
+ web.shared.middleware.api.spamPrevention.userLogin().reset(frame.options.ip, `${tokenParts.email}login`);
149
149
  return params;
150
150
  });
151
151
  });
@@ -51,7 +51,7 @@ module.exports = {
51
51
  return Promise.resolve()
52
52
  .then(() => exporter.doExport({include: frame.options.withRelated}))
53
53
  .catch((err) => {
54
- return Promise.reject(new errors.GhostError({err: err}));
54
+ return Promise.reject(new errors.InternalServerError({err: err}));
55
55
  });
56
56
  }
57
57
  },
@@ -107,7 +107,7 @@ module.exports = {
107
107
  }, {concurrency: 100});
108
108
  })
109
109
  .catch((err) => {
110
- throw new errors.GhostError({
110
+ throw new errors.InternalServerError({
111
111
  err: err
112
112
  });
113
113
  });
@@ -42,7 +42,7 @@ const session = {
42
42
  });
43
43
  });
44
44
  }).catch(async (err) => {
45
- if (!errors.utils.isIgnitionError(err)) {
45
+ if (!errors.utils.isGhostError(err)) {
46
46
  throw new errors.UnauthorizedError({
47
47
  message: tpl(messages.authAccessDenied),
48
48
  err
@@ -40,7 +40,7 @@ module.exports = {
40
40
  return models.Base.Model.generateSlug(allowedTypes[frame.options.type], frame.data.name, {status: 'all'})
41
41
  .then((slug) => {
42
42
  if (!slug) {
43
- return Promise.reject(new errors.GhostError({
43
+ return Promise.reject(new errors.InternalServerError({
44
44
  message: tpl(messages.couldNotGenerateSlug)
45
45
  }));
46
46
  }
@@ -62,11 +62,11 @@ const nonePublicAuth = (apiConfig, frame) => {
62
62
  return Promise.reject(err);
63
63
  }
64
64
 
65
- if (errors.utils.isIgnitionError(err)) {
65
+ if (errors.utils.isGhostError(err)) {
66
66
  return Promise.reject(err);
67
67
  }
68
68
 
69
- return Promise.reject(new errors.GhostError({
69
+ return Promise.reject(new errors.InternalServerError({
70
70
  err: err
71
71
  }));
72
72
  });
@@ -146,7 +146,7 @@ module.exports = {
146
146
  options = Object.assign(options, {context: {internal: true}});
147
147
  return auth.passwordreset.doReset(options, tokenParts, api.settings)
148
148
  .then((params) => {
149
- web.shared.middlewares.api.spamPrevention.userLogin().reset(frame.options.ip, `${tokenParts.email}login`);
149
+ web.shared.middleware.api.spamPrevention.userLogin().reset(frame.options.ip, `${tokenParts.email}login`);
150
150
  return params;
151
151
  });
152
152
  });
@@ -62,7 +62,7 @@ module.exports = {
62
62
  return Promise.resolve()
63
63
  .then(() => exporter.doExport({include: frame.options.withRelated}))
64
64
  .catch((err) => {
65
- return Promise.reject(new errors.GhostError({err: err}));
65
+ return Promise.reject(new errors.InternalServerError({err: err}));
66
66
  });
67
67
  }
68
68
  },
@@ -118,7 +118,7 @@ module.exports = {
118
118
  }, {concurrency: 100});
119
119
  })
120
120
  .catch((err) => {
121
- throw new errors.GhostError({
121
+ throw new errors.InternalServerError({
122
122
  err: err
123
123
  });
124
124
  });
@@ -42,7 +42,7 @@ const session = {
42
42
  });
43
43
  });
44
44
  }).catch(async (err) => {
45
- if (!errors.utils.isIgnitionError(err)) {
45
+ if (!errors.utils.isGhostError(err)) {
46
46
  throw new errors.UnauthorizedError({
47
47
  message: tpl(messages.accessDenied),
48
48
  err
@@ -39,7 +39,7 @@ module.exports = {
39
39
  return models.Base.Model.generateSlug(allowedTypes[frame.options.type], frame.data.name, {status: 'all'})
40
40
  .then((slug) => {
41
41
  if (!slug) {
42
- return Promise.reject(new errors.GhostError({message: tpl(messages.couldNotGenerateSlug)}));
42
+ return Promise.reject(new errors.InternalServerError({message: tpl(messages.couldNotGenerateSlug)}));
43
43
  }
44
44
  return slug;
45
45
  });
@@ -66,11 +66,11 @@ const nonePublicAuth = (apiConfig, frame) => {
66
66
  return Promise.reject(err);
67
67
  }
68
68
 
69
- if (errors.utils.isIgnitionError(err)) {
69
+ if (errors.utils.isGhostError(err)) {
70
70
  return Promise.reject(err);
71
71
  }
72
72
 
73
- return Promise.reject(new errors.GhostError({
73
+ return Promise.reject(new errors.InternalServerError({
74
74
  err: err
75
75
  }));
76
76
  });
@@ -17,6 +17,13 @@ function configure(dbConfig) {
17
17
  dbConfig.pool = {
18
18
  afterCreate(conn, cb) {
19
19
  conn.run('PRAGMA foreign_keys = ON', cb);
20
+
21
+ // These two are meant to improve performance at the cost of reliability
22
+ // Should be safe for tests. We add them here and leave them on
23
+ if (config.get('env').startsWith('testing')) {
24
+ conn.run('PRAGMA synchronous = OFF;');
25
+ conn.run('PRAGMA journal_mode = TRUNCATE;');
26
+ }
20
27
  }
21
28
  };
22
29
 
@@ -59,8 +59,8 @@ class DatabaseStateManager {
59
59
 
60
60
  // CASE: database connection errors, unknown cases
61
61
  let errorToThrow = error;
62
- if (!errors.utils.isIgnitionError(errorToThrow)) {
63
- errorToThrow = new errors.GhostError({message: errorToThrow.message, err: errorToThrow});
62
+ if (!errors.utils.isGhostError(errorToThrow)) {
63
+ errorToThrow = new errors.InternalServerError({message: errorToThrow.message, err: errorToThrow});
64
64
  }
65
65
 
66
66
  throw errorToThrow;
@@ -94,8 +94,8 @@ class DatabaseStateManager {
94
94
  }
95
95
  } catch (error) {
96
96
  let errorToThrow = error;
97
- if (!errors.utils.isIgnitionError(error)) {
98
- errorToThrow = new errors.GhostError({message: errorToThrow.message, err: errorToThrow});
97
+ if (!errors.utils.isGhostError(error)) {
98
+ errorToThrow = new errors.InternalServerError({message: errorToThrow.message, err: errorToThrow});
99
99
  }
100
100
 
101
101
  throw errorToThrow;
@@ -26,7 +26,7 @@ const exportFileName = async function exportFileName(options) {
26
26
 
27
27
  return title + 'ghost.' + datetime + '.json';
28
28
  } catch (err) {
29
- logging.error(new errors.GhostError({err: err}));
29
+ logging.error(new errors.InternalServerError({err: err}));
30
30
  return 'ghost.' + datetime + '.json';
31
31
  }
32
32
  };
@@ -30,7 +30,7 @@ JSONHandler = {
30
30
  // if importData follows JSON-API format `{ db: [exportedData] }`
31
31
  if (_.keys(importData).length === 1) {
32
32
  if (!importData.db || !Array.isArray(importData.db)) {
33
- throw new errors.GhostError({
33
+ throw new errors.InternalServerError({
34
34
  message: tpl(messages.invalidJsonFormat)
35
35
  });
36
36
  }
@@ -121,7 +121,7 @@ class ImportManager {
121
121
 
122
122
  fs.remove(self.fileToDelete, function (err) {
123
123
  if (err) {
124
- logging.error(new errors.GhostError({
124
+ logging.error(new errors.InternalServerError({
125
125
  err: err,
126
126
  context: tpl(messages.couldNotCleanUpFile.error),
127
127
  help: tpl(messages.couldNotCleanUpFile.context)
@@ -145,7 +145,7 @@ class Base {
145
145
  });
146
146
  }
147
147
  } else {
148
- if (!errors.utils.isIgnitionError(err)) {
148
+ if (!errors.utils.isGhostError(err)) {
149
149
  err = new errors.DataImportError({
150
150
  message: err.message,
151
151
  context: JSON.stringify(obj),
@@ -112,7 +112,7 @@ DataImporter = {
112
112
  });
113
113
  });
114
114
 
115
- sequence(ops)
115
+ return sequence(ops)
116
116
  .then(function () {
117
117
  results.forEach(function (promise) {
118
118
  if (!promise.isFulfilled()) {
@@ -121,9 +121,9 @@ DataImporter = {
121
121
  });
122
122
 
123
123
  if (errors.length === 0) {
124
- transacting.commit();
124
+ return;
125
125
  } else {
126
- transacting.rollback(errors);
126
+ throw errors;
127
127
  }
128
128
  });
129
129
  }).then(function () {
@@ -1,26 +1,9 @@
1
- const Promise = require('bluebird');
2
- const _ = require('lodash');
3
- const fixtures = require('../../schema/fixtures');
4
- const logging = require('@tryghost/logging');
1
+ const {fixtureManager} = require('../../schema/fixtures');
5
2
 
6
3
  module.exports.config = {
7
4
  transaction: true
8
5
  };
9
6
 
10
- module.exports.up = async (options) => {
11
- const localOptions = _.merge({
12
- context: {internal: true},
13
- migrating: true
14
- }, options);
15
-
16
- await Promise.mapSeries(fixtures.models, async (model) => {
17
- logging.info('Model: ' + model.name);
18
-
19
- await fixtures.utils.addFixturesForModel(model, localOptions);
20
- });
21
-
22
- await Promise.mapSeries(fixtures.relations, async (relation) => {
23
- logging.info('Relation: ' + relation.from.model + ' to ' + relation.to.model);
24
- await fixtures.utils.addFixturesForRelation(relation, localOptions);
25
- });
7
+ module.exports.up = async function insertFixtures(options) {
8
+ return await fixtureManager.addAllFixtures(options);
26
9
  };
@@ -143,7 +143,7 @@ function addPermissionToRole(config) {
143
143
  }).first();
144
144
 
145
145
  if (!permission) {
146
- throw new errors.GhostError({
146
+ throw new errors.InternalServerError({
147
147
  message: tpl(messages.permissionRoleActionError, {
148
148
  action: 'add',
149
149
  permission: config.permission,
@@ -158,7 +158,7 @@ function addPermissionToRole(config) {
158
158
  }).first();
159
159
 
160
160
  if (!role) {
161
- throw new errors.GhostError({
161
+ throw new errors.InternalServerError({
162
162
  message: tpl(messages.permissionRoleActionError, {
163
163
  action: 'add',
164
164
  permission: config.permission,
@@ -1,19 +1,19 @@
1
1
  const merge = require('lodash/merge');
2
- const utils = require('../../../schema/fixtures/utils');
2
+ const {fixtureManager} = require('../../../schema/fixtures');
3
3
  const models = require('../../../../models');
4
4
  const permissions = require('../../../../services/permissions');
5
5
  const logging = require('@tryghost/logging');
6
6
  const _private = {};
7
7
 
8
8
  _private.addRole = function addRole(options) {
9
- const contributorRole = utils.findModelFixtureEntry('Role', {name: 'Contributor'});
9
+ const contributorRole = fixtureManager.findModelFixtureEntry('Role', {name: 'Contributor'});
10
10
  const message = 'Adding "Contributor" role to roles table';
11
11
 
12
12
  return models.Role.findOne({name: contributorRole.name}, options)
13
13
  .then((role) => {
14
14
  if (!role) {
15
15
  logging.info(message);
16
- return utils.addFixturesForModel({name: 'Role', entries: [contributorRole]}, options);
16
+ return fixtureManager.addFixturesForModel({name: 'Role', entries: [contributorRole]}, options);
17
17
  }
18
18
 
19
19
  logging.warn(message);
@@ -22,10 +22,10 @@ _private.addRole = function addRole(options) {
22
22
  };
23
23
 
24
24
  _private.addContributorPermissions = function getPermissions(options) {
25
- const relations = utils.findRelationFixture('Role', 'Permission');
25
+ const relations = fixtureManager.findRelationFixture('Role', 'Permission');
26
26
  const message = 'Adding permissions_roles fixtures for the contributor role';
27
27
 
28
- return utils.addFixturesForRelation({
28
+ return fixtureManager.addFixturesForRelation({
29
29
  from: relations.from,
30
30
  to: relations.to,
31
31
  entries: {