ghost 5.3.1 → 5.5.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-custom-theme-settings-service-0.0.0.tgz +0 -0
- package/components/tryghost-domain-events-0.0.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-0.0.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-0.0.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-0.0.0.tgz +0 -0
- package/components/tryghost-magic-link-0.0.0.tgz +0 -0
- package/components/tryghost-member-analytics-service-0.0.0.tgz +0 -0
- package/components/tryghost-member-events-0.0.0.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-0.0.0.tgz +0 -0
- package/components/tryghost-members-api-0.0.0.tgz +0 -0
- package/components/tryghost-members-csv-0.0.0.tgz +0 -0
- package/components/tryghost-members-events-service-0.0.0.tgz +0 -0
- package/components/tryghost-members-importer-0.0.0.tgz +0 -0
- package/components/tryghost-members-offers-0.0.0.tgz +0 -0
- package/components/tryghost-members-payments-0.0.0.tgz +0 -0
- package/components/tryghost-members-ssr-0.0.0.tgz +0 -0
- package/components/tryghost-members-stripe-service-0.0.0.tgz +0 -0
- package/components/tryghost-verification-trigger-0.0.0.tgz +0 -0
- 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/screen.css +31 -8
- package/content/themes/casper/default.hbs +8 -5
- package/content/themes/casper/gulpfile.js +1 -1
- package/content/themes/casper/package.json +9 -9
- package/content/themes/casper/yarn.lock +1154 -1249
- package/core/boot.js +5 -0
- package/core/built/assets/{chunk.3.dc389a0f93cb5fabd695.js → chunk.3.550552fbc71864fb9738.js} +20 -20
- package/core/built/assets/fonts/Inter.ttf +0 -0
- package/core/built/assets/ghost-dark-5c2a961b35311d7298136e02289d98b2.css +1 -0
- package/core/built/assets/ghost.min-a89d10b3b58c1a5ebaca68cef93a404c.css +1 -0
- package/core/built/assets/{ghost.min-f4bba3a2a5ef256b82641345505d4f0f.js → ghost.min-c75f224decd20f9538179d7564cd2ab4.js} +3025 -2883
- package/core/built/assets/icons/event-comment.svg +3 -0
- package/core/built/assets/{vendor.min-4076498ccd6c8412365f43b156084ed8.js → vendor.min-cf3af99dca0c71937669305afb3686a1.js} +6122 -3197
- package/core/frontend/helpers/comments.js +22 -10
- package/core/frontend/helpers/ghost_head.js +22 -4
- package/core/frontend/helpers/total_members.js +17 -0
- package/core/frontend/helpers/total_paid_members.js +16 -0
- package/core/frontend/utils/frontend-apps.js +33 -0
- package/core/frontend/utils/member-count.js +50 -0
- package/core/frontend/web/middleware/cors.js +2 -1
- package/core/server/api/endpoints/comments-comments.js +50 -32
- package/core/server/api/endpoints/offers-public.js +2 -2
- package/core/server/api/endpoints/offers.js +8 -8
- package/core/server/api/endpoints/settings.js +62 -30
- package/core/server/api/endpoints/utils/serializers/input/settings.js +1 -0
- package/core/server/api/endpoints/utils/serializers/output/config.js +2 -1
- package/core/server/api/endpoints/utils/serializers/output/index.js +0 -4
- package/core/server/api/endpoints/utils/serializers/output/mappers/activity-feed-events.js +17 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +18 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/index.js +2 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/offers.js +28 -0
- package/core/server/api/endpoints/utils/serializers/output/members.js +12 -1
- package/core/server/api/endpoints/utils/serializers/output/settings.js +2 -1
- package/core/server/api/endpoints/utils/validators/input/settings.js +22 -2
- package/core/server/data/exporter/table-lists.js +2 -1
- package/core/server/data/migrations/versions/5.5/2022-07-18-14-29-add-comment-reporting-permissions.js +10 -0
- package/core/server/data/migrations/versions/5.5/2022-07-18-14-31-drop-reports-reason.js +3 -0
- package/core/server/data/migrations/versions/5.5/2022-07-18-14-32-drop-nullable-member-id-from-likes.js +4 -0
- package/core/server/data/migrations/versions/5.5/2022-07-18-14-33-fix-comments-on-delete-foreign-keys.js +119 -0
- package/core/server/data/migrations/versions/5.5/2022-07-21-08-56-add-jobs-table.js +11 -0
- package/core/server/data/schema/commands.js +7 -2
- package/core/server/data/schema/fixtures/fixtures.json +5 -0
- package/core/server/data/schema/schema.js +12 -4
- package/core/server/ghost-server.js +0 -22
- package/core/server/models/comment-report.js +34 -0
- package/core/server/models/comment.js +8 -7
- package/core/server/models/job.js +9 -0
- package/core/server/models/tag.js +4 -0
- package/core/server/services/comments/email-templates/new-comment-reply.hbs +2 -2
- package/core/server/services/comments/email-templates/new-comment-reply.txt.js +7 -8
- package/core/server/services/comments/email-templates/new-comment.hbs +2 -2
- package/core/server/services/comments/email-templates/new-comment.txt.js +7 -6
- package/core/server/services/comments/email-templates/report.hbs +199 -0
- package/core/server/services/comments/email-templates/report.txt.js +16 -0
- package/core/server/services/comments/emails.js +57 -1
- package/core/server/services/comments/service.js +194 -2
- package/core/server/services/jobs/job-service.js +24 -1
- package/core/server/services/mail/GhostMailer.js +1 -0
- package/core/server/services/members/SingleUseTokenProvider.js +3 -3
- package/core/server/services/members/api.js +2 -1
- package/core/server/services/members/config.js +4 -1
- package/core/server/services/members/middleware.js +14 -2
- package/core/server/services/members/settings.js +4 -90
- package/core/server/services/public-config/config.js +2 -1
- package/core/server/services/settings/emails/verify-email.js +166 -0
- package/core/server/services/settings/settings-bread-service.js +170 -4
- package/core/server/services/settings/settings-service.js +9 -1
- package/core/server/services/stripe/service.js +9 -1
- package/core/server/services/webhooks/serialize.js +5 -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/endpoints/admin/routes.js +6 -0
- package/core/server/web/api/endpoints/content/routes.js +2 -1
- package/core/server/web/api/middleware/cors.js +2 -1
- package/core/server/web/api/testmode/jobs/graceful-job.js +2 -2
- package/core/server/web/api/testmode/routes.js +14 -0
- package/core/server/web/comments/routes.js +2 -0
- package/core/server/web/members/app.js +2 -4
- package/core/shared/config/defaults.json +15 -7
- package/core/shared/config/env/config.testing.json +3 -2
- package/package.json +75 -60
- package/yarn.lock +1812 -1832
- package/core/built/assets/ghost-dark-9e5d1f0dfae41232e5e34e4d0df53ae0.css +0 -1
- package/core/built/assets/ghost.min-e7cfbd1800f8e99b9158f74f1e39cd76.css +0 -1
- package/core/server/api/endpoints/utils/serializers/output/offers.js +0 -16
|
@@ -1,99 +1,14 @@
|
|
|
1
|
-
const MagicLink = require('@tryghost/magic-link');
|
|
2
|
-
const {URL} = require('url');
|
|
3
|
-
const path = require('path');
|
|
4
1
|
const urlUtils = require('../../../shared/url-utils');
|
|
5
|
-
const settingsCache = require('../../../shared/settings-cache');
|
|
6
|
-
const logging = require('@tryghost/logging');
|
|
7
|
-
const mail = require('../mail');
|
|
8
|
-
const updateEmailTemplate = require('./emails/updateEmail');
|
|
9
2
|
const SingleUseTokenProvider = require('./SingleUseTokenProvider');
|
|
10
3
|
const models = require('../../models');
|
|
11
4
|
const MAGIC_LINK_TOKEN_VALIDITY = 24 * 60 * 60 * 1000;
|
|
12
5
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const {transporter, getSubject, getText, getHTML, getSigninURL} = {
|
|
17
|
-
transporter: {
|
|
18
|
-
sendMail(message) {
|
|
19
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
20
|
-
logging.warn(message.text);
|
|
21
|
-
}
|
|
22
|
-
let msg = Object.assign({
|
|
23
|
-
from: config.getAuthEmailFromAddress(),
|
|
24
|
-
subject: 'Update email address',
|
|
25
|
-
forceTextContent: true
|
|
26
|
-
}, message);
|
|
27
|
-
|
|
28
|
-
return ghostMailer.send(msg);
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
getSubject() {
|
|
32
|
-
return `Confirm your email address`;
|
|
33
|
-
},
|
|
34
|
-
getText(url, type, email) {
|
|
35
|
-
return `
|
|
36
|
-
Hey there,
|
|
37
|
-
|
|
38
|
-
Please confirm your email address with this link:
|
|
39
|
-
|
|
40
|
-
${url}
|
|
41
|
-
|
|
42
|
-
For your security, the link will expire in 24 hours time.
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
Sent to ${email}
|
|
47
|
-
If you did not make this request, you can simply delete this message. This email address will not be used.
|
|
48
|
-
`;
|
|
49
|
-
},
|
|
50
|
-
getHTML(url, type, email) {
|
|
51
|
-
const siteTitle = settingsCache.get('title');
|
|
52
|
-
return updateEmailTemplate({url, email, siteTitle});
|
|
53
|
-
},
|
|
54
|
-
getSigninURL(token, type) {
|
|
55
|
-
const signinURL = new URL(urlUtils.urlFor('api', {type: 'admin'}, true));
|
|
56
|
-
signinURL.pathname = path.join(signinURL.pathname, '/settings/members/email/');
|
|
57
|
-
signinURL.searchParams.set('token', token);
|
|
58
|
-
signinURL.searchParams.set('action', type);
|
|
59
|
-
return signinURL.href;
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const magicLinkService = new MagicLink({
|
|
64
|
-
transporter,
|
|
65
|
-
tokenProvider: new SingleUseTokenProvider(models.SingleUseToken, MAGIC_LINK_TOKEN_VALIDITY),
|
|
66
|
-
getSigninURL,
|
|
67
|
-
getText,
|
|
68
|
-
getHTML,
|
|
69
|
-
getSubject
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const sendEmailAddressUpdateMagicLink = ({email, type = 'supportAddressUpdate'}) => {
|
|
73
|
-
const [,toDomain] = email.split('@');
|
|
74
|
-
let fromEmail = `noreply@${toDomain}`;
|
|
75
|
-
if (fromEmail === email) {
|
|
76
|
-
fromEmail = `no-reply@${toDomain}`;
|
|
77
|
-
}
|
|
78
|
-
magicLinkService.transporter = {
|
|
79
|
-
sendMail(message) {
|
|
80
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
81
|
-
logging.warn(message.text);
|
|
82
|
-
}
|
|
83
|
-
let msg = Object.assign({
|
|
84
|
-
from: fromEmail,
|
|
85
|
-
subject: 'Update email address',
|
|
86
|
-
forceTextContent: true
|
|
87
|
-
}, message);
|
|
88
|
-
|
|
89
|
-
return ghostMailer.send(msg);
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
return magicLinkService.sendMagicLink({email, tokenData: {email}, subject: email, type});
|
|
93
|
-
};
|
|
6
|
+
// @todo: can get removed, since this is moved to the settings bread service
|
|
7
|
+
function createSettingsInstance() {
|
|
8
|
+
const oldTokenProvider = new SingleUseTokenProvider(models.SingleUseToken, MAGIC_LINK_TOKEN_VALIDITY);
|
|
94
9
|
|
|
95
10
|
const getEmailFromToken = async ({token}) => {
|
|
96
|
-
const data = await
|
|
11
|
+
const data = await oldTokenProvider.validate(token);
|
|
97
12
|
return data.email;
|
|
98
13
|
};
|
|
99
14
|
|
|
@@ -107,7 +22,6 @@ function createSettingsInstance(config) {
|
|
|
107
22
|
};
|
|
108
23
|
|
|
109
24
|
return {
|
|
110
|
-
sendEmailAddressUpdateMagicLink,
|
|
111
25
|
getEmailFromToken,
|
|
112
26
|
getAdminRedirectLink
|
|
113
27
|
};
|
|
@@ -18,7 +18,8 @@ module.exports = function getConfigProperties() {
|
|
|
18
18
|
mailgunIsConfigured: !!(config.get('bulkEmail') && config.get('bulkEmail').mailgun),
|
|
19
19
|
emailAnalytics: config.get('emailAnalytics'),
|
|
20
20
|
hostSettings: config.get('hostSettings'),
|
|
21
|
-
tenor: config.get('tenor')
|
|
21
|
+
tenor: config.get('tenor'),
|
|
22
|
+
editor: config.get('editor')
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
const billingUrl = config.get('hostSettings:billing:enabled') ? config.get('hostSettings:billing:url') : '';
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
module.exports = ({email, url}) => `
|
|
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>Confirm your email address</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
|
+
}
|
|
53
|
+
/* -------------------------------------
|
|
54
|
+
PRESERVE THESE STYLES IN THE HEAD
|
|
55
|
+
------------------------------------- */
|
|
56
|
+
@media all {
|
|
57
|
+
.ExternalClass {
|
|
58
|
+
width: 100%;
|
|
59
|
+
}
|
|
60
|
+
.ExternalClass,
|
|
61
|
+
.ExternalClass p,
|
|
62
|
+
.ExternalClass span,
|
|
63
|
+
.ExternalClass font,
|
|
64
|
+
.ExternalClass td,
|
|
65
|
+
.ExternalClass div {
|
|
66
|
+
line-height: 100%;
|
|
67
|
+
}
|
|
68
|
+
.recipient-link a {
|
|
69
|
+
color: inherit !important;
|
|
70
|
+
font-family: inherit !important;
|
|
71
|
+
font-size: inherit !important;
|
|
72
|
+
font-weight: inherit !important;
|
|
73
|
+
line-height: inherit !important;
|
|
74
|
+
text-decoration: none !important;
|
|
75
|
+
}
|
|
76
|
+
#MessageViewBody a {
|
|
77
|
+
color: inherit;
|
|
78
|
+
text-decoration: none;
|
|
79
|
+
font-size: inherit;
|
|
80
|
+
font-family: inherit;
|
|
81
|
+
font-weight: inherit;
|
|
82
|
+
line-height: inherit;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
hr {
|
|
86
|
+
border-width: 0;
|
|
87
|
+
height: 0;
|
|
88
|
+
margin-top: 34px;
|
|
89
|
+
margin-bottom: 34px;
|
|
90
|
+
border-bottom-width: 1px;
|
|
91
|
+
border-bottom-color: #EEF5F8;
|
|
92
|
+
}
|
|
93
|
+
</style>
|
|
94
|
+
</head>
|
|
95
|
+
<body style="background-color: #F4F8FB; 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.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
|
|
96
|
+
<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: #F4F8FB;">
|
|
97
|
+
<tr>
|
|
98
|
+
<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>
|
|
99
|
+
<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: 600px; padding: 10px; width: 600px;">
|
|
100
|
+
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 600px; padding: 30px 20px;">
|
|
101
|
+
|
|
102
|
+
<!-- START CENTERED WHITE CONTAINER -->
|
|
103
|
+
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 8px;">
|
|
104
|
+
|
|
105
|
+
<!-- START MAIN CONTENT AREA -->
|
|
106
|
+
<tr>
|
|
107
|
+
<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; padding: 40px 50px;">
|
|
108
|
+
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
|
|
109
|
+
<tr>
|
|
110
|
+
<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;">
|
|
111
|
+
<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;">Hey there,</p>
|
|
112
|
+
<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; margin: 0; line-height: 25px; margin-bottom: 32px;">Please confirm your email address with this link:</p>
|
|
113
|
+
<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;">
|
|
114
|
+
<tbody>
|
|
115
|
+
<tr>
|
|
116
|
+
<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;">
|
|
117
|
+
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
|
|
118
|
+
<tbody>
|
|
119
|
+
<tr>
|
|
120
|
+
<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: #15212A; border-radius: 5px; text-align: center;"> <a href="${url}" target="_blank" style="display: inline-block; color: #ffffff; background-color: #15212A; border: solid 1px #15212A; 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: #15212A;" data-test-verify-link>Confirm email address</a> </td>
|
|
121
|
+
</tr>
|
|
122
|
+
</tbody>
|
|
123
|
+
</table>
|
|
124
|
+
</td>
|
|
125
|
+
</tr>
|
|
126
|
+
</tbody>
|
|
127
|
+
</table>
|
|
128
|
+
<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; margin: 0; line-height: 25px; margin-bottom: 25px;">For your security, the link will expire in 24 hours time.</p>
|
|
129
|
+
<hr/>
|
|
130
|
+
<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; margin: 0; line-height: 25px; margin-bottom: 5px;">You can also copy & paste this URL into your browser:</p>
|
|
131
|
+
<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: 14px; line-height: 21px; margin-top: 0; color: #738A94;">${url}</p>
|
|
132
|
+
</td>
|
|
133
|
+
</tr>
|
|
134
|
+
</table>
|
|
135
|
+
</td>
|
|
136
|
+
</tr>
|
|
137
|
+
|
|
138
|
+
<!-- END MAIN CONTENT AREA -->
|
|
139
|
+
</table>
|
|
140
|
+
|
|
141
|
+
<!-- START FOOTER -->
|
|
142
|
+
<div class="footer" style="clear: both; Margin-top: 10px; text-align: center; width: 100%;">
|
|
143
|
+
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
|
|
144
|
+
<tr>
|
|
145
|
+
<td class="content-block" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; vertical-align: top; padding-bottom: 5px; padding-top: 15px; font-size: 13px; line-height: 21px; color: #738A94; text-align: center;">
|
|
146
|
+
If you did not make this request, you can simply delete this message.<br/>This email address will not be used.
|
|
147
|
+
</td>
|
|
148
|
+
</tr>
|
|
149
|
+
<tr>
|
|
150
|
+
<td class="content-block" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; vertical-align: top; padding-bottom: 10px; padding-top: 10px; font-size: 13px; color: #738A94; text-align: center;">
|
|
151
|
+
<span class="recipient-link" style="color: #738A94; font-size: 13px; text-align: center;">Sent to <a href="mailto:${email}" style="text-decoration: underline; color: #738A94; font-size: 13px; text-align: center;">${email}</a></span>
|
|
152
|
+
</td>
|
|
153
|
+
</tr>
|
|
154
|
+
</table>
|
|
155
|
+
</div>
|
|
156
|
+
<!-- END FOOTER -->
|
|
157
|
+
|
|
158
|
+
<!-- END CENTERED WHITE CONTAINER -->
|
|
159
|
+
</div>
|
|
160
|
+
</td>
|
|
161
|
+
<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>
|
|
162
|
+
</tr>
|
|
163
|
+
</table>
|
|
164
|
+
</body>
|
|
165
|
+
</html>
|
|
166
|
+
`;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const tpl = require('@tryghost/tpl');
|
|
3
|
-
const {NotFoundError, NoPermissionError, BadRequestError} = require('@tryghost/errors');
|
|
3
|
+
const {NotFoundError, NoPermissionError, BadRequestError, IncorrectUsageError} = require('@tryghost/errors');
|
|
4
4
|
const {obfuscatedSetting, isSecretSetting, hideValueIfSecret} = require('./settings-utils');
|
|
5
|
+
const logging = require('@tryghost/logging');
|
|
6
|
+
const MagicLink = require('@tryghost/magic-link');
|
|
7
|
+
const verifyEmailTemplate = require('./emails/verify-email');
|
|
5
8
|
|
|
9
|
+
const EMAIL_KEYS = ['members_support_address'];
|
|
6
10
|
const messages = {
|
|
7
11
|
problemFindingSetting: 'Problem finding setting: {key}',
|
|
8
12
|
accessCoreSettingFromExtReq: 'Attempted to access core setting from external request'
|
|
@@ -13,13 +17,67 @@ class SettingsBREADService {
|
|
|
13
17
|
*
|
|
14
18
|
* @param {Object} options
|
|
15
19
|
* @param {Object} options.SettingsModel
|
|
20
|
+
* @param {Object} options.mail
|
|
16
21
|
* @param {Object} options.settingsCache - SettingsCache instance
|
|
22
|
+
* @param {Object} options.singleUseTokenProvider
|
|
23
|
+
* @param {Object} options.urlUtils
|
|
17
24
|
* @param {Object} options.labsService - labs service instance
|
|
18
25
|
*/
|
|
19
|
-
constructor({SettingsModel, settingsCache, labsService}) {
|
|
26
|
+
constructor({SettingsModel, settingsCache, labsService, mail, singleUseTokenProvider, urlUtils}) {
|
|
20
27
|
this.SettingsModel = SettingsModel;
|
|
21
28
|
this.settingsCache = settingsCache;
|
|
22
29
|
this.labs = labsService;
|
|
30
|
+
|
|
31
|
+
/* email verification setup */
|
|
32
|
+
|
|
33
|
+
this.ghostMailer = new mail.GhostMailer();
|
|
34
|
+
|
|
35
|
+
const {transporter, getSubject, getText, getHTML, getSigninURL} = {
|
|
36
|
+
transporter: {
|
|
37
|
+
sendMail() {
|
|
38
|
+
// noop - overridden in `sendEmailVerificationMagicLink`
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
getSubject() {
|
|
42
|
+
// not used - overridden in `sendEmailVerificationMagicLink`
|
|
43
|
+
return `Verify email address`;
|
|
44
|
+
},
|
|
45
|
+
getText(url, type, email) {
|
|
46
|
+
return `
|
|
47
|
+
Hey there,
|
|
48
|
+
|
|
49
|
+
Please confirm your email address with this link:
|
|
50
|
+
|
|
51
|
+
${url}
|
|
52
|
+
|
|
53
|
+
For your security, the link will expire in 24 hours time.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
Sent to ${email}
|
|
58
|
+
If you did not make this request, you can simply delete this message. This email address will not be used.
|
|
59
|
+
`;
|
|
60
|
+
},
|
|
61
|
+
getHTML(url, type, email) {
|
|
62
|
+
return verifyEmailTemplate({url, email});
|
|
63
|
+
},
|
|
64
|
+
getSigninURL(token) {
|
|
65
|
+
// @todo: need to make this more generic?
|
|
66
|
+
const adminUrl = urlUtils.urlFor('admin', true);
|
|
67
|
+
const signinURL = new URL(adminUrl);
|
|
68
|
+
signinURL.hash = `/settings/members/?verifyEmail=${token}`;
|
|
69
|
+
return signinURL.href;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
this.magicLinkService = new MagicLink({
|
|
74
|
+
transporter,
|
|
75
|
+
tokenProvider: singleUseTokenProvider,
|
|
76
|
+
getSigninURL,
|
|
77
|
+
getText,
|
|
78
|
+
getHTML,
|
|
79
|
+
getSubject
|
|
80
|
+
});
|
|
23
81
|
}
|
|
24
82
|
|
|
25
83
|
/**
|
|
@@ -93,7 +151,7 @@ class SettingsBREADService {
|
|
|
93
151
|
* @returns
|
|
94
152
|
*/
|
|
95
153
|
async edit(settings, options, stripeConnectData) {
|
|
96
|
-
|
|
154
|
+
let filteredSettings = settings.filter((setting) => {
|
|
97
155
|
// The `stripe_connect_integration_token` "setting" is only used to set the `stripe_connect_*` settings.
|
|
98
156
|
return ![
|
|
99
157
|
'stripe_connect_integration_token',
|
|
@@ -152,9 +210,31 @@ class SettingsBREADService {
|
|
|
152
210
|
});
|
|
153
211
|
}
|
|
154
212
|
|
|
155
|
-
|
|
213
|
+
// remove any email properties that are not allowed to be set without verification
|
|
214
|
+
const {filteredSettings: refilteredSettings, emailsToVerify} = await this.prepSettingsForEmailVerification(filteredSettings, getSetting);
|
|
215
|
+
|
|
216
|
+
const modelArray = await this.SettingsModel.edit(refilteredSettings, options).then((result) => {
|
|
156
217
|
return this._formatBrowse(_.keyBy(_.invokeMap(result, 'toJSON'), 'key'), options.context);
|
|
157
218
|
});
|
|
219
|
+
|
|
220
|
+
return this.respondWithEmailVerification(modelArray, emailsToVerify);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async verifyKeyUpdate(token) {
|
|
224
|
+
const data = await this.magicLinkService.getDataFromToken(token);
|
|
225
|
+
const {key, value} = data;
|
|
226
|
+
|
|
227
|
+
// Verify keys (in case they ever change and we have old tokens)
|
|
228
|
+
if (!EMAIL_KEYS.includes(key)) {
|
|
229
|
+
throw new IncorrectUsageError({
|
|
230
|
+
message: 'Not allowed to update this setting key via tokens'
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return this.SettingsModel.edit({
|
|
235
|
+
key,
|
|
236
|
+
value
|
|
237
|
+
});
|
|
158
238
|
}
|
|
159
239
|
|
|
160
240
|
/**
|
|
@@ -205,6 +285,92 @@ class SettingsBREADService {
|
|
|
205
285
|
|
|
206
286
|
return settings;
|
|
207
287
|
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* @private
|
|
291
|
+
*/
|
|
292
|
+
async prepSettingsForEmailVerification(settings, getSetting) {
|
|
293
|
+
const filteredSettings = [];
|
|
294
|
+
const emailsToVerify = [];
|
|
295
|
+
|
|
296
|
+
for (const setting of settings) {
|
|
297
|
+
if (EMAIL_KEYS.includes(setting.key)) {
|
|
298
|
+
const email = setting.value;
|
|
299
|
+
const key = setting.key;
|
|
300
|
+
const hasChanged = getSetting(setting).value !== email;
|
|
301
|
+
|
|
302
|
+
if (await this.requiresEmailVerification({email, hasChanged})) {
|
|
303
|
+
emailsToVerify.push({email, key});
|
|
304
|
+
} else {
|
|
305
|
+
filteredSettings.push(setting);
|
|
306
|
+
}
|
|
307
|
+
} else {
|
|
308
|
+
filteredSettings.push(setting);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return {filteredSettings, emailsToVerify};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* @private
|
|
317
|
+
*/
|
|
318
|
+
async requiresEmailVerification({email, hasChanged}) {
|
|
319
|
+
if (!email || !hasChanged || email === 'noreply') {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// TODO: check for known/verified email
|
|
324
|
+
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* @private
|
|
330
|
+
*/
|
|
331
|
+
async respondWithEmailVerification(settings, emailsToVerify) {
|
|
332
|
+
if (emailsToVerify.length > 0) {
|
|
333
|
+
for (const {email, key} of emailsToVerify) {
|
|
334
|
+
await this.sendEmailVerificationMagicLink({email, key});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
settings.meta = settings.meta || {};
|
|
338
|
+
settings.meta.sent_email_verification = emailsToVerify.map(v => v.key);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return settings;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @private
|
|
346
|
+
*/
|
|
347
|
+
async sendEmailVerificationMagicLink({email, key}) {
|
|
348
|
+
const [,toDomain] = email.split('@');
|
|
349
|
+
|
|
350
|
+
let fromEmail = `noreply@${toDomain}`;
|
|
351
|
+
if (fromEmail === email) {
|
|
352
|
+
fromEmail = `no-reply@${toDomain}`;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const {ghostMailer} = this;
|
|
356
|
+
|
|
357
|
+
this.magicLinkService.transporter = {
|
|
358
|
+
sendMail(message) {
|
|
359
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
360
|
+
logging.warn(message.text);
|
|
361
|
+
}
|
|
362
|
+
let msg = Object.assign({
|
|
363
|
+
from: fromEmail,
|
|
364
|
+
subject: 'Verify email address',
|
|
365
|
+
forceTextContent: true
|
|
366
|
+
}, message);
|
|
367
|
+
|
|
368
|
+
return ghostMailer.send(msg);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
return this.magicLinkService.sendMagicLink({email, tokenData: {key, value: email}});
|
|
373
|
+
}
|
|
208
374
|
}
|
|
209
375
|
|
|
210
376
|
module.exports = SettingsBREADService;
|
|
@@ -12,6 +12,9 @@ const config = require('../../../shared/config');
|
|
|
12
12
|
const SettingsCache = require('../../../shared/settings-cache');
|
|
13
13
|
const SettingsBREADService = require('./settings-bread-service');
|
|
14
14
|
const {obfuscatedSetting, isSecretSetting, hideValueIfSecret} = require('./settings-utils');
|
|
15
|
+
const mail = require('../mail');
|
|
16
|
+
const SingleUseTokenProvider = require('../members/SingleUseTokenProvider');
|
|
17
|
+
const urlUtils = require('../../../shared/url-utils');
|
|
15
18
|
|
|
16
19
|
const ObjectId = require('bson-objectid');
|
|
17
20
|
|
|
@@ -19,6 +22,8 @@ const messages = {
|
|
|
19
22
|
incorrectKeyType: 'type must be one of "direct" or "connect".'
|
|
20
23
|
};
|
|
21
24
|
|
|
25
|
+
const MAGIC_LINK_TOKEN_VALIDITY = 24 * 60 * 60 * 1000;
|
|
26
|
+
|
|
22
27
|
/**
|
|
23
28
|
* @returns {SettingsBREADService} instance of the PostsService
|
|
24
29
|
*/
|
|
@@ -26,7 +31,10 @@ const getSettingsBREADServiceInstance = () => {
|
|
|
26
31
|
return new SettingsBREADService({
|
|
27
32
|
SettingsModel: models.Settings,
|
|
28
33
|
settingsCache: SettingsCache,
|
|
29
|
-
labsService: labs
|
|
34
|
+
labsService: labs,
|
|
35
|
+
mail,
|
|
36
|
+
singleUseTokenProvider: new SingleUseTokenProvider(models.SingleUseToken, MAGIC_LINK_TOKEN_VALIDITY),
|
|
37
|
+
urlUtils
|
|
30
38
|
});
|
|
31
39
|
};
|
|
32
40
|
|
|
@@ -25,7 +25,15 @@ const debouncedConfigureApi = _.debounce(() => {
|
|
|
25
25
|
|
|
26
26
|
module.exports = new StripeService({
|
|
27
27
|
membersService,
|
|
28
|
-
models: _.pick(models, [
|
|
28
|
+
models: _.pick(models, [
|
|
29
|
+
'Product',
|
|
30
|
+
'StripePrice',
|
|
31
|
+
'StripeCustomerSubscription',
|
|
32
|
+
'StripeProduct',
|
|
33
|
+
'MemberStripeCustomer',
|
|
34
|
+
'Offer',
|
|
35
|
+
'Settings'
|
|
36
|
+
]),
|
|
29
37
|
StripeWebhook: {
|
|
30
38
|
async get() {
|
|
31
39
|
return {
|
|
@@ -13,9 +13,14 @@ module.exports = (event, model) => {
|
|
|
13
13
|
ops.push(() => {
|
|
14
14
|
let frame = {options: {previous: false, context: {user: true}}};
|
|
15
15
|
|
|
16
|
+
// NOTE: below options are lost in the during event processing, a more holistic approach would be
|
|
17
|
+
// to pass them somehow along with the model
|
|
16
18
|
if (['posts', 'pages'].includes(docName)) {
|
|
17
19
|
frame.options.formats = ['mobiledoc', 'html', 'plaintext'];
|
|
18
20
|
frame.options.withRelated = ['tags', 'authors'];
|
|
21
|
+
model._originalOptions = {
|
|
22
|
+
withRelated: ['tags', 'authors']
|
|
23
|
+
};
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
return apiShared
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<title>Ghost Admin</title>
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.
|
|
11
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.5%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
|
|
12
12
|
|
|
13
13
|
<meta name="HandheldFriendly" content="True" />
|
|
14
14
|
<meta name="MobileOptimized" content="320" />
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
<link rel="stylesheet" href="assets/vendor.min-4a6661c574707ceca220aa2e76558995.css">
|
|
41
|
-
<link rel="stylesheet" href="assets/ghost.min-
|
|
41
|
+
<link rel="stylesheet" href="assets/ghost.min-a89d10b3b58c1a5ebaca68cef93a404c.css" title="light">
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
<script src="assets/vendor.min-
|
|
60
|
-
<script src="assets/ghost.min-
|
|
59
|
+
<script src="assets/vendor.min-cf3af99dca0c71937669305afb3686a1.js"></script>
|
|
60
|
+
<script src="assets/ghost.min-c75f224decd20f9538179d7564cd2ab4.js"></script>
|
|
61
61
|
|
|
62
62
|
</body>
|
|
63
63
|
</html>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<title>Ghost Admin</title>
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.
|
|
11
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.5%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
|
|
12
12
|
|
|
13
13
|
<meta name="HandheldFriendly" content="True" />
|
|
14
14
|
<meta name="MobileOptimized" content="320" />
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
<link rel="stylesheet" href="assets/vendor.min-4a6661c574707ceca220aa2e76558995.css">
|
|
41
|
-
<link rel="stylesheet" href="assets/ghost.min-
|
|
41
|
+
<link rel="stylesheet" href="assets/ghost.min-a89d10b3b58c1a5ebaca68cef93a404c.css" title="light">
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
<script src="assets/vendor.min-
|
|
60
|
-
<script src="assets/ghost.min-
|
|
59
|
+
<script src="assets/vendor.min-cf3af99dca0c71937669305afb3686a1.js"></script>
|
|
60
|
+
<script src="assets/ghost.min-c75f224decd20f9538179d7564cd2ab4.js"></script>
|
|
61
61
|
|
|
62
62
|
</body>
|
|
63
63
|
</html>
|
|
@@ -64,8 +64,14 @@ module.exports = function apiRoutes() {
|
|
|
64
64
|
|
|
65
65
|
router.get('/settings', mw.authAdminApi, http(api.settings.browse));
|
|
66
66
|
router.put('/settings', mw.authAdminApi, http(api.settings.edit));
|
|
67
|
+
router.put('/settings/verifications/', mw.authAdminApi, http(api.settings.verifyKeyUpdate));
|
|
68
|
+
|
|
69
|
+
/** @deprecated This endpoint is part of the old email verification flow for the support email */
|
|
67
70
|
router.get('/settings/members/email', http(api.settings.validateMembersEmailUpdate));
|
|
71
|
+
|
|
72
|
+
/** @deprecated This endpoint is part of the old email verification flow for the support email */
|
|
68
73
|
router.post('/settings/members/email', mw.authAdminApi, http(api.settings.updateMembersEmail));
|
|
74
|
+
|
|
69
75
|
router.del('/settings/stripe/connect', mw.authAdminApi, http(api.settings.disconnectStripeConnectIntegration));
|
|
70
76
|
|
|
71
77
|
// ## Users
|
|
@@ -3,11 +3,12 @@ const cors = require('cors');
|
|
|
3
3
|
const api = require('../../../../api').endpoints;
|
|
4
4
|
const http = require('../../../../api').shared.http;
|
|
5
5
|
const mw = require('./middleware');
|
|
6
|
+
const config = require('../../../../../shared/config');
|
|
6
7
|
|
|
7
8
|
module.exports = function apiRoutes() {
|
|
8
9
|
const router = express.Router('content api');
|
|
9
10
|
|
|
10
|
-
router.use(cors());
|
|
11
|
+
router.use(cors({maxAge: config.get('caching:cors:maxAge')}));
|
|
11
12
|
|
|
12
13
|
// ## Posts
|
|
13
14
|
router.get('/posts', mw.authenticatePublic, http(api.postsPublic.browse));
|