ghost 5.55.1 → 5.56.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.56.0.tgz +0 -0
- package/components/tryghost-adapter-cache-redis-5.56.0.tgz +0 -0
- package/components/{tryghost-adapter-manager-5.55.1.tgz → tryghost-adapter-manager-5.56.0.tgz} +0 -0
- package/components/tryghost-announcement-bar-settings-5.56.0.tgz +0 -0
- package/components/{tryghost-api-framework-5.55.1.tgz → tryghost-api-framework-5.56.0.tgz} +0 -0
- package/components/tryghost-api-version-compatibility-service-5.56.0.tgz +0 -0
- package/components/tryghost-audience-feedback-5.56.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.56.0.tgz +0 -0
- package/components/tryghost-collections-5.56.0.tgz +0 -0
- package/components/tryghost-constants-5.56.0.tgz +0 -0
- package/components/{tryghost-custom-theme-settings-service-5.55.1.tgz → tryghost-custom-theme-settings-service-5.56.0.tgz} +0 -0
- package/components/{tryghost-data-generator-5.55.1.tgz → tryghost-data-generator-5.56.0.tgz} +0 -0
- package/components/tryghost-domain-events-5.56.0.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.56.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.56.0.tgz +0 -0
- package/components/{tryghost-email-analytics-service-5.55.1.tgz → tryghost-email-analytics-service-5.56.0.tgz} +0 -0
- package/components/tryghost-email-content-generator-5.56.0.tgz +0 -0
- package/components/tryghost-email-events-5.56.0.tgz +0 -0
- package/components/tryghost-email-service-5.56.0.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.56.0.tgz +0 -0
- package/components/tryghost-event-aware-cache-wrapper-5.56.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.56.0.tgz +0 -0
- package/components/tryghost-external-media-inliner-5.56.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.56.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.56.0.tgz +0 -0
- package/components/tryghost-i18n-5.56.0.tgz +0 -0
- package/components/{tryghost-importer-handler-content-files-5.55.1.tgz → tryghost-importer-handler-content-files-5.56.0.tgz} +0 -0
- package/components/tryghost-importer-revue-5.56.0.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.56.0.tgz +0 -0
- package/components/{tryghost-job-manager-5.55.1.tgz → tryghost-job-manager-5.56.0.tgz} +0 -0
- package/components/tryghost-link-redirects-5.56.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.56.0.tgz +0 -0
- package/components/tryghost-link-tracking-5.56.0.tgz +0 -0
- package/components/{tryghost-magic-link-5.55.1.tgz → tryghost-magic-link-5.56.0.tgz} +0 -0
- package/components/tryghost-mail-events-5.56.0.tgz +0 -0
- package/components/{tryghost-mailgun-client-5.55.1.tgz → tryghost-mailgun-client-5.56.0.tgz} +0 -0
- package/components/{tryghost-member-attribution-5.55.1.tgz → tryghost-member-attribution-5.56.0.tgz} +0 -0
- package/components/tryghost-member-events-5.56.0.tgz +0 -0
- package/components/tryghost-members-api-5.56.0.tgz +0 -0
- package/components/tryghost-members-csv-5.56.0.tgz +0 -0
- package/components/tryghost-members-events-service-5.56.0.tgz +0 -0
- package/components/tryghost-members-importer-5.56.0.tgz +0 -0
- package/components/{tryghost-members-offers-5.55.1.tgz → tryghost-members-offers-5.56.0.tgz} +0 -0
- package/components/tryghost-members-payments-5.56.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.56.0.tgz +0 -0
- package/components/{tryghost-members-stripe-service-5.55.1.tgz → tryghost-members-stripe-service-5.56.0.tgz} +0 -0
- package/components/{tryghost-mentions-email-report-5.55.1.tgz → tryghost-mentions-email-report-5.56.0.tgz} +0 -0
- package/components/tryghost-milestones-5.56.0.tgz +0 -0
- package/components/{tryghost-minifier-5.55.1.tgz → tryghost-minifier-5.56.0.tgz} +0 -0
- package/components/tryghost-model-to-domain-event-interceptor-5.56.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.56.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.56.0.tgz +0 -0
- package/components/{tryghost-mw-error-handler-5.55.1.tgz → tryghost-mw-error-handler-5.56.0.tgz} +0 -0
- package/components/tryghost-mw-session-from-token-5.56.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.56.0.tgz +0 -0
- package/components/tryghost-mw-version-match-5.56.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.56.0.tgz +0 -0
- package/components/tryghost-nql-filter-expansions-5.56.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.56.0.tgz +0 -0
- package/components/{tryghost-package-json-5.55.1.tgz → tryghost-package-json-5.56.0.tgz} +0 -0
- package/components/tryghost-post-revisions-5.56.0.tgz +0 -0
- package/components/tryghost-posts-service-5.56.0.tgz +0 -0
- package/components/tryghost-referrers-5.56.0.tgz +0 -0
- package/components/{tryghost-security-5.55.1.tgz → tryghost-security-5.56.0.tgz} +0 -0
- package/components/{tryghost-session-service-5.55.1.tgz → tryghost-session-service-5.56.0.tgz} +0 -0
- package/components/tryghost-settings-path-manager-5.56.0.tgz +0 -0
- package/components/tryghost-slack-notifications-5.56.0.tgz +0 -0
- package/components/tryghost-staff-service-5.56.0.tgz +0 -0
- package/components/tryghost-stats-service-5.56.0.tgz +0 -0
- package/components/{tryghost-tiers-5.55.1.tgz → tryghost-tiers-5.56.0.tgz} +0 -0
- package/components/tryghost-update-check-service-5.56.0.tgz +0 -0
- package/components/tryghost-verification-trigger-5.56.0.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.56.0.tgz +0 -0
- package/components/{tryghost-webmentions-5.55.1.tgz → tryghost-webmentions-5.56.0.tgz} +0 -0
- package/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/built/screen.css.map +1 -1
- package/content/themes/casper/assets/css/screen.css +22 -8
- package/content/themes/casper/page.hbs +26 -24
- package/core/built/admin/assets/chunk.143.a1c19bf68817d7c7ddf6.js +39 -0
- package/core/built/admin/assets/{chunk.178.318362f453e3e1f6655d.js → chunk.178.1e3a659730595eff43b8.js} +4 -4
- package/core/built/admin/assets/{chunk.757.c768dcbeaf1cee916395.js → chunk.757.ada6ed049fa8078cd416.js} +16 -17
- package/core/built/admin/assets/ghost-864e00075c219a4cb7a202a90a523bc4.css +1 -0
- package/core/built/admin/assets/{ghost-135607a3c421c472feb9f59f14c625fb.js → ghost-9081d01d86c90563530801b2304e1f1a.js} +212 -200
- package/core/built/admin/assets/ghost-dark-dc299e0aa5731557a3d5ef3583177487.css +1 -0
- package/core/built/admin/assets/{vendor-0a8fc851393c473cd269f04fc4e97974.js → vendor-16709e2a05d1300febb44b2e8f759976.js} +4 -4
- package/core/built/admin/index.html +6 -6
- package/core/frontend/src/cards/css/header_v2.css +58 -81
- package/core/frontend/src/cards/css/signup.css +1 -1
- package/core/server/api/endpoints/collections-public.js +53 -0
- package/core/server/api/endpoints/collections.js +27 -3
- package/core/server/api/endpoints/index.js +4 -0
- package/core/server/api/endpoints/newsletters.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/mappers/collections.js +8 -16
- package/core/server/data/migrations/versions/5.56/2023-07-14-10-11-12-add-email-disabled-field-to-members.js +7 -0
- package/core/server/data/migrations/versions/5.56/2023-07-15-10-11-12-update-members-email-disabled-field.js +22 -0
- package/core/server/data/schema/schema.js +1 -0
- package/core/server/models/member.js +5 -1
- package/core/server/models/newsletter.js +10 -0
- package/core/server/models/post.js +2 -23
- package/core/server/services/collections/PostsRepository.js +3 -1
- package/core/server/services/collections/service.js +3 -3
- package/core/server/services/members/middleware.js +2 -3
- package/core/server/services/segment/DomainEventsAnalytics.js +1 -1
- package/core/server/services/url/UrlGenerator.js +2 -21
- package/core/server/web/api/endpoints/admin/routes.js +3 -1
- package/core/server/web/api/endpoints/content/routes.js +3 -0
- package/core/server/web/shared/middleware/api/spam-prevention.js +31 -1
- package/core/server/web/shared/middleware/brute.js +13 -0
- package/core/shared/config/defaults.json +6 -0
- package/core/shared/config/env/config.testing-browser.json +6 -0
- package/core/shared/config/env/config.testing-mysql.json +6 -1
- package/core/shared/config/env/config.testing.json +6 -0
- package/core/shared/labs.js +3 -1
- package/package.json +146 -145
- package/yarn.lock +895 -3668
- package/components/tryghost-adapter-cache-memory-ttl-5.55.1.tgz +0 -0
- package/components/tryghost-adapter-cache-redis-5.55.1.tgz +0 -0
- package/components/tryghost-announcement-bar-settings-5.55.1.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.55.1.tgz +0 -0
- package/components/tryghost-audience-feedback-5.55.1.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.55.1.tgz +0 -0
- package/components/tryghost-collections-5.55.1.tgz +0 -0
- package/components/tryghost-constants-5.55.1.tgz +0 -0
- package/components/tryghost-domain-events-5.55.1.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.55.1.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.55.1.tgz +0 -0
- package/components/tryghost-email-content-generator-5.55.1.tgz +0 -0
- package/components/tryghost-email-events-5.55.1.tgz +0 -0
- package/components/tryghost-email-service-5.55.1.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.55.1.tgz +0 -0
- package/components/tryghost-event-aware-cache-wrapper-5.55.1.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.55.1.tgz +0 -0
- package/components/tryghost-external-media-inliner-5.55.1.tgz +0 -0
- package/components/tryghost-extract-api-key-5.55.1.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.55.1.tgz +0 -0
- package/components/tryghost-i18n-5.55.1.tgz +0 -0
- package/components/tryghost-importer-revue-5.55.1.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.55.1.tgz +0 -0
- package/components/tryghost-link-redirects-5.55.1.tgz +0 -0
- package/components/tryghost-link-replacer-5.55.1.tgz +0 -0
- package/components/tryghost-link-tracking-5.55.1.tgz +0 -0
- package/components/tryghost-mail-events-5.55.1.tgz +0 -0
- package/components/tryghost-member-events-5.55.1.tgz +0 -0
- package/components/tryghost-members-api-5.55.1.tgz +0 -0
- package/components/tryghost-members-csv-5.55.1.tgz +0 -0
- package/components/tryghost-members-events-service-5.55.1.tgz +0 -0
- package/components/tryghost-members-importer-5.55.1.tgz +0 -0
- package/components/tryghost-members-payments-5.55.1.tgz +0 -0
- package/components/tryghost-members-ssr-5.55.1.tgz +0 -0
- package/components/tryghost-milestones-5.55.1.tgz +0 -0
- package/components/tryghost-model-to-domain-event-interceptor-5.55.1.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.55.1.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.55.1.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.55.1.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.55.1.tgz +0 -0
- package/components/tryghost-mw-version-match-5.55.1.tgz +0 -0
- package/components/tryghost-mw-vhost-5.55.1.tgz +0 -0
- package/components/tryghost-oembed-service-5.55.1.tgz +0 -0
- package/components/tryghost-post-revisions-5.55.1.tgz +0 -0
- package/components/tryghost-posts-service-5.55.1.tgz +0 -0
- package/components/tryghost-referrers-5.55.1.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.55.1.tgz +0 -0
- package/components/tryghost-slack-notifications-5.55.1.tgz +0 -0
- package/components/tryghost-staff-service-5.55.1.tgz +0 -0
- package/components/tryghost-stats-service-5.55.1.tgz +0 -0
- package/components/tryghost-update-check-service-5.55.1.tgz +0 -0
- package/components/tryghost-verification-trigger-5.55.1.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.55.1.tgz +0 -0
- package/core/built/admin/assets/chunk.143.e7f30f0ca62dfd07aa66.js +0 -34
- package/core/built/admin/assets/ghost-4c37b19a127806313f64fcd759eaa0e2.css +0 -1
- package/core/built/admin/assets/ghost-dark-b80a5d657c788174451fd218428af744.css +0 -1
- /package/core/built/admin/assets/{chunk.757.c768dcbeaf1cee916395.js.LICENSE.txt → chunk.757.ada6ed049fa8078cd416.js.LICENSE.txt} +0 -0
|
@@ -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('Setting email_disabled to true for all members that have their email on the suppression list');
|
|
7
|
+
|
|
8
|
+
await knex('members')
|
|
9
|
+
.join('suppressions', 'members.email', 'suppressions.email')
|
|
10
|
+
.update({
|
|
11
|
+
email_disabled: true
|
|
12
|
+
});
|
|
13
|
+
},
|
|
14
|
+
async function down(knex) {
|
|
15
|
+
logging.info('Setting email_disabled to false for all members');
|
|
16
|
+
|
|
17
|
+
await knex('members')
|
|
18
|
+
.update({
|
|
19
|
+
email_disabled: false
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
);
|
|
@@ -429,6 +429,7 @@ module.exports = {
|
|
|
429
429
|
email_count: {type: 'integer', unsigned: true, nullable: false, defaultTo: 0},
|
|
430
430
|
email_opened_count: {type: 'integer', unsigned: true, nullable: false, defaultTo: 0},
|
|
431
431
|
email_open_rate: {type: 'integer', unsigned: true, nullable: true, index: true},
|
|
432
|
+
email_disabled: {type: 'boolean', nullable: false, defaultTo: false},
|
|
432
433
|
last_seen_at: {type: 'dateTime', nullable: true},
|
|
433
434
|
last_commented_at: {type: 'dateTime', nullable: true},
|
|
434
435
|
created_at: {type: 'dateTime', nullable: false},
|
|
@@ -471,7 +471,11 @@ const Member = ghostBookshelf.Model.extend({
|
|
|
471
471
|
// we use raw queries instead of model relationships because model hydration is expensive
|
|
472
472
|
const query = ghostBookshelf.knex('members_newsletters')
|
|
473
473
|
.join('newsletters', 'members_newsletters.newsletter_id', '=', 'newsletters.id')
|
|
474
|
-
.
|
|
474
|
+
.join('members', 'members_newsletters.member_id', '=', 'members.id')
|
|
475
|
+
.where({
|
|
476
|
+
'newsletters.status': 'active',
|
|
477
|
+
'members.email_disabled': false
|
|
478
|
+
})
|
|
475
479
|
.distinct('member_id as id');
|
|
476
480
|
|
|
477
481
|
if (unfilteredOptions.transacting) {
|
|
@@ -151,6 +151,16 @@ const Newsletter = ghostBookshelf.Model.extend({
|
|
|
151
151
|
.whereRaw('members_newsletters.newsletter_id = newsletters.id')
|
|
152
152
|
.as('count__members');
|
|
153
153
|
});
|
|
154
|
+
},
|
|
155
|
+
active_members(modelOrCollection) {
|
|
156
|
+
modelOrCollection.query('columns', 'newsletters.*', (qb) => {
|
|
157
|
+
qb.count('members_newsletters.id')
|
|
158
|
+
.from('members_newsletters')
|
|
159
|
+
.join('members', 'members.id', 'members_newsletters.member_id')
|
|
160
|
+
.whereRaw('members_newsletters.newsletter_id = newsletters.id')
|
|
161
|
+
.andWhere('members.email_disabled', false)
|
|
162
|
+
.as('count__active_members');
|
|
163
|
+
});
|
|
154
164
|
}
|
|
155
165
|
};
|
|
156
166
|
},
|
|
@@ -7,6 +7,7 @@ const tpl = require('@tryghost/tpl');
|
|
|
7
7
|
const errors = require('@tryghost/errors');
|
|
8
8
|
const nql = require('@tryghost/nql');
|
|
9
9
|
const htmlToPlaintext = require('@tryghost/html-to-plaintext');
|
|
10
|
+
const {posts: postExpansions} = require('@tryghost/nql-filter-expansions');
|
|
10
11
|
const ghostBookshelf = require('./base');
|
|
11
12
|
const config = require('../../shared/config');
|
|
12
13
|
const settingsCache = require('../../shared/settings-cache');
|
|
@@ -290,28 +291,6 @@ Post = ghostBookshelf.Model.extend({
|
|
|
290
291
|
filterExpansions: function filterExpansions() {
|
|
291
292
|
const postsMetaKeys = _.without(ghostBookshelf.model('PostsMeta').prototype.orderAttributes(), 'posts_meta.id', 'posts_meta.post_id');
|
|
292
293
|
|
|
293
|
-
const expansions = [{
|
|
294
|
-
key: 'primary_tag',
|
|
295
|
-
replacement: 'tags.slug',
|
|
296
|
-
expansion: 'posts_tags.sort_order:0+tags.visibility:public'
|
|
297
|
-
}, {
|
|
298
|
-
key: 'primary_author',
|
|
299
|
-
replacement: 'authors.slug',
|
|
300
|
-
expansion: 'posts_authors.sort_order:0+authors.visibility:public'
|
|
301
|
-
}, {
|
|
302
|
-
key: 'authors',
|
|
303
|
-
replacement: 'authors.slug'
|
|
304
|
-
}, {
|
|
305
|
-
key: 'author',
|
|
306
|
-
replacement: 'authors.slug'
|
|
307
|
-
}, {
|
|
308
|
-
key: 'tag',
|
|
309
|
-
replacement: 'tags.slug'
|
|
310
|
-
}, {
|
|
311
|
-
key: 'tags',
|
|
312
|
-
replacement: 'tags.slug'
|
|
313
|
-
}];
|
|
314
|
-
|
|
315
294
|
const postMetaKeyExpansions = postsMetaKeys.map((pmk) => {
|
|
316
295
|
return {
|
|
317
296
|
key: pmk.split('.')[1],
|
|
@@ -319,7 +298,7 @@ Post = ghostBookshelf.Model.extend({
|
|
|
319
298
|
};
|
|
320
299
|
});
|
|
321
300
|
|
|
322
|
-
return
|
|
301
|
+
return postExpansions.concat(postMetaKeyExpansions);
|
|
323
302
|
},
|
|
324
303
|
|
|
325
304
|
filterRelations: function filterRelations() {
|
|
@@ -20,7 +20,9 @@ class PostsRepository {
|
|
|
20
20
|
id: postJson.id,
|
|
21
21
|
featured: postJson.featured,
|
|
22
22
|
published_at: this.moment(postJson.published_at).toISOString(true),
|
|
23
|
-
tags: postJson.tags.map(tag =>
|
|
23
|
+
tags: postJson.tags.map(tag => ({
|
|
24
|
+
slug: tag.slug
|
|
25
|
+
}))
|
|
24
26
|
};
|
|
25
27
|
});
|
|
26
28
|
}
|
|
@@ -2,8 +2,8 @@ const {
|
|
|
2
2
|
CollectionsService
|
|
3
3
|
} = require('@tryghost/collections');
|
|
4
4
|
const BookshelfCollectionsRepository = require('./BookshelfCollectionsRepository');
|
|
5
|
-
const labs = require('../../../shared/labs');
|
|
6
5
|
|
|
6
|
+
let inited = false;
|
|
7
7
|
class CollectionsServiceWrapper {
|
|
8
8
|
/** @type {CollectionsService} */
|
|
9
9
|
api;
|
|
@@ -31,10 +31,10 @@ class CollectionsServiceWrapper {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
async init() {
|
|
34
|
-
if (
|
|
34
|
+
if (inited) {
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
inited = true;
|
|
38
38
|
this.api.subscribeToEvents();
|
|
39
39
|
require('./intercept-events')();
|
|
40
40
|
}
|
|
@@ -105,11 +105,10 @@ const deleteSuppression = async function (req, res) {
|
|
|
105
105
|
try {
|
|
106
106
|
const member = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
107
107
|
const options = {
|
|
108
|
-
id: member.id
|
|
109
|
-
withRelated: ['newsletters']
|
|
108
|
+
id: member.id
|
|
110
109
|
};
|
|
111
110
|
await emailSuppressionList.removeEmail(member.email);
|
|
112
|
-
await membersService.api.members.update({
|
|
111
|
+
await membersService.api.members.update({email_disabled: false}, options);
|
|
113
112
|
|
|
114
113
|
res.writeHead(204);
|
|
115
114
|
res.end();
|
|
@@ -65,7 +65,7 @@ module.exports = class DomainEventsAnalytics {
|
|
|
65
65
|
if (event.data.milestone
|
|
66
66
|
&& event.data.milestone.value === 100
|
|
67
67
|
) {
|
|
68
|
-
const eventName = event.data.milestone.type === 'arr' ? '$100
|
|
68
|
+
const eventName = event.data.milestone.type === 'arr' ? '$100 ARR reached' : '100 Members reached';
|
|
69
69
|
|
|
70
70
|
try {
|
|
71
71
|
this.#analytics.track(Object.assign(this.#trackDefaults, {}, {event: this.#prefix + eventName}));
|
|
@@ -3,26 +3,7 @@ const nql = require('@tryghost/nql');
|
|
|
3
3
|
const debug = require('@tryghost/debug')('services:url:generator');
|
|
4
4
|
const localUtils = require('../../../shared/url-utils');
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
const EXPANSIONS = [{
|
|
8
|
-
key: 'author',
|
|
9
|
-
replacement: 'authors.slug'
|
|
10
|
-
}, {
|
|
11
|
-
key: 'tags',
|
|
12
|
-
replacement: 'tags.slug'
|
|
13
|
-
}, {
|
|
14
|
-
key: 'tag',
|
|
15
|
-
replacement: 'tags.slug'
|
|
16
|
-
}, {
|
|
17
|
-
key: 'authors',
|
|
18
|
-
replacement: 'authors.slug'
|
|
19
|
-
}, {
|
|
20
|
-
key: 'primary_tag',
|
|
21
|
-
replacement: 'primary_tag.slug'
|
|
22
|
-
}, {
|
|
23
|
-
key: 'primary_author',
|
|
24
|
-
replacement: 'primary_author.slug'
|
|
25
|
-
}];
|
|
6
|
+
const {posts: postExpansions} = require('@tryghost/nql-filter-expansions');
|
|
26
7
|
|
|
27
8
|
/**
|
|
28
9
|
* The UrlGenerator class is responsible to generate urls based on a router's conditions.
|
|
@@ -57,7 +38,7 @@ class UrlGenerator {
|
|
|
57
38
|
if (filter) {
|
|
58
39
|
this.filter = filter;
|
|
59
40
|
this.nql = nql(this.filter, {
|
|
60
|
-
expansions:
|
|
41
|
+
expansions: postExpansions,
|
|
61
42
|
transformer: nql.utils.mapKeyValues({
|
|
62
43
|
key: {
|
|
63
44
|
from: 'page',
|
|
@@ -22,6 +22,7 @@ module.exports = function apiRoutes() {
|
|
|
22
22
|
// ## Collections
|
|
23
23
|
router.get('/collections', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.browse));
|
|
24
24
|
router.get('/collections/:id', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.read));
|
|
25
|
+
router.get('/collections/slug/:slug', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.read));
|
|
25
26
|
router.post('/collections', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.add));
|
|
26
27
|
router.put('/collections/:id', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.edit));
|
|
27
28
|
router.del('/collections/:id', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.destroy));
|
|
@@ -313,7 +314,8 @@ module.exports = function apiRoutes() {
|
|
|
313
314
|
|
|
314
315
|
// ## Email Preview
|
|
315
316
|
router.get('/email_previews/posts/:id', mw.authAdminApi, http(api.email_previews.read));
|
|
316
|
-
|
|
317
|
+
// preview sending have an additional rate limiter to prevent abuse
|
|
318
|
+
router.post('/email_previews/posts/:id', shared.middleware.brute.previewEmailLimiter, mw.authAdminApi, http(api.email_previews.sendTestEmail));
|
|
317
319
|
|
|
318
320
|
// ## Emails
|
|
319
321
|
router.get('/emails', mw.authAdminApi, http(api.emails.browse));
|
|
@@ -39,5 +39,8 @@ module.exports = function apiRoutes() {
|
|
|
39
39
|
router.get('/tiers', mw.authenticatePublic, http(api.tiersPublic.browse));
|
|
40
40
|
router.get('/offers/:id', mw.authenticatePublic, http(api.offersPublic.read));
|
|
41
41
|
|
|
42
|
+
router.get('/collections/:id', mw.authenticatePublic, http(api.collectionsPublic.readById));
|
|
43
|
+
router.get('/collections/slug/:slug', mw.authenticatePublic, http(api.collectionsPublic.readBySlug));
|
|
44
|
+
|
|
42
45
|
return router;
|
|
43
46
|
};
|
|
@@ -21,7 +21,8 @@ const messages = {
|
|
|
21
21
|
context: 'Too many login attempts.'
|
|
22
22
|
},
|
|
23
23
|
tooManyAttempts: 'Too many attempts.',
|
|
24
|
-
webmentionsBlock: 'Too many mention attempts'
|
|
24
|
+
webmentionsBlock: 'Too many mention attempts',
|
|
25
|
+
emailPreviewBlock: 'Only 10 test emails can be sent per hour'
|
|
25
26
|
};
|
|
26
27
|
let spamPrivateBlock = spam.private_block || {};
|
|
27
28
|
let spamGlobalBlock = spam.global_block || {};
|
|
@@ -31,6 +32,7 @@ let spamUserLogin = spam.user_login || {};
|
|
|
31
32
|
let spamMemberLogin = spam.member_login || {};
|
|
32
33
|
let spamContentApiKey = spam.content_api_key || {};
|
|
33
34
|
let spamWebmentionsBlock = spam.webmentions_block || {};
|
|
35
|
+
let spamEmailPreviewBlock = spam.email_preview_block || {};
|
|
34
36
|
|
|
35
37
|
let store;
|
|
36
38
|
let memoryStore;
|
|
@@ -43,6 +45,7 @@ let membersAuthInstance;
|
|
|
43
45
|
let membersAuthEnumerationInstance;
|
|
44
46
|
let userResetInstance;
|
|
45
47
|
let contentApiKeyInstance;
|
|
48
|
+
let emailPreviewBlockInstance;
|
|
46
49
|
|
|
47
50
|
const spamConfigKeys = ['freeRetries', 'minWait', 'maxWait', 'lifetime'];
|
|
48
51
|
|
|
@@ -152,6 +155,32 @@ const webmentionsBlock = () => {
|
|
|
152
155
|
return webmentionsBlockInstance;
|
|
153
156
|
};
|
|
154
157
|
|
|
158
|
+
const emailPreviewBlock = () => {
|
|
159
|
+
const ExpressBrute = require('express-brute');
|
|
160
|
+
const BruteKnex = require('brute-knex');
|
|
161
|
+
const db = require('../../../../data/db');
|
|
162
|
+
|
|
163
|
+
store = store || new BruteKnex({
|
|
164
|
+
tablename: 'brute',
|
|
165
|
+
createTable: false,
|
|
166
|
+
knex: db.knex
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
emailPreviewBlockInstance = emailPreviewBlockInstance || new ExpressBrute(store,
|
|
170
|
+
extend({
|
|
171
|
+
attachResetToRequest: false,
|
|
172
|
+
failCallback(req, res, next) {
|
|
173
|
+
return next(new errors.TooManyRequestsError({
|
|
174
|
+
message: messages.emailPreviewBlock
|
|
175
|
+
}));
|
|
176
|
+
},
|
|
177
|
+
handleStoreError: handleStoreError
|
|
178
|
+
}, pick(spamEmailPreviewBlock, spamConfigKeys))
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
return emailPreviewBlockInstance;
|
|
182
|
+
};
|
|
183
|
+
|
|
155
184
|
const membersAuth = () => {
|
|
156
185
|
const ExpressBrute = require('express-brute');
|
|
157
186
|
const BruteKnex = require('brute-knex');
|
|
@@ -349,6 +378,7 @@ module.exports = {
|
|
|
349
378
|
privateBlog: privateBlog,
|
|
350
379
|
contentApiKey: contentApiKey,
|
|
351
380
|
webmentionsBlock: webmentionsBlock,
|
|
381
|
+
emailPreviewBlock: emailPreviewBlock,
|
|
352
382
|
reset: () => {
|
|
353
383
|
store = undefined;
|
|
354
384
|
memoryStore = undefined;
|
|
@@ -117,5 +117,18 @@ module.exports = {
|
|
|
117
117
|
return _next('webmention_blocked');
|
|
118
118
|
}
|
|
119
119
|
})(req, res, next);
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Blocks preview email spam
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
previewEmailLimiter(req, res, next) {
|
|
127
|
+
return spamPrevention.emailPreviewBlock().getMiddleware({
|
|
128
|
+
ignoreIP: false,
|
|
129
|
+
key(_req, _res, _next) {
|
|
130
|
+
return _next('preview_email_blocked');
|
|
131
|
+
}
|
|
132
|
+
})(req, res, next);
|
|
120
133
|
}
|
|
121
134
|
};
|