ghost 5.60.0 → 5.61.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/components/tryghost-adapter-cache-memory-ttl-5.61.0.tgz +0 -0
- package/components/tryghost-adapter-cache-redis-5.61.0.tgz +0 -0
- package/components/tryghost-adapter-manager-5.61.0.tgz +0 -0
- package/components/tryghost-announcement-bar-settings-5.61.0.tgz +0 -0
- package/components/tryghost-api-framework-5.61.0.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.61.0.tgz +0 -0
- package/components/tryghost-audience-feedback-5.61.0.tgz +0 -0
- package/components/tryghost-bookshelf-repository-5.61.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.61.0.tgz +0 -0
- package/components/tryghost-collections-5.61.0.tgz +0 -0
- package/components/tryghost-constants-5.61.0.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.61.0.tgz +0 -0
- package/components/{tryghost-data-generator-5.60.0.tgz → tryghost-data-generator-5.61.0.tgz} +0 -0
- package/components/tryghost-domain-events-5.61.0.tgz +0 -0
- package/components/tryghost-donations-5.61.0.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.61.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.61.0.tgz +0 -0
- package/components/{tryghost-email-analytics-service-5.60.0.tgz → tryghost-email-analytics-service-5.61.0.tgz} +0 -0
- package/components/tryghost-email-content-generator-5.61.0.tgz +0 -0
- package/components/tryghost-email-events-5.61.0.tgz +0 -0
- package/components/{tryghost-email-service-5.60.0.tgz → tryghost-email-service-5.61.0.tgz} +0 -0
- package/components/tryghost-email-suppression-list-5.61.0.tgz +0 -0
- package/components/tryghost-event-aware-cache-wrapper-5.61.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.61.0.tgz +0 -0
- package/components/{tryghost-external-media-inliner-5.60.0.tgz → tryghost-external-media-inliner-5.61.0.tgz} +0 -0
- package/components/{tryghost-extract-api-key-5.60.0.tgz → tryghost-extract-api-key-5.61.0.tgz} +0 -0
- package/components/tryghost-html-to-plaintext-5.61.0.tgz +0 -0
- package/components/tryghost-i18n-5.61.0.tgz +0 -0
- package/components/tryghost-importer-handler-content-files-5.61.0.tgz +0 -0
- package/components/{tryghost-importer-revue-5.60.0.tgz → tryghost-importer-revue-5.61.0.tgz} +0 -0
- package/components/tryghost-in-memory-repository-5.61.0.tgz +0 -0
- package/components/tryghost-job-manager-5.61.0.tgz +0 -0
- package/components/tryghost-link-redirects-5.61.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.61.0.tgz +0 -0
- package/components/tryghost-link-tracking-5.61.0.tgz +0 -0
- package/components/tryghost-magic-link-5.61.0.tgz +0 -0
- package/components/tryghost-mail-events-5.61.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.61.0.tgz +0 -0
- package/components/{tryghost-member-attribution-5.60.0.tgz → tryghost-member-attribution-5.61.0.tgz} +0 -0
- package/components/tryghost-member-events-5.61.0.tgz +0 -0
- package/components/tryghost-members-api-5.61.0.tgz +0 -0
- package/components/tryghost-members-csv-5.61.0.tgz +0 -0
- package/components/tryghost-members-events-service-5.61.0.tgz +0 -0
- package/components/tryghost-members-importer-5.61.0.tgz +0 -0
- package/components/tryghost-members-offers-5.61.0.tgz +0 -0
- package/components/tryghost-members-payments-5.61.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.61.0.tgz +0 -0
- package/components/{tryghost-members-stripe-service-5.60.0.tgz → tryghost-members-stripe-service-5.61.0.tgz} +0 -0
- package/components/tryghost-mentions-email-report-5.61.0.tgz +0 -0
- package/components/tryghost-milestones-5.61.0.tgz +0 -0
- package/components/tryghost-minifier-5.61.0.tgz +0 -0
- package/components/{tryghost-model-to-domain-event-interceptor-5.60.0.tgz → tryghost-model-to-domain-event-interceptor-5.61.0.tgz} +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.61.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.61.0.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.61.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.61.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.61.0.tgz +0 -0
- package/components/tryghost-mw-version-match-5.61.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.61.0.tgz +0 -0
- package/components/tryghost-nql-filter-expansions-5.61.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.61.0.tgz +0 -0
- package/components/tryghost-package-json-5.61.0.tgz +0 -0
- package/components/tryghost-post-events-5.61.0.tgz +0 -0
- package/components/tryghost-post-revisions-5.61.0.tgz +0 -0
- package/components/tryghost-posts-service-5.61.0.tgz +0 -0
- package/components/tryghost-recommendations-5.61.0.tgz +0 -0
- package/components/tryghost-referrers-5.61.0.tgz +0 -0
- package/components/tryghost-security-5.61.0.tgz +0 -0
- package/components/tryghost-session-service-5.61.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.61.0.tgz +0 -0
- package/components/tryghost-slack-notifications-5.61.0.tgz +0 -0
- package/components/tryghost-staff-service-5.61.0.tgz +0 -0
- package/components/tryghost-stats-service-5.61.0.tgz +0 -0
- package/components/{tryghost-tiers-5.60.0.tgz → tryghost-tiers-5.61.0.tgz} +0 -0
- package/components/tryghost-update-check-service-5.61.0.tgz +0 -0
- package/components/tryghost-verification-trigger-5.61.0.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.61.0.tgz +0 -0
- package/components/tryghost-webmentions-5.61.0.tgz +0 -0
- package/core/boot.js +6 -1
- package/core/built/admin/assets/{chunk.143.88cad5b682130d4a2e2e.js → chunk.143.b297dc36977ec0b8aa47.js} +5 -5
- package/core/built/admin/assets/{chunk.178.cf63585e0c59dc316a42.js → chunk.178.308d80b60fce060ca358.js} +4 -4
- package/core/built/admin/assets/{ghost-fc7b85b759f27e607667914dcd685402.js → ghost-4416ac3815c54eac76c2590d1154a628.js} +33 -36
- package/core/built/admin/assets/ghost-8a4e981c272f793157133814ca7c7e84.css +1 -0
- package/core/built/admin/assets/ghost-dark-084169b0e968ef763dfbbf63b253e0c6.css +1 -0
- package/core/built/admin/assets/{vendor-fff5b0b3c122441beb3170947ae27b9d.js → vendor-3631184082d609038638c1e169a002e7.js} +666 -666
- package/core/built/admin/index.html +6 -6
- package/core/frontend/src/cards/css/header_v2.css +10 -9
- package/core/frontend/src/cards/css/signup.css +13 -12
- package/core/frontend/web/middleware/cors.js +1 -1
- package/core/frontend/web/middleware/handle-image-sizes.js +1 -1
- package/core/frontend/web/middleware/serve-public-file.js +4 -4
- package/core/frontend/web/site.js +5 -2
- package/core/server/api/endpoints/index.js +8 -0
- package/core/server/api/endpoints/recommendations-public.js +24 -0
- package/core/server/api/endpoints/recommendations.js +71 -0
- package/core/server/api/endpoints/utils/serializers/input/settings.js +2 -1
- package/core/server/data/exporter/table-lists.js +2 -1
- package/core/server/data/migrations/versions/5.61/2023-08-29-10-17-add-recommendations-crud-permissions.js +50 -0
- package/core/server/data/migrations/versions/5.61/2023-08-29-11-39-10-add-recommendations-table.js +18 -0
- package/core/server/data/migrations/versions/5.61/2023-08-30-07-37-04-add-recommendations-enabled-settings.js +8 -0
- package/core/server/data/schema/default-settings/default-settings.json +10 -0
- package/core/server/data/schema/fixtures/fixtures.json +35 -5
- package/core/server/data/schema/schema.js +12 -0
- package/core/server/models/base/plugins/raw-knex.js +4 -3
- package/core/server/models/post.js +1 -1
- package/core/server/models/recommendation.js +10 -0
- package/core/server/services/api-version-compatibility/index.js +2 -2
- package/core/server/services/api-version-compatibility/mw-version-rewrites.js +1 -1
- package/core/server/services/auth/api-key/admin.js +3 -3
- package/core/server/services/collections/service.js +5 -3
- package/core/server/services/members/middleware.js +10 -10
- package/core/server/services/mentions/WebmentionMetadata.js +2 -1
- package/core/server/services/mentions/service.js +4 -0
- package/core/server/services/{collections/intercept-events.js → model-to-domain-event-interceptor/index.js} +9 -1
- package/core/server/services/recommendations/RecommendationServiceWrapper.js +57 -0
- package/core/server/services/recommendations/index.js +3 -0
- package/core/server/web/admin/app.js +2 -2
- package/core/server/web/api/endpoints/admin/middleware.js +1 -1
- package/core/server/web/api/endpoints/admin/routes.js +9 -3
- package/core/server/web/api/endpoints/content/routes.js +3 -0
- package/core/server/web/api/middleware/cors.js +1 -1
- package/core/server/web/api/middleware/upload.js +2 -2
- package/core/server/web/parent/middleware/request-id.js +1 -1
- package/core/server/web/shared/middleware/uncapitalise.js +1 -1
- package/core/server/web/shared/middleware/url-redirects.js +3 -3
- package/core/server/web/well-known.js +1 -1
- package/core/shared/labs.js +2 -1
- package/core/shared/settings-cache/public.js +2 -1
- package/index.js +5 -0
- package/newrelic.js +53 -0
- package/package.json +162 -159
- package/yarn.lock +2513 -1166
- package/components/tryghost-adapter-cache-memory-ttl-5.60.0.tgz +0 -0
- package/components/tryghost-adapter-cache-redis-5.60.0.tgz +0 -0
- package/components/tryghost-adapter-manager-5.60.0.tgz +0 -0
- package/components/tryghost-announcement-bar-settings-5.60.0.tgz +0 -0
- package/components/tryghost-api-framework-5.60.0.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.60.0.tgz +0 -0
- package/components/tryghost-audience-feedback-5.60.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.60.0.tgz +0 -0
- package/components/tryghost-collections-5.60.0.tgz +0 -0
- package/components/tryghost-constants-5.60.0.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.60.0.tgz +0 -0
- package/components/tryghost-domain-events-5.60.0.tgz +0 -0
- package/components/tryghost-donations-5.60.0.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.60.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.60.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.60.0.tgz +0 -0
- package/components/tryghost-email-events-5.60.0.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.60.0.tgz +0 -0
- package/components/tryghost-event-aware-cache-wrapper-5.60.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.60.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.60.0.tgz +0 -0
- package/components/tryghost-i18n-5.60.0.tgz +0 -0
- package/components/tryghost-importer-handler-content-files-5.60.0.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.60.0.tgz +0 -0
- package/components/tryghost-job-manager-5.60.0.tgz +0 -0
- package/components/tryghost-link-redirects-5.60.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.60.0.tgz +0 -0
- package/components/tryghost-link-tracking-5.60.0.tgz +0 -0
- package/components/tryghost-magic-link-5.60.0.tgz +0 -0
- package/components/tryghost-mail-events-5.60.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.60.0.tgz +0 -0
- package/components/tryghost-member-events-5.60.0.tgz +0 -0
- package/components/tryghost-members-api-5.60.0.tgz +0 -0
- package/components/tryghost-members-csv-5.60.0.tgz +0 -0
- package/components/tryghost-members-events-service-5.60.0.tgz +0 -0
- package/components/tryghost-members-importer-5.60.0.tgz +0 -0
- package/components/tryghost-members-offers-5.60.0.tgz +0 -0
- package/components/tryghost-members-payments-5.60.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.60.0.tgz +0 -0
- package/components/tryghost-mentions-email-report-5.60.0.tgz +0 -0
- package/components/tryghost-milestones-5.60.0.tgz +0 -0
- package/components/tryghost-minifier-5.60.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.60.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.60.0.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.60.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.60.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.60.0.tgz +0 -0
- package/components/tryghost-mw-version-match-5.60.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.60.0.tgz +0 -0
- package/components/tryghost-nql-filter-expansions-5.60.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.60.0.tgz +0 -0
- package/components/tryghost-package-json-5.60.0.tgz +0 -0
- package/components/tryghost-post-events-5.60.0.tgz +0 -0
- package/components/tryghost-post-revisions-5.60.0.tgz +0 -0
- package/components/tryghost-posts-service-5.60.0.tgz +0 -0
- package/components/tryghost-referrers-5.60.0.tgz +0 -0
- package/components/tryghost-security-5.60.0.tgz +0 -0
- package/components/tryghost-session-service-5.60.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.60.0.tgz +0 -0
- package/components/tryghost-slack-notifications-5.60.0.tgz +0 -0
- package/components/tryghost-staff-service-5.60.0.tgz +0 -0
- package/components/tryghost-stats-service-5.60.0.tgz +0 -0
- package/components/tryghost-update-check-service-5.60.0.tgz +0 -0
- package/components/tryghost-verification-trigger-5.60.0.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.60.0.tgz +0 -0
- package/components/tryghost-webmentions-5.60.0.tgz +0 -0
- package/core/built/admin/assets/chunk.208.735d31e95ce6fbd467b8.js +0 -12512
- package/core/built/admin/assets/chunk.208.735d31e95ce6fbd467b8.js.LICENSE.txt +0 -250
- package/core/built/admin/assets/ghost-c2c2b6cf53d999c72995cafa4166b9fb.css +0 -1
- package/core/built/admin/assets/ghost-dark-d44be1f4bc4e8920af2416d1e56a341f.css +0 -1
|
@@ -25,7 +25,7 @@ const init = () => {
|
|
|
25
25
|
});
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
module.exports.errorHandler = (err, req, res, next)
|
|
28
|
+
module.exports.errorHandler = function apiVersionCompatibilityErrorHandler(err, req, res, next) {
|
|
29
29
|
return versionMismatchHandler(serviceInstance)(err, req, res, next);
|
|
30
30
|
};
|
|
31
31
|
|
|
@@ -38,7 +38,7 @@ module.exports.errorHandler = (err, req, res, next) => {
|
|
|
38
38
|
* @param {import('express').Response} res
|
|
39
39
|
* @param {import('express').NextFunction} next
|
|
40
40
|
*/
|
|
41
|
-
module.exports.contentVersion = (req, res, next)
|
|
41
|
+
module.exports.contentVersion = function apiVersionCompatibilityContentVersion(req, res, next) {
|
|
42
42
|
res.header('Content-Version', `v${ghostVersion.safe}`);
|
|
43
43
|
res.vary('Accept-Version');
|
|
44
44
|
|
|
@@ -8,7 +8,7 @@ const urlUtils = require('../../../shared/url-utils');
|
|
|
8
8
|
* @param {import('express').Response} res
|
|
9
9
|
* @param {import('express').NextFunction} next
|
|
10
10
|
*/
|
|
11
|
-
module.exports = (req, res, next)
|
|
11
|
+
module.exports = function mwVersionRewrites(req, res, next) {
|
|
12
12
|
let {version} = legacyApiPathMatch(req.url);
|
|
13
13
|
|
|
14
14
|
// If we don't match a valid version, carry on
|
|
@@ -44,7 +44,7 @@ const _extractTokenFromUrl = function extractTokenFromUrl(reqUrl) {
|
|
|
44
44
|
return query.token;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
const authenticate = (req, res, next)
|
|
47
|
+
const authenticate = function apiKeyAdminAuth(req, res, next) {
|
|
48
48
|
// CASE: we don't have an Authorization header so allow fallthrough to other
|
|
49
49
|
// auth middleware or final "ensure authenticated" check
|
|
50
50
|
if (!req.headers || !req.headers.authorization) {
|
|
@@ -63,7 +63,7 @@ const authenticate = (req, res, next) => {
|
|
|
63
63
|
return authenticateWithToken(req, res, next, {token, JWT_OPTIONS: JWT_OPTIONS_DEFAULTS});
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
-
const authenticateWithUrl = (req, res, next)
|
|
66
|
+
const authenticateWithUrl = function apiKeyAuthenticateWithUrl(req, res, next) {
|
|
67
67
|
const token = _extractTokenFromUrl(req.originalUrl);
|
|
68
68
|
if (!token) {
|
|
69
69
|
return next(new errors.UnauthorizedError({
|
|
@@ -89,7 +89,7 @@ const authenticateWithUrl = (req, res, next) => {
|
|
|
89
89
|
* - the "Audience" claim should match the requested API path
|
|
90
90
|
* https://tools.ietf.org/html/rfc7519#section-4.1.3
|
|
91
91
|
*/
|
|
92
|
-
const authenticateWithToken = async (req, res, next, {token, JWT_OPTIONS})
|
|
92
|
+
const authenticateWithToken = async function apiKeyAuthenticateWithToken(req, res, next, {token, JWT_OPTIONS}) {
|
|
93
93
|
const decoded = jwt.decode(token, {complete: true});
|
|
94
94
|
|
|
95
95
|
if (!decoded || !decoded.header) {
|
|
@@ -2,7 +2,6 @@ const {
|
|
|
2
2
|
CollectionsService
|
|
3
3
|
} = require('@tryghost/collections');
|
|
4
4
|
const BookshelfCollectionsRepository = require('./BookshelfCollectionsRepository');
|
|
5
|
-
const labs = require('../../../shared/labs');
|
|
6
5
|
|
|
7
6
|
let inited = false;
|
|
8
7
|
class CollectionsServiceWrapper {
|
|
@@ -32,15 +31,18 @@ class CollectionsServiceWrapper {
|
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
async init() {
|
|
35
|
-
|
|
34
|
+
const config = require('../../../shared/config');
|
|
35
|
+
const labs = require('../../../shared/labs');
|
|
36
|
+
// host setting OR labs "collections" flag has to be enabled to run collections service
|
|
37
|
+
if (!config.get('hostSettings:collections:enabled') && !(labs.isSet('collections'))) {
|
|
36
38
|
return;
|
|
37
39
|
}
|
|
40
|
+
|
|
38
41
|
if (inited) {
|
|
39
42
|
return;
|
|
40
43
|
}
|
|
41
44
|
inited = true;
|
|
42
45
|
this.api.subscribeToEvents();
|
|
43
|
-
require('./intercept-events')();
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -16,7 +16,7 @@ const messages = {
|
|
|
16
16
|
|
|
17
17
|
// @TODO: This piece of middleware actually belongs to the frontend, not to the member app
|
|
18
18
|
// Need to figure a way to separate these things (e.g. frontend actually talks to members API)
|
|
19
|
-
const loadMemberSession = async function (req, res, next) {
|
|
19
|
+
const loadMemberSession = async function loadMemberSession(req, res, next) {
|
|
20
20
|
try {
|
|
21
21
|
const member = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
22
22
|
Object.assign(req, {member});
|
|
@@ -32,7 +32,7 @@ const loadMemberSession = async function (req, res, next) {
|
|
|
32
32
|
* Require member authentication, and make it possible to authenticate via uuid.
|
|
33
33
|
* You can chain this after loadMemberSession to make it possible to authenticate via both the uuid and the session.
|
|
34
34
|
*/
|
|
35
|
-
const authMemberByUuid = async function (req, res, next) {
|
|
35
|
+
const authMemberByUuid = async function authMemberByUuid(req, res, next) {
|
|
36
36
|
try {
|
|
37
37
|
const uuid = req.query.uuid;
|
|
38
38
|
if (!uuid) {
|
|
@@ -60,7 +60,7 @@ const authMemberByUuid = async function (req, res, next) {
|
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
const getIdentityToken = async function (req, res) {
|
|
63
|
+
const getIdentityToken = async function getIdentityToken(req, res) {
|
|
64
64
|
try {
|
|
65
65
|
const token = await membersService.ssr.getIdentityTokenForMemberFromSession(req, res);
|
|
66
66
|
res.writeHead(200);
|
|
@@ -71,7 +71,7 @@ const getIdentityToken = async function (req, res) {
|
|
|
71
71
|
}
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
const deleteSession = async function (req, res) {
|
|
74
|
+
const deleteSession = async function deleteSession(req, res) {
|
|
75
75
|
try {
|
|
76
76
|
await membersService.ssr.deleteSession(req, res);
|
|
77
77
|
res.writeHead(204);
|
|
@@ -87,7 +87,7 @@ const deleteSession = async function (req, res) {
|
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
-
const getMemberData = async function (req, res) {
|
|
90
|
+
const getMemberData = async function getMemberData(req, res) {
|
|
91
91
|
try {
|
|
92
92
|
const member = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
93
93
|
if (member) {
|
|
@@ -101,7 +101,7 @@ const getMemberData = async function (req, res) {
|
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
103
|
|
|
104
|
-
const deleteSuppression = async function (req, res) {
|
|
104
|
+
const deleteSuppression = async function deleteSuppression(req, res) {
|
|
105
105
|
try {
|
|
106
106
|
const member = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
107
107
|
const options = {
|
|
@@ -123,7 +123,7 @@ const deleteSuppression = async function (req, res) {
|
|
|
123
123
|
}
|
|
124
124
|
};
|
|
125
125
|
|
|
126
|
-
const getMemberNewsletters = async function (req, res) {
|
|
126
|
+
const getMemberNewsletters = async function getMemberNewsletters(req, res) {
|
|
127
127
|
try {
|
|
128
128
|
const memberUuid = req.query.uuid;
|
|
129
129
|
|
|
@@ -151,7 +151,7 @@ const getMemberNewsletters = async function (req, res) {
|
|
|
151
151
|
}
|
|
152
152
|
};
|
|
153
153
|
|
|
154
|
-
const updateMemberNewsletters = async function (req, res) {
|
|
154
|
+
const updateMemberNewsletters = async function updateMemberNewsletters(req, res) {
|
|
155
155
|
try {
|
|
156
156
|
const memberUuid = req.query.uuid;
|
|
157
157
|
if (!memberUuid) {
|
|
@@ -182,7 +182,7 @@ const updateMemberNewsletters = async function (req, res) {
|
|
|
182
182
|
}
|
|
183
183
|
};
|
|
184
184
|
|
|
185
|
-
const updateMemberData = async function (req, res) {
|
|
185
|
+
const updateMemberData = async function updateMemberData(req, res) {
|
|
186
186
|
try {
|
|
187
187
|
const data = _.pick(req.body, 'name', 'expertise', 'subscribed', 'newsletters', 'enable_comment_notifications');
|
|
188
188
|
const member = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
@@ -209,7 +209,7 @@ const updateMemberData = async function (req, res) {
|
|
|
209
209
|
}
|
|
210
210
|
};
|
|
211
211
|
|
|
212
|
-
const createSessionFromMagicLink = async function (req, res, next) {
|
|
212
|
+
const createSessionFromMagicLink = async function createSessionFromMagicLink(req, res, next) {
|
|
213
213
|
if (!req.url.includes('token=')) {
|
|
214
214
|
return next();
|
|
215
215
|
}
|
|
@@ -14,7 +14,8 @@ module.exports = class WebmentionMetadata {
|
|
|
14
14
|
author: data.metadata.author,
|
|
15
15
|
image: data.metadata.thumbnail ? new URL(data.metadata.thumbnail) : null,
|
|
16
16
|
favicon: data.metadata.icon ? new URL(data.metadata.icon) : null,
|
|
17
|
-
body: data.body
|
|
17
|
+
body: data.body,
|
|
18
|
+
contentType: data.contentType
|
|
18
19
|
};
|
|
19
20
|
return result;
|
|
20
21
|
}
|
|
@@ -28,6 +28,8 @@ module.exports = {
|
|
|
28
28
|
/** @type {import('@tryghost/webmentions/lib/MentionsAPI')} */
|
|
29
29
|
api: null,
|
|
30
30
|
controller: new MentionController(),
|
|
31
|
+
/** @type {import('@tryghost/webmentions/lib/MentionSendingService')} */
|
|
32
|
+
sendingService: null,
|
|
31
33
|
didInit: false,
|
|
32
34
|
async init() {
|
|
33
35
|
if (this.didInit) {
|
|
@@ -107,5 +109,7 @@ module.exports = {
|
|
|
107
109
|
}
|
|
108
110
|
});
|
|
109
111
|
sendingService.listen(events);
|
|
112
|
+
|
|
113
|
+
this.sendingService = sendingService;
|
|
110
114
|
}
|
|
111
115
|
};
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
let inited = false;
|
|
2
|
+
|
|
3
|
+
module.exports.init = async () => {
|
|
4
|
+
if (inited) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
inited = true;
|
|
8
|
+
|
|
2
9
|
const DomainEvents = require('@tryghost/domain-events/lib/DomainEvents');
|
|
3
10
|
const {ModelToDomainEventInterceptor} = require('@tryghost/model-to-domain-event-interceptor');
|
|
4
11
|
const events = require('../../lib/common/events');
|
|
@@ -6,5 +13,6 @@ module.exports = () => {
|
|
|
6
13
|
ModelEvents: events,
|
|
7
14
|
DomainEvents: DomainEvents
|
|
8
15
|
});
|
|
16
|
+
|
|
9
17
|
eventInterceptor.init();
|
|
10
18
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
class RecommendationServiceWrapper {
|
|
2
|
+
/**
|
|
3
|
+
* @type {import('@tryghost/recommendations').RecommendationRepository}
|
|
4
|
+
*/
|
|
5
|
+
repository;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @type {import('@tryghost/recommendations').RecommendationController}
|
|
9
|
+
*/
|
|
10
|
+
controller;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @type {import('@tryghost/recommendations').RecommendationService}
|
|
14
|
+
*/
|
|
15
|
+
service;
|
|
16
|
+
|
|
17
|
+
init() {
|
|
18
|
+
if (this.repository) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const config = require('../../../shared/config');
|
|
23
|
+
const urlUtils = require('../../../shared/url-utils');
|
|
24
|
+
const models = require('../../models');
|
|
25
|
+
const sentry = require('../../../shared/sentry');
|
|
26
|
+
const {BookshelfRecommendationRepository, RecommendationService, RecommendationController, WellknownService} = require('@tryghost/recommendations');
|
|
27
|
+
|
|
28
|
+
const mentions = require('../mentions');
|
|
29
|
+
|
|
30
|
+
if (!mentions.sendingService) {
|
|
31
|
+
// eslint-disable-next-line ghost/ghost-custom/no-native-error
|
|
32
|
+
throw new Error('MentionSendingService not intialized, but this is a dependency of RecommendationServiceWrapper. Check boot order.');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const wellknownService = new WellknownService({
|
|
36
|
+
dir: config.getContentPath('public'),
|
|
37
|
+
urlUtils
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
this.repository = new BookshelfRecommendationRepository(models.Recommendation, {
|
|
41
|
+
sentry
|
|
42
|
+
});
|
|
43
|
+
this.service = new RecommendationService({
|
|
44
|
+
repository: this.repository,
|
|
45
|
+
wellknownService,
|
|
46
|
+
mentionSendingService: mentions.sendingService
|
|
47
|
+
});
|
|
48
|
+
this.controller = new RecommendationController({
|
|
49
|
+
service: this.service
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
53
|
+
this.service.init().catch(console.error);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = RecommendationServiceWrapper;
|
|
@@ -25,7 +25,7 @@ module.exports = function setupAdminApp() {
|
|
|
25
25
|
adminApp.use('/assets', serveStatic(
|
|
26
26
|
path.join(config.get('paths').adminAssets, 'assets'), {
|
|
27
27
|
maxAge: (configMaxAge || configMaxAge === 0) ? configMaxAge : constants.ONE_YEAR_MS,
|
|
28
|
-
immutable: true,
|
|
28
|
+
immutable: true,
|
|
29
29
|
fallthrough: false
|
|
30
30
|
}
|
|
31
31
|
));
|
|
@@ -59,7 +59,7 @@ module.exports = function setupAdminApp() {
|
|
|
59
59
|
// Finally, routing
|
|
60
60
|
adminApp.get('*', require('./controller'));
|
|
61
61
|
|
|
62
|
-
adminApp.use((err, req, res, next)
|
|
62
|
+
adminApp.use(function fourOhFourMw(err, req, res, next) {
|
|
63
63
|
if (err.statusCode && err.statusCode === 404) {
|
|
64
64
|
// Remove 404 errors for next middleware to inject
|
|
65
65
|
next();
|
|
@@ -8,7 +8,7 @@ const messages = {
|
|
|
8
8
|
notImplemented: 'The server does not support the functionality required to fulfill the request.'
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
const notImplemented = function (req, res, next) {
|
|
11
|
+
const notImplemented = function notImplemented(req, res, next) {
|
|
12
12
|
// CASE: user is logged in, allow
|
|
13
13
|
if (!req.api_key) {
|
|
14
14
|
return next();
|
|
@@ -20,9 +20,9 @@ module.exports = function apiRoutes() {
|
|
|
20
20
|
router.post('/mail_events', mw.publicAdminApi, http(api.mailEvents.add));
|
|
21
21
|
|
|
22
22
|
// ## Collections
|
|
23
|
-
router.get('/collections', mw.authAdminApi,
|
|
24
|
-
router.get('/collections/:id', mw.authAdminApi,
|
|
25
|
-
router.get('/collections/slug/:slug', mw.authAdminApi,
|
|
23
|
+
router.get('/collections', mw.authAdminApi, http(api.collections.browse));
|
|
24
|
+
router.get('/collections/:id', mw.authAdminApi, http(api.collections.read));
|
|
25
|
+
router.get('/collections/slug/:slug', mw.authAdminApi, http(api.collections.read));
|
|
26
26
|
router.post('/collections', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.add));
|
|
27
27
|
router.put('/collections/:id', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.edit));
|
|
28
28
|
router.del('/collections/:id', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.destroy));
|
|
@@ -347,5 +347,11 @@ module.exports = function apiRoutes() {
|
|
|
347
347
|
router.get('/links', mw.authAdminApi, http(api.links.browse));
|
|
348
348
|
router.put('/links/bulk', mw.authAdminApi, http(api.links.bulkEdit));
|
|
349
349
|
|
|
350
|
+
// Recommendations
|
|
351
|
+
router.get('/recommendations', mw.authAdminApi, http(api.recommendations.browse));
|
|
352
|
+
router.post('/recommendations', mw.authAdminApi, http(api.recommendations.add));
|
|
353
|
+
router.put('/recommendations/:id', mw.authAdminApi, http(api.recommendations.edit));
|
|
354
|
+
router.del('/recommendations/:id', mw.authAdminApi, http(api.recommendations.destroy));
|
|
355
|
+
|
|
350
356
|
return router;
|
|
351
357
|
};
|
|
@@ -42,5 +42,8 @@ module.exports = function apiRoutes() {
|
|
|
42
42
|
router.get('/collections/:id', mw.authenticatePublic, http(api.collectionsPublic.readById));
|
|
43
43
|
router.get('/collections/slug/:slug', mw.authenticatePublic, http(api.collectionsPublic.readBySlug));
|
|
44
44
|
|
|
45
|
+
// ## Recommendations
|
|
46
|
+
router.get('/recommendations', mw.authenticatePublic, http(api.recommendationsPublic.browse));
|
|
47
|
+
|
|
45
48
|
return router;
|
|
46
49
|
};
|
|
@@ -87,7 +87,7 @@ function corsOptionsDelegate(req, cb) {
|
|
|
87
87
|
* @param {Express.Response} res
|
|
88
88
|
* @param {Function} next
|
|
89
89
|
*/
|
|
90
|
-
const handleCaching = (req, res, next)
|
|
90
|
+
const handleCaching = function handleCaching(req, res, next) {
|
|
91
91
|
const method = req.method && req.method.toUpperCase && req.method.toUpperCase();
|
|
92
92
|
if (method === 'OPTIONS') {
|
|
93
93
|
// @NOTE: try to add native support for dynamic 'vary' header value in 'cors' module
|
|
@@ -47,7 +47,7 @@ const upload = multer({dest: os.tmpdir()});
|
|
|
47
47
|
|
|
48
48
|
const deleteSingleFile = file => fs.unlink(file.path).catch(err => logging.error(err));
|
|
49
49
|
|
|
50
|
-
const single = name => (req, res, next)
|
|
50
|
+
const single = name => function singleUploadFunction(req, res, next) {
|
|
51
51
|
const singleUpload = upload.single(name);
|
|
52
52
|
|
|
53
53
|
singleUpload(req, res, (err) => {
|
|
@@ -77,7 +77,7 @@ const single = name => (req, res, next) => {
|
|
|
77
77
|
});
|
|
78
78
|
};
|
|
79
79
|
|
|
80
|
-
const media = (fileName, thumbName) => (req, res, next)
|
|
80
|
+
const media = (fileName, thumbName) => function mediaUploadFunction(req, res, next) {
|
|
81
81
|
const mediaUpload = upload.fields([{
|
|
82
82
|
name: fileName,
|
|
83
83
|
maxCount: 1
|
|
@@ -3,7 +3,7 @@ const uuid = require('uuid');
|
|
|
3
3
|
/**
|
|
4
4
|
* @TODO: move this middleware to Framework monorepo?
|
|
5
5
|
*/
|
|
6
|
-
module.exports = (req, res, next)
|
|
6
|
+
module.exports = function requestIdMw(req, res, next) {
|
|
7
7
|
const requestId = req.get('X-Request-ID') || uuid.v4();
|
|
8
8
|
|
|
9
9
|
// Set a value for internal use
|
|
@@ -21,7 +21,7 @@ const messages = {
|
|
|
21
21
|
pageNotFound: 'Page not found"'
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
const uncapitalise = (req, res, next)
|
|
24
|
+
const uncapitalise = function uncapitalise(req, res, next) {
|
|
25
25
|
let pathToTest = (req.baseUrl ? req.baseUrl : '') + req.path;
|
|
26
26
|
let redirectPath;
|
|
27
27
|
let decodedURI;
|
|
@@ -87,7 +87,7 @@ _private.getFrontendRedirectUrl = ({requestedHost, requestedUrl, queryParameters
|
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
-
_private.redirect = (req, res, next, redirectFn)
|
|
90
|
+
_private.redirect = function urlRedirectsRedirect(req, res, next, redirectFn) {
|
|
91
91
|
const redirectUrl = redirectFn({
|
|
92
92
|
requestedHost: req.vhost ? req.vhost.host : req.get('host'),
|
|
93
93
|
requestedUrl: url.parse(req.originalUrl || req.url).pathname,
|
|
@@ -104,11 +104,11 @@ _private.redirect = (req, res, next, redirectFn) => {
|
|
|
104
104
|
next();
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
-
const frontendRedirect = (req, res, next)
|
|
107
|
+
const frontendRedirect = function frontendRedirect(req, res, next) {
|
|
108
108
|
_private.redirect(req, res, next, _private.getFrontendRedirectUrl);
|
|
109
109
|
};
|
|
110
110
|
|
|
111
|
-
const adminRedirect = (req, res, next)
|
|
111
|
+
const adminRedirect = function adminRedirect(req, res, next) {
|
|
112
112
|
_private.redirect(req, res, next, _private.getAdminRedirectUrl);
|
|
113
113
|
};
|
|
114
114
|
|
|
@@ -18,7 +18,7 @@ module.exports = function setupWellKnownApp() {
|
|
|
18
18
|
|
|
19
19
|
const cache = cacheControl('public', {maxAge: config.get('caching:wellKnown:maxAge')});
|
|
20
20
|
|
|
21
|
-
wellKnownApp.get('/jwks.json', cache, async (req, res)
|
|
21
|
+
wellKnownApp.get('/jwks.json', cache, async function jwksMiddleware(req, res) {
|
|
22
22
|
const jwks = await getSafePublicJWKS();
|
|
23
23
|
|
|
24
24
|
// there's only one key in the store atm
|
package/core/shared/labs.js
CHANGED
package/index.js
CHANGED
package/newrelic.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const config = require('./core/shared/config');
|
|
2
|
+
|
|
3
|
+
exports.config = {
|
|
4
|
+
app_name: ['Ghost'],
|
|
5
|
+
license_key: config.get('newRelic:licenseKey'),
|
|
6
|
+
labels: {
|
|
7
|
+
env: process.env.PRO_ENV || 'unknown',
|
|
8
|
+
site: config.get('hostSettings:siteId')
|
|
9
|
+
},
|
|
10
|
+
// Agent only imported when enabled, but this will further cement that
|
|
11
|
+
agent_enabled: config.get('newRelic:enabled'),
|
|
12
|
+
distributed_tracing: {
|
|
13
|
+
enabled: true
|
|
14
|
+
},
|
|
15
|
+
logging: {
|
|
16
|
+
level: 'info'
|
|
17
|
+
},
|
|
18
|
+
slow_sql: {
|
|
19
|
+
// Default is false.
|
|
20
|
+
enabled: true,
|
|
21
|
+
// Default is 10. Increasing this limit increases memory usage
|
|
22
|
+
// Defines the maximum number of slow queries the agent collects per minute.
|
|
23
|
+
// The agent discards additional queries after the limit is reached.
|
|
24
|
+
max_samples: 10
|
|
25
|
+
},
|
|
26
|
+
error_collector: {
|
|
27
|
+
ignore_classes: [
|
|
28
|
+
// @NOTE: add more error classes to ignore here
|
|
29
|
+
'ValidationError',
|
|
30
|
+
'NoPermissionError'
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
transaction_tracer: {
|
|
34
|
+
enabled: true,
|
|
35
|
+
record_sql: 'obfuscated'
|
|
36
|
+
},
|
|
37
|
+
allow_all_headers: true,
|
|
38
|
+
attributes: {
|
|
39
|
+
exclude: [
|
|
40
|
+
// Default exclusions (TODO: add more!):
|
|
41
|
+
'request.headers.cookie',
|
|
42
|
+
'request.headers.authorization',
|
|
43
|
+
'request.headers.proxyAuthorization',
|
|
44
|
+
'request.headers.setCookie*',
|
|
45
|
+
'request.headers.x*',
|
|
46
|
+
'response.headers.cookie',
|
|
47
|
+
'response.headers.authorization',
|
|
48
|
+
'response.headers.proxyAuthorization',
|
|
49
|
+
'response.headers.setCookie*',
|
|
50
|
+
'response.headers.x*'
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
};
|