ghost 5.96.2 → 5.97.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/tryghost-adapter-cache-memory-ttl-5.97.1.tgz +0 -0
- package/components/{tryghost-adapter-cache-redis-5.96.2.tgz → tryghost-adapter-cache-redis-5.97.1.tgz} +0 -0
- package/components/tryghost-adapter-manager-5.97.1.tgz +0 -0
- package/components/tryghost-announcement-bar-settings-5.97.1.tgz +0 -0
- package/components/{tryghost-api-framework-5.96.2.tgz → tryghost-api-framework-5.97.1.tgz} +0 -0
- package/components/tryghost-api-version-compatibility-service-5.97.1.tgz +0 -0
- package/components/tryghost-audience-feedback-5.97.1.tgz +0 -0
- package/components/tryghost-bookshelf-repository-5.97.1.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.97.1.tgz +0 -0
- package/components/tryghost-collections-5.97.1.tgz +0 -0
- package/components/tryghost-constants-5.97.1.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.97.1.tgz +0 -0
- package/components/{tryghost-data-generator-5.96.2.tgz → tryghost-data-generator-5.97.1.tgz} +0 -0
- package/components/tryghost-domain-events-5.97.1.tgz +0 -0
- package/components/tryghost-donations-5.97.1.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.97.1.tgz +0 -0
- package/components/tryghost-email-addresses-5.97.1.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.97.1.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.97.1.tgz +0 -0
- package/components/{tryghost-email-content-generator-5.96.2.tgz → tryghost-email-content-generator-5.97.1.tgz} +0 -0
- package/components/tryghost-email-events-5.97.1.tgz +0 -0
- package/components/tryghost-email-service-5.97.1.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.97.1.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.97.1.tgz +0 -0
- package/components/tryghost-external-media-inliner-5.97.1.tgz +0 -0
- package/components/tryghost-extract-api-key-5.97.1.tgz +0 -0
- package/components/{tryghost-ghost-5.96.2.tgz → tryghost-ghost-5.97.1.tgz} +0 -0
- package/components/tryghost-html-to-plaintext-5.97.1.tgz +0 -0
- package/components/tryghost-i18n-5.97.1.tgz +0 -0
- package/components/tryghost-importer-handler-content-files-5.97.1.tgz +0 -0
- package/components/tryghost-importer-revue-5.97.1.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.97.1.tgz +0 -0
- package/components/{tryghost-job-manager-5.96.2.tgz → tryghost-job-manager-5.97.1.tgz} +0 -0
- package/components/tryghost-link-redirects-5.97.1.tgz +0 -0
- package/components/tryghost-link-replacer-5.97.1.tgz +0 -0
- package/components/tryghost-link-tracking-5.97.1.tgz +0 -0
- package/components/tryghost-magic-link-5.97.1.tgz +0 -0
- package/components/{tryghost-mail-events-5.96.2.tgz → tryghost-mail-events-5.97.1.tgz} +0 -0
- package/components/{tryghost-mailgun-client-5.96.2.tgz → tryghost-mailgun-client-5.97.1.tgz} +0 -0
- package/components/tryghost-member-attribution-5.97.1.tgz +0 -0
- package/components/tryghost-member-events-5.97.1.tgz +0 -0
- package/components/tryghost-members-api-5.97.1.tgz +0 -0
- package/components/tryghost-members-csv-5.97.1.tgz +0 -0
- package/components/{tryghost-members-events-service-5.96.2.tgz → tryghost-members-events-service-5.97.1.tgz} +0 -0
- package/components/tryghost-members-importer-5.97.1.tgz +0 -0
- package/components/{tryghost-members-offers-5.96.2.tgz → tryghost-members-offers-5.97.1.tgz} +0 -0
- package/components/tryghost-members-payments-5.97.1.tgz +0 -0
- package/components/tryghost-members-ssr-5.97.1.tgz +0 -0
- package/components/{tryghost-members-stripe-service-5.96.2.tgz → tryghost-members-stripe-service-5.97.1.tgz} +0 -0
- package/components/tryghost-mentions-email-report-5.97.1.tgz +0 -0
- package/components/tryghost-metrics-server-5.97.1.tgz +0 -0
- package/components/tryghost-milestones-5.97.1.tgz +0 -0
- package/components/tryghost-minifier-5.97.1.tgz +0 -0
- package/components/{tryghost-model-to-domain-event-interceptor-5.96.2.tgz → tryghost-model-to-domain-event-interceptor-5.97.1.tgz} +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.97.1.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.97.1.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.97.1.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.97.1.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.97.1.tgz +0 -0
- package/components/tryghost-mw-version-match-5.97.1.tgz +0 -0
- package/components/tryghost-mw-vhost-5.97.1.tgz +0 -0
- package/components/tryghost-nql-filter-expansions-5.97.1.tgz +0 -0
- package/components/{tryghost-oembed-service-5.96.2.tgz → tryghost-oembed-service-5.97.1.tgz} +0 -0
- package/components/tryghost-package-json-5.97.1.tgz +0 -0
- package/components/tryghost-post-events-5.97.1.tgz +0 -0
- package/components/tryghost-post-revisions-5.97.1.tgz +0 -0
- package/components/tryghost-posts-service-5.97.1.tgz +0 -0
- package/components/tryghost-recommendations-5.97.1.tgz +0 -0
- package/components/tryghost-referrers-5.97.1.tgz +0 -0
- package/components/{tryghost-security-5.96.2.tgz → tryghost-security-5.97.1.tgz} +0 -0
- package/components/tryghost-session-service-5.97.1.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.97.1.tgz +0 -0
- package/components/tryghost-slack-notifications-5.97.1.tgz +0 -0
- package/components/{tryghost-staff-service-5.96.2.tgz → tryghost-staff-service-5.97.1.tgz} +0 -0
- package/components/{tryghost-stats-service-5.96.2.tgz → tryghost-stats-service-5.97.1.tgz} +0 -0
- package/components/{tryghost-tiers-5.96.2.tgz → tryghost-tiers-5.97.1.tgz} +0 -0
- package/components/tryghost-update-check-service-5.97.1.tgz +0 -0
- package/components/tryghost-verification-trigger-5.97.1.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.97.1.tgz +0 -0
- package/components/{tryghost-webmentions-5.96.2.tgz → tryghost-webmentions-5.97.1.tgz} +0 -0
- package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +1 -1
- package/core/built/admin/assets/admin-x-activitypub/{index-def21e23.mjs → index-ce1044b4.mjs} +522 -525
- package/core/built/admin/assets/admin-x-activitypub/{modals-09282c4e.mjs → modals-35dd1224.mjs} +2 -2
- package/core/built/admin/assets/admin-x-demo/admin-x-demo.js +1 -1
- package/core/built/admin/assets/admin-x-demo/{index-3d57e7aa.mjs → index-4e95bd22.mjs} +4 -4
- package/core/built/admin/assets/admin-x-demo/{modals-191eb359.mjs → modals-1dd517c9.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{CodeEditorView-85a3642c.mjs → CodeEditorView-56b3751e.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-ebbd8de9.mjs → index-a57b83d6.mjs} +5 -5
- package/core/built/admin/assets/admin-x-settings/{index-eeeca6f6.mjs → index-ae6dfa81.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{modals-8a98634b.mjs → modals-8dbfe675.mjs} +1322 -1305
- package/core/built/admin/assets/{chunk.524.e61cf6deaaec6bf6f9d2.js → chunk.524.8daf11dc6681a463afc3.js} +5 -5
- package/core/built/admin/assets/{chunk.582.6fb536052fa03fba4adb.js → chunk.582.8e8f4dd40e8a4c117a8c.js} +6 -6
- package/core/built/admin/assets/{chunk.874.e1d421507701daac30fb.js → chunk.874.5eb920d19e75683234c2.js} +6 -6
- package/core/built/admin/assets/ghost-84fc705038c0ea946e4e5048ccb1fea5.css +1 -0
- package/core/built/admin/assets/{ghost-286f9a114755e8c3c7e9c37550f624a9.js → ghost-b7702081322ac2a966ad205b1d07416c.js} +57 -62
- package/core/built/admin/assets/ghost-dark-b00268cf596f7bba8699dce557d65585.css +1 -0
- package/core/built/admin/index.html +5 -5
- package/core/frontend/services/assets-minification/AssetsMinificationBase.js +4 -4
- package/core/server/api/endpoints/comments.js +18 -2
- package/core/server/api/endpoints/utils/serializers/input/settings.js +3 -1
- package/core/server/api/endpoints/utils/serializers/output/members.js +2 -1
- package/core/server/data/db/DatabaseStateManager.js +29 -2
- package/core/server/data/migrations/versions/5.97/2024-10-08-14-25-27-added-body-font-settings.js +8 -0
- package/core/server/data/migrations/versions/5.97/2024-10-08-14-36-58-added-heading-font-setting.js +8 -0
- package/core/server/data/migrations/versions/5.97/2024-10-10-01-02-03-add-signin-urls-permissions.js +6 -0
- package/core/server/data/schema/default-settings/default-settings.json +8 -0
- package/core/server/data/schema/fixtures/fixtures.json +2 -1
- package/core/server/models/base/plugins/data-manipulation.js +11 -15
- package/core/server/models/base/plugins/raw-knex.js +3 -3
- package/core/server/models/base/plugins/sanitize.js +12 -13
- package/core/server/models/post.js +5 -3
- package/core/server/services/members/api.js +3 -1
- package/core/server/services/members/utils.js +1 -0
- package/core/server/services/settings-helpers/SettingsHelpers.js +25 -0
- package/core/server/services/url/Resource.js +1 -2
- package/core/server/services/url/Resources.js +12 -12
- package/core/server/services/url/UrlGenerator.js +1 -2
- package/core/server/services/url/config.js +9 -0
- package/core/server/web/api/middleware/upload.js +2 -1
- package/package.json +219 -153
- package/yarn.lock +203 -244
- package/components/tryghost-adapter-cache-memory-ttl-5.96.2.tgz +0 -0
- package/components/tryghost-adapter-manager-5.96.2.tgz +0 -0
- package/components/tryghost-announcement-bar-settings-5.96.2.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.96.2.tgz +0 -0
- package/components/tryghost-audience-feedback-5.96.2.tgz +0 -0
- package/components/tryghost-bookshelf-repository-5.96.2.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.96.2.tgz +0 -0
- package/components/tryghost-collections-5.96.2.tgz +0 -0
- package/components/tryghost-constants-5.96.2.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.96.2.tgz +0 -0
- package/components/tryghost-domain-events-5.96.2.tgz +0 -0
- package/components/tryghost-donations-5.96.2.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.96.2.tgz +0 -0
- package/components/tryghost-email-addresses-5.96.2.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.96.2.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.96.2.tgz +0 -0
- package/components/tryghost-email-events-5.96.2.tgz +0 -0
- package/components/tryghost-email-service-5.96.2.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.96.2.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.96.2.tgz +0 -0
- package/components/tryghost-external-media-inliner-5.96.2.tgz +0 -0
- package/components/tryghost-extract-api-key-5.96.2.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.96.2.tgz +0 -0
- package/components/tryghost-i18n-5.96.2.tgz +0 -0
- package/components/tryghost-importer-handler-content-files-5.96.2.tgz +0 -0
- package/components/tryghost-importer-revue-5.96.2.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.96.2.tgz +0 -0
- package/components/tryghost-link-redirects-5.96.2.tgz +0 -0
- package/components/tryghost-link-replacer-5.96.2.tgz +0 -0
- package/components/tryghost-link-tracking-5.96.2.tgz +0 -0
- package/components/tryghost-magic-link-5.96.2.tgz +0 -0
- package/components/tryghost-member-attribution-5.96.2.tgz +0 -0
- package/components/tryghost-member-events-5.96.2.tgz +0 -0
- package/components/tryghost-members-api-5.96.2.tgz +0 -0
- package/components/tryghost-members-csv-5.96.2.tgz +0 -0
- package/components/tryghost-members-importer-5.96.2.tgz +0 -0
- package/components/tryghost-members-payments-5.96.2.tgz +0 -0
- package/components/tryghost-members-ssr-5.96.2.tgz +0 -0
- package/components/tryghost-mentions-email-report-5.96.2.tgz +0 -0
- package/components/tryghost-metrics-server-5.96.2.tgz +0 -0
- package/components/tryghost-milestones-5.96.2.tgz +0 -0
- package/components/tryghost-minifier-5.96.2.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.96.2.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.96.2.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.96.2.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.96.2.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.96.2.tgz +0 -0
- package/components/tryghost-mw-version-match-5.96.2.tgz +0 -0
- package/components/tryghost-mw-vhost-5.96.2.tgz +0 -0
- package/components/tryghost-nql-filter-expansions-5.96.2.tgz +0 -0
- package/components/tryghost-package-json-5.96.2.tgz +0 -0
- package/components/tryghost-post-events-5.96.2.tgz +0 -0
- package/components/tryghost-post-revisions-5.96.2.tgz +0 -0
- package/components/tryghost-posts-service-5.96.2.tgz +0 -0
- package/components/tryghost-recommendations-5.96.2.tgz +0 -0
- package/components/tryghost-referrers-5.96.2.tgz +0 -0
- package/components/tryghost-session-service-5.96.2.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.96.2.tgz +0 -0
- package/components/tryghost-slack-notifications-5.96.2.tgz +0 -0
- package/components/tryghost-update-check-service-5.96.2.tgz +0 -0
- package/components/tryghost-verification-trigger-5.96.2.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.96.2.tgz +0 -0
- package/core/built/admin/assets/ghost-c75b9d653495f9cfce528f6e10900b3f.css +0 -1
- package/core/built/admin/assets/ghost-dark-6ea629e177f77314f5d75c0ca97190ab.css +0 -1
- /package/core/built/admin/assets/{chunk.874.e1d421507701daac30fb.js.LICENSE.txt → chunk.874.5eb920d19e75683234c2.js.LICENSE.txt} +0 -0
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<title>Ghost Admin</title>
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22cdnUrl%22%3A%22%22%2C%22editorUrl%22%3A%22%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.
|
|
11
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22cdnUrl%22%3A%22%22%2C%22editorUrl%22%3A%22%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.97%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%22ee37f0b9eb%22%2C%22adminXDemoFilename%22%3A%22admin-x-demo.js%22%2C%22adminXDemoHash%22%3A%2224d6ef8fbd%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%22b1e0cd5a92%22%2C%22adminXActivitypubFilename%22%3A%22admin-x-activitypub.js%22%2C%22adminXActivitypubHash%22%3A%229368ade858%22%2C%22adminXActivitypubCustomUrl%22%3A%22https%3A%2F%2Fcdn.jsdelivr.net%2Fnpm%2F%40tryghost%2Fadmin-x-activitypub%400%2Fdist%2Fadmin-x-activitypub.js%22%7D" />
|
|
12
12
|
|
|
13
13
|
<meta name="HandheldFriendly" content="True" />
|
|
14
14
|
<meta name="MobileOptimized" content="320" />
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
</style>
|
|
38
38
|
|
|
39
39
|
<link integrity="" rel="stylesheet" href="assets/vendor-0ede59da8efb5e28fa929557f7ff7154.css">
|
|
40
|
-
<link integrity="" rel="stylesheet" href="assets/ghost-
|
|
40
|
+
<link integrity="" rel="stylesheet" href="assets/ghost-84fc705038c0ea946e4e5048ccb1fea5.css" title="light">
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
</head>
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
58
58
|
|
|
59
59
|
<script src="assets/vendor-88cd9f5cf5eba65221e2d2a636531eaa.js"></script>
|
|
60
|
-
<script src="assets/chunk.874.
|
|
61
|
-
<script src="assets/chunk.524.
|
|
62
|
-
<script src="assets/ghost-
|
|
60
|
+
<script src="assets/chunk.874.5eb920d19e75683234c2.js"></script>
|
|
61
|
+
<script src="assets/chunk.524.8daf11dc6681a463afc3.js"></script>
|
|
62
|
+
<script src="assets/ghost-b7702081322ac2a966ad205b1d07416c.js"></script>
|
|
63
63
|
</body>
|
|
64
64
|
</html>
|
|
@@ -48,16 +48,16 @@ module.exports = class AssetsMinificationBase {
|
|
|
48
48
|
|
|
49
49
|
async minify(globs, options) {
|
|
50
50
|
try {
|
|
51
|
-
|
|
51
|
+
const result = await this.minifier.minify(globs, options);
|
|
52
|
+
this.ready = true;
|
|
53
|
+
return result;
|
|
52
54
|
} catch (error) {
|
|
53
55
|
if (error.code === 'EACCES') {
|
|
54
56
|
logging.error('Ghost was not able to write asset files due to permissions.');
|
|
55
57
|
return;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
throw error;
|
|
59
|
-
} finally {
|
|
60
|
-
this.ready = true;
|
|
60
|
+
throw error;
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
const models = require('../../models');
|
|
2
2
|
|
|
3
|
+
function handleCacheHeaders(model, frame) {
|
|
4
|
+
if (model) {
|
|
5
|
+
const postId = model.get('post_id');
|
|
6
|
+
const parentId = model.get('parent_id');
|
|
7
|
+
const pathsToInvalidate = [
|
|
8
|
+
postId ? `/api/members/comments/post/${postId}/` : null,
|
|
9
|
+
parentId ? `/api/members/comments/${parentId}/replies/` : null
|
|
10
|
+
].filter(path => path !== null);
|
|
11
|
+
frame.setHeader('X-Cache-Invalidate', pathsToInvalidate.join(', '));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
3
15
|
/** @type {import('@tryghost/api-framework').Controller} */
|
|
4
16
|
const controller = {
|
|
5
17
|
docName: 'comments',
|
|
@@ -19,11 +31,15 @@ const controller = {
|
|
|
19
31
|
}
|
|
20
32
|
},
|
|
21
33
|
permissions: true,
|
|
22
|
-
query(frame) {
|
|
23
|
-
|
|
34
|
+
async query(frame) {
|
|
35
|
+
const result = await models.Comment.edit({
|
|
24
36
|
id: frame.data.comments[0].id,
|
|
25
37
|
status: frame.data.comments[0].status
|
|
26
38
|
}, frame.options);
|
|
39
|
+
|
|
40
|
+
handleCacheHeaders(result, frame);
|
|
41
|
+
|
|
42
|
+
return result;
|
|
27
43
|
}
|
|
28
44
|
}
|
|
29
45
|
};
|
|
@@ -167,7 +167,8 @@ function serializeMember(member, options) {
|
|
|
167
167
|
email_recipients: json.email_recipients,
|
|
168
168
|
status: json.status,
|
|
169
169
|
last_seen_at: json.last_seen_at,
|
|
170
|
-
attribution: serializeAttribution(json.attribution)
|
|
170
|
+
attribution: serializeAttribution(json.attribution),
|
|
171
|
+
unsubscribe_url: json.unsubscribe_url
|
|
171
172
|
};
|
|
172
173
|
|
|
173
174
|
if (json.products) {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
const KnexMigrator = require('knex-migrator');
|
|
2
2
|
const errors = require('@tryghost/errors');
|
|
3
3
|
const logging = require('@tryghost/logging');
|
|
4
|
+
const metrics = require('@tryghost/metrics');
|
|
5
|
+
|
|
6
|
+
const sentry = require('../../../shared/sentry');
|
|
4
7
|
|
|
5
8
|
const states = {
|
|
6
9
|
READY: 0,
|
|
@@ -61,9 +64,14 @@ class DatabaseStateManager {
|
|
|
61
64
|
// CASE: database connection errors, unknown cases
|
|
62
65
|
let errorToThrow = error;
|
|
63
66
|
if (!errors.utils.isGhostError(errorToThrow)) {
|
|
64
|
-
errorToThrow = new errors.InternalServerError({
|
|
67
|
+
errorToThrow = new errors.InternalServerError({
|
|
68
|
+
code: 'DATABASE_ERROR',
|
|
69
|
+
message: errorToThrow.message,
|
|
70
|
+
err: errorToThrow
|
|
71
|
+
});
|
|
65
72
|
}
|
|
66
73
|
|
|
74
|
+
sentry.captureException(errorToThrow);
|
|
67
75
|
throw errorToThrow;
|
|
68
76
|
}
|
|
69
77
|
}
|
|
@@ -79,11 +87,25 @@ class DatabaseStateManager {
|
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
if (state === states.NEEDS_INITIALISATION) {
|
|
90
|
+
const beforeInitializationTime = Date.now();
|
|
91
|
+
|
|
82
92
|
await this.knexMigrator.init();
|
|
93
|
+
|
|
94
|
+
metrics.metric('migrations', {
|
|
95
|
+
value: Date.now() - beforeInitializationTime,
|
|
96
|
+
type: 'initialization'
|
|
97
|
+
});
|
|
83
98
|
}
|
|
84
99
|
|
|
85
100
|
if (state === states.NEEDS_MIGRATION) {
|
|
101
|
+
const beforeMigrationTime = Date.now();
|
|
102
|
+
|
|
86
103
|
await this.knexMigrator.migrate();
|
|
104
|
+
|
|
105
|
+
metrics.metric('migrations', {
|
|
106
|
+
value: Date.now() - beforeMigrationTime,
|
|
107
|
+
type: 'migrations'
|
|
108
|
+
});
|
|
87
109
|
}
|
|
88
110
|
|
|
89
111
|
state = await this.getState();
|
|
@@ -92,9 +114,14 @@ class DatabaseStateManager {
|
|
|
92
114
|
} catch (error) {
|
|
93
115
|
let errorToThrow = error;
|
|
94
116
|
if (!errors.utils.isGhostError(error)) {
|
|
95
|
-
errorToThrow = new errors.InternalServerError({
|
|
117
|
+
errorToThrow = new errors.InternalServerError({
|
|
118
|
+
code: 'DATABASE_ERROR',
|
|
119
|
+
message: errorToThrow.message,
|
|
120
|
+
err: errorToThrow
|
|
121
|
+
});
|
|
96
122
|
}
|
|
97
123
|
|
|
124
|
+
sentry.captureException(errorToThrow);
|
|
98
125
|
throw errorToThrow;
|
|
99
126
|
}
|
|
100
127
|
}
|
|
@@ -26,7 +26,7 @@ module.exports = function (Bookshelf) {
|
|
|
26
26
|
* before we insert dates into the database, we have to normalize
|
|
27
27
|
* date format is now in each db the same
|
|
28
28
|
*/
|
|
29
|
-
fixDatesWhenSave: function
|
|
29
|
+
fixDatesWhenSave: function fixDatesWhenSave(attrs) {
|
|
30
30
|
const self = this;
|
|
31
31
|
|
|
32
32
|
_.each(attrs, function each(value, key) {
|
|
@@ -49,15 +49,12 @@ module.exports = function (Bookshelf) {
|
|
|
49
49
|
* mysql:
|
|
50
50
|
* - knex wraps the UTC value into a local JS Date
|
|
51
51
|
*/
|
|
52
|
-
fixDatesWhenFetch: function
|
|
53
|
-
const
|
|
54
|
-
let dateMoment;
|
|
52
|
+
fixDatesWhenFetch: function fixDatesWhenFetch(attrs) {
|
|
53
|
+
const tableDef = schema.tables[this.tableName];
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
&& schema.tables[self.tableName][key].type === 'dateTime') {
|
|
60
|
-
dateMoment = moment(value);
|
|
55
|
+
Object.keys(attrs).forEach((key) => {
|
|
56
|
+
if (attrs[key] && tableDef?.[key]?.type === 'dateTime') {
|
|
57
|
+
const dateMoment = moment(attrs[key]);
|
|
61
58
|
|
|
62
59
|
// CASE: You are somehow able to store e.g. 0000-00-00 00:00:00
|
|
63
60
|
// Protect the code base and return the current date time.
|
|
@@ -74,12 +71,11 @@ module.exports = function (Bookshelf) {
|
|
|
74
71
|
|
|
75
72
|
// Convert integers to real booleans
|
|
76
73
|
fixBools: function fixBools(attrs) {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
attrs[key] = value ? true : false;
|
|
74
|
+
const tableDef = schema.tables[this.tableName];
|
|
75
|
+
|
|
76
|
+
Object.keys(attrs).forEach((key) => {
|
|
77
|
+
if (tableDef?.[key]?.type === 'boolean') {
|
|
78
|
+
attrs[key] = !!attrs[key];
|
|
83
79
|
}
|
|
84
80
|
});
|
|
85
81
|
|
|
@@ -172,14 +172,14 @@ module.exports = function (Bookshelf) {
|
|
|
172
172
|
const relationsToAttach = _.zipObject(_.keys(props), relationsToAttachArray);
|
|
173
173
|
|
|
174
174
|
objects = _.map(objects, (object) => {
|
|
175
|
-
|
|
175
|
+
for (const relation in relationsToAttach) {
|
|
176
176
|
if (!relationsToAttach[relation][object.id]) {
|
|
177
177
|
object[relation] = [];
|
|
178
|
-
|
|
178
|
+
continue;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
object[relation] = relationsToAttach[relation][object.id];
|
|
182
|
-
}
|
|
182
|
+
}
|
|
183
183
|
|
|
184
184
|
object = Bookshelf.registry.models[modelName].prototype.toJSON.bind({
|
|
185
185
|
attributes: object,
|
|
@@ -35,21 +35,21 @@ module.exports = function (Bookshelf) {
|
|
|
35
35
|
|
|
36
36
|
switch (methodName) {
|
|
37
37
|
case 'toJSON':
|
|
38
|
-
return baseOptions
|
|
38
|
+
return [...baseOptions, 'shallow', 'columns', 'previous'];
|
|
39
39
|
case 'destroy':
|
|
40
|
-
return baseOptions
|
|
40
|
+
return [...baseOptions, ...extraOptions, 'id', 'destroyBy', 'require'];
|
|
41
41
|
case 'add':
|
|
42
|
-
return baseOptions
|
|
42
|
+
return [...baseOptions, ...extraOptions, 'autoRefresh'];
|
|
43
43
|
case 'edit':
|
|
44
|
-
return baseOptions
|
|
44
|
+
return [...baseOptions, ...extraOptions, 'id', 'require', 'autoRefresh'];
|
|
45
45
|
case 'findOne':
|
|
46
|
-
return baseOptions
|
|
46
|
+
return [...baseOptions, ...extraOptions, 'columns', 'require', 'mongoTransformer'];
|
|
47
47
|
case 'findAll':
|
|
48
|
-
return baseOptions
|
|
48
|
+
return [...baseOptions, ...extraOptions, 'filter', 'columns', 'mongoTransformer'];
|
|
49
49
|
case 'findPage':
|
|
50
|
-
return baseOptions
|
|
50
|
+
return [...baseOptions, ...extraOptions, 'filter', 'order', 'autoOrder', 'page', 'limit', 'columns', 'mongoTransformer'];
|
|
51
51
|
default:
|
|
52
|
-
return baseOptions
|
|
52
|
+
return [...baseOptions, ...extraOptions];
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
|
|
@@ -164,11 +164,10 @@ module.exports = function (Bookshelf) {
|
|
|
164
164
|
|
|
165
165
|
let options = _.cloneDeep(unfilteredOptions);
|
|
166
166
|
const extraAllowedProperties = filterConfig.extraAllowedProperties || [];
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
options = _.pick(options, permittedOptions);
|
|
167
|
+
const permittedOptions = [...new Set([...this.permittedOptions(methodName, options), ...extraAllowedProperties])];
|
|
168
|
+
options = Object.fromEntries(
|
|
169
|
+
Object.entries(options).filter(([key]) => permittedOptions.includes(key))
|
|
170
|
+
);
|
|
172
171
|
|
|
173
172
|
if (this.defaultRelations) {
|
|
174
173
|
options = this.defaultRelations(methodName, options);
|
|
@@ -1290,11 +1290,13 @@ Post = ghostBookshelf.Model.extend({
|
|
|
1290
1290
|
options.withRelated = _.union(['authors', 'tags', 'post_revisions', 'post_revisions.author'], options.withRelated || []);
|
|
1291
1291
|
}
|
|
1292
1292
|
|
|
1293
|
-
const META_ATTRIBUTES = _.without(ghostBookshelf.model('PostsMeta').prototype.permittedAttributes(), 'id', 'post_id');
|
|
1294
|
-
|
|
1295
1293
|
// NOTE: only include post_meta relation when requested in 'columns' or by default
|
|
1296
1294
|
// optimization is needed to be able to perform .findAll on large SQLite datasets
|
|
1297
|
-
if (!options.columns
|
|
1295
|
+
if (!options.columns
|
|
1296
|
+
|| (
|
|
1297
|
+
options.columns
|
|
1298
|
+
&& _.intersection(_.without(ghostBookshelf.model('PostsMeta').prototype.permittedAttributes(), 'id', 'post_id'), options.columns).length)
|
|
1299
|
+
) {
|
|
1298
1300
|
options.withRelated = _.union(['posts_meta'], options.withRelated || []);
|
|
1299
1301
|
}
|
|
1300
1302
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const stripeService = require('../stripe');
|
|
2
2
|
const settingsCache = require('../../../shared/settings-cache');
|
|
3
|
+
const settingsHelpers = require('../../services/settings-helpers');
|
|
3
4
|
const MembersApi = require('@tryghost/members-api');
|
|
4
5
|
const logging = require('@tryghost/logging');
|
|
5
6
|
const mail = require('../mail');
|
|
@@ -236,7 +237,8 @@ function createApiInstance(config) {
|
|
|
236
237
|
memberAttributionService: memberAttributionService.service,
|
|
237
238
|
emailSuppressionList,
|
|
238
239
|
settingsCache,
|
|
239
|
-
sentry
|
|
240
|
+
sentry,
|
|
241
|
+
settingsHelpers
|
|
240
242
|
});
|
|
241
243
|
|
|
242
244
|
return membersApiInstance;
|
|
@@ -22,6 +22,7 @@ module.exports.formattedMemberResponse = function formattedMemberResponse(member
|
|
|
22
22
|
firstname: member.name && member.name.split(' ')[0],
|
|
23
23
|
expertise: member.expertise,
|
|
24
24
|
avatar_image: member.avatar_image,
|
|
25
|
+
unsubscribe_url: member.unsubscribe_url,
|
|
25
26
|
subscribed: !!member.subscribed,
|
|
26
27
|
subscriptions: member.subscriptions || [],
|
|
27
28
|
paid: member.status !== 'free',
|
|
@@ -2,6 +2,7 @@ const tpl = require('@tryghost/tpl');
|
|
|
2
2
|
const errors = require('@tryghost/errors');
|
|
3
3
|
const {EmailAddressParser} = require('@tryghost/email-addresses');
|
|
4
4
|
const logging = require('@tryghost/logging');
|
|
5
|
+
const crypto = require('crypto');
|
|
5
6
|
|
|
6
7
|
const messages = {
|
|
7
8
|
incorrectKeyType: 'type must be one of "direct" or "connect".'
|
|
@@ -179,6 +180,30 @@ class SettingsHelpers {
|
|
|
179
180
|
return this.#managedEmailEnabled() || this.labs.isSet('newEmailAddresses');
|
|
180
181
|
}
|
|
181
182
|
|
|
183
|
+
createUnsubscribeUrl(uuid, options = {}) {
|
|
184
|
+
const siteUrl = this.urlUtils.urlFor('home', true);
|
|
185
|
+
const unsubscribeUrl = new URL(siteUrl);
|
|
186
|
+
const key = this.getMembersValidationKey();
|
|
187
|
+
unsubscribeUrl.pathname = `${unsubscribeUrl.pathname}/unsubscribe/`.replace('//', '/');
|
|
188
|
+
if (uuid) {
|
|
189
|
+
// hash key with member uuid for verification (and to not leak uuid) - it's possible to update member email prefs without logging in
|
|
190
|
+
// @ts-ignore
|
|
191
|
+
const hmac = crypto.createHmac('sha256', key).update(`${uuid}`).digest('hex');
|
|
192
|
+
unsubscribeUrl.searchParams.set('uuid', uuid);
|
|
193
|
+
unsubscribeUrl.searchParams.set('key', hmac);
|
|
194
|
+
} else {
|
|
195
|
+
unsubscribeUrl.searchParams.set('preview', '1');
|
|
196
|
+
}
|
|
197
|
+
if (options.newsletterUuid) {
|
|
198
|
+
unsubscribeUrl.searchParams.set('newsletter', options.newsletterUuid);
|
|
199
|
+
}
|
|
200
|
+
if (options.comments) {
|
|
201
|
+
unsubscribeUrl.searchParams.set('comments', '1');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return unsubscribeUrl.href;
|
|
205
|
+
}
|
|
206
|
+
|
|
182
207
|
// PRIVATE
|
|
183
208
|
|
|
184
209
|
#managedEmailEnabled() {
|
|
@@ -116,7 +116,7 @@ class Resources {
|
|
|
116
116
|
* @returns {Promise}
|
|
117
117
|
* @private
|
|
118
118
|
*/
|
|
119
|
-
_fetch(resourceConfig, options = {offset: 0, limit: 999}) {
|
|
119
|
+
async _fetch(resourceConfig, options = {offset: 0, limit: 999}) {
|
|
120
120
|
debug('_fetch', resourceConfig.type, resourceConfig.modelOptions);
|
|
121
121
|
|
|
122
122
|
let modelOptions = _.cloneDeep(resourceConfig.modelOptions);
|
|
@@ -128,19 +128,19 @@ class Resources {
|
|
|
128
128
|
modelOptions.limit = options.limit;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
debug('fetched', resourceConfig.type, objects.length);
|
|
131
|
+
const objects = await models.Base.Model.raw_knex.fetchAll(modelOptions);
|
|
132
|
+
debug('fetched', resourceConfig.type, objects.length);
|
|
134
133
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
_.each(objects, (object) => {
|
|
135
|
+
this.data[resourceConfig.type].push(new Resource(resourceConfig.type, object));
|
|
136
|
+
});
|
|
138
137
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
138
|
+
if (objects.length && isSQLite) {
|
|
139
|
+
options.offset = options.offset + options.limit;
|
|
140
|
+
return this._fetch(resourceConfig, {offset: options.offset, limit: options.limit});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return objects;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
/**
|
|
@@ -170,11 +170,10 @@ class UrlGenerator {
|
|
|
170
170
|
return false;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
const url = this._generateUrl(resource);
|
|
174
|
-
|
|
175
173
|
// CASE 1: route has no custom filter, it will own the resource for sure
|
|
176
174
|
// CASE 2: find out if my filter matches the resource
|
|
177
175
|
if ((!this.filter) || (this.nql.queryJSON(resource.data))) {
|
|
176
|
+
const url = this._generateUrl(resource);
|
|
178
177
|
this.urls.add({
|
|
179
178
|
url: url,
|
|
180
179
|
generatorId: this.uid,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* They contain minimum filters for public accessibility of resources.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
// TODO: switch exclude lists to include lists to make this more explicit
|
|
6
7
|
module.exports = [
|
|
7
8
|
{
|
|
8
9
|
type: 'posts',
|
|
@@ -32,6 +33,10 @@ module.exports = [
|
|
|
32
33
|
'twitter_description',
|
|
33
34
|
'custom_template',
|
|
34
35
|
'locale',
|
|
36
|
+
'newsletter_id',
|
|
37
|
+
'show_title_and_feature_image',
|
|
38
|
+
'email_recipient_filter',
|
|
39
|
+
'comment_id',
|
|
35
40
|
'tiers'
|
|
36
41
|
],
|
|
37
42
|
withRelated: ['tags', 'authors'],
|
|
@@ -81,6 +86,10 @@ module.exports = [
|
|
|
81
86
|
'authors',
|
|
82
87
|
'primary_tag',
|
|
83
88
|
'primary_author',
|
|
89
|
+
'newsletter_id',
|
|
90
|
+
'show_title_and_feature_image',
|
|
91
|
+
'email_recipient_filter',
|
|
92
|
+
'comment_id',
|
|
84
93
|
'tiers'
|
|
85
94
|
],
|
|
86
95
|
filter: 'status:published+type:page'
|
|
@@ -6,7 +6,6 @@ const errors = require('@tryghost/errors');
|
|
|
6
6
|
const config = require('../../../../shared/config');
|
|
7
7
|
const tpl = require('@tryghost/tpl');
|
|
8
8
|
const logging = require('@tryghost/logging');
|
|
9
|
-
const {JSDOM} = require('jsdom');
|
|
10
9
|
|
|
11
10
|
const messages = {
|
|
12
11
|
db: {
|
|
@@ -160,6 +159,8 @@ const checkFileIsValid = (fileData, types, extensions) => {
|
|
|
160
159
|
*
|
|
161
160
|
*/
|
|
162
161
|
const isSvgSafe = (filepath) => {
|
|
162
|
+
const {JSDOM} = require('jsdom');
|
|
163
|
+
|
|
163
164
|
const fileContent = fs.readFileSync(filepath, 'utf8');
|
|
164
165
|
const document = new JSDOM(fileContent).window.document;
|
|
165
166
|
document.body.innerHTML = fileContent;
|