ghost 5.8.1 → 5.9.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 (117) hide show
  1. package/components/tryghost-adapter-manager-0.0.0.tgz +0 -0
  2. package/components/tryghost-api-framework-0.0.0.tgz +0 -0
  3. package/components/tryghost-bootstrap-socket-0.0.0.tgz +0 -0
  4. package/components/tryghost-custom-theme-settings-service-0.0.0.tgz +0 -0
  5. package/components/tryghost-email-analytics-provider-mailgun-0.0.0.tgz +0 -0
  6. package/components/tryghost-email-analytics-service-0.0.0.tgz +0 -0
  7. package/components/tryghost-job-manager-0.0.0.tgz +0 -0
  8. package/components/tryghost-mailgun-client-0.0.0.tgz +0 -0
  9. package/components/tryghost-member-analytics-service-0.0.0.tgz +0 -0
  10. package/components/tryghost-members-api-0.0.0.tgz +0 -0
  11. package/components/tryghost-members-importer-0.0.0.tgz +0 -0
  12. package/components/tryghost-members-offers-0.0.0.tgz +0 -0
  13. package/components/tryghost-members-payments-0.0.0.tgz +0 -0
  14. package/components/tryghost-members-ssr-0.0.0.tgz +0 -0
  15. package/components/tryghost-members-stripe-service-0.0.0.tgz +0 -0
  16. package/components/tryghost-minifier-0.0.0.tgz +0 -0
  17. package/components/tryghost-mw-cache-control-0.0.0.tgz +0 -0
  18. package/components/tryghost-mw-error-handler-0.0.0.tgz +0 -0
  19. package/components/tryghost-package-json-0.0.0.tgz +0 -0
  20. package/components/tryghost-session-service-0.0.0.tgz +0 -0
  21. package/components/tryghost-settings-path-manager-0.0.0.tgz +0 -0
  22. package/components/tryghost-update-check-service-0.0.0.tgz +0 -0
  23. package/content/themes/casper/README.md +1 -1
  24. package/content/themes/casper/assets/built/portal.min.js +3 -0
  25. package/content/themes/casper/assets/built/screen.css +1 -1
  26. package/content/themes/casper/assets/built/screen.css.map +1 -1
  27. package/content/themes/casper/assets/css/screen.css +119 -37
  28. package/content/themes/casper/package.json +3 -3
  29. package/content/themes/casper/partials/post-card.hbs +3 -1
  30. package/content/themes/casper/post.hbs +7 -7
  31. package/content/themes/casper/yarn.lock +69 -68
  32. package/core/boot.js +2 -0
  33. package/core/built/admin/assets/{chunk.143.904e017ad397bb681e9d.js → chunk.143.e35fdb482bc822313f0c.js} +5 -5
  34. package/core/built/admin/assets/{chunk.178.9541bdb92bded29cd60d.js → chunk.178.1d381d687652f2597fe2.js} +4 -4
  35. package/core/built/admin/assets/{chunk.351.cbc224ca65c14ef5322d.js → chunk.351.73f27952f867334a8228.js} +3 -3
  36. package/core/built/admin/assets/{chunk.351.cbc224ca65c14ef5322d.js.LICENSE.txt → chunk.351.73f27952f867334a8228.js.LICENSE.txt} +0 -0
  37. package/core/built/admin/assets/{ghost-b469423d0fbe5e40af17b560f7e3cead.css → ghost-686c383caa6a3469cefb939ab10e21b6.css} +1 -1
  38. package/core/built/admin/assets/{ghost-dark-bcb6f4517a2dfe23a0a280632bfca00c.css → ghost-dark-6814c399ff5b3d9c8efe2d92bc7ec779.css} +1 -1
  39. package/core/built/admin/assets/{ghost-a66a04418efe85083a3adca0fb16bb52.js → ghost-eca1a709a74b1af277e48aad4e16c9db.js} +94 -92
  40. package/core/built/admin/assets/{vendor-46baf13852f545c6c89756c8e0ccbff2.js → vendor-516c9e43b4aeb92079dc1ab92c9ce492.js} +3 -3
  41. package/core/built/admin/index.html +6 -6
  42. package/core/frontend/helpers/comment_count.js +7 -15
  43. package/core/frontend/helpers/comments.js +4 -16
  44. package/core/frontend/helpers/ghost_head.js +1 -1
  45. package/core/frontend/public/ghost.min.css +1 -1
  46. package/core/frontend/src/comment-counts/js/comment-counts.js +12 -1
  47. package/core/server/api/endpoints/comments-members.js +23 -1
  48. package/core/server/api/endpoints/index.js +52 -52
  49. package/core/server/api/endpoints/utils/serializers/input/comments.js +18 -0
  50. package/core/server/api/endpoints/utils/serializers/input/db.js +1 -1
  51. package/core/server/api/endpoints/utils/serializers/input/index.js +4 -0
  52. package/core/server/api/endpoints/utils/serializers/input/integrations.js +2 -2
  53. package/core/server/api/endpoints/utils/serializers/input/tiers.js +1 -1
  54. package/core/server/api/{shared → endpoints/utils}/serializers/input/utils/settings-filter-type-group-mapper.js +0 -0
  55. package/core/server/api/{shared → endpoints/utils}/serializers/input/utils/settings-key-group-mapper.js +0 -0
  56. package/core/server/api/{shared → endpoints/utils}/serializers/input/utils/settings-key-type-mapper.js +0 -0
  57. package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +12 -12
  58. package/core/server/api/endpoints/utils/serializers/output/tiers.js +1 -1
  59. package/core/server/api/index.js +0 -2
  60. package/core/server/data/db/connection.js +0 -4
  61. package/core/server/data/importer/importers/data/settings.js +2 -2
  62. package/core/server/data/migrations/versions/5.9/2022-08-09-08-32-added-new-integration-type.js +24 -0
  63. package/core/server/data/schema/clients/mysql.js +0 -15
  64. package/core/server/data/schema/commands.js +0 -9
  65. package/core/server/data/schema/fixtures/fixtures.json +1 -1
  66. package/core/server/data/schema/schema.js +3 -3
  67. package/core/server/models/base/plugins/user-type.js +1 -9
  68. package/core/server/models/comment.js +96 -15
  69. package/core/server/models/label.js +14 -0
  70. package/core/server/models/newsletter.js +21 -0
  71. package/core/server/models/stripe-customer-subscription.js +6 -0
  72. package/core/server/models/tag.js +20 -0
  73. package/core/server/models/user.js +20 -0
  74. package/core/server/run-update-check.js +1 -1
  75. package/core/server/services/auth/api-key/admin.js +1 -1
  76. package/core/server/services/auth/api-key/content.js +1 -1
  77. package/core/server/services/bulk-email/bulk-email-processor.js +18 -11
  78. package/core/server/services/bulk-email/index.js +1 -17
  79. package/core/server/services/comments/controller.js +9 -0
  80. package/core/server/services/comments/email-templates/new-comment-reply.hbs +2 -2
  81. package/core/server/services/comments/email-templates/new-comment.hbs +2 -2
  82. package/core/server/services/comments/email-templates/report.hbs +2 -2
  83. package/core/server/services/comments/service.js +16 -3
  84. package/core/server/services/comments/stats.js +2 -0
  85. package/core/server/services/email-analytics/jobs/fetch-latest.js +1 -1
  86. package/core/server/services/mega/post-email-serializer.js +2 -2
  87. package/core/server/services/permissions/can-this.js +154 -161
  88. package/core/server/services/permissions/parse-context.js +1 -8
  89. package/core/server/services/webhooks/serialize.js +3 -3
  90. package/core/server/web/api/endpoints/admin/routes.js +1 -1
  91. package/core/server/web/api/endpoints/content/routes.js +1 -1
  92. package/core/server/web/api/testmode/jobs/graceful-job.js +1 -1
  93. package/core/server/web/comments/routes.js +2 -1
  94. package/core/server/web/shared/middleware/index.js +1 -1
  95. package/core/shared/config/defaults.json +2 -2
  96. package/core/shared/labs.js +0 -1
  97. package/package.json +31 -29
  98. package/yarn.lock +330 -276
  99. package/core/server/api/README.md +0 -130
  100. package/core/server/api/shared/frame.js +0 -95
  101. package/core/server/api/shared/headers.js +0 -152
  102. package/core/server/api/shared/http.js +0 -127
  103. package/core/server/api/shared/index.js +0 -25
  104. package/core/server/api/shared/pipeline.js +0 -259
  105. package/core/server/api/shared/serializers/handle.js +0 -140
  106. package/core/server/api/shared/serializers/index.js +0 -13
  107. package/core/server/api/shared/serializers/input/all.js +0 -41
  108. package/core/server/api/shared/serializers/input/index.js +0 -5
  109. package/core/server/api/shared/serializers/output/index.js +0 -1
  110. package/core/server/api/shared/utils/index.js +0 -5
  111. package/core/server/api/shared/utils/options.js +0 -23
  112. package/core/server/api/shared/validators/handle.js +0 -68
  113. package/core/server/api/shared/validators/index.js +0 -9
  114. package/core/server/api/shared/validators/input/all.js +0 -213
  115. package/core/server/api/shared/validators/input/index.js +0 -5
  116. package/core/server/services/bulk-email/mailgun.js +0 -122
  117. package/core/server/web/shared/middleware/cache-control.js +0 -43
@@ -1,130 +0,0 @@
1
- # API
2
-
3
- ## Stages
4
-
5
- Each request goes through the following stages:
6
-
7
- - input validation
8
- - input serialisation
9
- - permissions
10
- - query
11
- - output serialisation
12
-
13
- The framework we are building pipes a request through these stages in respect of the API controller configuration.
14
-
15
-
16
- ## Frame
17
-
18
- Is a class, which holds all the information for request processing. We pass this instance by reference.
19
- Each function can modify the original instance. No need to return the class instance.
20
-
21
- ### Structure
22
-
23
- ```
24
- {
25
- original: Object,
26
- options: Object,
27
- data: Object,
28
- user: Object,
29
- file: Object,
30
- files: Array
31
- }
32
- ```
33
-
34
- ### Example
35
-
36
- ```
37
- {
38
- original: {
39
- include: 'tags'
40
- },
41
- options: {
42
- withRelated: ['tags']
43
- },
44
- data: {
45
- posts: []
46
- }
47
- }
48
- ```
49
-
50
- ## API Controller
51
-
52
- A controller is no longer just a function, it's a set of configurations.
53
-
54
- ### Structure
55
-
56
- ```
57
- edit: function || object
58
- ```
59
-
60
- ```
61
- edit: {
62
- headers: object,
63
- options: Array,
64
- data: Array,
65
- validation: object | function,
66
- permissions: boolean | object | function,
67
- query: function
68
- }
69
- ```
70
-
71
- ### Examples
72
-
73
-
74
- ```
75
- edit: {
76
- headers: {
77
- cacheInvalidate: true
78
- },
79
- // Allowed url/query params
80
- options: ['include']
81
- // Url/query param validation configuration
82
- validation: {
83
- options: {
84
- include: {
85
- required: true,
86
- values: ['tags']
87
- }
88
- }
89
- },
90
- permissions: true,
91
- // Returns a model response!
92
- query(frame) {
93
- return models.Post.edit(frame.data, frame.options);
94
- }
95
- }
96
- ```
97
-
98
- ```
99
- read: {
100
- // Allowed url/query params, which will be remembered inside `frame.data`
101
- // This is helpful for READ requests e.g. `model.findOne(frame.data, frame.options)`.
102
- // Our model layer requires sending the where clauses as first parameter.
103
- data: ['slug']
104
- validation: {
105
- data: {
106
- slug: {
107
- values: ['eins']
108
- }
109
- }
110
- },
111
- permissions: true,
112
- query(frame) {
113
- return models.Post.findOne(frame.data, frame.options);
114
- }
115
- }
116
- ```
117
-
118
- ```
119
- edit: {
120
- validation() {
121
- // custom validation, skip framework
122
- },
123
- permissions: {
124
- unsafeAttrs: ['author']
125
- },
126
- query(frame) {
127
- return models.Post.edit(frame.data, frame.options);
128
- }
129
- }
130
- ```
@@ -1,95 +0,0 @@
1
- const debug = require('@tryghost/debug')('api:shared:frame');
2
- const _ = require('lodash');
3
-
4
- /**
5
- * @description The "frame" holds all information of a request.
6
- *
7
- * Each party can modify the frame by reference.
8
- * A request hits a lot of stages in the API implementation and that's why modification by reference was the
9
- * easiest to use. We always have access to the original input, we never loose track of it.
10
- */
11
- class Frame {
12
- constructor(obj = {}) {
13
- this.original = obj;
14
-
15
- /**
16
- * options: Query params, url params, context and custom options
17
- * data: Body or if the ctrl wants query/url params inside body
18
- * user: Logged in user
19
- * file: Uploaded file
20
- * files: Uploaded files
21
- * apiType: Content or admin api access
22
- */
23
- this.options = {};
24
- this.data = {};
25
- this.user = {};
26
- this.file = {};
27
- this.files = [];
28
- this.apiType = null;
29
- }
30
-
31
- /**
32
- * @description Configure the frame.
33
- *
34
- * If you instantiate a new frame, all the data you pass in, land in `this.original`. This is helpful
35
- * for debugging to see what the original input was.
36
- *
37
- * This function will prepare the incoming data for further processing.
38
- * Based on the API ctrl implemented, this fn will pick allowed properties to either options or data.
39
- */
40
- configure(apiConfig) {
41
- debug('configure');
42
-
43
- if (apiConfig.options) {
44
- if (typeof apiConfig.options === 'function') {
45
- apiConfig.options = apiConfig.options(this);
46
- }
47
-
48
- if (Object.prototype.hasOwnProperty.call(this.original, 'query')) {
49
- Object.assign(this.options, _.pick(this.original.query, apiConfig.options));
50
- }
51
-
52
- if (Object.prototype.hasOwnProperty.call(this.original, 'params')) {
53
- Object.assign(this.options, _.pick(this.original.params, apiConfig.options));
54
- }
55
-
56
- if (Object.prototype.hasOwnProperty.call(this.original, 'options')) {
57
- Object.assign(this.options, _.pick(this.original.options, apiConfig.options));
58
- }
59
- }
60
-
61
- this.options.context = this.original.context;
62
-
63
- if (this.original.body && Object.keys(this.original.body).length) {
64
- this.data = _.cloneDeep(this.original.body);
65
- } else {
66
- if (apiConfig.data) {
67
- if (typeof apiConfig.data === 'function') {
68
- apiConfig.data = apiConfig.data(this);
69
- }
70
-
71
- if (Object.prototype.hasOwnProperty.call(this.original, 'query')) {
72
- Object.assign(this.data, _.pick(this.original.query, apiConfig.data));
73
- }
74
-
75
- if (Object.prototype.hasOwnProperty.call(this.original, 'params')) {
76
- Object.assign(this.data, _.pick(this.original.params, apiConfig.data));
77
- }
78
-
79
- if (Object.prototype.hasOwnProperty.call(this.original, 'options')) {
80
- Object.assign(this.data, _.pick(this.original.options, apiConfig.data));
81
- }
82
- }
83
- }
84
-
85
- this.user = this.original.user;
86
- this.file = this.original.file;
87
- this.files = this.original.files;
88
-
89
- debug('original', this.original);
90
- debug('options', this.options);
91
- debug('data', this.data);
92
- }
93
- }
94
-
95
- module.exports = Frame;
@@ -1,152 +0,0 @@
1
- const url = require('url');
2
- const debug = require('@tryghost/debug')('api:shared:headers');
3
- const Promise = require('bluebird');
4
- const INVALIDATE_ALL = '/*';
5
-
6
- const cacheInvalidate = (result, options = {}) => {
7
- let value = options.value;
8
-
9
- return {
10
- 'X-Cache-Invalidate': value || INVALIDATE_ALL
11
- };
12
- };
13
-
14
- const disposition = {
15
- /**
16
- * @description Generate CSV header.
17
- *
18
- * @param {Object} result - API response
19
- * @param {Object} options
20
- * @return {Object}
21
- */
22
- csv(result, options = {}) {
23
- let value = options.value;
24
-
25
- if (typeof options.value === 'function') {
26
- value = options.value();
27
- }
28
-
29
- return {
30
- 'Content-Disposition': `Attachment; filename="${value}"`,
31
- 'Content-Type': 'text/csv'
32
- };
33
- },
34
-
35
- /**
36
- * @description Generate JSON header.
37
- *
38
- * @param {Object} result - API response
39
- * @param {Object} options
40
- * @return {Object}
41
- */
42
- json(result, options = {}) {
43
- return {
44
- 'Content-Disposition': `Attachment; filename="${options.value}"`,
45
- 'Content-Type': 'application/json',
46
- 'Content-Length': Buffer.byteLength(JSON.stringify(result))
47
- };
48
- },
49
-
50
- /**
51
- * @description Generate YAML header.
52
- *
53
- * @param {Object} result - API response
54
- * @param {Object} options
55
- * @return {Object}
56
- */
57
- yaml(result, options = {}) {
58
- return {
59
- 'Content-Disposition': `Attachment; filename="${options.value}"`,
60
- 'Content-Type': 'application/yaml',
61
- 'Content-Length': Buffer.byteLength(JSON.stringify(result))
62
- };
63
- },
64
-
65
- /**
66
- * @description Content Disposition Header
67
- *
68
- * Create a header that invokes the 'Save As' dialog in the browser when exporting the database to file. The 'filename'
69
- * parameter is governed by [RFC6266](http://tools.ietf.org/html/rfc6266#section-4.3).
70
- *
71
- * For encoding whitespace and non-ISO-8859-1 characters, you MUST use the "filename*=" attribute, NOT "filename=".
72
- * Ideally, both. Examples: http://tools.ietf.org/html/rfc6266#section-5
73
- *
74
- * We'll use ISO-8859-1 characters here to keep it simple.
75
- *
76
- * @see http://tools.ietf.org/html/rfc598
77
- */
78
- file(result, options = {}) {
79
- return Promise.resolve()
80
- .then(() => {
81
- let value = options.value;
82
-
83
- if (typeof options.value === 'function') {
84
- value = options.value();
85
- }
86
-
87
- return value;
88
- })
89
- .then((filename) => {
90
- return {
91
- 'Content-Disposition': `Attachment; filename="${filename}"`
92
- };
93
- });
94
- }
95
- };
96
-
97
- module.exports = {
98
- /**
99
- * @description Get header based on ctrl configuration.
100
- *
101
- * @param {Object} result - API response
102
- * @param {Object} apiConfigHeaders
103
- * @param {Object} frame
104
- * @return {Promise}
105
- */
106
- async get(result, apiConfigHeaders = {}, frame) {
107
- let headers = {};
108
-
109
- if (apiConfigHeaders.disposition) {
110
- const dispositionHeader = await disposition[apiConfigHeaders.disposition.type](result, apiConfigHeaders.disposition);
111
-
112
- if (dispositionHeader) {
113
- Object.assign(headers, dispositionHeader);
114
- }
115
- }
116
-
117
- if (apiConfigHeaders.cacheInvalidate) {
118
- const cacheInvalidationHeader = cacheInvalidate(result, apiConfigHeaders.cacheInvalidate);
119
-
120
- if (cacheInvalidationHeader) {
121
- Object.assign(headers, cacheInvalidationHeader);
122
- }
123
- }
124
-
125
- const locationHeaderDisabled = apiConfigHeaders && apiConfigHeaders.location === false;
126
- const hasFrameData = frame
127
- && (frame.method === 'add')
128
- && result[frame.docName]
129
- && result[frame.docName][0]
130
- && result[frame.docName][0].id;
131
-
132
- if (!locationHeaderDisabled && hasFrameData) {
133
- const protocol = (frame.original.url.secure === false) ? 'http://' : 'https://';
134
- const resourceId = result[frame.docName][0].id;
135
-
136
- let locationURL = url.resolve(`${protocol}${frame.original.url.host}`,frame.original.url.pathname);
137
- if (!locationURL.endsWith('/')) {
138
- locationURL += '/';
139
- }
140
- locationURL += `${resourceId}/`;
141
-
142
- const locationHeader = {
143
- Location: locationURL
144
- };
145
-
146
- Object.assign(headers, locationHeader);
147
- }
148
-
149
- debug(headers);
150
- return headers;
151
- }
152
- };
@@ -1,127 +0,0 @@
1
- const url = require('url');
2
- const debug = require('@tryghost/debug')('api:shared:http');
3
- const shared = require('../shared');
4
- const models = require('../../models');
5
-
6
- /**
7
- * @description HTTP wrapper.
8
- *
9
- * This wrapper is used in the routes definition (see web/).
10
- * The wrapper receives the express request, prepares the frame and forwards the request to the pipeline.
11
- *
12
- * @param {Function} apiImpl - Pipeline wrapper, which executes the target ctrl function.
13
- * @return {Function}
14
- */
15
- const http = (apiImpl) => {
16
- return async (req, res, next) => {
17
- debug(`External API request to ${req.url}`);
18
- let apiKey = null;
19
- let integration = null;
20
- let user = null;
21
-
22
- if (req.api_key) {
23
- apiKey = {
24
- id: req.api_key.get('id'),
25
- type: req.api_key.get('type')
26
- };
27
- integration = {
28
- id: req.api_key.get('integration_id')
29
- };
30
- }
31
-
32
- // NOTE: "external user" is only used in the subscriber app. External user is ID "0".
33
- if ((req.user && req.user.id) || (req.user && models.User.isExternalUser(req.user.id))) {
34
- user = req.user.id;
35
- }
36
-
37
- const frame = new shared.Frame({
38
- body: req.body,
39
- file: req.file,
40
- files: req.files,
41
- query: req.query,
42
- params: req.params,
43
- user: req.user,
44
- session: req.session,
45
- url: {
46
- host: req.vhost ? req.vhost.host : req.get('host'),
47
- pathname: url.parse(req.originalUrl || req.url).pathname,
48
- secure: req.secure
49
- },
50
- context: {
51
- api_key: apiKey,
52
- user: user,
53
- integration: integration,
54
- member: (req.member || null)
55
- }
56
- });
57
-
58
- frame.configure({
59
- options: apiImpl.options,
60
- data: apiImpl.data
61
- });
62
-
63
- try {
64
- const result = await apiImpl(frame);
65
-
66
- debug(`External API request to ${frame.docName}.${frame.method}`);
67
- const headers = await shared.headers.get(result, apiImpl.headers, frame) || {};
68
-
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
- }
74
-
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
- }
81
-
82
- res.status(statusCode);
83
-
84
- // CASE: generate headers based on the api ctrl configuration
85
- res.set(headers);
86
-
87
- const send = (format) => {
88
- if (format === 'plain') {
89
- debug('plain text response');
90
- return res.send(result);
91
- }
92
-
93
- debug('json response');
94
- res.json(result || {});
95
- };
96
-
97
- let responseFormat;
98
-
99
- if (apiImpl.response){
100
- if (typeof apiImpl.response.format === 'function') {
101
- const apiResponseFormat = apiImpl.response.format();
102
-
103
- if (apiResponseFormat.then) { // is promise
104
- return apiResponseFormat.then((formatName) => {
105
- send(formatName);
106
- });
107
- } else {
108
- responseFormat = apiResponseFormat;
109
- }
110
- } else {
111
- responseFormat = apiImpl.response.format;
112
- }
113
- }
114
-
115
- send(responseFormat);
116
- } catch (err) {
117
- req.frameOptions = {
118
- docName: frame.docName,
119
- method: frame.method
120
- };
121
-
122
- next(err);
123
- }
124
- };
125
- };
126
-
127
- module.exports = http;
@@ -1,25 +0,0 @@
1
- module.exports = {
2
- get headers() {
3
- return require('./headers');
4
- },
5
-
6
- get http() {
7
- return require('./http');
8
- },
9
-
10
- get Frame() {
11
- return require('./frame');
12
- },
13
-
14
- get pipeline() {
15
- return require('./pipeline');
16
- },
17
-
18
- get validators() {
19
- return require('./validators');
20
- },
21
-
22
- get serializers() {
23
- return require('./serializers');
24
- }
25
- };