ghost 4.20.4 → 4.21.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/.eslintrc.js +1 -1
- package/core/built/assets/{chunk.3.777d43e2ce954ba8b2f5.js → chunk.3.065ee3c3bdf674bd81a4.js} +17 -17
- package/core/built/assets/{ghost-dark-20e2892d4f30d0d1183c9ac725ea37d0.css → ghost-dark-1328db4a7dd128305646305a8731bcfe.css} +1 -1
- package/core/built/assets/{ghost.min-57e46fd3b1145ecf2cbd185a13611f3b.css → ghost.min-5abc69c04ad1d5301a857e01009b9c05.css} +1 -1
- package/core/built/assets/{ghost.min-07b6a50c54b3e2e190332c28c7255d2f.js → ghost.min-6c546c322127ae6d1d1b0ddbf34be75b.js} +131 -118
- package/core/built/assets/{vendor.min-af502ac4142871500fc424f6a5a254ec.js → vendor.min-c6ef90bfd7eff256e10b85583bfe9a74.js} +401 -402
- package/core/frontend/apps/amp/lib/helpers/amp_content.js +16 -4
- package/core/frontend/meta/title.js +15 -5
- package/core/server/api/canary/members.js +6 -103
- package/core/server/api/canary/membersStripeConnect.js +0 -10
- package/core/server/api/canary/utils/serializers/output/images.js +4 -0
- package/core/server/api/v2/utils/serializers/output/images.js +4 -0
- package/core/server/api/v3/utils/serializers/output/images.js +4 -0
- package/core/server/services/members/api.js +1 -1
- package/core/server/services/members/emails/signup.js +2 -2
- package/core/server/services/members/stripe-connect.js +14 -0
- package/core/server/web/admin/views/default-prod.html +4 -4
- package/core/server/web/admin/views/default.html +4 -4
- package/package.json +24 -24
- package/yarn.lock +128 -161
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// there if available. The cacheId is a combination of `updated_at` and the `slug`.
|
|
9
9
|
const Promise = require('bluebird');
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const {DateTime, Interval} = require('luxon');
|
|
12
12
|
const errors = require('@tryghost/errors');
|
|
13
13
|
const logging = require('@tryghost/logging');
|
|
14
14
|
|
|
@@ -119,14 +119,26 @@ function getAmperizeHTML(html, post) {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
let Amperize = require('amperize');
|
|
122
|
-
let startedAtMoment = moment();
|
|
123
122
|
|
|
124
123
|
amperize = amperize || new Amperize();
|
|
125
124
|
|
|
126
|
-
|
|
125
|
+
const startedAtMoment = DateTime.now();
|
|
126
|
+
|
|
127
|
+
let cacheDateTime;
|
|
128
|
+
let postDateTime;
|
|
129
|
+
|
|
130
|
+
if (amperizeCache[post.id]) {
|
|
131
|
+
const {updated_at: ampCacheUpdatedAt} = amperizeCache[post.id];
|
|
132
|
+
const {updated_at: postUpdatedAt} = post;
|
|
133
|
+
|
|
134
|
+
cacheDateTime = DateTime.fromJSDate(new Date(ampCacheUpdatedAt));
|
|
135
|
+
postDateTime = DateTime.fromJSDate(new Date(postUpdatedAt));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!amperizeCache[post.id] || cacheDateTime.diff(postDateTime).valueOf() < 0) {
|
|
127
139
|
return new Promise((resolve) => {
|
|
128
140
|
amperize.parse(html, (err, res) => {
|
|
129
|
-
logging.info('amp.parse', post.url,
|
|
141
|
+
logging.info('amp.parse', post.url, Interval.fromDateTimes(startedAtMoment, DateTime.now()).length('milliseconds') + 'ms');
|
|
130
142
|
|
|
131
143
|
if (err) {
|
|
132
144
|
if (err.src) {
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const settingsCache = require('../../shared/settings-cache');
|
|
3
3
|
|
|
4
|
+
function optionalString(test, string) {
|
|
5
|
+
if (test) {
|
|
6
|
+
return string;
|
|
7
|
+
}
|
|
8
|
+
return '';
|
|
9
|
+
}
|
|
10
|
+
|
|
4
11
|
function getTitle(data, root, options = {}) {
|
|
5
12
|
const context = root ? root.context : null;
|
|
6
|
-
const siteTitle = settingsCache.get('title');
|
|
13
|
+
const siteTitle = settingsCache.get('title') || '';
|
|
7
14
|
const pagination = root ? root.pagination : null;
|
|
8
15
|
|
|
9
16
|
// options.property = null/'og'/'twitter'
|
|
@@ -16,6 +23,9 @@ function getTitle(data, root, options = {}) {
|
|
|
16
23
|
pageString = _.has(options.hash, 'page') ? options.hash.page.replace('%', pagination.page) : ' (Page ' + pagination.page + ')';
|
|
17
24
|
}
|
|
18
25
|
|
|
26
|
+
const dashSiteTitle = optionalString(siteTitle, ' - ' + siteTitle);
|
|
27
|
+
const dashSiteTitlePage = optionalString(siteTitle || pageString, ' -' + optionalString(siteTitle, ' ' + siteTitle) + pageString);
|
|
28
|
+
|
|
19
29
|
// If there's a specific meta title
|
|
20
30
|
if (data.meta_title) {
|
|
21
31
|
title = data.meta_title;
|
|
@@ -28,16 +38,16 @@ function getTitle(data, root, options = {}) {
|
|
|
28
38
|
}
|
|
29
39
|
// Author title, paged
|
|
30
40
|
} else if (_.includes(context, 'author') && data.author && _.includes(context, 'paged')) {
|
|
31
|
-
title = data.author.name +
|
|
41
|
+
title = data.author.name + dashSiteTitlePage;
|
|
32
42
|
// Author title, index
|
|
33
43
|
} else if (_.includes(context, 'author') && data.author) {
|
|
34
|
-
title = data.author.name +
|
|
44
|
+
title = data.author.name + dashSiteTitle;
|
|
35
45
|
// Tag title, paged
|
|
36
46
|
} else if (_.includes(context, 'tag') && data.tag && _.includes(context, 'paged')) {
|
|
37
|
-
title = data.tag.meta_title || data.tag.name +
|
|
47
|
+
title = data.tag.meta_title || data.tag.name + dashSiteTitlePage;
|
|
38
48
|
// Tag title, index
|
|
39
49
|
} else if (_.includes(context, 'tag') && data.tag) {
|
|
40
|
-
title = data.tag[optionsPropertyName] || data.tag.meta_title || data.tag.name +
|
|
50
|
+
title = data.tag[optionsPropertyName] || data.tag.meta_title || data.tag.name + dashSiteTitle;
|
|
41
51
|
// Post title
|
|
42
52
|
} else if (_.includes(context, 'post') && data.post) {
|
|
43
53
|
title = data.post[optionsPropertyName] || data.post.meta_title || data.post.title;
|
|
@@ -59,8 +59,7 @@ module.exports = {
|
|
|
59
59
|
permissions: true,
|
|
60
60
|
validation: {},
|
|
61
61
|
async query(frame) {
|
|
62
|
-
|
|
63
|
-
const page = await membersService.api.members.list(frame.options);
|
|
62
|
+
const page = await membersService.api.memberBREADService.browse(frame.options);
|
|
64
63
|
|
|
65
64
|
return page;
|
|
66
65
|
}
|
|
@@ -84,7 +83,7 @@ module.exports = {
|
|
|
84
83
|
},
|
|
85
84
|
permissions: true,
|
|
86
85
|
async query(frame) {
|
|
87
|
-
|
|
86
|
+
const member = await membersService.api.memberBREADService.read(frame.data, frame.options);
|
|
88
87
|
|
|
89
88
|
if (!member) {
|
|
90
89
|
throw new errors.NotFoundError({
|
|
@@ -115,72 +114,9 @@ module.exports = {
|
|
|
115
114
|
},
|
|
116
115
|
permissions: true,
|
|
117
116
|
async query(frame) {
|
|
118
|
-
|
|
119
|
-
frame.options.withRelated = ['stripeSubscriptions', 'products', 'labels', 'stripeSubscriptions.stripePrice', 'stripeSubscriptions.stripePrice.stripeProduct'];
|
|
120
|
-
if (!labsService.isSet('multipleProducts')) {
|
|
121
|
-
delete frame.data.products;
|
|
122
|
-
}
|
|
123
|
-
try {
|
|
124
|
-
if (!membersService.config.isStripeConnected()
|
|
125
|
-
&& (frame.data.members[0].stripe_customer_id || frame.data.members[0].comped)) {
|
|
126
|
-
const property = frame.data.members[0].comped ? 'comped' : 'stripe_customer_id';
|
|
127
|
-
|
|
128
|
-
throw new errors.ValidationError({
|
|
129
|
-
message: tpl(messages.stripeNotConnected.message),
|
|
130
|
-
context: tpl(messages.stripeNotConnected.context),
|
|
131
|
-
help: tpl(messages.stripeNotConnected.help),
|
|
132
|
-
property
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
member = await membersService.api.members.create(frame.data.members[0], frame.options);
|
|
137
|
-
|
|
138
|
-
if (frame.data.members[0].stripe_customer_id) {
|
|
139
|
-
await membersService.api.members.linkStripeCustomer({
|
|
140
|
-
customer_id: frame.data.members[0].stripe_customer_id,
|
|
141
|
-
member_id: member.id
|
|
142
|
-
}, frame.options);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (!labsService.isSet('multipleProducts')) {
|
|
146
|
-
if (frame.data.members[0].comped) {
|
|
147
|
-
await membersService.api.members.setComplimentarySubscription(member);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (frame.options.send_email) {
|
|
152
|
-
await membersService.api.sendEmailWithMagicLink({email: member.get('email'), requestedType: frame.options.email_type});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return member;
|
|
156
|
-
} catch (error) {
|
|
157
|
-
if (error.code && error.message.toLowerCase().indexOf('unique') !== -1) {
|
|
158
|
-
throw new errors.ValidationError({
|
|
159
|
-
message: tpl(messages.memberAlreadyExists.message),
|
|
160
|
-
context: tpl(messages.memberAlreadyExists.context, {
|
|
161
|
-
action: 'add'
|
|
162
|
-
})
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// NOTE: failed to link Stripe customer/plan/subscription or have thrown custom Stripe connection error.
|
|
167
|
-
// It's a bit ugly doing regex matching to detect errors, but it's the easiest way that works without
|
|
168
|
-
// introducing additional logic/data format into current error handling
|
|
169
|
-
const isStripeLinkingError = error.message && (error.message.match(/customer|plan|subscription/g));
|
|
170
|
-
if (member && isStripeLinkingError) {
|
|
171
|
-
if (error.message.indexOf('customer') && error.code === 'resource_missing') {
|
|
172
|
-
error.message = `Member not imported. ${error.message}`;
|
|
173
|
-
error.context = tpl(messages.stripeCustomerNotFound.context);
|
|
174
|
-
error.help = tpl(messages.stripeCustomerNotFound.help);
|
|
175
|
-
}
|
|
117
|
+
const member = await membersService.api.memberBREADService.add(frame.data.members[0], frame.options);
|
|
176
118
|
|
|
177
|
-
|
|
178
|
-
id: member.get('id')
|
|
179
|
-
}, frame.options);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
throw error;
|
|
183
|
-
}
|
|
119
|
+
return member;
|
|
184
120
|
}
|
|
185
121
|
},
|
|
186
122
|
|
|
@@ -199,42 +135,9 @@ module.exports = {
|
|
|
199
135
|
},
|
|
200
136
|
permissions: true,
|
|
201
137
|
async query(frame) {
|
|
202
|
-
|
|
203
|
-
delete frame.data.products;
|
|
204
|
-
}
|
|
205
|
-
try {
|
|
206
|
-
frame.options.withRelated = ['stripeSubscriptions', 'products', 'labels', 'stripeSubscriptions.stripePrice', 'stripeSubscriptions.stripePrice.stripeProduct'];
|
|
207
|
-
const member = await membersService.api.members.update(frame.data.members[0], frame.options);
|
|
208
|
-
|
|
209
|
-
const hasCompedSubscription = !!member.related('stripeSubscriptions').find(sub => sub.get('plan_nickname') === 'Complimentary' && sub.get('status') === 'active');
|
|
210
|
-
|
|
211
|
-
if (!labsService.isSet('multipleProducts')) {
|
|
212
|
-
if (typeof frame.data.members[0].comped === 'boolean') {
|
|
213
|
-
if (frame.data.members[0].comped && !hasCompedSubscription) {
|
|
214
|
-
await membersService.api.members.setComplimentarySubscription(member);
|
|
215
|
-
} else if (!(frame.data.members[0].comped) && hasCompedSubscription) {
|
|
216
|
-
await membersService.api.members.cancelComplimentarySubscription(member);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
await member.load(['stripeSubscriptions', 'products', 'stripeSubscriptions.stripePrice', 'stripeSubscriptions.stripePrice.stripeProduct']);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
await member.load(['stripeSubscriptions.customer', 'stripeSubscriptions.stripePrice', 'stripeSubscriptions.stripePrice.stripeProduct']);
|
|
224
|
-
|
|
225
|
-
return member;
|
|
226
|
-
} catch (error) {
|
|
227
|
-
if (error.code && error.message.toLowerCase().indexOf('unique') !== -1) {
|
|
228
|
-
throw new errors.ValidationError({
|
|
229
|
-
message: tpl(messages.memberAlreadyExists.message),
|
|
230
|
-
context: tpl(messages.memberAlreadyExists.context, {
|
|
231
|
-
action: 'edit'
|
|
232
|
-
})
|
|
233
|
-
});
|
|
234
|
-
}
|
|
138
|
+
const member = await membersService.api.memberBREADService.edit(frame.data.members[0], frame.options);
|
|
235
139
|
|
|
236
|
-
|
|
237
|
-
}
|
|
140
|
+
return member;
|
|
238
141
|
}
|
|
239
142
|
},
|
|
240
143
|
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
const membersService = require('../../services/members');
|
|
2
|
-
const config = require('../../../shared/config');
|
|
3
|
-
const urlUtils = require('../../../shared/url-utils');
|
|
4
|
-
const {BadRequestError} = require('@tryghost/errors');
|
|
5
2
|
|
|
6
3
|
module.exports = {
|
|
7
4
|
docName: 'members_stripe_connect',
|
|
@@ -18,13 +15,6 @@ module.exports = {
|
|
|
18
15
|
}
|
|
19
16
|
},
|
|
20
17
|
query(frame) {
|
|
21
|
-
const siteUrl = urlUtils.getSiteUrl();
|
|
22
|
-
const productionMode = config.get('env') === 'production';
|
|
23
|
-
const siteUrlUsingSSL = /^https/.test(siteUrl);
|
|
24
|
-
const cannotConnectToStripe = productionMode && !siteUrlUsingSSL;
|
|
25
|
-
if (cannotConnectToStripe) {
|
|
26
|
-
throw new BadRequestError('Cannot connect to stripe unless site is using https://');
|
|
27
|
-
}
|
|
28
18
|
// This is something you have to do if you want to use the "framework" with access to the raw req/res
|
|
29
19
|
frame.response = async function (req, res) {
|
|
30
20
|
function setSessionProp(prop, val) {
|
|
@@ -8,6 +8,10 @@ module.exports = {
|
|
|
8
8
|
return frame.response = {
|
|
9
9
|
images: [{
|
|
10
10
|
url: mapper.mapImage(path),
|
|
11
|
+
// NOTE: ref field is here to have reference point on the client
|
|
12
|
+
// for example when substituting existing images in the mobiledoc
|
|
13
|
+
// this field would serve as an identifier to find images to replace
|
|
14
|
+
// once the response is back. Think of it as ID on the client's side.
|
|
11
15
|
ref: frame.data.ref || null
|
|
12
16
|
}]
|
|
13
17
|
};
|
|
@@ -8,6 +8,10 @@ module.exports = {
|
|
|
8
8
|
return frame.response = {
|
|
9
9
|
images: [{
|
|
10
10
|
url: mapper.mapImage(path),
|
|
11
|
+
// NOTE: ref field is here to have reference point on the client
|
|
12
|
+
// for example when substituting existing images in the mobiledoc
|
|
13
|
+
// this field would serve as an identifier to find images to replace
|
|
14
|
+
// once the response is back. Think of it as ID on the client's side.
|
|
11
15
|
ref: frame.data.ref || null
|
|
12
16
|
}]
|
|
13
17
|
};
|
|
@@ -8,6 +8,10 @@ module.exports = {
|
|
|
8
8
|
return frame.response = {
|
|
9
9
|
images: [{
|
|
10
10
|
url: mapper.mapImage(path),
|
|
11
|
+
// NOTE: ref field is here to have reference point on the client
|
|
12
|
+
// for example when substituting existing images in the mobiledoc
|
|
13
|
+
// this field would serve as an identifier to find images to replace
|
|
14
|
+
// once the response is back. Think of it as ID on the client's side.
|
|
11
15
|
ref: frame.data.ref || null
|
|
12
16
|
}]
|
|
13
17
|
};
|
|
@@ -80,7 +80,7 @@ function createApiInstance(config) {
|
|
|
80
80
|
return `
|
|
81
81
|
Hey there!
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
Tap the link below to complete the signup process for ${siteTitle}, and be automatically signed in:
|
|
84
84
|
|
|
85
85
|
${url}
|
|
86
86
|
|
|
@@ -107,7 +107,7 @@ module.exports = ({siteTitle, email, url, accentColor = '#15212A', siteDomain, s
|
|
|
107
107
|
<div class="content" style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 600px; padding: 30px 20px;">
|
|
108
108
|
|
|
109
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;">
|
|
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
111
|
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 8px;">
|
|
112
112
|
|
|
113
113
|
<!-- START MAIN CONTENT AREA -->
|
|
@@ -117,7 +117,7 @@ module.exports = ({siteTitle, email, url, accentColor = '#15212A', siteDomain, s
|
|
|
117
117
|
<tr>
|
|
118
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
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;">Hey there!</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;">
|
|
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 complete the signup process for ${siteTitle}, and be automatically signed in:</p>
|
|
121
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
122
|
<tbody>
|
|
123
123
|
<tr>
|
|
@@ -4,6 +4,9 @@ const {Buffer} = require('buffer');
|
|
|
4
4
|
const {randomBytes} = require('crypto');
|
|
5
5
|
const {URL} = require('url');
|
|
6
6
|
|
|
7
|
+
const config = require('../../../shared/config');
|
|
8
|
+
const urlUtils = require('../../../shared/url-utils');
|
|
9
|
+
|
|
7
10
|
const messages = {
|
|
8
11
|
incorrectState: 'State did not match.'
|
|
9
12
|
};
|
|
@@ -24,6 +27,7 @@ const redirectURI = 'https://stripe.ghost.org';
|
|
|
24
27
|
* @returns {Promise<URL>}
|
|
25
28
|
*/
|
|
26
29
|
async function getStripeConnectOAuthUrl(setSessionProp, mode = 'live') {
|
|
30
|
+
checkCanConnect();
|
|
27
31
|
const randomState = randomBytes(16).toString('hex');
|
|
28
32
|
const state = Buffer.from(JSON.stringify({
|
|
29
33
|
mode,
|
|
@@ -71,6 +75,16 @@ async function getStripeConnectTokenData(encodedData, getSessionProp) {
|
|
|
71
75
|
};
|
|
72
76
|
}
|
|
73
77
|
|
|
78
|
+
function checkCanConnect() {
|
|
79
|
+
const siteUrl = urlUtils.getSiteUrl();
|
|
80
|
+
const productionMode = config.get('env') === 'production';
|
|
81
|
+
const siteUrlUsingSSL = /^https/.test(siteUrl);
|
|
82
|
+
const cannotConnectToStripe = productionMode && !siteUrlUsingSSL;
|
|
83
|
+
if (cannotConnectToStripe) {
|
|
84
|
+
throw new errors.BadRequestError('Cannot connect to stripe unless site is using https://');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
74
88
|
module.exports = {
|
|
75
89
|
getStripeConnectOAuthUrl,
|
|
76
90
|
getStripeConnectTokenData,
|
|
@@ -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%224.
|
|
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%224.21%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%22emberKeyboard%22%3A%7B%22disableInputsInitializer%22%3Atrue%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" />
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
<link rel="stylesheet" href="assets/vendor.min-987af30228885bce50f05c4723fe6f53.css">
|
|
44
|
-
<link rel="stylesheet" href="assets/ghost.min-
|
|
44
|
+
<link rel="stylesheet" href="assets/ghost.min-5abc69c04ad1d5301a857e01009b9c05.css" title="light">
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
<script src="assets/vendor.min-
|
|
63
|
-
<script src="assets/ghost.min-
|
|
62
|
+
<script src="assets/vendor.min-c6ef90bfd7eff256e10b85583bfe9a74.js"></script>
|
|
63
|
+
<script src="assets/ghost.min-6c546c322127ae6d1d1b0ddbf34be75b.js"></script>
|
|
64
64
|
|
|
65
65
|
</body>
|
|
66
66
|
</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%224.
|
|
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%224.21%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%22emberKeyboard%22%3A%7B%22disableInputsInitializer%22%3Atrue%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" />
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
<link rel="stylesheet" href="assets/vendor.min-987af30228885bce50f05c4723fe6f53.css">
|
|
44
|
-
<link rel="stylesheet" href="assets/ghost.min-
|
|
44
|
+
<link rel="stylesheet" href="assets/ghost.min-5abc69c04ad1d5301a857e01009b9c05.css" title="light">
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
<script src="assets/vendor.min-
|
|
63
|
-
<script src="assets/ghost.min-
|
|
62
|
+
<script src="assets/vendor.min-c6ef90bfd7eff256e10b85583bfe9a74.js"></script>
|
|
63
|
+
<script src="assets/ghost.min-6c546c322127ae6d1d1b0ddbf34be75b.js"></script>
|
|
64
64
|
|
|
65
65
|
</body>
|
|
66
66
|
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ghost",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.21.0",
|
|
4
4
|
"description": "The professional publishing platform",
|
|
5
5
|
"author": "Ghost Foundation",
|
|
6
6
|
"homepage": "https://ghost.org",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"fix": "yarn fix:client && yarn fix:server"
|
|
50
50
|
},
|
|
51
51
|
"engines": {
|
|
52
|
-
"node": "^12.22.1 || ^14.16.
|
|
52
|
+
"node": "^12.22.1 || ^14.17.0 || ^16.13.0",
|
|
53
53
|
"cli": "^1.17.0"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
@@ -64,22 +64,22 @@
|
|
|
64
64
|
"@tryghost/constants": "0.1.12",
|
|
65
65
|
"@tryghost/custom-theme-settings-service": "0.3.1",
|
|
66
66
|
"@tryghost/debug": "0.1.9",
|
|
67
|
-
"@tryghost/email-analytics-provider-mailgun": "1.0.
|
|
68
|
-
"@tryghost/email-analytics-service": "1.0.
|
|
67
|
+
"@tryghost/email-analytics-provider-mailgun": "1.0.5",
|
|
68
|
+
"@tryghost/email-analytics-service": "1.0.4",
|
|
69
69
|
"@tryghost/errors": "0.2.17",
|
|
70
70
|
"@tryghost/express-dynamic-redirects": "0.2.1",
|
|
71
71
|
"@tryghost/helpers": "1.1.52",
|
|
72
72
|
"@tryghost/image-transform": "1.0.17",
|
|
73
73
|
"@tryghost/job-manager": "0.8.11",
|
|
74
|
-
"@tryghost/kg-card-factory": "3.0
|
|
75
|
-
"@tryghost/kg-default-atoms": "3.
|
|
76
|
-
"@tryghost/kg-default-cards": "5.0
|
|
77
|
-
"@tryghost/kg-markdown-html-renderer": "5.0
|
|
78
|
-
"@tryghost/kg-mobiledoc-html-renderer": "5.
|
|
74
|
+
"@tryghost/kg-card-factory": "3.1.0",
|
|
75
|
+
"@tryghost/kg-default-atoms": "3.1.0",
|
|
76
|
+
"@tryghost/kg-default-cards": "5.1.0",
|
|
77
|
+
"@tryghost/kg-markdown-html-renderer": "5.1.0",
|
|
78
|
+
"@tryghost/kg-mobiledoc-html-renderer": "5.2.0",
|
|
79
79
|
"@tryghost/limit-service": "0.6.5",
|
|
80
|
-
"@tryghost/logging": "0.
|
|
80
|
+
"@tryghost/logging": "1.0.0",
|
|
81
81
|
"@tryghost/magic-link": "1.0.14",
|
|
82
|
-
"@tryghost/members-api": "2.
|
|
82
|
+
"@tryghost/members-api": "2.6.0",
|
|
83
83
|
"@tryghost/members-csv": "1.1.8",
|
|
84
84
|
"@tryghost/members-importer": "0.3.4",
|
|
85
85
|
"@tryghost/members-offers": "0.10.1",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"@tryghost/social-urls": "0.1.26",
|
|
97
97
|
"@tryghost/string": "0.1.20",
|
|
98
98
|
"@tryghost/tpl": "0.1.8",
|
|
99
|
-
"@tryghost/update-check-service": "0.2.
|
|
99
|
+
"@tryghost/update-check-service": "0.2.5",
|
|
100
100
|
"@tryghost/url-utils": "2.0.2",
|
|
101
101
|
"@tryghost/validator": "0.1.8",
|
|
102
102
|
"@tryghost/version": "0.1.7",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"bluebird": "3.7.2",
|
|
108
108
|
"body-parser": "1.19.0",
|
|
109
109
|
"bookshelf": "1.2.0",
|
|
110
|
-
"bookshelf-relations": "2.
|
|
110
|
+
"bookshelf-relations": "2.3.0",
|
|
111
111
|
"brute-knex": "4.0.1",
|
|
112
112
|
"bson-objectid": "2.0.1",
|
|
113
113
|
"bthreads": "0.5.1",
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"ghost-storage-base": "0.0.6",
|
|
129
129
|
"glob": "7.2.0",
|
|
130
130
|
"got": "9.6.0",
|
|
131
|
-
"gscan": "4.
|
|
131
|
+
"gscan": "4.10.0",
|
|
132
132
|
"html-to-text": "5.1.1",
|
|
133
133
|
"image-size": "1.0.0",
|
|
134
134
|
"intl": "1.2.5",
|
|
@@ -139,19 +139,19 @@
|
|
|
139
139
|
"juice": "8.0.0",
|
|
140
140
|
"keypair": "1.0.4",
|
|
141
141
|
"knex": "0.21.21",
|
|
142
|
-
"knex-migrator": "4.0
|
|
142
|
+
"knex-migrator": "4.1.0",
|
|
143
143
|
"lodash": "4.17.21",
|
|
144
144
|
"luxon": "2.0.2",
|
|
145
145
|
"mailgun-js": "0.22.0",
|
|
146
|
-
"metascraper": "5.
|
|
147
|
-
"metascraper-author": "5.
|
|
148
|
-
"metascraper-description": "5.
|
|
149
|
-
"metascraper-image": "5.
|
|
150
|
-
"metascraper-logo": "5.
|
|
151
|
-
"metascraper-logo-favicon": "5.
|
|
152
|
-
"metascraper-publisher": "5.
|
|
153
|
-
"metascraper-title": "5.
|
|
154
|
-
"metascraper-url": "5.
|
|
146
|
+
"metascraper": "5.25.0",
|
|
147
|
+
"metascraper-author": "5.25.0",
|
|
148
|
+
"metascraper-description": "5.25.0",
|
|
149
|
+
"metascraper-image": "5.25.0",
|
|
150
|
+
"metascraper-logo": "5.25.0",
|
|
151
|
+
"metascraper-logo-favicon": "5.25.0",
|
|
152
|
+
"metascraper-publisher": "5.25.0",
|
|
153
|
+
"metascraper-title": "5.25.0",
|
|
154
|
+
"metascraper-url": "5.25.0",
|
|
155
155
|
"moment": "2.24.0",
|
|
156
156
|
"moment-timezone": "0.5.23",
|
|
157
157
|
"multer": "1.4.3",
|