ghost 4.22.3 → 4.25.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/.c8rc.json +24 -0
- package/.eslintrc.js +39 -0
- package/Gruntfile.js +0 -1
- package/content/public/README.md +3 -0
- package/content/themes/casper/assets/built/casper.js +1 -1
- package/content/themes/casper/assets/built/casper.js.map +1 -1
- package/content/themes/casper/assets/built/global.css +1 -1
- package/content/themes/casper/assets/built/global.css.map +1 -1
- 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/global.css +6 -1
- package/content/themes/casper/assets/css/screen.css +32 -216
- package/content/themes/casper/default.hbs +2 -2
- package/content/themes/casper/package.json +3 -2
- package/content/themes/casper/post.hbs +1 -1
- package/content/themes/casper/yarn.lock +173 -123
- package/core/app.js +12 -1
- package/core/boot.js +47 -28
- package/core/bridge.js +10 -10
- package/core/built/assets/{chunk.3.324fd0cc598c73650219.js → chunk.3.8f95b516d88ff4eec64c.js} +18 -18
- package/core/built/assets/ghost-dark-d690e732e17ffc794e2e59c1467ca282.css +1 -0
- package/core/built/assets/ghost.min-043bb7480a0810109b130f13b2a4235e.css +1 -0
- package/core/built/assets/{ghost.min-7da921f6c6cac3fe10da1ba104575440.js → ghost.min-bc72f685c1c9adc9885925c1412435a5.js} +563 -605
- package/core/built/assets/icons/audio-upload.svg +8 -0
- package/core/built/assets/icons/powered-by-tenor.svg +35 -0
- package/core/built/assets/icons/tenor.svg +7 -0
- package/core/built/assets/{vendor.min-413f887176a041e6dbf88214ca9a7481.js → vendor.min-d1234c632a54502777c34e50752fa3fc.js} +4622 -3631
- package/core/frontend/apps/amp/lib/helpers/amp_content.js +2 -2
- package/core/frontend/apps/amp/lib/views/amp.hbs +112 -0
- package/core/frontend/apps/private-blogging/index.js +1 -1
- package/core/frontend/apps/private-blogging/lib/router.js +1 -1
- package/core/frontend/services/apps/index.js +1 -1
- package/core/frontend/services/apps/loader.js +3 -3
- package/core/frontend/services/card-assets/index.js +0 -12
- package/core/frontend/services/card-assets/service.js +29 -28
- package/core/frontend/services/helpers/handlebars.js +1 -1
- package/core/frontend/services/routing/CollectionRouter.js +4 -5
- package/core/frontend/services/routing/EmailRouter.js +1 -1
- package/core/frontend/services/routing/ParentRouter.js +0 -8
- package/core/frontend/services/routing/PreviewRouter.js +1 -1
- package/core/frontend/services/routing/StaticPagesRouter.js +1 -1
- package/core/frontend/services/routing/StaticRoutesRouter.js +4 -4
- package/core/frontend/services/routing/TaxonomyRouter.js +3 -3
- package/core/frontend/services/routing/{middlewares → middleware}/index.js +0 -0
- package/core/frontend/services/routing/{middlewares → middleware}/page-param.js +0 -0
- package/core/frontend/services/routing/router-manager.js +7 -2
- package/core/frontend/services/rss/generate-feed.js +2 -1
- package/core/frontend/services/theme-engine/middleware/ensure-active-theme.js +34 -0
- package/core/frontend/services/theme-engine/middleware/index.js +6 -0
- package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +116 -0
- package/core/frontend/services/theme-engine/middleware/update-local-template-data.js +9 -0
- package/core/frontend/services/theme-engine/middleware/update-local-template-options.js +57 -0
- package/core/frontend/src/cards/css/bookmark.css +72 -47
- package/core/frontend/src/cards/css/button.css +4 -0
- package/core/frontend/src/cards/css/callout.css +40 -3
- package/core/frontend/src/cards/css/gallery.css +15 -10
- package/core/frontend/src/cards/css/nft.css +20 -11
- package/core/frontend/src/cards/css/toggle.css +58 -0
- package/core/frontend/src/cards/js/toggle.js +16 -0
- package/core/frontend/web/middleware/error-handler.js +93 -0
- package/core/frontend/web/middleware/handle-image-sizes.js +3 -6
- package/core/frontend/web/middleware/index.js +1 -0
- package/core/frontend/web/middleware/serve-public-file.js +39 -16
- package/core/frontend/web/site.js +11 -14
- package/core/server/adapters/scheduling/SchedulingDefault.js +2 -2
- package/core/server/adapters/storage/LocalStorageBase.js +2 -2
- package/core/server/api/canary/authentication.js +1 -1
- package/core/server/api/canary/db.js +2 -2
- package/core/server/api/canary/media.js +3 -2
- package/core/server/api/canary/oembed.js +16 -1
- package/core/server/api/canary/session.js +1 -1
- package/core/server/api/canary/slugs.js +1 -1
- package/core/server/api/canary/utils/permissions.js +2 -2
- package/core/server/api/canary/utils/serializers/output/config.js +2 -6
- package/core/server/api/v2/authentication.js +1 -1
- package/core/server/api/v2/db.js +2 -2
- package/core/server/api/v2/session.js +1 -1
- package/core/server/api/v2/slugs.js +1 -1
- package/core/server/api/v2/utils/permissions.js +2 -2
- package/core/server/api/v3/authentication.js +1 -1
- package/core/server/api/v3/db.js +2 -2
- package/core/server/api/v3/session.js +1 -1
- package/core/server/api/v3/slugs.js +1 -1
- package/core/server/api/v3/utils/permissions.js +2 -2
- package/core/server/data/db/connection.js +7 -0
- package/core/server/data/db/state-manager.js +4 -4
- package/core/server/data/exporter/export-filename.js +1 -1
- package/core/server/data/importer/handlers/json.js +1 -1
- package/core/server/data/importer/import-manager.js +1 -1
- package/core/server/data/importer/importers/data/base.js +1 -1
- package/core/server/data/importer/importers/data/data-importer.js +3 -3
- package/core/server/data/migrations/init/2-create-fixtures.js +3 -20
- package/core/server/data/migrations/utils.js +2 -2
- package/core/server/data/migrations/versions/1.21/1-add-contributor-role.js +5 -5
- package/core/server/data/migrations/versions/1.25/1-update-koenig-beta-html.js +1 -0
- package/core/server/data/migrations/versions/2.15/2-insert-zapier-integration.js +3 -3
- package/core/server/data/migrations/versions/2.2/3-insert-admin-integration-role.js +5 -5
- package/core/server/data/migrations/versions/2.27/1-insert-ghost-db-backup-role.js +5 -6
- package/core/server/data/migrations/versions/2.27/2-insert-db-backup-integration.js +3 -4
- package/core/server/data/migrations/versions/2.28/3-insert-ghost-scheduler-role.js +7 -7
- package/core/server/data/migrations/versions/2.28/4-insert-scheduler-integration.js +3 -3
- package/core/server/data/migrations/versions/3.1/08-add-uuid-values-to-members.js +1 -0
- package/core/server/data/migrations/versions/3.22/02-settings-key-renames.js +2 -0
- package/core/server/data/migrations/versions/3.22/05-migrate-members-subscription-settings.js +3 -0
- package/core/server/data/migrations/versions/3.22/06-migrate-stripe-connect-settings.js +2 -0
- package/core/server/data/migrations/versions/3.23/01-migrate-bulk-email-settings.js +1 -0
- package/core/server/data/migrations/versions/3.29/01-remove-duplicate-subscriptions.js +2 -0
- package/core/server/data/migrations/versions/3.29/02-remove-duplicate-customers.js +2 -0
- package/core/server/data/migrations/versions/3.38/04-populate-recipient-filter-column.js +2 -0
- package/core/server/data/migrations/versions/4.0/01-update-mobiledoc.js +2 -0
- package/core/server/data/migrations/versions/4.0/03-populate-status-column-for-members.js +4 -0
- package/core/server/data/migrations/versions/4.0/06-populate-members-subscribe-events-table.js +1 -0
- package/core/server/data/migrations/versions/4.0/17-populate-members-status-events-table.js +1 -0
- package/core/server/data/migrations/versions/4.0/18-transform-urls-absolute-to-transform-ready.js +5 -0
- package/core/server/data/migrations/versions/4.0/22-solve-orphaned-webhooks.js +1 -0
- package/core/server/data/migrations/versions/4.0/23-regenerate-posts-html.js +1 -0
- package/core/server/data/migrations/versions/4.0/25-populate-members-paid-subscription-events-table.js +2 -1
- package/core/server/data/migrations/versions/4.12/02-fix-member-statuses.js +1 -0
- package/core/server/data/migrations/versions/4.14/01-fix-comped-member-statuses.js +3 -0
- package/core/server/data/migrations/versions/4.14/02-fix-free-members-status-events.js +1 -0
- package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +2 -0
- package/core/server/data/migrations/versions/4.23/01-truncate-offer-names.js +59 -0
- package/core/server/data/migrations/versions/4.3/04-attach-members-to-product.js +1 -0
- package/core/server/data/migrations/versions/4.4/01-restore-free-members-signup-setting-from-backup.js +1 -0
- package/core/server/data/migrations/versions/4.6/01-remove-comped-status.js +1 -0
- package/core/server/data/migrations/versions/4.8/04-migrate-show-newsletter-header-setting.js +1 -0
- package/core/server/data/migrations/versions/4.9/05-fix-missed-mobiledoc-url-transforms.js +1 -0
- package/core/server/data/migrations/versions/4.9/06-add-comped-status.js +1 -0
- package/core/server/data/migrations/versions/4.9/07-update-comped-members-status-events.js +1 -0
- package/core/server/data/schema/commands.js +2 -2
- package/core/server/data/schema/fixtures/fixture-manager.js +340 -0
- package/core/server/data/schema/fixtures/index.js +8 -2
- package/core/server/ghost-server.js +2 -2
- package/core/server/lib/image/image-size.js +2 -2
- package/core/server/models/base/listeners.js +2 -2
- package/core/server/models/member-email-change-event.js +2 -2
- package/core/server/models/member-login-event.js +2 -2
- package/core/server/models/member-paid-subscription-event.js +3 -3
- package/core/server/models/member-payment-event.js +3 -3
- package/core/server/models/member-product-event.js +6 -6
- package/core/server/models/member-status-event.js +5 -3
- package/core/server/models/member-subscribe-event.js +9 -3
- package/core/server/models/relations/authors.js +1 -1
- package/core/server/models/settings.js +1 -1
- package/core/server/services/auth/passwordreset.js +1 -1
- package/core/server/services/auth/setup.js +1 -1
- package/core/server/services/email-analytics/jobs/index.js +1 -1
- package/core/server/services/mega/mega.js +6 -4
- package/core/server/services/mega/post-email-serializer.js +5 -1
- package/core/server/services/mega/segment-parser.js +1 -2
- package/core/server/services/mega/template.js +52 -37
- package/core/server/services/members/api.js +22 -0
- package/core/server/services/members/config.js +1 -1
- package/core/server/services/members/emails/signup-paid.js +168 -0
- package/core/server/services/members/service.js +6 -2
- package/core/server/services/members/stripe-connect.js +4 -2
- package/core/server/services/nft-oembed.js +13 -22
- package/core/server/services/oembed.js +28 -24
- package/core/server/services/permissions/can-this.js +1 -1
- package/core/server/services/public-config/config.js +1 -1
- package/core/server/services/redirects/api.js +20 -25
- package/core/server/services/redirects/index.js +18 -10
- package/core/server/services/redirects/utils.js +14 -0
- package/core/server/services/redirects/validation.js +10 -0
- package/core/server/services/route-settings/default-settings-manager.js +1 -1
- package/core/server/services/route-settings/index.js +40 -17
- package/core/server/services/route-settings/route-settings.js +120 -115
- package/core/server/services/route-settings/settings-loader.js +18 -36
- package/core/server/services/route-settings/yaml-parser.js +1 -1
- package/core/server/services/slack.js +1 -1
- package/core/server/services/themes/activation-bridge.js +3 -3
- package/core/server/services/themes/storage.js +2 -2
- package/core/server/services/twitter-embed.js +80 -0
- package/core/server/services/url/LocalFileCache.js +75 -0
- package/core/server/services/url/Resources.js +8 -2
- package/core/server/services/url/UrlGenerator.js +23 -20
- package/core/server/services/url/UrlService.js +75 -63
- package/core/server/services/url/index.js +17 -3
- package/core/server/services/xmlrpc.js +2 -2
- package/core/server/web/admin/app.js +7 -10
- package/core/server/web/admin/controller.js +35 -12
- package/core/server/web/admin/middleware/redirect-admin-urls.js +15 -0
- package/core/server/web/admin/views/default-prod.html +4 -4
- package/core/server/web/admin/views/default.html +4 -4
- package/core/server/web/api/app.js +1 -1
- package/core/server/web/api/canary/admin/app.js +3 -6
- package/core/server/web/api/canary/admin/middleware.js +7 -7
- package/core/server/web/api/canary/admin/routes.js +5 -5
- package/core/server/web/api/canary/content/app.js +3 -6
- package/core/server/web/api/canary/content/middleware.js +3 -3
- package/core/server/web/api/v2/admin/app.js +3 -6
- package/core/server/web/api/v2/admin/middleware.js +7 -7
- package/core/server/web/api/v2/admin/routes.js +5 -5
- package/core/server/web/api/v2/content/app.js +3 -6
- package/core/server/web/api/v2/content/middleware.js +3 -3
- package/core/server/web/api/v3/admin/app.js +3 -6
- package/core/server/web/api/v3/admin/middleware.js +7 -7
- package/core/server/web/api/v3/admin/routes.js +5 -5
- package/core/server/web/api/v3/content/app.js +3 -6
- package/core/server/web/api/v3/content/middleware.js +3 -3
- package/core/server/web/members/app.js +6 -9
- package/core/server/web/oauth/app.js +0 -4
- package/core/server/web/parent/app.js +17 -9
- package/core/server/web/parent/frontend.js +1 -1
- package/core/server/web/shared/index.js +2 -2
- package/core/server/web/shared/{middlewares → middleware}/api/index.js +0 -0
- package/core/server/web/shared/{middlewares → middleware}/api/spam-prevention.js +0 -0
- package/core/server/web/shared/{middlewares → middleware}/brute.js +0 -0
- package/core/server/web/shared/{middlewares → middleware}/cache-control.js +0 -0
- package/core/server/web/shared/middleware/error-handler.js +224 -0
- package/core/server/web/shared/{middlewares → middleware}/index.js +0 -4
- package/core/server/web/shared/{middlewares → middleware}/pretty-urls.js +0 -0
- package/core/server/web/shared/{middlewares → middleware}/uncapitalise.js +0 -0
- package/core/server/web/shared/{middlewares → middleware}/url-redirects.js +0 -0
- package/core/shared/config/defaults.json +13 -1
- package/core/shared/config/helpers.js +42 -0
- package/core/shared/config/loader.js +1 -1
- package/core/shared/labs.js +9 -5
- package/core/shared/sentry.js +1 -1
- package/loggingrc.js +19 -20
- package/package.json +38 -37
- package/yarn.lock +1064 -892
- package/content/themes/casper/assets/js/gallery-card.js +0 -24
- package/core/built/assets/ghost-dark-39fb496d051565531062d7e047d1c0b1.css +0 -1
- package/core/built/assets/ghost.min-4207edfc1ae0a3f9f6505ca00d20b0c0.css +0 -1
- package/core/frontend/services/theme-engine/middleware.js +0 -209
- package/core/server/data/schema/fixtures/utils.js +0 -321
- package/core/server/web/parent/vhost-utils.js +0 -39
- package/core/server/web/shared/middlewares/error-handler.js +0 -329
- package/core/server/web/shared/middlewares/maintenance.js +0 -25
|
@@ -131,7 +131,7 @@ const transformEmailRecipientFilter = (emailRecipientFilter, {errorProperty = 'e
|
|
|
131
131
|
// `paid` and `free` were swapped out for NQL filters in 4.5.0, we shouldn't see them here now
|
|
132
132
|
case 'paid':
|
|
133
133
|
case 'free':
|
|
134
|
-
throw new errors.
|
|
134
|
+
throw new errors.InternalServerError({
|
|
135
135
|
message: tpl(messages.unexpectedFilterError, {
|
|
136
136
|
property: errorProperty,
|
|
137
137
|
value: emailRecipientFilter
|
|
@@ -140,7 +140,7 @@ const transformEmailRecipientFilter = (emailRecipientFilter, {errorProperty = 'e
|
|
|
140
140
|
case 'all':
|
|
141
141
|
return 'subscribed:true';
|
|
142
142
|
case 'none':
|
|
143
|
-
throw new errors.
|
|
143
|
+
throw new errors.InternalServerError({
|
|
144
144
|
message: tpl(messages.noneFilterError, {
|
|
145
145
|
property: errorProperty
|
|
146
146
|
})
|
|
@@ -352,7 +352,7 @@ async function sendEmailJob({emailModel, options}) {
|
|
|
352
352
|
error: errorMessage
|
|
353
353
|
}, {patch: true});
|
|
354
354
|
|
|
355
|
-
throw new errors.
|
|
355
|
+
throw new errors.InternalServerError({
|
|
356
356
|
err: error,
|
|
357
357
|
context: tpl(messages.sendEmailRequestFailed)
|
|
358
358
|
});
|
|
@@ -416,7 +416,9 @@ function partitionMembersBySegment(memberRows, segments) {
|
|
|
416
416
|
segmentedMemberRows = memberRows.filter(member => member.status !== 'free');
|
|
417
417
|
memberRows = memberRows.filter(member => member.status === 'free');
|
|
418
418
|
} else {
|
|
419
|
-
throw new errors.ValidationError(
|
|
419
|
+
throw new errors.ValidationError({
|
|
420
|
+
message: tpl(messages.invalidSegment)
|
|
421
|
+
});
|
|
420
422
|
}
|
|
421
423
|
|
|
422
424
|
partitions[memberSegment] = segmentedMemberRows;
|
|
@@ -3,7 +3,6 @@ const template = require('./template');
|
|
|
3
3
|
const settingsCache = require('../../../shared/settings-cache');
|
|
4
4
|
const urlUtils = require('../../../shared/url-utils');
|
|
5
5
|
const moment = require('moment-timezone');
|
|
6
|
-
const cheerio = require('cheerio');
|
|
7
6
|
const api = require('../../api');
|
|
8
7
|
const {URL} = require('url');
|
|
9
8
|
const mobiledocLib = require('../../lib/mobiledoc');
|
|
@@ -24,6 +23,8 @@ const formatHtmlForEmail = function formatHtmlForEmail(html) {
|
|
|
24
23
|
|
|
25
24
|
// convert juiced HTML to a DOM-like interface for further manipulation
|
|
26
25
|
// happens after inlining of CSS so we can change element types without worrying about styling
|
|
26
|
+
|
|
27
|
+
const cheerio = require('cheerio');
|
|
27
28
|
const _cheerio = cheerio.load(juicedHtml);
|
|
28
29
|
|
|
29
30
|
// force all links to open in new tab
|
|
@@ -243,6 +244,7 @@ const serialize = async (postModel, options = {isBrowserPreview: false, apiVersi
|
|
|
243
244
|
|
|
244
245
|
// perform any email specific adjustments to the mobiledoc->HTML render output
|
|
245
246
|
// body wrapper is required so we can get proper top-level selections
|
|
247
|
+
const cheerio = require('cheerio');
|
|
246
248
|
let _cheerio = cheerio.load(`<body>${post.html}</body>`);
|
|
247
249
|
// remove leading/trailing HRs
|
|
248
250
|
_cheerio(`
|
|
@@ -311,6 +313,8 @@ const serialize = async (postModel, options = {isBrowserPreview: false, apiVersi
|
|
|
311
313
|
};
|
|
312
314
|
|
|
313
315
|
function renderEmailForSegment(email, memberSegment) {
|
|
316
|
+
const cheerio = require('cheerio');
|
|
317
|
+
|
|
314
318
|
const result = {...email};
|
|
315
319
|
const $ = cheerio.load(result.html);
|
|
316
320
|
|
|
@@ -544,60 +544,75 @@ figure blockquote p {
|
|
|
544
544
|
border-width: 0.8em 0 0.8em 1.5em;
|
|
545
545
|
}
|
|
546
546
|
|
|
547
|
-
|
|
547
|
+
.kg-nft-link {
|
|
548
|
+
display: block;
|
|
549
|
+
text-decoration: none !important;
|
|
550
|
+
color: #15212A !important;
|
|
551
|
+
font-family: inherit !important;
|
|
552
|
+
font-size: 14px;
|
|
553
|
+
line-height: 1.3em;
|
|
554
|
+
padding-top: 4px;
|
|
555
|
+
padding-right: 20px;
|
|
556
|
+
padding-left: 20px;
|
|
557
|
+
padding-bottom: 4px;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
.kg-callout-card {
|
|
548
561
|
display: flex;
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
|
552
|
-
font-size: 15px;
|
|
553
|
-
text-decoration: none;
|
|
562
|
+
margin: 0 0 1.5em 0;
|
|
563
|
+
padding: 20px 28px;
|
|
554
564
|
border-radius: 3px;
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.kg-callout-card p {
|
|
568
|
+
margin: 0
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.kg-callout-card-grey {
|
|
572
|
+
background: #eef0f2;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.kg-callout-card-white {
|
|
559
576
|
background: #fff;
|
|
560
|
-
|
|
577
|
+
box-shadow: inset 0 0 0 1px #dddedf;
|
|
561
578
|
}
|
|
562
579
|
|
|
563
|
-
.kg-
|
|
564
|
-
|
|
580
|
+
.kg-callout-card-blue {
|
|
581
|
+
background: #E9F6FB;
|
|
565
582
|
}
|
|
566
583
|
|
|
567
|
-
.kg-
|
|
568
|
-
|
|
584
|
+
.kg-callout-card-green {
|
|
585
|
+
background: #E8F8EA;
|
|
569
586
|
}
|
|
570
587
|
|
|
571
|
-
.kg-
|
|
572
|
-
|
|
573
|
-
justify-content: space-between;
|
|
574
|
-
gap: 20px;
|
|
588
|
+
.kg-callout-card-yellow {
|
|
589
|
+
background: #FCF4E3;
|
|
575
590
|
}
|
|
576
591
|
|
|
577
|
-
.kg-
|
|
578
|
-
|
|
579
|
-
font-size: 19px;
|
|
580
|
-
font-weight: 700;
|
|
581
|
-
margin: 0;
|
|
582
|
-
color: #222;
|
|
592
|
+
.kg-callout-card-red {
|
|
593
|
+
background: #FBE9E9;
|
|
583
594
|
}
|
|
584
595
|
|
|
585
|
-
.kg-
|
|
586
|
-
|
|
587
|
-
margin: 4px 0 0;
|
|
588
|
-
color: #ababab;
|
|
596
|
+
.kg-callout-card-pink {
|
|
597
|
+
background: #FCEEF8;
|
|
589
598
|
}
|
|
590
599
|
|
|
591
|
-
.kg-
|
|
592
|
-
|
|
593
|
-
color: #222;
|
|
600
|
+
.kg-callout-card-purple {
|
|
601
|
+
background: #F2EDFC;
|
|
594
602
|
}
|
|
595
603
|
|
|
596
|
-
.kg-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
604
|
+
.kg-callout-card-accent {
|
|
605
|
+
background: ${templateSettings.accentColor || '#15212A'};
|
|
606
|
+
color: #fff;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.kg-callout-card-accent a {
|
|
610
|
+
color: #fff;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.kg-callout-emoji {
|
|
614
|
+
padding-right: 12px;
|
|
615
|
+
font-size: 20px;
|
|
601
616
|
}
|
|
602
617
|
|
|
603
618
|
/* -------------------------------------
|
|
@@ -6,6 +6,7 @@ const mail = require('../mail');
|
|
|
6
6
|
const models = require('../../models');
|
|
7
7
|
const signinEmail = require('./emails/signin');
|
|
8
8
|
const signupEmail = require('./emails/signup');
|
|
9
|
+
const signupPaidEmail = require('./emails/signup-paid');
|
|
9
10
|
const subscribeEmail = require('./emails/subscribe');
|
|
10
11
|
const updateEmail = require('./emails/updateEmail');
|
|
11
12
|
const SingleUseTokenProvider = require('./SingleUseTokenProvider');
|
|
@@ -49,6 +50,8 @@ function createApiInstance(config) {
|
|
|
49
50
|
return `📫 Confirm your subscription to ${siteTitle}`;
|
|
50
51
|
case 'signup':
|
|
51
52
|
return `🙌 Complete your sign up to ${siteTitle}!`;
|
|
53
|
+
case 'signup-paid':
|
|
54
|
+
return `🙌 Thank you for signing up to ${siteTitle}!`;
|
|
52
55
|
case 'updateEmail':
|
|
53
56
|
return `📫 Confirm your email update for ${siteTitle}!`;
|
|
54
57
|
case 'signin':
|
|
@@ -93,6 +96,23 @@ function createApiInstance(config) {
|
|
|
93
96
|
Sent to ${email}
|
|
94
97
|
If you did not make this request, you can simply delete this message. You will not be signed up, and no account will be created for you.
|
|
95
98
|
`;
|
|
99
|
+
case 'signup-paid':
|
|
100
|
+
return `
|
|
101
|
+
Thank you for subscribing to ${siteTitle}!
|
|
102
|
+
|
|
103
|
+
Tap the link below to be automatically signed in:
|
|
104
|
+
|
|
105
|
+
${url}
|
|
106
|
+
|
|
107
|
+
For your security, the link will expire in 24 hours time.
|
|
108
|
+
|
|
109
|
+
See you soon!
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
Sent to ${email}
|
|
114
|
+
Thank you for subscribing to ${siteTitle}!
|
|
115
|
+
`;
|
|
96
116
|
case 'updateEmail':
|
|
97
117
|
return `
|
|
98
118
|
Hey there,
|
|
@@ -139,6 +159,8 @@ function createApiInstance(config) {
|
|
|
139
159
|
return subscribeEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
|
|
140
160
|
case 'signup':
|
|
141
161
|
return signupEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
|
|
162
|
+
case 'signup-paid':
|
|
163
|
+
return signupPaidEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
|
|
142
164
|
case 'updateEmail':
|
|
143
165
|
return updateEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
|
|
144
166
|
case 'signin':
|
|
@@ -98,7 +98,7 @@ class MembersConfigProvider {
|
|
|
98
98
|
*/
|
|
99
99
|
getStripeKeys(type) {
|
|
100
100
|
if (type !== 'direct' && type !== 'connect') {
|
|
101
|
-
throw new errors.IncorrectUsageError(tpl(messages.incorrectKeyType));
|
|
101
|
+
throw new errors.IncorrectUsageError({message: tpl(messages.incorrectKeyType)});
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
const secretKey = this._settingsCache.get(`stripe_${type === 'connect' ? 'connect_' : ''}secret_key`);
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
module.exports = ({siteTitle, email, url, accentColor = '#15212A', siteDomain, siteUrl}) => `
|
|
2
|
+
<!doctype html>
|
|
3
|
+
<html>
|
|
4
|
+
<head>
|
|
5
|
+
<meta name="viewport" content="width=device-width">
|
|
6
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
7
|
+
<title>🙌 Thank you for signing up to ${siteTitle}!</title>
|
|
8
|
+
<style>
|
|
9
|
+
/* -------------------------------------
|
|
10
|
+
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
11
|
+
------------------------------------- */
|
|
12
|
+
@media only screen and (max-width: 620px) {
|
|
13
|
+
table[class=body] h1 {
|
|
14
|
+
font-size: 28px !important;
|
|
15
|
+
margin-bottom: 10px !important;
|
|
16
|
+
}
|
|
17
|
+
table[class=body] p,
|
|
18
|
+
table[class=body] ul,
|
|
19
|
+
table[class=body] ol,
|
|
20
|
+
table[class=body] td,
|
|
21
|
+
table[class=body] span,
|
|
22
|
+
table[class=body] a {
|
|
23
|
+
font-size: 16px !important;
|
|
24
|
+
}
|
|
25
|
+
table[class=body] .wrapper,
|
|
26
|
+
table[class=body] .article {
|
|
27
|
+
padding: 10px !important;
|
|
28
|
+
}
|
|
29
|
+
table[class=body] .content {
|
|
30
|
+
padding: 0 !important;
|
|
31
|
+
}
|
|
32
|
+
table[class=body] .container {
|
|
33
|
+
padding: 0 !important;
|
|
34
|
+
width: 100% !important;
|
|
35
|
+
}
|
|
36
|
+
table[class=body] .main {
|
|
37
|
+
border-left-width: 0 !important;
|
|
38
|
+
border-radius: 0 !important;
|
|
39
|
+
border-right-width: 0 !important;
|
|
40
|
+
}
|
|
41
|
+
table[class=body] .btn table {
|
|
42
|
+
width: 100% !important;
|
|
43
|
+
}
|
|
44
|
+
table[class=body] .btn a {
|
|
45
|
+
width: 100% !important;
|
|
46
|
+
}
|
|
47
|
+
table[class=body] .img-responsive {
|
|
48
|
+
height: auto !important;
|
|
49
|
+
max-width: 100% !important;
|
|
50
|
+
width: auto !important;
|
|
51
|
+
}
|
|
52
|
+
table[class=body] p[class=small],
|
|
53
|
+
table[class=body] a[class=small] {
|
|
54
|
+
font-size: 11px !important;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/* -------------------------------------
|
|
58
|
+
PRESERVE THESE STYLES IN THE HEAD
|
|
59
|
+
------------------------------------- */
|
|
60
|
+
@media all {
|
|
61
|
+
.ExternalClass {
|
|
62
|
+
width: 100%;
|
|
63
|
+
}
|
|
64
|
+
.ExternalClass,
|
|
65
|
+
.ExternalClass p,
|
|
66
|
+
.ExternalClass span,
|
|
67
|
+
.ExternalClass font,
|
|
68
|
+
.ExternalClass td,
|
|
69
|
+
.ExternalClass div {
|
|
70
|
+
line-height: 100%;
|
|
71
|
+
}
|
|
72
|
+
.recipient-link a {
|
|
73
|
+
color: inherit !important;
|
|
74
|
+
font-family: inherit !important;
|
|
75
|
+
font-size: inherit !important;
|
|
76
|
+
font-weight: inherit !important;
|
|
77
|
+
line-height: inherit !important;
|
|
78
|
+
text-decoration: none !important;
|
|
79
|
+
}
|
|
80
|
+
#MessageViewBody a {
|
|
81
|
+
color: inherit;
|
|
82
|
+
text-decoration: none;
|
|
83
|
+
font-size: inherit;
|
|
84
|
+
font-family: inherit;
|
|
85
|
+
font-weight: inherit;
|
|
86
|
+
line-height: inherit;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
hr {
|
|
90
|
+
border-width: 0;
|
|
91
|
+
height: 0;
|
|
92
|
+
margin-top: 34px;
|
|
93
|
+
margin-bottom: 34px;
|
|
94
|
+
border-bottom-width: 1px;
|
|
95
|
+
border-bottom-color: #EEF5F8;
|
|
96
|
+
}
|
|
97
|
+
a {
|
|
98
|
+
color: #3A464C;
|
|
99
|
+
}
|
|
100
|
+
</style>
|
|
101
|
+
</head>
|
|
102
|
+
<body class="" style="background-color: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
|
|
103
|
+
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: #FFFFFF;">
|
|
104
|
+
<tr>
|
|
105
|
+
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;"> </td>
|
|
106
|
+
<td class="container" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; display: block; margin: 0 auto; max-width: 540px; padding: 10px; width: 540px;">
|
|
107
|
+
<div class="content" style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 600px; padding: 30px 20px;">
|
|
108
|
+
|
|
109
|
+
<!-- START CENTERED WHITE CONTAINER -->
|
|
110
|
+
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Complete signup for ${siteTitle}!</span>
|
|
111
|
+
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 8px;">
|
|
112
|
+
|
|
113
|
+
<!-- START MAIN CONTENT AREA -->
|
|
114
|
+
<tr>
|
|
115
|
+
<td class="wrapper" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; box-sizing: border-box;">
|
|
116
|
+
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
|
|
117
|
+
<tr>
|
|
118
|
+
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
|
|
119
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 20px; color: #15212A; font-weight: bold; line-height: 25px; margin: 0; margin-bottom: 15px;">Thank you for subscribing to ${siteTitle}</p>
|
|
120
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; line-height: 25px; margin: 0; margin-bottom: 32px;">Tap the link below to be automatically signed in:</p>
|
|
121
|
+
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
|
|
122
|
+
<tbody>
|
|
123
|
+
<tr>
|
|
124
|
+
<td align="left" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; padding-bottom: 35px;">
|
|
125
|
+
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
|
|
126
|
+
<tbody>
|
|
127
|
+
<tr>
|
|
128
|
+
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; background-color: ${accentColor}; border-radius: 5px; text-align: center;"> <a href="${url}" target="_blank" style="display: inline-block; color: #ffffff; background-color: ${accentColor}; border: solid 1px ${accentColor}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: ${accentColor};">Confirm signup</a> </td>
|
|
129
|
+
</tr>
|
|
130
|
+
</tbody>
|
|
131
|
+
</table>
|
|
132
|
+
</td>
|
|
133
|
+
</tr>
|
|
134
|
+
</tbody>
|
|
135
|
+
</table>
|
|
136
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; line-height: 25px; margin: 0; margin-bottom: 25px;">For your security, the link will expire in 24 hours time.</p>
|
|
137
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; line-height: 25px; margin: 0; margin-bottom: 30px;">See you soon!</p>
|
|
138
|
+
<hr/>
|
|
139
|
+
<p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; color: #3A464C; font-weight: normal; line-height: 25px; margin: 0; margin-bottom: 5px;">You can also copy & paste this URL into your browser:</p>
|
|
140
|
+
<p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 25px; margin-top: 0; color: #3A464C;">${url}</p>
|
|
141
|
+
</td>
|
|
142
|
+
</tr>
|
|
143
|
+
</table>
|
|
144
|
+
</td>
|
|
145
|
+
</tr>
|
|
146
|
+
|
|
147
|
+
<!-- START FOOTER -->
|
|
148
|
+
<tr>
|
|
149
|
+
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; padding-top: 2px;">
|
|
150
|
+
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 11px; color: #738A94; font-weight: normal; margin: 0; margin-bottom: 2px;">This message was sent from <a class="small" href="${siteUrl}" style="text-decoration: underline; color: #738A94; font-size: 11px;">${siteDomain}</a> to <a class="small" href="mailto:${email}" style="text-decoration: underline; color: #738A94; font-size: 11px;">${email}</a></p>
|
|
151
|
+
</td>
|
|
152
|
+
</tr>
|
|
153
|
+
|
|
154
|
+
<!-- END FOOTER -->
|
|
155
|
+
|
|
156
|
+
<!-- END MAIN CONTENT AREA -->
|
|
157
|
+
</table>
|
|
158
|
+
|
|
159
|
+
<!-- END CENTERED WHITE CONTAINER -->
|
|
160
|
+
</div>
|
|
161
|
+
</td>
|
|
162
|
+
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;"> </td>
|
|
163
|
+
</tr>
|
|
164
|
+
</table>
|
|
165
|
+
</body>
|
|
166
|
+
</html>
|
|
167
|
+
`;
|
|
168
|
+
|
|
@@ -159,12 +159,16 @@ const membersService = {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
if (stripeService.api.configured && stripeService.api.mode === 'live') {
|
|
162
|
-
throw new errors.IncorrectUsageError(
|
|
162
|
+
throw new errors.IncorrectUsageError({
|
|
163
|
+
message: tpl(messages.noLiveKeysInDevelopment)
|
|
164
|
+
});
|
|
163
165
|
}
|
|
164
166
|
} else {
|
|
165
167
|
const siteUrl = urlUtils.getSiteUrl();
|
|
166
168
|
if (!/^https/.test(siteUrl) && stripeService.api.configured) {
|
|
167
|
-
throw new errors.IncorrectUsageError(
|
|
169
|
+
throw new errors.IncorrectUsageError({
|
|
170
|
+
message: tpl(messages.sslRequiredForStripe)
|
|
171
|
+
});
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
174
|
if (!membersApi) {
|
|
@@ -63,7 +63,7 @@ async function getStripeConnectTokenData(encodedData, getSessionProp) {
|
|
|
63
63
|
const state = await getSessionProp(STATE_PROP);
|
|
64
64
|
|
|
65
65
|
if (state !== data.s) {
|
|
66
|
-
throw new errors.NoPermissionError(tpl(messages.incorrectState));
|
|
66
|
+
throw new errors.NoPermissionError({message: tpl(messages.incorrectState)});
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
return {
|
|
@@ -81,7 +81,9 @@ function checkCanConnect() {
|
|
|
81
81
|
const siteUrlUsingSSL = /^https/.test(siteUrl);
|
|
82
82
|
const cannotConnectToStripe = productionMode && !siteUrlUsingSSL;
|
|
83
83
|
if (cannotConnectToStripe) {
|
|
84
|
-
throw new errors.BadRequestError(
|
|
84
|
+
throw new errors.BadRequestError({
|
|
85
|
+
message: 'Cannot connect to stripe unless site is using https://'
|
|
86
|
+
});
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
|
|
@@ -28,42 +28,33 @@ class NFTOEmbedProvider {
|
|
|
28
28
|
* @param {URL} url
|
|
29
29
|
* @param {IExternalRequest} externalRequest
|
|
30
30
|
*
|
|
31
|
-
* @returns {Promise<
|
|
31
|
+
* @returns {Promise<object>}
|
|
32
32
|
*/
|
|
33
33
|
async getOEmbedData(url, externalRequest) {
|
|
34
34
|
const [match, transaction, asset] = url.pathname.match(OPENSEA_PATH_REGEX);
|
|
35
35
|
if (!match) {
|
|
36
36
|
return null;
|
|
37
37
|
}
|
|
38
|
-
const
|
|
39
|
-
|
|
38
|
+
const headers = {};
|
|
39
|
+
if (this.dependencies.config.apiKey) {
|
|
40
|
+
headers['X-API-KEY'] = this.dependencies.config.apiKey;
|
|
41
|
+
}
|
|
42
|
+
const result = await externalRequest(`https://api.opensea.io/api/v1/asset/${transaction}/${asset}/?format=json`, {
|
|
43
|
+
json: true,
|
|
44
|
+
headers
|
|
40
45
|
});
|
|
41
46
|
return {
|
|
42
47
|
version: '1.0',
|
|
43
|
-
type: '
|
|
48
|
+
type: 'nft',
|
|
44
49
|
title: result.body.name,
|
|
45
50
|
author_name: result.body.creator.user.username,
|
|
46
51
|
author_url: `https://opensea.io/${result.body.creator.user.username}`,
|
|
47
52
|
provider_name: 'OpenSea',
|
|
48
53
|
provider_url: 'https://opensea.io',
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<div class="kg-nft-header">
|
|
54
|
-
<h4 class="kg-nft-title"> ${result.body.name} </h4>
|
|
55
|
-
</div>
|
|
56
|
-
<div class="kg-nft-creator">
|
|
57
|
-
Created by <span class="kg-nft-creator-name">${result.body.creator.user.username}</span>
|
|
58
|
-
${(result.body.collection.name ? `• ${result.body.collection.name}` : ``)}
|
|
59
|
-
</div>
|
|
60
|
-
${(result.body.description ? `<p class="kg-nft-description">${result.body.description}</p>` : ``)}
|
|
61
|
-
</div>
|
|
62
|
-
</a>
|
|
63
|
-
`,
|
|
64
|
-
width: 1000,
|
|
65
|
-
height: 1000,
|
|
66
|
-
noIframe: true
|
|
54
|
+
image_url: result.body.image_url,
|
|
55
|
+
creator_name: result.body.creator.user.username,
|
|
56
|
+
description: result.body.description,
|
|
57
|
+
collection_name: result.body.collection.name
|
|
67
58
|
};
|
|
68
59
|
}
|
|
69
60
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const errors = require('@tryghost/errors');
|
|
2
2
|
const tpl = require('@tryghost/tpl');
|
|
3
|
+
const logging = require('@tryghost/logging');
|
|
3
4
|
const {extract, hasProvider} = require('oembed-parser');
|
|
4
5
|
const cheerio = require('cheerio');
|
|
5
6
|
const _ = require('lodash');
|
|
@@ -62,6 +63,7 @@ class OEmbed {
|
|
|
62
63
|
*/
|
|
63
64
|
constructor({config, externalRequest}) {
|
|
64
65
|
this.config = config;
|
|
66
|
+
|
|
65
67
|
/** @type {IExternalRequest} */
|
|
66
68
|
this.externalRequest = async (url, requestConfig) => {
|
|
67
69
|
if (this.isIpOrLocalhost(url)) {
|
|
@@ -73,6 +75,7 @@ class OEmbed {
|
|
|
73
75
|
}
|
|
74
76
|
return response;
|
|
75
77
|
};
|
|
78
|
+
|
|
76
79
|
/** @type {ICustomProvider[]} */
|
|
77
80
|
this.customProviders = [];
|
|
78
81
|
}
|
|
@@ -107,24 +110,6 @@ class OEmbed {
|
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
112
|
|
|
110
|
-
/**
|
|
111
|
-
* @param {string} url
|
|
112
|
-
*/
|
|
113
|
-
errorHandler(url) {
|
|
114
|
-
/**
|
|
115
|
-
* @param {Error|errors.GhostError} err
|
|
116
|
-
*/
|
|
117
|
-
return async (err) => {
|
|
118
|
-
// allow specific validation errors through for better error messages
|
|
119
|
-
if (errors.utils.isIgnitionError(err) && err.errorType === 'ValidationError') {
|
|
120
|
-
throw err;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// default to unknown provider to avoid leaking any app specifics
|
|
124
|
-
return this.unknownProvider(url);
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
113
|
async fetchBookmarkData(url) {
|
|
129
114
|
const metascraper = require('metascraper')([
|
|
130
115
|
require('metascraper-url')(),
|
|
@@ -296,10 +281,13 @@ class OEmbed {
|
|
|
296
281
|
* @returns {Promise<Object>}
|
|
297
282
|
*/
|
|
298
283
|
async fetchOembedDataFromUrl(url, type) {
|
|
299
|
-
let data;
|
|
300
|
-
|
|
301
284
|
try {
|
|
302
285
|
const urlObject = new URL(url);
|
|
286
|
+
|
|
287
|
+
// Trimming solves the difference of url validation between `new URL(url)`
|
|
288
|
+
// and metascraper.
|
|
289
|
+
url = url.trim();
|
|
290
|
+
|
|
303
291
|
for (const provider of this.customProviders) {
|
|
304
292
|
if (await provider.canSupportRequest(urlObject)) {
|
|
305
293
|
const result = await provider.getOEmbedData(urlObject, this.externalRequest);
|
|
@@ -309,23 +297,39 @@ class OEmbed {
|
|
|
309
297
|
}
|
|
310
298
|
}
|
|
311
299
|
|
|
300
|
+
// fetch only bookmark when explicitly requested
|
|
312
301
|
if (type === 'bookmark') {
|
|
313
302
|
return this.fetchBookmarkData(url);
|
|
314
303
|
}
|
|
315
304
|
|
|
316
|
-
|
|
305
|
+
// attempt to fetch oembed
|
|
306
|
+
let data = await this.fetchOembedData(url);
|
|
317
307
|
|
|
308
|
+
// fallback to bookmark when we can't get oembed
|
|
318
309
|
if (!data && !type) {
|
|
319
310
|
data = await this.fetchBookmarkData(url);
|
|
320
311
|
}
|
|
321
312
|
|
|
313
|
+
// couldn't get anything, throw a validation error
|
|
322
314
|
if (!data) {
|
|
323
|
-
|
|
315
|
+
return this.unknownProvider(url);
|
|
324
316
|
}
|
|
325
317
|
|
|
326
318
|
return data;
|
|
327
|
-
} catch (
|
|
328
|
-
|
|
319
|
+
} catch (err) {
|
|
320
|
+
// allow specific validation errors through for better error messages
|
|
321
|
+
if (errors.utils.isGhostError(err) && err.errorType === 'ValidationError') {
|
|
322
|
+
throw err;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// log the real error because we're going to throw a generic "Unknown provider" error
|
|
326
|
+
logging.error(new errors.InternalServerError({
|
|
327
|
+
message: 'Encountered error when fetching oembed',
|
|
328
|
+
err
|
|
329
|
+
}));
|
|
330
|
+
|
|
331
|
+
// default to unknown provider to avoid leaking any app specifics
|
|
332
|
+
return this.unknownProvider(url);
|
|
329
333
|
}
|
|
330
334
|
}
|
|
331
335
|
}
|
|
@@ -128,7 +128,7 @@ CanThisResult.prototype.beginCheck = function (context) {
|
|
|
128
128
|
context = parseContext(context);
|
|
129
129
|
|
|
130
130
|
if (actionsMap.empty()) {
|
|
131
|
-
throw new errors.
|
|
131
|
+
throw new errors.InternalServerError({message: tpl(messages.noActionsMapFoundError)});
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
// Kick off loading of user permissions if necessary
|
|
@@ -17,7 +17,7 @@ module.exports = function getConfigProperties() {
|
|
|
17
17
|
mailgunIsConfigured: config.get('bulkEmail') && config.get('bulkEmail').mailgun,
|
|
18
18
|
emailAnalytics: config.get('emailAnalytics'),
|
|
19
19
|
hostSettings: config.get('hostSettings'),
|
|
20
|
-
|
|
20
|
+
tenor: config.get('tenor')
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
const billingUrl = config.get('hostSettings:billing:enabled') ? config.get('hostSettings:billing:url') : '';
|