ghost 4.42.1 → 4.44.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.
- package/content/themes/casper/package.json +2 -3
- package/content/themes/casper/partials/post-card.hbs +1 -1
- package/core/built/assets/ghost-dark-470c1ef06b10e5c40ad05f3a642eaaea.css +1 -0
- package/core/built/assets/{ghost.min-20096eef632760c3a2906e243adbd24b.js → ghost.min-1e7dce606e92a03207d15ae7eb3d3c23.js} +411 -323
- package/core/built/assets/ghost.min-d0c17e8314b5583c0df5d05fab3c051c.css +1 -0
- package/core/built/assets/{vendor.min-21f79c68a284acb1b70039f3f63e5507.js → vendor.min-fe2c9b1235b4119b5406b788db2db434.js} +88 -82
- package/core/frontend/apps/amp/lib/helpers/amp_analytics.js +1 -1
- package/core/frontend/apps/amp/lib/helpers/amp_components.js +1 -1
- package/core/frontend/apps/amp/lib/helpers/amp_content.js +1 -1
- package/core/frontend/apps/amp/lib/helpers/amp_style.js +1 -1
- package/core/frontend/apps/amp/lib/router.js +6 -5
- package/core/frontend/apps/private-blogging/lib/helpers/input_password.js +1 -1
- package/core/frontend/apps/private-blogging/lib/router.js +2 -2
- package/core/frontend/helpers/asset.js +1 -1
- package/core/frontend/helpers/author.js +1 -1
- package/core/frontend/helpers/authors.js +1 -1
- package/core/frontend/helpers/body_class.js +1 -1
- package/core/frontend/helpers/cancel_link.js +1 -1
- package/core/frontend/helpers/concat.js +1 -1
- package/core/frontend/helpers/content.js +1 -1
- package/core/frontend/helpers/date.js +1 -1
- package/core/frontend/helpers/encode.js +1 -1
- package/core/frontend/helpers/excerpt.js +1 -1
- package/core/frontend/helpers/facebook_url.js +1 -1
- package/core/frontend/helpers/foreach.js +2 -2
- package/core/frontend/helpers/get.js +1 -1
- package/core/frontend/helpers/ghost_foot.js +1 -1
- package/core/frontend/helpers/ghost_head.js +1 -1
- package/core/frontend/helpers/lang.js +1 -1
- package/core/frontend/helpers/link.js +1 -1
- package/core/frontend/helpers/link_class.js +1 -1
- package/core/frontend/helpers/match.js +1 -1
- package/core/frontend/helpers/navigation.js +1 -1
- package/core/frontend/helpers/pagination.js +1 -1
- package/core/frontend/helpers/plural.js +1 -1
- package/core/frontend/helpers/post_class.js +1 -1
- package/core/frontend/helpers/prev_post.js +6 -5
- package/core/frontend/helpers/products.js +1 -1
- package/core/frontend/helpers/reading_time.js +2 -2
- package/core/frontend/helpers/t.js +1 -1
- package/core/frontend/helpers/tags.js +1 -1
- package/core/frontend/helpers/tiers.js +1 -1
- package/core/frontend/helpers/title.js +1 -1
- package/core/frontend/helpers/twitter_url.js +1 -1
- package/core/frontend/helpers/url.js +1 -1
- package/core/frontend/meta/url.js +4 -4
- package/core/{server/data/schema → frontend/services/data}/checks.js +4 -4
- package/core/frontend/services/{routing/helpers → data}/entry-lookup.js +3 -3
- package/core/frontend/services/{routing/helpers → data}/fetch-data.js +3 -3
- package/core/frontend/services/data/index.js +5 -0
- package/core/frontend/services/{rendering.js → handlebars.js} +2 -1
- package/core/frontend/services/helpers/handlebars.js +1 -1
- package/core/frontend/services/proxy.js +2 -4
- package/core/frontend/services/{routing/helpers → rendering}/context.js +0 -0
- package/core/frontend/services/{routing/helpers → rendering}/error.js +0 -0
- package/core/frontend/services/{routing/helpers → rendering}/format-response.js +1 -1
- package/core/frontend/services/{routing/helpers → rendering}/index.js +0 -8
- package/core/frontend/services/{routing/helpers → rendering}/render-entries.js +1 -1
- package/core/frontend/services/{routing/helpers → rendering}/render-entry.js +1 -1
- package/core/frontend/services/{routing/helpers → rendering}/renderer.js +1 -1
- package/core/frontend/services/{routing/helpers → rendering}/secure.js +0 -0
- package/core/frontend/services/{routing/helpers → rendering}/templates.js +2 -2
- package/core/frontend/services/routing/CollectionRouter.js +1 -1
- package/core/frontend/services/routing/controllers/channel.js +9 -9
- package/core/frontend/services/routing/controllers/collection.js +9 -9
- package/core/frontend/services/routing/controllers/email-post.js +5 -6
- package/core/frontend/services/routing/controllers/entry.js +6 -6
- package/core/frontend/services/routing/controllers/preview.js +5 -6
- package/core/frontend/services/routing/controllers/rss.js +4 -3
- package/core/frontend/services/routing/controllers/static.js +5 -5
- package/core/frontend/services/routing/controllers/unsubscribe.js +2 -2
- package/core/frontend/services/routing/index.js +0 -4
- package/core/frontend/web/middleware/error-handler.js +2 -2
- package/core/server/api/canary/authentication.js +2 -2
- package/core/server/api/canary/posts.js +1 -0
- package/core/server/api/canary/stats.js +9 -0
- package/core/server/api/canary/utils/serializers/output/members.js +8 -0
- package/core/server/api/canary/utils/validators/input/index.js +6 -0
- package/core/server/api/shared/http.js +52 -51
- package/core/server/data/exporter/table-lists.js +1 -0
- package/core/server/data/migrations/utils.js +33 -1
- package/core/server/data/migrations/versions/4.42/2022-03-21-17-17-add.js +5 -0
- package/core/server/data/migrations/versions/4.43/2022-03-28-19-26-recreate-newsletter-table.js +29 -0
- package/core/server/data/migrations/versions/4.43/2022-03-29-14-45-add-members-newsletters-table.js +7 -0
- package/core/server/data/migrations/versions/4.43/2022-04-01-10-13-add-post-newsletter-relation.js +108 -0
- package/core/server/data/migrations/versions/4.43/2022-04-06-09-47-add-type-column-to-paid-subscription-events.js +7 -0
- package/core/server/data/migrations/versions/4.43/2022-04-06-14-56-add-email-newsletter-relation.js +8 -0
- package/core/server/data/migrations/versions/4.43/2022-04-08-10-45-add-subscription-id-to-mrr-events.js +7 -0
- package/core/server/data/migrations/versions/4.44/2022-04-06-15-22-populate-type-column-for-paid-subscription-events.js +21 -0
- package/core/server/data/migrations/versions/4.44/2022-04-08-11-54-add-cancelled-events.js +51 -0
- package/core/server/data/migrations/versions/4.44/2022-04-11-08-24-add-newsletter-permissions.js +33 -0
- package/core/server/data/migrations/versions/4.44/2022-04-11-10-54-add-mrr-to-subscriptions.js +8 -0
- package/core/server/data/migrations/versions/4.44/2022-04-12-07-33-fill-mrr.js +29 -0
- package/core/server/data/migrations/versions/4.44/2022-04-13-12-00-remove-newsletter-sender-name-not-null-constraint.js +33 -0
- package/core/server/data/migrations/versions/4.44/2022-04-15-07-53-add-offer-id-to-subscriptions.js +9 -0
- package/core/server/data/schema/commands.js +6 -1
- package/core/server/data/schema/fixtures/fixtures.json +26 -1
- package/core/server/data/schema/index.js +0 -1
- package/core/server/data/schema/schema.js +36 -16
- package/core/server/models/base/bookshelf.js +1 -1
- package/core/server/models/base/plugins/crud.js +8 -0
- package/core/server/models/member.js +21 -1
- package/core/server/models/newsletter.js +42 -1
- package/core/server/models/post.js +12 -2
- package/core/server/models/stripe-customer-subscription.js +4 -0
- package/core/server/services/auth/setup.js +21 -8
- package/core/server/services/mega/mega.js +3 -1
- package/core/server/services/members/api.js +3 -1
- package/core/server/services/members/middleware.js +13 -3
- package/core/server/services/members/service.js +3 -11
- package/core/server/services/members/utils.js +13 -1
- package/core/server/services/newsletters/index.js +10 -0
- package/core/server/services/newsletters/service.js +24 -0
- package/core/server/services/posts/posts-service.js +20 -1
- package/core/server/services/slack.js +11 -3
- package/core/server/services/stats/lib/members-stats-service.js +30 -34
- package/core/server/services/stats/lib/mrr-stats-service.js +154 -0
- package/core/server/services/stats/service.js +3 -1
- package/core/server/services/stripe/service.js +1 -0
- package/core/server/web/admin/views/default-prod.html +4 -4
- package/core/server/web/admin/views/default.html +4 -4
- package/core/server/web/api/canary/admin/routes.js +1 -0
- package/core/shared/config/defaults.json +2 -2
- package/package.json +39 -39
- package/yarn.lock +410 -369
- package/content/themes/casper/assets/css/csscomb.json +0 -240
- package/core/built/assets/ghost-dark-a93afb20027060d760ac6d78f115a76f.css +0 -1
- package/core/built/assets/ghost.min-ce35ef1b76d9a943ab912c076773b132.css +0 -1
|
File without changes
|
|
@@ -6,8 +6,8 @@ const _ = require('lodash');
|
|
|
6
6
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const url = require('url');
|
|
9
|
-
const config = require('
|
|
10
|
-
const themeEngine = require('
|
|
9
|
+
const config = require('../../../shared/config');
|
|
10
|
+
const themeEngine = require('../theme-engine');
|
|
11
11
|
const _private = {};
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -30,7 +30,7 @@ class CollectionRouter extends ParentRouter {
|
|
|
30
30
|
value: object.permalink
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
// @NOTE: see
|
|
33
|
+
// @NOTE: see renderer/templates - we use unshift to prepend the templates
|
|
34
34
|
this.templates = (object.templates || []).reverse();
|
|
35
35
|
|
|
36
36
|
this.filter = object.filter;
|
|
@@ -4,7 +4,8 @@ const tpl = require('@tryghost/tpl');
|
|
|
4
4
|
const errors = require('@tryghost/errors');
|
|
5
5
|
const security = require('@tryghost/security');
|
|
6
6
|
const themeEngine = require('../../theme-engine');
|
|
7
|
-
const
|
|
7
|
+
const dataService = require('../../data');
|
|
8
|
+
const renderer = require('../../rendering');
|
|
8
9
|
|
|
9
10
|
const messages = {
|
|
10
11
|
pageNotFound: 'Page not found.'
|
|
@@ -50,7 +51,7 @@ module.exports = function channelController(req, res, next) {
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
return
|
|
54
|
+
return dataService.fetchData(pathOptions, res.routerOptions, res.locals)
|
|
54
55
|
.then(function handleResult(result) {
|
|
55
56
|
// CASE: requested page is greater than number of pages we have
|
|
56
57
|
if (pathOptions.page > result.meta.pagination.pages) {
|
|
@@ -60,16 +61,15 @@ module.exports = function channelController(req, res, next) {
|
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
// Format data 1
|
|
63
|
-
// @TODO: See
|
|
64
|
-
|
|
64
|
+
// @TODO: See renderer/secure for explanation.
|
|
65
|
+
renderer.secure(req, result.posts);
|
|
65
66
|
|
|
66
|
-
// @TODO: See
|
|
67
|
+
// @TODO: See renderer/secure for explanation.
|
|
67
68
|
_.each(result.data, function (data) {
|
|
68
|
-
|
|
69
|
+
renderer.secure(req, data);
|
|
69
70
|
});
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
return renderer(result);
|
|
72
|
+
return renderer.renderEntries(req, res)(result);
|
|
73
73
|
})
|
|
74
|
-
.catch(
|
|
74
|
+
.catch(renderer.handleError(next));
|
|
75
75
|
};
|
|
@@ -5,7 +5,8 @@ const errors = require('@tryghost/errors');
|
|
|
5
5
|
const security = require('@tryghost/security');
|
|
6
6
|
const {routerManager} = require('../');
|
|
7
7
|
const themeEngine = require('../../theme-engine');
|
|
8
|
-
const
|
|
8
|
+
const renderer = require('../../rendering');
|
|
9
|
+
const dataService = require('../../data');
|
|
9
10
|
|
|
10
11
|
const messages = {
|
|
11
12
|
pageNotFound: 'Page not found.'
|
|
@@ -49,7 +50,7 @@ module.exports = function collectionController(req, res, next) {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
debug('fetching data');
|
|
52
|
-
return
|
|
53
|
+
return dataService.fetchData(pathOptions, res.routerOptions, res.locals)
|
|
53
54
|
.then(function handleResult(result) {
|
|
54
55
|
// CASE: requested page is greater than number of pages we have
|
|
55
56
|
if (pathOptions.page > result.meta.pagination.pages) {
|
|
@@ -80,16 +81,15 @@ module.exports = function collectionController(req, res, next) {
|
|
|
80
81
|
});
|
|
81
82
|
|
|
82
83
|
// Format data 1
|
|
83
|
-
// @TODO: See
|
|
84
|
-
|
|
84
|
+
// @TODO: See renderer/secure for explanation.
|
|
85
|
+
renderer.secure(req, result.posts);
|
|
85
86
|
|
|
86
|
-
// @TODO: See
|
|
87
|
+
// @TODO: See renderer/secure for explanation.
|
|
87
88
|
_.each(result.data, function (data) {
|
|
88
|
-
|
|
89
|
+
renderer.secure(req, data);
|
|
89
90
|
});
|
|
90
91
|
|
|
91
|
-
|
|
92
|
-
return renderer(result);
|
|
92
|
+
return renderer.renderEntries(req, res)(result);
|
|
93
93
|
})
|
|
94
|
-
.catch(
|
|
94
|
+
.catch(renderer.handleError(next));
|
|
95
95
|
};
|
|
@@ -2,7 +2,7 @@ const debug = require('@tryghost/debug')('services:routing:controllers:emailpost
|
|
|
2
2
|
const config = require('../../../../shared/config');
|
|
3
3
|
const {routerManager} = require('../');
|
|
4
4
|
const urlUtils = require('../../../../shared/url-utils');
|
|
5
|
-
const
|
|
5
|
+
const renderer = require('../../rendering');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @description Email Post Controller.
|
|
@@ -55,11 +55,10 @@ module.exports = function emailPostController(req, res, next) {
|
|
|
55
55
|
post.access = !!post.html;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
// @TODO: See
|
|
59
|
-
|
|
58
|
+
// @TODO: See renderer/secure
|
|
59
|
+
renderer.secure(req, post);
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
return renderer(post);
|
|
61
|
+
return renderer.renderEntry(req, res)(post);
|
|
63
62
|
})
|
|
64
|
-
.catch(
|
|
63
|
+
.catch(renderer.handleError(next));
|
|
65
64
|
};
|
|
@@ -3,7 +3,8 @@ const url = require('url');
|
|
|
3
3
|
const config = require('../../../../shared/config');
|
|
4
4
|
const {routerManager} = require('../');
|
|
5
5
|
const urlUtils = require('../../../../shared/url-utils');
|
|
6
|
-
const
|
|
6
|
+
const dataService = require('../../data');
|
|
7
|
+
const renderer = require('../../rendering');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* @description Entry controller.
|
|
@@ -15,7 +16,7 @@ const helpers = require('../helpers');
|
|
|
15
16
|
module.exports = function entryController(req, res, next) {
|
|
16
17
|
debug('entryController', res.routerOptions);
|
|
17
18
|
|
|
18
|
-
return
|
|
19
|
+
return dataService.entryLookup(req.path, res.routerOptions, res.locals)
|
|
19
20
|
.then(function then(lookup) {
|
|
20
21
|
// Format data 1
|
|
21
22
|
const entry = lookup ? lookup.entry : false;
|
|
@@ -83,10 +84,9 @@ module.exports = function entryController(req, res, next) {
|
|
|
83
84
|
}));
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
|
|
87
|
+
renderer.secure(req, entry);
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
return renderer(entry);
|
|
89
|
+
return renderer.renderEntry(req, res)(entry);
|
|
90
90
|
})
|
|
91
|
-
.catch(
|
|
91
|
+
.catch(renderer.handleError(next));
|
|
92
92
|
};
|
|
@@ -2,7 +2,7 @@ const debug = require('@tryghost/debug')('services:routing:controllers:preview')
|
|
|
2
2
|
const config = require('../../../../shared/config');
|
|
3
3
|
const {routerManager} = require('../');
|
|
4
4
|
const urlUtils = require('../../../../shared/url-utils');
|
|
5
|
-
const
|
|
5
|
+
const renderer = require('../../rendering');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @description Preview Controller.
|
|
@@ -57,11 +57,10 @@ module.exports = function previewController(req, res, next) {
|
|
|
57
57
|
post.access = !!post.html;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// @TODO: See
|
|
61
|
-
|
|
60
|
+
// @TODO: See renderer/secure
|
|
61
|
+
renderer.secure(req, post);
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
return renderer(post);
|
|
63
|
+
return renderer.renderEntry(req, res)(post);
|
|
65
64
|
})
|
|
66
|
-
.catch(
|
|
65
|
+
.catch(renderer.handleError(next));
|
|
67
66
|
};
|
|
@@ -4,7 +4,8 @@ const url = require('url');
|
|
|
4
4
|
const security = require('@tryghost/security');
|
|
5
5
|
const settingsCache = require('../../../../shared/settings-cache');
|
|
6
6
|
const rssService = require('../../rss');
|
|
7
|
-
const
|
|
7
|
+
const renderer = require('../../rendering');
|
|
8
|
+
const dataService = require('../../data');
|
|
8
9
|
|
|
9
10
|
// @TODO: is this really correct? Should we be using meta data title?
|
|
10
11
|
function getTitle(relatedData) {
|
|
@@ -35,7 +36,7 @@ module.exports = function rssController(req, res, next) {
|
|
|
35
36
|
// @TODO: This belongs to the rss service O_o
|
|
36
37
|
const baseUrl = url.parse(req.originalUrl).pathname;
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
dataService.fetchData(pathOptions, res.routerOptions, res.locals)
|
|
39
40
|
.then(function formatResult(result) {
|
|
40
41
|
const response = _.pick(result, ['posts', 'meta']);
|
|
41
42
|
|
|
@@ -47,5 +48,5 @@ module.exports = function rssController(req, res, next) {
|
|
|
47
48
|
.then(function (data) {
|
|
48
49
|
return rssService.render(res, baseUrl, data);
|
|
49
50
|
})
|
|
50
|
-
.catch(
|
|
51
|
+
.catch(renderer.handleError(next));
|
|
51
52
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const Promise = require('bluebird');
|
|
3
3
|
const debug = require('@tryghost/debug')('services:routing:controllers:static');
|
|
4
|
-
const
|
|
4
|
+
const renderer = require('../../rendering');
|
|
5
5
|
|
|
6
6
|
function processQuery(query, locals) {
|
|
7
7
|
const api = require('../../proxy').api[locals.apiVersion];
|
|
@@ -60,12 +60,12 @@ module.exports = function staticController(req, res, next) {
|
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
// @TODO: See
|
|
63
|
+
// @TODO: See renderer/secure for more context.
|
|
64
64
|
_.each(response.data, function (data) {
|
|
65
|
-
|
|
65
|
+
renderer.secure(req, data);
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
renderer.renderer(req, res, renderer.formatResponse.entries(response));
|
|
69
69
|
})
|
|
70
|
-
.catch(
|
|
70
|
+
.catch(renderer.handleError(next));
|
|
71
71
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('services:routing:controllers:unsubscribe');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const megaService = require('../../../../server/services/mega');
|
|
4
|
-
const
|
|
4
|
+
const renderer = require('../../rendering');
|
|
5
5
|
|
|
6
6
|
module.exports = async function unsubscribeController(req, res) {
|
|
7
7
|
debug('unsubscribeController');
|
|
@@ -22,5 +22,5 @@ module.exports = async function unsubscribeController(req, res) {
|
|
|
22
22
|
defaultTemplate: path.resolve(__dirname, '../../../views/', templateName)
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
return
|
|
25
|
+
return renderer.renderer(req, res, data);
|
|
26
26
|
};
|
|
@@ -4,7 +4,7 @@ const tpl = require('@tryghost/tpl');
|
|
|
4
4
|
const sentry = require('../../../shared/sentry');
|
|
5
5
|
|
|
6
6
|
const config = require('../../../shared/config');
|
|
7
|
-
const
|
|
7
|
+
const renderer = require('../../services/rendering');
|
|
8
8
|
|
|
9
9
|
// @TODO: make this properly shared code
|
|
10
10
|
const {prepareError, prepareStack} = require('@tryghost/mw-error-handler');
|
|
@@ -55,7 +55,7 @@ const themeErrorRenderer = (err, req, res, next) => {
|
|
|
55
55
|
|
|
56
56
|
// Template
|
|
57
57
|
// @TODO: very dirty !!!!!!
|
|
58
|
-
|
|
58
|
+
renderer.templates.setTemplate(req, res);
|
|
59
59
|
|
|
60
60
|
// It can be that something went wrong with the theme or otherwise loading handlebars
|
|
61
61
|
// This ensures that no matter what res.render will work here
|
|
@@ -51,14 +51,14 @@ module.exports = {
|
|
|
51
51
|
})
|
|
52
52
|
.then((data) => {
|
|
53
53
|
try {
|
|
54
|
-
return auth.setup.doFixtures(data
|
|
54
|
+
return auth.setup.doFixtures(data);
|
|
55
55
|
} catch (e) {
|
|
56
56
|
return data;
|
|
57
57
|
}
|
|
58
58
|
})
|
|
59
59
|
.then((data) => {
|
|
60
60
|
try {
|
|
61
|
-
return auth.setup.
|
|
61
|
+
return auth.setup.doProductAndNewsletter(data, api);
|
|
62
62
|
} catch (e) {
|
|
63
63
|
return data;
|
|
64
64
|
}
|
|
@@ -10,5 +10,14 @@ module.exports = {
|
|
|
10
10
|
async query() {
|
|
11
11
|
return await statsService.members.getCountHistory();
|
|
12
12
|
}
|
|
13
|
+
},
|
|
14
|
+
mrr: {
|
|
15
|
+
permissions: {
|
|
16
|
+
docName: 'members',
|
|
17
|
+
method: 'browse'
|
|
18
|
+
},
|
|
19
|
+
async query() {
|
|
20
|
+
return await statsService.mrr.getHistory();
|
|
21
|
+
}
|
|
13
22
|
}
|
|
14
23
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
//@ts-check
|
|
2
2
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:members');
|
|
3
3
|
const {unparse} = require('@tryghost/members-csv');
|
|
4
|
+
const labsService = require('../../../../../../shared/labs');
|
|
4
5
|
|
|
5
6
|
module.exports = {
|
|
6
7
|
hasActiveStripeSubscriptions: createSerializer('hasActiveStripeSubscriptions', passthrough),
|
|
@@ -131,6 +132,13 @@ function serializeMember(member, options) {
|
|
|
131
132
|
serialized.products = json.products;
|
|
132
133
|
}
|
|
133
134
|
|
|
135
|
+
if (json.newsletters && labsService.isSet('multipleNewsletters')) {
|
|
136
|
+
json.newsletters.sort((a, b) => {
|
|
137
|
+
return a.sort_order - b.sort_order;
|
|
138
|
+
});
|
|
139
|
+
serialized.newsletters = json.newsletters;
|
|
140
|
+
}
|
|
141
|
+
|
|
134
142
|
return serialized;
|
|
135
143
|
}
|
|
136
144
|
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// ESLint Override Notice
|
|
2
|
+
// This is a valid index.js file - it just exports a lot of stuff!
|
|
3
|
+
// Long term we would like to change the API architecture to reduce this file,
|
|
4
|
+
// but that's not the problem the index.js max - line eslint "proxy" rule is there to solve.
|
|
5
|
+
/* eslint-disable max-lines */
|
|
6
|
+
|
|
1
7
|
module.exports = {
|
|
2
8
|
get passwordreset() {
|
|
3
9
|
return require('./passwordreset');
|
|
@@ -13,7 +13,7 @@ const models = require('../../models');
|
|
|
13
13
|
* @return {Function}
|
|
14
14
|
*/
|
|
15
15
|
const http = (apiImpl) => {
|
|
16
|
-
return (req, res, next) => {
|
|
16
|
+
return async (req, res, next) => {
|
|
17
17
|
debug(`External API request to ${req.url}`);
|
|
18
18
|
let apiKey = null;
|
|
19
19
|
let integration = null;
|
|
@@ -60,69 +60,70 @@ const http = (apiImpl) => {
|
|
|
60
60
|
data: apiImpl.data
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
debug(`External API request to ${frame.docName}.${frame.method}`);
|
|
66
|
-
return shared.headers.get(result, apiImpl.headers, frame)
|
|
67
|
-
.then(headers => ({result, headers}));
|
|
68
|
-
})
|
|
69
|
-
.then(({result, headers}) => {
|
|
70
|
-
// CASE: api ctrl wants to handle the express response (e.g. streams)
|
|
71
|
-
if (typeof result === 'function') {
|
|
72
|
-
debug('ctrl function call');
|
|
73
|
-
return result(req, res, next);
|
|
74
|
-
}
|
|
63
|
+
try {
|
|
64
|
+
const result = await apiImpl(frame);
|
|
75
65
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
statusCode = apiImpl.statusCode(result);
|
|
79
|
-
} else if (apiImpl.statusCode) {
|
|
80
|
-
statusCode = apiImpl.statusCode;
|
|
81
|
-
}
|
|
66
|
+
debug(`External API request to ${frame.docName}.${frame.method}`);
|
|
67
|
+
const headers = await shared.headers.get(result, apiImpl.headers, frame) || {};
|
|
82
68
|
|
|
83
|
-
|
|
69
|
+
// CASE: api ctrl wants to handle the express response (e.g. streams)
|
|
70
|
+
if (typeof result === 'function') {
|
|
71
|
+
debug('ctrl function call');
|
|
72
|
+
return result(req, res, next);
|
|
73
|
+
}
|
|
84
74
|
|
|
85
|
-
|
|
86
|
-
|
|
75
|
+
let statusCode = 200;
|
|
76
|
+
if (typeof apiImpl.statusCode === 'function') {
|
|
77
|
+
statusCode = apiImpl.statusCode(result);
|
|
78
|
+
} else if (apiImpl.statusCode) {
|
|
79
|
+
statusCode = apiImpl.statusCode;
|
|
80
|
+
}
|
|
87
81
|
|
|
88
|
-
|
|
89
|
-
if (format === 'plain') {
|
|
90
|
-
debug('plain text response');
|
|
91
|
-
return res.send(result);
|
|
92
|
-
}
|
|
82
|
+
res.status(statusCode);
|
|
93
83
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
84
|
+
// CASE: generate headers based on the api ctrl configuration
|
|
85
|
+
if (req && req.headers && req.headers['accept-version'] && res.locals) {
|
|
86
|
+
headers['Content-Version'] = `v${res.locals.safeVersion}`;
|
|
87
|
+
}
|
|
88
|
+
res.set(headers);
|
|
97
89
|
|
|
98
|
-
|
|
90
|
+
const send = (format) => {
|
|
91
|
+
if (format === 'plain') {
|
|
92
|
+
debug('plain text response');
|
|
93
|
+
return res.send(result);
|
|
94
|
+
}
|
|
99
95
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
debug('json response');
|
|
97
|
+
res.json(result || {});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
let responseFormat;
|
|
101
|
+
|
|
102
|
+
if (apiImpl.response){
|
|
103
|
+
if (typeof apiImpl.response.format === 'function') {
|
|
104
|
+
const apiResponseFormat = apiImpl.response.format();
|
|
103
105
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
} else {
|
|
109
|
-
responseFormat = apiResponseFormat;
|
|
110
|
-
}
|
|
106
|
+
if (apiResponseFormat.then) { // is promise
|
|
107
|
+
return apiResponseFormat.then((formatName) => {
|
|
108
|
+
send(formatName);
|
|
109
|
+
});
|
|
111
110
|
} else {
|
|
112
|
-
responseFormat =
|
|
111
|
+
responseFormat = apiResponseFormat;
|
|
113
112
|
}
|
|
113
|
+
} else {
|
|
114
|
+
responseFormat = apiImpl.response.format;
|
|
114
115
|
}
|
|
116
|
+
}
|
|
115
117
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
};
|
|
118
|
+
send(responseFormat);
|
|
119
|
+
} catch (err) {
|
|
120
|
+
req.frameOptions = {
|
|
121
|
+
docName: frame.docName,
|
|
122
|
+
method: frame.method
|
|
123
|
+
};
|
|
123
124
|
|
|
124
|
-
|
|
125
|
-
|
|
125
|
+
next(err);
|
|
126
|
+
}
|
|
126
127
|
};
|
|
127
128
|
};
|
|
128
129
|
|
|
@@ -65,6 +65,36 @@ function dropTables(names) {
|
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Creates a migration which will drop an existing table and then re-add a new table based on provided spec
|
|
70
|
+
* @param {string} name - table name
|
|
71
|
+
* @param {Object} tableSpec - copy of table schema definition as defined in schema.js at the moment of writing the migration,
|
|
72
|
+
* this parameter MUST be present, otherwise @daniellockyer will hunt you down
|
|
73
|
+
*
|
|
74
|
+
* @returns {Object} migration object returning config/up/down properties
|
|
75
|
+
*/
|
|
76
|
+
function recreateTable(name, tableSpec) {
|
|
77
|
+
return createNonTransactionalMigration(
|
|
78
|
+
async function up(connection) {
|
|
79
|
+
const exists = await connection.schema.hasTable(name);
|
|
80
|
+
|
|
81
|
+
if (!exists) {
|
|
82
|
+
logging.warn(`Failed to drop table: ${name} - table does not exist`);
|
|
83
|
+
} else {
|
|
84
|
+
logging.info(`Dropping table: ${name}`);
|
|
85
|
+
await commands.deleteTable(name, connection);
|
|
86
|
+
logging.info(`Re-adding table: ${name}`);
|
|
87
|
+
await commands.createTable(name, connection, tableSpec);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
async function down() {
|
|
91
|
+
// noop: we cannot go back to old table schema
|
|
92
|
+
logging.warn(`Ignoring rollback for table recreate: ${name}`);
|
|
93
|
+
return Promise.resolve();
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
68
98
|
/**
|
|
69
99
|
* Creates a migration which will add a permission to the database
|
|
70
100
|
*
|
|
@@ -375,7 +405,8 @@ function createAddColumnMigration(table, column, columnDefinition) {
|
|
|
375
405
|
column,
|
|
376
406
|
dbIsInCorrectState: hasColumn => hasColumn === false,
|
|
377
407
|
operation: commands.dropColumn,
|
|
378
|
-
operationVerb: 'Removing'
|
|
408
|
+
operationVerb: 'Removing',
|
|
409
|
+
columnDefinition
|
|
379
410
|
})
|
|
380
411
|
);
|
|
381
412
|
}
|
|
@@ -462,6 +493,7 @@ function addSetting({key, value, type, group}) {
|
|
|
462
493
|
module.exports = {
|
|
463
494
|
addTable,
|
|
464
495
|
dropTables,
|
|
496
|
+
recreateTable,
|
|
465
497
|
addPermission,
|
|
466
498
|
addPermissionToRole,
|
|
467
499
|
addPermissionWithRoles,
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// ESLint Override Notice
|
|
2
|
+
// This file was named incorrectly and it didn't flag up in our eslint rules.
|
|
3
|
+
// The ESLint match-regex rule has now been updated to catch this, but this file has to be excluded.
|
|
4
|
+
/* eslint-disable ghost/filenames/match-regex */
|
|
5
|
+
|
|
1
6
|
const {addTable} = require('../../utils');
|
|
2
7
|
|
|
3
8
|
module.exports = addTable('newsletters', {
|
package/core/server/data/migrations/versions/4.43/2022-03-28-19-26-recreate-newsletter-table.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const {recreateTable} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = recreateTable('newsletters', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
name: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
6
|
+
description: {type: 'string', maxlength: 2000, nullable: true},
|
|
7
|
+
slug: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
8
|
+
sender_name: {type: 'string', maxlength: 191, nullable: false},
|
|
9
|
+
sender_email: {type: 'string', maxlength: 191, nullable: true},
|
|
10
|
+
sender_reply_to: {type: 'string', maxlength: 191, nullable: false, defaultTo: 'newsletter', validations: {isIn: [['newsletter', 'support']]}},
|
|
11
|
+
status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'active'},
|
|
12
|
+
visibility: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
maxlength: 50,
|
|
15
|
+
nullable: false,
|
|
16
|
+
defaultTo: 'members'
|
|
17
|
+
},
|
|
18
|
+
subscribe_on_signup: {type: 'bool', nullable: false, defaultTo: true},
|
|
19
|
+
sort_order: {type: 'integer', nullable: false, unsigned: true, defaultTo: 0},
|
|
20
|
+
header_image: {type: 'string', maxlength: 2000, nullable: true},
|
|
21
|
+
show_header_icon: {type: 'bool', nullable: false, defaultTo: true},
|
|
22
|
+
show_header_title: {type: 'bool', nullable: false, defaultTo: true},
|
|
23
|
+
title_font_category: {type: 'string', maxlength: 191, nullable: false, defaultTo: 'sans_serif', validations: {isIn: [['serif', 'sans_serif']]}},
|
|
24
|
+
title_alignment: {type: 'string', maxlength: 191, nullable: false, defaultTo: 'center', validations: {isIn: [['center', 'left']]}},
|
|
25
|
+
show_feature_image: {type: 'bool', nullable: false, defaultTo: true},
|
|
26
|
+
body_font_category: {type: 'string', maxlength: 191, nullable: false, defaultTo: 'sans_serif', validations: {isIn: [['serif', 'sans_serif']]}},
|
|
27
|
+
footer_content: {type: 'text', maxlength: 1000000000, nullable: true},
|
|
28
|
+
show_badge: {type: 'bool', nullable: false, defaultTo: true}
|
|
29
|
+
});
|
package/core/server/data/migrations/versions/4.43/2022-03-29-14-45-add-members-newsletters-table.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const {addTable} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = addTable('members_newsletters', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
6
|
+
newsletter_id: {type: 'string', maxlength: 24, nullable: false, references: 'newsletters.id', cascadeDelete: true}
|
|
7
|
+
});
|