ghost 5.10.1 → 5.12.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-manager-5.10.1.tgz → tryghost-adapter-manager-5.12.1.tgz} +0 -0
- package/components/tryghost-api-framework-5.12.1.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.12.1.tgz +0 -0
- package/components/{tryghost-bootstrap-socket-5.10.1.tgz → tryghost-bootstrap-socket-5.12.1.tgz} +0 -0
- package/components/tryghost-constants-5.12.1.tgz +0 -0
- package/components/{tryghost-custom-theme-settings-service-5.10.1.tgz → tryghost-custom-theme-settings-service-5.12.1.tgz} +0 -0
- package/components/tryghost-domain-events-5.12.1.tgz +0 -0
- package/components/{tryghost-email-analytics-provider-mailgun-5.10.1.tgz → tryghost-email-analytics-provider-mailgun-5.12.1.tgz} +0 -0
- package/components/{tryghost-email-analytics-service-5.10.1.tgz → tryghost-email-analytics-service-5.12.1.tgz} +0 -0
- package/components/{tryghost-email-content-generator-5.10.1.tgz → tryghost-email-content-generator-5.12.1.tgz} +0 -0
- package/components/tryghost-express-dynamic-redirects-5.12.1.tgz +0 -0
- package/components/tryghost-extract-api-key-5.12.1.tgz +0 -0
- package/components/{tryghost-html-to-plaintext-5.10.1.tgz → tryghost-html-to-plaintext-5.12.1.tgz} +0 -0
- package/components/{tryghost-job-manager-5.10.1.tgz → tryghost-job-manager-5.12.1.tgz} +0 -0
- package/components/tryghost-magic-link-5.12.1.tgz +0 -0
- package/components/tryghost-mailgun-client-5.12.1.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.12.1.tgz +0 -0
- package/components/tryghost-member-attribution-5.12.1.tgz +0 -0
- package/components/tryghost-member-events-5.12.1.tgz +0 -0
- package/components/{tryghost-members-analytics-ingress-5.10.1.tgz → tryghost-members-analytics-ingress-5.12.1.tgz} +0 -0
- package/components/tryghost-members-api-5.12.1.tgz +0 -0
- package/components/{tryghost-members-csv-5.10.1.tgz → tryghost-members-csv-5.12.1.tgz} +0 -0
- package/components/{tryghost-members-events-service-5.10.1.tgz → tryghost-members-events-service-5.12.1.tgz} +0 -0
- package/components/tryghost-members-importer-5.12.1.tgz +0 -0
- package/components/tryghost-members-offers-5.12.1.tgz +0 -0
- package/components/{tryghost-members-payments-5.10.1.tgz → tryghost-members-payments-5.12.1.tgz} +0 -0
- package/components/tryghost-members-ssr-5.12.1.tgz +0 -0
- package/components/{tryghost-members-stripe-service-5.10.1.tgz → tryghost-members-stripe-service-5.12.1.tgz} +0 -0
- package/components/tryghost-minifier-5.12.1.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.12.1.tgz +0 -0
- package/components/{tryghost-mw-cache-control-5.10.1.tgz → tryghost-mw-cache-control-5.12.1.tgz} +0 -0
- package/components/tryghost-mw-error-handler-5.12.1.tgz +0 -0
- package/components/{tryghost-mw-session-from-token-5.10.1.tgz → tryghost-mw-session-from-token-5.12.1.tgz} +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.12.1.tgz +0 -0
- package/components/tryghost-mw-vhost-5.12.1.tgz +0 -0
- package/components/{tryghost-oembed-service-5.10.1.tgz → tryghost-oembed-service-5.12.1.tgz} +0 -0
- package/components/{tryghost-package-json-5.10.1.tgz → tryghost-package-json-5.12.1.tgz} +0 -0
- package/components/{tryghost-security-5.10.1.tgz → tryghost-security-5.12.1.tgz} +0 -0
- package/components/{tryghost-session-service-5.10.1.tgz → tryghost-session-service-5.12.1.tgz} +0 -0
- package/components/tryghost-settings-path-manager-5.12.1.tgz +0 -0
- package/components/tryghost-staff-service-5.12.1.tgz +0 -0
- package/components/{tryghost-update-check-service-5.10.1.tgz → tryghost-update-check-service-5.12.1.tgz} +0 -0
- package/components/tryghost-verification-trigger-5.12.1.tgz +0 -0
- package/components/{tryghost-version-notifications-data-service-5.10.1.tgz → tryghost-version-notifications-data-service-5.12.1.tgz} +0 -0
- package/core/boot.js +2 -0
- package/core/built/admin/assets/chunk.143.cae75c9fa9b7050ffee8.js +49 -0
- package/core/built/admin/assets/{chunk.174.0364e8abdae8210d8e6d.js → chunk.174.ae492405065373dbe102.js} +1 -1
- package/core/built/admin/assets/{chunk.178.bb46965ba0483c3e79ae.js → chunk.178.0487ee9895eefb6e2baf.js} +4 -4
- package/core/built/admin/assets/{chunk.351.ea4a4ff4b40d5f2ad141.js → chunk.579.65e09dd89eec70d059a0.js} +3 -11
- package/core/built/admin/assets/{chunk.351.ea4a4ff4b40d5f2ad141.js.LICENSE.txt → chunk.579.65e09dd89eec70d059a0.js.LICENSE.txt} +0 -0
- package/core/built/admin/assets/{ghost-ced03a7ac75c3148e0ea7d1bf51e39fc.js → ghost-0526c96b20843697927c1d06a9010197.js} +779 -610
- package/core/built/admin/assets/ghost-0bbbef127e5dc0c0651fc442c4fdba8e.css +1 -0
- package/core/built/admin/assets/ghost-dark-27e002e66fbfdfaf3efcde63d5429c38.css +1 -0
- package/core/built/admin/assets/icons/calendar-stroke.svg +1 -0
- package/core/built/admin/assets/icons/event-canceled-subscription--feature-attribution.svg +6 -0
- package/core/built/admin/assets/icons/event-comment--feature-attribution.svg +3 -0
- package/core/built/admin/assets/icons/event-email-delivery-failed--feature-attribution.svg +6 -0
- package/core/built/admin/assets/icons/event-logged-in--feature-attribution.svg +5 -0
- package/core/built/admin/assets/icons/event-made-a-payment--feature-attribution.svg +7 -0
- package/core/built/admin/assets/icons/event-opened-email--feature-attribution.svg +6 -0
- package/core/built/admin/assets/icons/event-received-email--feature-attribution.svg +5 -0
- package/core/built/admin/assets/icons/event-signed-up--feature-attribution.svg +6 -0
- package/core/built/admin/assets/icons/event-started-subscription--feature-attribution.svg +6 -0
- package/core/built/admin/assets/icons/event-subscribed-to-email--feature-attribution.svg +8 -0
- package/core/built/admin/assets/icons/event-subscriptions--feature-attribution.svg +5 -0
- package/core/built/admin/assets/icons/event-unsubscribed-from-email--feature-attribution.svg +5 -0
- package/core/built/admin/assets/icons/pen-stroke.svg +1 -0
- package/core/built/admin/assets/{vendor-a1ae7a38d5c38fcba5609eed4e37f02a.js → vendor-52613f40d62355e9ac64cbfa211169bb.js} +88 -60
- package/core/built/admin/index.html +6 -6
- package/core/frontend/helpers/search.js +5 -20
- package/core/frontend/meta/get-meta.js +1 -2
- package/core/frontend/meta/image-dimensions.js +47 -39
- package/core/server/api/endpoints/comments-members.js +10 -7
- package/core/server/api/endpoints/invites.js +1 -9
- package/core/server/api/endpoints/labels.js +1 -7
- package/core/server/api/endpoints/members.js +3 -13
- package/core/server/api/endpoints/offers.js +2 -2
- package/core/server/api/endpoints/pages.js +2 -10
- package/core/server/api/endpoints/posts.js +1 -9
- package/core/server/api/endpoints/settings.js +0 -94
- package/core/server/api/endpoints/snippets.js +1 -9
- package/core/server/api/endpoints/tags.js +1 -7
- package/core/server/api/endpoints/utils/serializers/input/pages.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/members.js +2 -1
- package/core/server/api/endpoints/utils/serializers/output/site.js +1 -0
- package/core/server/api/endpoints/utils/serializers/output/utils/clean.js +6 -7
- package/core/server/api/endpoints/utils/validators/input/settings.js +1 -20
- package/core/server/api/endpoints/webhooks.js +2 -19
- package/core/server/data/migrations/versions/5.11/2022-08-22-11-03-add-member-alert-settings-columns-to-users.js +21 -0
- package/core/server/data/migrations/versions/5.11/2022-08-23-13-41-backfill-members-created-events.js +32 -0
- package/core/server/data/migrations/versions/5.11/2022-08-23-13-59-fix-page-resource-type.js +22 -0
- package/core/server/data/schema/fixtures/fixtures.json +3 -0
- package/core/server/data/schema/schema.js +23 -4
- package/core/server/lib/image/gravatar.js +8 -7
- package/core/server/lib/image/image-size.js +60 -56
- package/core/server/models/action.js +6 -19
- package/core/server/models/base/plugins/actions.js +26 -3
- package/core/server/models/member-created-event.js +10 -2
- package/core/server/models/member-paid-subscription-event.js +4 -0
- package/core/server/models/member.js +18 -0
- package/core/server/models/offer.js +3 -0
- package/core/server/models/post.js +2 -3
- package/core/server/models/product.js +3 -0
- package/core/server/models/settings.js +4 -0
- package/core/server/models/subscription-created-event.js +10 -2
- package/core/server/models/user.js +41 -7
- package/core/server/services/auth/api-key/admin.js +0 -3
- package/core/server/services/auth/passwordreset.js +0 -3
- package/core/server/services/explore/service.js +7 -6
- package/core/server/services/mega/mega.js +7 -4
- package/core/server/services/mega/template.js +44 -16
- package/core/server/services/member-attribution/index.js +34 -6
- package/core/server/services/members/api.js +4 -0
- package/core/server/services/members/middleware.js +6 -2
- package/core/server/services/members/service.js +6 -3
- package/core/server/services/public-config/site.js +1 -0
- package/core/server/services/route-settings/default-settings-manager.js +19 -17
- package/core/server/services/staff/index.js +26 -0
- package/core/server/services/webhooks/trigger.js +14 -5
- package/core/server/web/api/endpoints/admin/middleware.js +1 -3
- package/core/server/web/api/endpoints/admin/routes.js +0 -7
- package/core/shared/config/defaults.json +3 -2
- package/core/shared/labs.js +8 -7
- package/package.json +86 -84
- package/yarn.lock +134 -181
- package/components/tryghost-api-framework-5.10.1.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.10.1.tgz +0 -0
- package/components/tryghost-constants-5.10.1.tgz +0 -0
- package/components/tryghost-domain-events-5.10.1.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.10.1.tgz +0 -0
- package/components/tryghost-extract-api-key-5.10.1.tgz +0 -0
- package/components/tryghost-magic-link-5.10.1.tgz +0 -0
- package/components/tryghost-mailgun-client-5.10.1.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.10.1.tgz +0 -0
- package/components/tryghost-member-attribution-5.10.1.tgz +0 -0
- package/components/tryghost-member-events-5.10.1.tgz +0 -0
- package/components/tryghost-members-api-5.10.1.tgz +0 -0
- package/components/tryghost-members-importer-5.10.1.tgz +0 -0
- package/components/tryghost-members-offers-5.10.1.tgz +0 -0
- package/components/tryghost-members-ssr-5.10.1.tgz +0 -0
- package/components/tryghost-minifier-5.10.1.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.10.1.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.10.1.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.10.1.tgz +0 -0
- package/components/tryghost-mw-vhost-5.10.1.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.10.1.tgz +0 -0
- package/components/tryghost-verification-trigger-5.10.1.tgz +0 -0
- package/core/built/admin/assets/chunk.143.71da0e884297554a8ec4.js +0 -41
- package/core/built/admin/assets/ghost-13baab17b3f54b21f341fb8f36f83110.css +0 -1
- package/core/built/admin/assets/ghost-dark-b0500577a42e2770994e6aef0e70f182.css +0 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const ObjectID = require('bson-objectid').default;
|
|
2
|
+
const logging = require('@tryghost/logging');
|
|
3
|
+
|
|
4
|
+
const {createTransactionalMigration} = require('../../utils');
|
|
5
|
+
|
|
6
|
+
module.exports = createTransactionalMigration(
|
|
7
|
+
async function up(knex) {
|
|
8
|
+
const members = await knex('members')
|
|
9
|
+
.select('id', 'created_at');
|
|
10
|
+
|
|
11
|
+
if (members.length === 0) {
|
|
12
|
+
logging.warn(`Skipping migration because no members found`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const toInsert = members.map((member) => {
|
|
17
|
+
return {
|
|
18
|
+
id: ObjectID().toHexString(),
|
|
19
|
+
member_id: member.id,
|
|
20
|
+
created_at: member.created_at,
|
|
21
|
+
source: 'member'
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
logging.info(`Inserting ${toInsert.length} members created events`);
|
|
26
|
+
await knex.batchInsert('members_created_events', toInsert);
|
|
27
|
+
},
|
|
28
|
+
async function down(knex) {
|
|
29
|
+
logging.info(`Clearing all members created events`);
|
|
30
|
+
await knex('members_created_events').del();
|
|
31
|
+
}
|
|
32
|
+
);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
const {createTransactionalMigration} = require('../../utils');
|
|
3
|
+
|
|
4
|
+
module.exports = createTransactionalMigration(
|
|
5
|
+
async function up(knex) {
|
|
6
|
+
logging.info(`Changing Action event 'page' resource_type to 'post'`);
|
|
7
|
+
|
|
8
|
+
const affectedRows = await knex('actions')
|
|
9
|
+
.update({
|
|
10
|
+
resource_type: 'post',
|
|
11
|
+
context: JSON.stringify({
|
|
12
|
+
type: 'page'
|
|
13
|
+
})
|
|
14
|
+
})
|
|
15
|
+
.where('resource_type', 'page');
|
|
16
|
+
|
|
17
|
+
logging.info(`Updated ${affectedRows} Action events from 'page' to 'post'`);
|
|
18
|
+
},
|
|
19
|
+
async function down() {
|
|
20
|
+
// no-op: we don't want to put `pages` back as a resource type
|
|
21
|
+
}
|
|
22
|
+
);
|
|
@@ -626,6 +626,9 @@
|
|
|
626
626
|
"name": "Ghost",
|
|
627
627
|
"email": "ghost@example.com",
|
|
628
628
|
"status": "inactive",
|
|
629
|
+
"free_member_signup_notification": true,
|
|
630
|
+
"paid_subscription_started_notification": true,
|
|
631
|
+
"paid_subscription_canceled_notification": false,
|
|
629
632
|
"roles": []
|
|
630
633
|
}
|
|
631
634
|
]
|
|
@@ -149,6 +149,9 @@ module.exports = {
|
|
|
149
149
|
tour: {type: 'text', maxlength: 65535, nullable: true},
|
|
150
150
|
last_seen: {type: 'dateTime', nullable: true},
|
|
151
151
|
comment_notifications: {type: 'boolean', nullable: false, defaultTo: true},
|
|
152
|
+
free_member_signup_notification: {type: 'boolean', nullable: false, defaultTo: true},
|
|
153
|
+
paid_subscription_started_notification: {type: 'boolean', nullable: false, defaultTo: true},
|
|
154
|
+
paid_subscription_canceled_notification: {type: 'boolean', nullable: false, defaultTo: false},
|
|
152
155
|
created_at: {type: 'dateTime', nullable: false},
|
|
153
156
|
created_by: {type: 'string', maxlength: 24, nullable: false},
|
|
154
157
|
updated_at: {type: 'dateTime', nullable: true},
|
|
@@ -480,9 +483,17 @@ module.exports = {
|
|
|
480
483
|
created_at: {type: 'dateTime', nullable: false},
|
|
481
484
|
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
482
485
|
attribution_id: {type: 'string', maxlength: 24, nullable: true},
|
|
483
|
-
attribution_type: {
|
|
486
|
+
attribution_type: {
|
|
487
|
+
type: 'string', maxlength: 50, nullable: true, validations: {
|
|
488
|
+
isIn: [['url', 'post', 'page', 'author', 'tag']]
|
|
489
|
+
}
|
|
490
|
+
},
|
|
484
491
|
attribution_url: {type: 'string', maxlength: 2000, nullable: true},
|
|
485
|
-
source: {
|
|
492
|
+
source: {
|
|
493
|
+
type: 'string', maxlength: 50, nullable: false, validations: {
|
|
494
|
+
isIn: [['member', 'import', 'system', 'api', 'admin']]
|
|
495
|
+
}
|
|
496
|
+
}
|
|
486
497
|
},
|
|
487
498
|
members_cancel_events: {
|
|
488
499
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
@@ -610,7 +621,11 @@ module.exports = {
|
|
|
610
621
|
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
611
622
|
subscription_id: {type: 'string', maxlength: 24, nullable: false, references: 'members_stripe_customers_subscriptions.id', cascadeDelete: true},
|
|
612
623
|
attribution_id: {type: 'string', maxlength: 24, nullable: true},
|
|
613
|
-
attribution_type: {
|
|
624
|
+
attribution_type: {
|
|
625
|
+
type: 'string', maxlength: 50, nullable: true, validations: {
|
|
626
|
+
isIn: [['url', 'post', 'page', 'author', 'tag']]
|
|
627
|
+
}
|
|
628
|
+
},
|
|
614
629
|
attribution_url: {type: 'string', maxlength: 2000, nullable: true}
|
|
615
630
|
},
|
|
616
631
|
offer_redemptions: {
|
|
@@ -625,7 +640,11 @@ module.exports = {
|
|
|
625
640
|
member_id: {type: 'string', maxlength: 24, nullable: false, unique: false, references: 'members.id', cascadeDelete: true},
|
|
626
641
|
subscribed: {type: 'bool', nullable: false, defaultTo: true},
|
|
627
642
|
created_at: {type: 'dateTime', nullable: false},
|
|
628
|
-
source: {
|
|
643
|
+
source: {
|
|
644
|
+
type: 'string', maxlength: 50, nullable: true, validations: {
|
|
645
|
+
isIn: [['member', 'import', 'system', 'api', 'admin']]
|
|
646
|
+
}
|
|
647
|
+
},
|
|
629
648
|
newsletter_id: {type: 'string', maxlength: 24, nullable: true, references: 'newsletters.id', cascadeDelete: false}
|
|
630
649
|
},
|
|
631
650
|
stripe_products: {
|
|
@@ -40,15 +40,16 @@ class Gravatar {
|
|
|
40
40
|
image: imageUrl
|
|
41
41
|
};
|
|
42
42
|
})
|
|
43
|
-
.catch(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
.catch(function (err) {
|
|
44
|
+
if (err.statusCode === 404) {
|
|
45
|
+
return {
|
|
46
|
+
image: undefined
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
49
50
|
// ignore error, just resolve with no image url
|
|
50
51
|
});
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
module.exports = Gravatar;
|
|
55
|
+
module.exports = Gravatar;
|
|
@@ -163,39 +163,41 @@ class ImageSize {
|
|
|
163
163
|
width: dimensions.width,
|
|
164
164
|
height: dimensions.height
|
|
165
165
|
};
|
|
166
|
-
}).catch(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
166
|
+
}).catch((err) => {
|
|
167
|
+
if (err.code === 'URL_MISSING_INVALID') {
|
|
168
|
+
return Promise.reject(new errors.InternalServerError({
|
|
169
|
+
message: err.message,
|
|
170
|
+
code: 'IMAGE_SIZE_URL',
|
|
171
|
+
statusCode: err.statusCode,
|
|
172
|
+
context: err.url || imagePath
|
|
173
|
+
}));
|
|
174
|
+
} else if (err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT' || err.code === 'ECONNRESET' || err.statusCode === 408) {
|
|
175
|
+
return Promise.reject(new errors.InternalServerError({
|
|
176
|
+
message: 'Request timed out.',
|
|
177
|
+
code: 'IMAGE_SIZE_URL',
|
|
178
|
+
statusCode: err.statusCode,
|
|
179
|
+
context: err.url || imagePath
|
|
180
|
+
}));
|
|
181
|
+
} else if (err.code === 'ENOENT' || err.code === 'ENOTFOUND' || err.statusCode === 404) {
|
|
182
|
+
return Promise.reject(new errors.NotFoundError({
|
|
183
|
+
message: 'Image not found.',
|
|
184
|
+
code: 'IMAGE_SIZE_URL',
|
|
185
|
+
statusCode: err.statusCode,
|
|
186
|
+
context: err.url || imagePath
|
|
187
|
+
}));
|
|
188
|
+
} else {
|
|
189
|
+
if (errors.utils.isGhostError(err)) {
|
|
190
|
+
return Promise.reject(err);
|
|
191
|
+
}
|
|
191
192
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
193
|
+
return Promise.reject(new errors.InternalServerError({
|
|
194
|
+
message: 'Unknown Request error.',
|
|
195
|
+
code: 'IMAGE_SIZE_URL',
|
|
196
|
+
statusCode: err.statusCode,
|
|
197
|
+
context: err.url || imagePath,
|
|
198
|
+
err: err
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
199
201
|
});
|
|
200
202
|
}
|
|
201
203
|
|
|
@@ -237,32 +239,34 @@ class ImageSize {
|
|
|
237
239
|
height: dimensions.height
|
|
238
240
|
};
|
|
239
241
|
})
|
|
240
|
-
.catch(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
242
|
+
.catch((err) => {
|
|
243
|
+
if (err.code === 'ENOENT') {
|
|
244
|
+
return Promise.reject(new errors.NotFoundError({
|
|
245
|
+
message: err.message,
|
|
246
|
+
code: 'IMAGE_SIZE_STORAGE',
|
|
247
|
+
err: err,
|
|
248
|
+
context: filePath,
|
|
249
|
+
errorDetails: {
|
|
250
|
+
originalPath: imagePath,
|
|
251
|
+
reqFilePath: filePath
|
|
252
|
+
}
|
|
253
|
+
}));
|
|
254
|
+
} else {
|
|
255
|
+
if (errors.utils.isGhostError(err)) {
|
|
256
|
+
return Promise.reject(err);
|
|
249
257
|
}
|
|
250
|
-
}));
|
|
251
|
-
}).catch((err) => {
|
|
252
|
-
if (errors.utils.isGhostError(err)) {
|
|
253
|
-
return Promise.reject(err);
|
|
254
|
-
}
|
|
255
258
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
259
|
+
return Promise.reject(new errors.InternalServerError({
|
|
260
|
+
message: err.message,
|
|
261
|
+
code: 'IMAGE_SIZE_STORAGE',
|
|
262
|
+
err: err,
|
|
263
|
+
context: filePath,
|
|
264
|
+
errorDetails: {
|
|
265
|
+
originalPath: imagePath,
|
|
266
|
+
reqFilePath: filePath
|
|
267
|
+
}
|
|
268
|
+
}));
|
|
269
|
+
}
|
|
266
270
|
});
|
|
267
271
|
}
|
|
268
272
|
|
|
@@ -1,34 +1,21 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
1
|
const ghostBookshelf = require('./base');
|
|
3
2
|
|
|
4
|
-
const candidates = [];
|
|
5
|
-
|
|
6
3
|
const Action = ghostBookshelf.Model.extend({
|
|
7
4
|
tableName: 'actions',
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
candidates() {
|
|
7
|
+
return Object.keys(ghostBookshelf.registry.models).map((key) => {
|
|
8
|
+
const model = ghostBookshelf.registry.models[key];
|
|
9
|
+
return [model, model.prototype.tableName.replace(/s$/, '')];
|
|
12
10
|
});
|
|
13
|
-
this.constructor.__super__.initialize.apply(this, arguments);
|
|
14
11
|
},
|
|
15
12
|
|
|
16
13
|
actor() {
|
|
17
|
-
return this.morphTo('actor', ['actor_type', 'actor_id'], ...candidates);
|
|
14
|
+
return this.morphTo('actor', ['actor_type', 'actor_id'], ...this.candidates());
|
|
18
15
|
},
|
|
19
16
|
|
|
20
17
|
resource() {
|
|
21
|
-
return this.morphTo('resource', ['resource_type', 'resource_id'], ...candidates);
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
toJSON(unfilteredOptions) {
|
|
25
|
-
const options = Action.filterOptions(unfilteredOptions, 'toJSON');
|
|
26
|
-
const attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options);
|
|
27
|
-
|
|
28
|
-
// @TODO: context is not implemented yet
|
|
29
|
-
delete attrs.context;
|
|
30
|
-
|
|
31
|
-
return attrs;
|
|
18
|
+
return this.morphTo('resource', ['resource_type', 'resource_id'], ...this.candidates());
|
|
32
19
|
}
|
|
33
20
|
}, {
|
|
34
21
|
orderDefaultOptions: function orderDefaultOptions() {
|
|
@@ -29,14 +29,37 @@ module.exports = function (Bookshelf) {
|
|
|
29
29
|
resourceType = resourceType.bind(this)();
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
if (!resourceType) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let context = {};
|
|
37
|
+
|
|
38
|
+
if (this.actionsExtraContext && Array.isArray(this.actionsExtraContext)) {
|
|
39
|
+
for (const c of this.actionsExtraContext) {
|
|
40
|
+
context[c] = this.get(c) || this.previous(c);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (event === 'deleted') {
|
|
45
|
+
context.primary_name = (this.previous('title') || this.previous('name'));
|
|
46
|
+
} else if (['added', 'edited'].includes(event)) {
|
|
47
|
+
context.primary_name = (this.get('title') || this.get('name') || this.previous('title') || this.previous('name'));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const data = {
|
|
51
|
+
event,
|
|
35
52
|
resource_id: this.id || this.previous('id'),
|
|
36
53
|
resource_type: resourceType,
|
|
37
54
|
actor_id: actor.id,
|
|
38
55
|
actor_type: actor.type
|
|
39
56
|
};
|
|
57
|
+
|
|
58
|
+
if (context && Object.keys(context).length) {
|
|
59
|
+
data.context = context;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return data;
|
|
40
63
|
},
|
|
41
64
|
|
|
42
65
|
/**
|
|
@@ -8,8 +8,16 @@ const MemberCreatedEvent = ghostBookshelf.Model.extend({
|
|
|
8
8
|
return this.belongsTo('Member', 'member_id', 'id');
|
|
9
9
|
},
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
return this.belongsTo('Post', 'attribution_id', 'id');
|
|
11
|
+
postAttribution() {
|
|
12
|
+
return this.belongsTo('Post', 'attribution_id', 'id');
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
userAttribution() {
|
|
16
|
+
return this.belongsTo('User', 'attribution_id', 'id');
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
tagAttribution() {
|
|
20
|
+
return this.belongsTo('Tag', 'attribution_id', 'id');
|
|
13
21
|
}
|
|
14
22
|
}, {
|
|
15
23
|
async edit() {
|
|
@@ -8,6 +8,10 @@ const MemberPaidSubscriptionEvent = ghostBookshelf.Model.extend({
|
|
|
8
8
|
return this.belongsTo('Member', 'member_id', 'id');
|
|
9
9
|
},
|
|
10
10
|
|
|
11
|
+
subscriptionCreatedEvent() {
|
|
12
|
+
return this.belongsTo('SubscriptionCreatedEvent', 'subscription_id', 'subscription_id');
|
|
13
|
+
},
|
|
14
|
+
|
|
11
15
|
customQuery(qb, options) {
|
|
12
16
|
if (options.aggregateMRRDeltas) {
|
|
13
17
|
if (options.limit || options.filter) {
|
|
@@ -40,6 +40,12 @@ const Member = ghostBookshelf.Model.extend({
|
|
|
40
40
|
}, {
|
|
41
41
|
key: 'newsletters',
|
|
42
42
|
replacement: 'newsletters.slug'
|
|
43
|
+
}, {
|
|
44
|
+
key: 'signup',
|
|
45
|
+
replacement: 'signups.attribution_id'
|
|
46
|
+
}, {
|
|
47
|
+
key: 'conversion',
|
|
48
|
+
replacement: 'conversions.attribution_id'
|
|
43
49
|
}];
|
|
44
50
|
},
|
|
45
51
|
|
|
@@ -74,6 +80,18 @@ const Member = ghostBookshelf.Model.extend({
|
|
|
74
80
|
joinFrom: 'member_id',
|
|
75
81
|
joinTo: 'customer_id',
|
|
76
82
|
joinToForeign: 'customer_id'
|
|
83
|
+
},
|
|
84
|
+
signups: {
|
|
85
|
+
tableName: 'members_created_events',
|
|
86
|
+
tableNameAs: 'signups',
|
|
87
|
+
type: 'oneToOne',
|
|
88
|
+
joinFrom: 'member_id'
|
|
89
|
+
},
|
|
90
|
+
conversions: {
|
|
91
|
+
tableName: 'members_subscription_created_events',
|
|
92
|
+
tableNameAs: 'conversions',
|
|
93
|
+
type: 'oneToOne',
|
|
94
|
+
joinFrom: 'member_id'
|
|
77
95
|
}
|
|
78
96
|
};
|
|
79
97
|
},
|
|
@@ -39,9 +39,8 @@ Post = ghostBookshelf.Model.extend({
|
|
|
39
39
|
tableName: 'posts',
|
|
40
40
|
|
|
41
41
|
actionsCollectCRUD: true,
|
|
42
|
-
actionsResourceType:
|
|
43
|
-
|
|
44
|
-
},
|
|
42
|
+
actionsResourceType: 'post',
|
|
43
|
+
actionsExtraContext: ['type'],
|
|
45
44
|
|
|
46
45
|
/**
|
|
47
46
|
* @NOTE
|
|
@@ -97,6 +97,10 @@ Settings = ghostBookshelf.Model.extend({
|
|
|
97
97
|
|
|
98
98
|
tableName: 'settings',
|
|
99
99
|
|
|
100
|
+
actionsCollectCRUD: true,
|
|
101
|
+
actionsResourceType: 'setting',
|
|
102
|
+
actionsExtraContext: ['key', 'group'],
|
|
103
|
+
|
|
100
104
|
emitChange: function emitChange(event, options) {
|
|
101
105
|
const eventToTrigger = 'settings' + '.' + event;
|
|
102
106
|
ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
|
|
@@ -12,8 +12,16 @@ const SubscriptionCreatedEvent = ghostBookshelf.Model.extend({
|
|
|
12
12
|
return this.belongsTo('StripeCustomerSubscription', 'subscription_id', 'id');
|
|
13
13
|
},
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
return this.belongsTo('Post', 'attribution_id', 'id');
|
|
15
|
+
postAttribution() {
|
|
16
|
+
return this.belongsTo('Post', 'attribution_id', 'id');
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
userAttribution() {
|
|
20
|
+
return this.belongsTo('User', 'attribution_id', 'id');
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
tagAttribution() {
|
|
24
|
+
return this.belongsTo('Tag', 'attribution_id', 'id');
|
|
17
25
|
}
|
|
18
26
|
}, {
|
|
19
27
|
async edit() {
|
|
@@ -65,7 +65,10 @@ User = ghostBookshelf.Model.extend({
|
|
|
65
65
|
password: security.identifier.uid(50),
|
|
66
66
|
visibility: 'public',
|
|
67
67
|
status: 'active',
|
|
68
|
-
comment_notifications: true
|
|
68
|
+
comment_notifications: true,
|
|
69
|
+
free_member_signup_notification: true,
|
|
70
|
+
paid_subscription_started_notification: true,
|
|
71
|
+
paid_subscription_canceled_notification: false
|
|
69
72
|
};
|
|
70
73
|
},
|
|
71
74
|
|
|
@@ -485,6 +488,33 @@ User = ghostBookshelf.Model.extend({
|
|
|
485
488
|
return query.fetch(options);
|
|
486
489
|
},
|
|
487
490
|
|
|
491
|
+
/**
|
|
492
|
+
* Returns users who should receive a specific type of alert
|
|
493
|
+
* @param {'free-signup'|'paid-started'|'paid-canceled'} type The type of alert to fetch users for
|
|
494
|
+
* @param {any} options
|
|
495
|
+
* @return {Promise<[Object]>} Array of users
|
|
496
|
+
*/
|
|
497
|
+
getEmailAlertUsers(type, options) {
|
|
498
|
+
options = options || {};
|
|
499
|
+
|
|
500
|
+
let filter = 'status:active';
|
|
501
|
+
if (type === 'free-signup') {
|
|
502
|
+
filter += '+free_member_signup_notification:true';
|
|
503
|
+
} else if (type === 'paid-started') {
|
|
504
|
+
filter += '+paid_subscription_started_notification:true';
|
|
505
|
+
} else if (type === 'paid-canceled') {
|
|
506
|
+
filter += '+paid_subscription_canceled_notification:true';
|
|
507
|
+
}
|
|
508
|
+
const updatedOptions = _.merge({}, options, {filter, withRelated: ['roles']});
|
|
509
|
+
return this.findAll(updatedOptions).then((users) => {
|
|
510
|
+
return users.toJSON().filter((user) => {
|
|
511
|
+
return user?.roles?.some((role) => {
|
|
512
|
+
return ['Owner', 'Administrator'].includes(role.name);
|
|
513
|
+
});
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
},
|
|
517
|
+
|
|
488
518
|
/**
|
|
489
519
|
* ### Edit
|
|
490
520
|
*
|
|
@@ -1002,10 +1032,10 @@ User = ghostBookshelf.Model.extend({
|
|
|
1002
1032
|
let ownerRole;
|
|
1003
1033
|
let contextUser;
|
|
1004
1034
|
|
|
1005
|
-
return Promise.
|
|
1035
|
+
return Promise.all([
|
|
1006
1036
|
ghostBookshelf.model('Role').findOne({name: 'Owner'}),
|
|
1007
1037
|
User.findOne({id: options.context.user}, {withRelated: ['roles']})
|
|
1008
|
-
)
|
|
1038
|
+
])
|
|
1009
1039
|
.then((results) => {
|
|
1010
1040
|
ownerRole = results[0];
|
|
1011
1041
|
contextUser = results[1];
|
|
@@ -1018,8 +1048,10 @@ User = ghostBookshelf.Model.extend({
|
|
|
1018
1048
|
}));
|
|
1019
1049
|
}
|
|
1020
1050
|
|
|
1021
|
-
return Promise.
|
|
1022
|
-
|
|
1051
|
+
return Promise.all([
|
|
1052
|
+
ghostBookshelf.model('Role').findOne({name: 'Administrator'}),
|
|
1053
|
+
User.findOne({id: object.id}, {withRelated: ['roles']})
|
|
1054
|
+
]);
|
|
1023
1055
|
})
|
|
1024
1056
|
.then((results) => {
|
|
1025
1057
|
const adminRole = results[0];
|
|
@@ -1046,9 +1078,11 @@ User = ghostBookshelf.Model.extend({
|
|
|
1046
1078
|
}
|
|
1047
1079
|
|
|
1048
1080
|
// convert owner to admin
|
|
1049
|
-
return Promise.
|
|
1081
|
+
return Promise.all([
|
|
1082
|
+
contextUser.roles().updatePivot({role_id: adminRole.id}),
|
|
1050
1083
|
user.roles().updatePivot({role_id: ownerRole.id}),
|
|
1051
|
-
user.id
|
|
1084
|
+
user.id
|
|
1085
|
+
]);
|
|
1052
1086
|
})
|
|
1053
1087
|
.then((results) => {
|
|
1054
1088
|
return Users.forge()
|
|
@@ -144,9 +144,6 @@ function doReset(options, tokenParts, settingsAPI) {
|
|
|
144
144
|
updatedUser.set('status', 'active');
|
|
145
145
|
return updatedUser.save(options);
|
|
146
146
|
})
|
|
147
|
-
.catch(errors.ValidationError, (err) => {
|
|
148
|
-
return Promise.reject(err);
|
|
149
|
-
})
|
|
150
147
|
.catch((err) => {
|
|
151
148
|
if (errors.utils.isGhostError(err)) {
|
|
152
149
|
return Promise.reject(err);
|
|
@@ -26,18 +26,19 @@ module.exports = class ExploreService {
|
|
|
26
26
|
const totalMembers = await this.MembersService.stats.getTotalMembers();
|
|
27
27
|
const mrrStats = await this.StatsService.getMRRHistory();
|
|
28
28
|
|
|
29
|
-
const {description, icon, title, url, accent_color: accentColor} = this.PublicConfigService.site;
|
|
29
|
+
const {description, icon, title, url, accent_color: accentColor, locale} = this.PublicConfigService.site;
|
|
30
30
|
|
|
31
31
|
const exploreProperties = {
|
|
32
32
|
version: ghostVersion.full,
|
|
33
|
-
totalMembers,
|
|
34
|
-
mrrStats,
|
|
33
|
+
total_members: totalMembers,
|
|
34
|
+
mrr_stats: mrrStats,
|
|
35
35
|
site: {
|
|
36
36
|
description,
|
|
37
37
|
icon,
|
|
38
38
|
title,
|
|
39
39
|
url,
|
|
40
|
-
accentColor
|
|
40
|
+
accent_color: accentColor,
|
|
41
|
+
locale
|
|
41
42
|
},
|
|
42
43
|
stripe: {
|
|
43
44
|
configured: this.StripeService.api.configured,
|
|
@@ -46,10 +47,10 @@ module.exports = class ExploreService {
|
|
|
46
47
|
};
|
|
47
48
|
|
|
48
49
|
const mostRecentlyPublishedPost = await this.PostsService.getMostRecentlyPublishedPost();
|
|
49
|
-
exploreProperties.
|
|
50
|
+
exploreProperties.most_recently_published_at = mostRecentlyPublishedPost?.get('published_at') || null;
|
|
50
51
|
|
|
51
52
|
const owner = await this.UserModel.findOne({role: 'Owner', status: 'all'});
|
|
52
|
-
exploreProperties.
|
|
53
|
+
exploreProperties.owner_email = owner?.get('email') || null;
|
|
53
54
|
|
|
54
55
|
return exploreProperties;
|
|
55
56
|
}
|