ghost 6.0.0-alpha.1 → 6.0.0-alpha.2
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-i18n-6.0.0-alpha.2.tgz +0 -0
- package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +2 -2
- package/core/built/admin/assets/admin-x-activitypub/{index-rDFm98Ub.mjs → index-BZDwG-OG.mjs} +21488 -15398
- package/core/built/admin/assets/admin-x-activitypub/{index-BhgdXgH_.mjs → index-DTlSQCGz.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{CodeEditorView-CA2VVtOE.mjs → CodeEditorView-CCUvrZhe.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +3 -3
- package/core/built/admin/assets/admin-x-settings/{index-KA2tjCkS.mjs → index-Cubs_8W6.mjs} +8085 -8529
- package/core/built/admin/assets/admin-x-settings/{index-Dl3F40x5.mjs → index-D0ejKdD5.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{modals-B0zezufO.mjs → modals-DSxs9dLy.mjs} +8385 -8331
- package/core/built/admin/assets/{chunk.524.996c1c4d269fa6a50e90.js → chunk.524.0953dd72ae1efbabe0de.js} +6 -6
- package/core/built/admin/assets/{chunk.582.75cf44e5d1b925adf16d.js → chunk.582.3caa825c2a91efc48f1d.js} +9 -9
- package/core/built/admin/assets/{ghost-5a5b2112df68dfaf6813ce38cad16847.js → ghost-db0f84981913aec8a672c57aa22da07a.js} +58 -69
- package/core/built/admin/assets/posts/posts.js +21585 -21648
- package/core/built/admin/assets/stats/stats.js +21539 -21598
- package/core/built/admin/assets/{vendor-c89102f24c3d9502e9db741509767580.js → vendor-aed0068cf9b67d042dd23a6343545b7b.js} +1 -1
- package/core/built/admin/index.html +4 -4
- package/core/frontend/web/middleware/frontend-caching.js +6 -1
- package/core/frontend/web/middleware/static-theme.js +21 -4
- package/core/server/data/tinybird/ARCHITECTURE.md +0 -4
- package/core/server/data/tinybird/DOCS.md +0 -4
- package/core/server/services/explore-ping/ExplorePingService.js +3 -1
- package/core/server/services/members/members-api/controllers/RouterController.js +28 -10
- package/core/server/services/members/members-api/utils/normalize-email.js +31 -0
- package/core/server/services/public-config/config.js +0 -1
- package/core/server/services/settings/SettingsBREADService.js +5 -1
- package/core/server/services/settings/settings-service.js +3 -1
- package/core/server/services/settings-helpers/SettingsHelpers.js +0 -12
- package/core/server/web/admin/app.js +5 -6
- package/core/server/web/shared/middleware/cache-control.js +2 -1
- package/core/shared/config/defaults.json +6 -0
- package/core/shared/labs.js +0 -3
- package/package.json +10 -10
- package/tsconfig.tsbuildinfo +1 -1
- package/yarn.lock +140 -95
- package/components/tryghost-i18n-6.0.0-alpha.1.tgz +0 -0
- package/core/built/admin/assets/img/twitter-7a7a0ba12d9b5bfb8a2058764a827c31.svg +0 -4
|
@@ -9553,4 +9553,4 @@ e.default=class{constructor(e){if(this._data=new t.default,e)for(let t=0;t<e.len
|
|
|
9553
9553
|
return this}get(e){let t=this._data[e]
|
|
9554
9554
|
return t===r.UNDEFINED_KEY?void 0:t}set(e,t){return this._data[e]=t,this}delete(e){return this._data[e]=r.UNDEFINED_KEY,!0}}})
|
|
9555
9555
|
|
|
9556
|
-
//# sourceMappingURL=vendor-
|
|
9556
|
+
//# sourceMappingURL=vendor-326b46cbc2845d47f1e0af43ba21caec.map
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Ghost</title>
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22cdnUrl%22%3A%22%22%2C%22editorUrl%22%3A%22%22%2C%22rootURL%22%3A%22%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%226.0%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%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%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%2237bd1e3e4d%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%
|
|
9
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22cdnUrl%22%3A%22%22%2C%22editorUrl%22%3A%22%22%2C%22rootURL%22%3A%22%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%226.0%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%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%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%2237bd1e3e4d%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%22d033b6dcc1%22%2C%22adminXActivitypubFilename%22%3A%22admin-x-activitypub.js%22%2C%22adminXActivitypubHash%22%3A%2235ce5f3180%22%2C%22postsFilename%22%3A%22posts.js%22%2C%22postsHash%22%3A%22be6ec893bf%22%2C%22statsFilename%22%3A%22stats.js%22%2C%22statsHash%22%3A%225ddc4bf3f4%22%2C%22adminXActivitypubCustomUrl%22%3A%22https%3A%2F%2Fcdn.jsdelivr.net%2Fghost%2Fadmin-x-activitypub%400%2Fdist%2Fadmin-x-activitypub.js%22%7D" />
|
|
10
10
|
|
|
11
11
|
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1, minimal-ui, viewport-fit=cover" />
|
|
12
12
|
<meta name="pinterest" content="nopin" />
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
|
|
48
48
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
49
49
|
|
|
50
|
-
<script src="assets/vendor-
|
|
50
|
+
<script src="assets/vendor-aed0068cf9b67d042dd23a6343545b7b.js"></script>
|
|
51
51
|
<script src="assets/chunk.728.985c45ad584b4b91ca60.js"></script>
|
|
52
|
-
<script src="assets/chunk.524.
|
|
53
|
-
<script src="assets/ghost-
|
|
52
|
+
<script src="assets/chunk.524.0953dd72ae1efbabe0de.js"></script>
|
|
53
|
+
<script src="assets/ghost-db0f84981913aec8a672c57aa22da07a.js"></script>
|
|
54
54
|
</body>
|
|
55
55
|
</html>
|
|
@@ -51,6 +51,11 @@ const getMiddleware = async (getFreeTier = async () => {
|
|
|
51
51
|
return shared.middleware.cacheControl('private')(req, res, next);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
// CASE: Never cache preview routes
|
|
55
|
+
if (req.path?.startsWith('/p/')) {
|
|
56
|
+
return shared.middleware.cacheControl('noCache')(req, res, next);
|
|
57
|
+
}
|
|
58
|
+
|
|
54
59
|
// CASE: Cache member's content if this feature is enabled
|
|
55
60
|
if (req.member && shouldCacheMembersContent) {
|
|
56
61
|
// Set the 'cache-control' header to 'public'
|
|
@@ -74,4 +79,4 @@ const getMiddleware = async (getFreeTier = async () => {
|
|
|
74
79
|
module.exports = {
|
|
75
80
|
getMiddleware,
|
|
76
81
|
calculateMemberTier // exported for testing
|
|
77
|
-
};
|
|
82
|
+
};
|
|
@@ -59,18 +59,35 @@ function forwardToExpressStatic(req, res, next) {
|
|
|
59
59
|
return next();
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
// We allow robots.txt to fall through to the next middleware, so that we can return our default robots.txt
|
|
63
|
+
// We also allow sitemap.xml and sitemap-:resource.xml to fall through so that we can serve our defaults if they're not found in the theme
|
|
64
|
+
const fallthroughFiles = [
|
|
65
|
+
'/robots.txt',
|
|
66
|
+
'/sitemap.xml',
|
|
67
|
+
'/sitemap-posts.xml',
|
|
68
|
+
'/sitemap-pages.xml',
|
|
69
|
+
'/sitemap-tags.xml',
|
|
70
|
+
'/sitemap-authors.xml',
|
|
71
|
+
'/sitemap-users.xml',
|
|
72
|
+
'/sitemap.xsl'
|
|
73
|
+
];
|
|
74
|
+
const fallthrough = fallthroughFiles.includes(req.path) ? true : false;
|
|
63
75
|
|
|
64
|
-
// @NOTE: the maxAge config passed below are in milliseconds and the config
|
|
65
|
-
// is specified in seconds. See https://github.com/expressjs/serve-static/issues/150 for more context
|
|
66
76
|
express.static(themeEngine.getActive().path, {
|
|
67
|
-
|
|
77
|
+
// @NOTE: the maxAge config passed below are in milliseconds and the config
|
|
78
|
+
// is specified in seconds. See https://github.com/expressjs/serve-static/issues/150 for more context
|
|
79
|
+
maxAge: config.get('caching:theme:maxAge') * 1000,
|
|
80
|
+
fallthrough
|
|
68
81
|
}
|
|
69
82
|
)(req, res, next);
|
|
70
83
|
}
|
|
71
84
|
|
|
72
85
|
function staticTheme() {
|
|
73
86
|
return function denyStatic(req, res, next) {
|
|
87
|
+
if (!path.extname(req.path)) {
|
|
88
|
+
return next();
|
|
89
|
+
}
|
|
90
|
+
|
|
74
91
|
if (!isAllowedFile(req.path.toLowerCase()) && isDeniedFile(req.path.toLowerCase())) {
|
|
75
92
|
return next();
|
|
76
93
|
}
|
|
@@ -303,10 +303,6 @@ Click Tracking → redirects → members_click_events
|
|
|
303
303
|
2. MySQL tracks member signup with attribution
|
|
304
304
|
3. MySQL tracks paid conversion with attribution
|
|
305
305
|
|
|
306
|
-
## API Endpoints
|
|
307
|
-
|
|
308
|
-
All endpoints require authentication (`mw.authAdminApi`) and are gated behind `labs.isSet('trafficAnalytics')`.
|
|
309
|
-
|
|
310
306
|
### Core Stats Endpoints
|
|
311
307
|
|
|
312
308
|
```javascript
|
|
@@ -311,10 +311,6 @@ Click Tracking → redirects → members_click_events
|
|
|
311
311
|
3. MySQL tracks paid conversion with attribution
|
|
312
312
|
4. Combined view shows complete funnel
|
|
313
313
|
|
|
314
|
-
## API Endpoints
|
|
315
|
-
|
|
316
|
-
All endpoints require authentication (`mw.authAdminApi`) and are gated behind `labs.isSet('trafficAnalytics')`.
|
|
317
|
-
|
|
318
314
|
### Core Stats Endpoints
|
|
319
315
|
|
|
320
316
|
```javascript
|
|
@@ -33,7 +33,9 @@ module.exports = class ExplorePingService {
|
|
|
33
33
|
ghost: this.ghostVersion.full,
|
|
34
34
|
site_uuid: this.settingsCache.get('site_uuid'),
|
|
35
35
|
url: this.config.get('url'),
|
|
36
|
-
theme: this.settingsCache.get('active_theme')
|
|
36
|
+
theme: this.settingsCache.get('active_theme'),
|
|
37
|
+
facebook: this.settingsCache.get('facebook'),
|
|
38
|
+
twitter: this.settingsCache.get('twitter')
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
try {
|
|
@@ -4,6 +4,7 @@ const sanitizeHtml = require('sanitize-html');
|
|
|
4
4
|
const {BadRequestError, NoPermissionError, UnauthorizedError, DisabledFeatureError, NotFoundError} = require('@tryghost/errors');
|
|
5
5
|
const errors = require('@tryghost/errors');
|
|
6
6
|
const {isEmail} = require('@tryghost/validator');
|
|
7
|
+
const normalizeEmail = require('../utils/normalize-email');
|
|
7
8
|
|
|
8
9
|
const messages = {
|
|
9
10
|
emailRequired: 'Email is required.',
|
|
@@ -585,6 +586,23 @@ module.exports = class RouterController {
|
|
|
585
586
|
});
|
|
586
587
|
}
|
|
587
588
|
|
|
589
|
+
// Normalize email to prevent homograph attacks
|
|
590
|
+
let normalizedEmail;
|
|
591
|
+
|
|
592
|
+
try {
|
|
593
|
+
normalizedEmail = normalizeEmail(email);
|
|
594
|
+
|
|
595
|
+
if (normalizedEmail !== email) {
|
|
596
|
+
logging.info(`Email normalized from ${email} to ${normalizedEmail} for magic link`);
|
|
597
|
+
}
|
|
598
|
+
} catch (err) {
|
|
599
|
+
logging.error(`Failed to normalize [${email}]: ${err.message}`);
|
|
600
|
+
|
|
601
|
+
throw new errors.BadRequestError({
|
|
602
|
+
message: tpl(messages.invalidEmail)
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
|
|
588
606
|
if (honeypot) {
|
|
589
607
|
logging.warn('Honeypot field filled, this is likely a bot');
|
|
590
608
|
|
|
@@ -606,9 +624,9 @@ module.exports = class RouterController {
|
|
|
606
624
|
|
|
607
625
|
try {
|
|
608
626
|
if (emailType === 'signup' || emailType === 'subscribe') {
|
|
609
|
-
await this._handleSignup(req, referrer);
|
|
627
|
+
await this._handleSignup(req, normalizedEmail, referrer);
|
|
610
628
|
} else {
|
|
611
|
-
await this._handleSignin(req, referrer);
|
|
629
|
+
await this._handleSignin(req, normalizedEmail, referrer);
|
|
612
630
|
}
|
|
613
631
|
|
|
614
632
|
res.writeHead(201);
|
|
@@ -626,7 +644,7 @@ module.exports = class RouterController {
|
|
|
626
644
|
}
|
|
627
645
|
}
|
|
628
646
|
|
|
629
|
-
async _handleSignup(req, referrer = null) {
|
|
647
|
+
async _handleSignup(req, normalizedEmail, referrer = null) {
|
|
630
648
|
if (!this._allowSelfSignup()) {
|
|
631
649
|
if (this._settingsCache.get('members_signup_access') === 'paid') {
|
|
632
650
|
throw new errors.BadRequestError({
|
|
@@ -640,14 +658,14 @@ module.exports = class RouterController {
|
|
|
640
658
|
}
|
|
641
659
|
|
|
642
660
|
const blockedEmailDomains = this._settingsCache.get('all_blocked_email_domains');
|
|
643
|
-
const emailDomain =
|
|
661
|
+
const emailDomain = normalizedEmail.split('@')[1]?.toLowerCase();
|
|
644
662
|
if (emailDomain && blockedEmailDomains.includes(emailDomain)) {
|
|
645
663
|
throw new errors.BadRequestError({
|
|
646
664
|
message: tpl(messages.blockedEmailDomain)
|
|
647
665
|
});
|
|
648
666
|
}
|
|
649
667
|
|
|
650
|
-
const {
|
|
668
|
+
const {emailType} = req.body;
|
|
651
669
|
|
|
652
670
|
const tokenData = {
|
|
653
671
|
labels: req.body.labels,
|
|
@@ -657,13 +675,13 @@ module.exports = class RouterController {
|
|
|
657
675
|
attribution: await this._memberAttributionService.getAttribution(req.body.urlHistory)
|
|
658
676
|
};
|
|
659
677
|
|
|
660
|
-
return await this._sendEmailWithMagicLink({email, tokenData, requestedType: emailType, referrer});
|
|
678
|
+
return await this._sendEmailWithMagicLink({email: normalizedEmail, tokenData, requestedType: emailType, referrer});
|
|
661
679
|
}
|
|
662
680
|
|
|
663
|
-
async _handleSignin(req, referrer = null) {
|
|
664
|
-
const {
|
|
681
|
+
async _handleSignin(req, normalizedEmail, referrer = null) {
|
|
682
|
+
const {emailType} = req.body;
|
|
665
683
|
|
|
666
|
-
const member = await this._memberRepository.get({email});
|
|
684
|
+
const member = await this._memberRepository.get({email: normalizedEmail});
|
|
667
685
|
|
|
668
686
|
if (!member) {
|
|
669
687
|
throw new errors.BadRequestError({
|
|
@@ -672,7 +690,7 @@ module.exports = class RouterController {
|
|
|
672
690
|
}
|
|
673
691
|
|
|
674
692
|
const tokenData = {};
|
|
675
|
-
return await this._sendEmailWithMagicLink({email, tokenData, requestedType: emailType, referrer});
|
|
693
|
+
return await this._sendEmailWithMagicLink({email: normalizedEmail, tokenData, requestedType: emailType, referrer});
|
|
676
694
|
}
|
|
677
695
|
|
|
678
696
|
/**
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const punycode = require('punycode/');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalizes email addresses by converting Unicode domains to ASCII (punycode)
|
|
5
|
+
* This prevents homograph attacks where Unicode characters are used to spoof
|
|
6
|
+
* domains
|
|
7
|
+
*
|
|
8
|
+
* @param {string} email The email address to normalize
|
|
9
|
+
* @returns {string} The normalized email address
|
|
10
|
+
* @throws {Error} When punycode conversion fails
|
|
11
|
+
*/
|
|
12
|
+
function normalizeEmail(email) {
|
|
13
|
+
if (!email || typeof email !== 'string') {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const atIndex = email.lastIndexOf('@');
|
|
18
|
+
|
|
19
|
+
if (atIndex === -1) {
|
|
20
|
+
return email;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const localPart = email.substring(0, atIndex);
|
|
24
|
+
const domainPart = email.substring(atIndex + 1);
|
|
25
|
+
|
|
26
|
+
const asciiDomain = punycode.toASCII(domainPart);
|
|
27
|
+
|
|
28
|
+
return `${localPart}@${asciiDomain}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = normalizeEmail;
|
|
@@ -29,7 +29,6 @@ module.exports = function getConfigProperties() {
|
|
|
29
29
|
configProperties.exploreTestimonialsUrl = config.get('explore:testimonials_url');
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
// WIP tinybird stats feature - it's entirely config driven instead of using an alpha flag for now
|
|
33
32
|
if (config.get('tinybird') && config.get('tinybird:stats')) {
|
|
34
33
|
const statsConfig = config.get('tinybird:stats');
|
|
35
34
|
const siteUuid = statsConfig.id || settingsCache.get('site_uuid');
|
|
@@ -24,12 +24,14 @@ class SettingsBREADService {
|
|
|
24
24
|
* @param {Object} options.singleUseTokenProvider
|
|
25
25
|
* @param {Object} options.urlUtils
|
|
26
26
|
* @param {Object} options.labsService - labs service instance
|
|
27
|
+
* @param {Object} options.limitsService - limits service instance
|
|
27
28
|
* @param {{service: Object}} options.emailAddressService
|
|
28
29
|
*/
|
|
29
|
-
constructor({SettingsModel, settingsCache, labsService, mail, singleUseTokenProvider, urlUtils, emailAddressService}) {
|
|
30
|
+
constructor({SettingsModel, settingsCache, labsService, limitsService, mail, singleUseTokenProvider, urlUtils, emailAddressService}) {
|
|
30
31
|
this.SettingsModel = SettingsModel;
|
|
31
32
|
this.settingsCache = settingsCache;
|
|
32
33
|
this.labs = labsService;
|
|
34
|
+
this.limitsService = limitsService;
|
|
33
35
|
this.emailAddressService = emailAddressService;
|
|
34
36
|
|
|
35
37
|
/* email verification setup */
|
|
@@ -194,6 +196,8 @@ class SettingsBREADService {
|
|
|
194
196
|
}
|
|
195
197
|
|
|
196
198
|
if (stripeConnectData) {
|
|
199
|
+
await this.limitsService.errorIfWouldGoOverLimit('limitStripeConnect');
|
|
200
|
+
|
|
197
201
|
filteredSettings.push({
|
|
198
202
|
key: 'stripe_connect_publishable_key',
|
|
199
203
|
value: stripeConnectData.public_key
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
const events = require('../../lib/common/events');
|
|
6
6
|
const models = require('../../models');
|
|
7
7
|
const labs = require('../../../shared/labs');
|
|
8
|
+
const limits = require('../limits');
|
|
8
9
|
const config = require('../../../shared/config');
|
|
9
10
|
const adapterManager = require('../adapter-manager');
|
|
10
11
|
const SettingsCache = require('../../../shared/settings-cache');
|
|
@@ -30,6 +31,7 @@ const getSettingsBREADServiceInstance = () => {
|
|
|
30
31
|
SettingsModel: models.Settings,
|
|
31
32
|
settingsCache: SettingsCache,
|
|
32
33
|
labsService: labs,
|
|
34
|
+
limitsService: limits,
|
|
33
35
|
mail,
|
|
34
36
|
singleUseTokenProvider: new SingleUseTokenProvider({
|
|
35
37
|
SingleUseTokenModel: models.SingleUseToken,
|
|
@@ -110,7 +112,7 @@ module.exports = {
|
|
|
110
112
|
fields.push(new CalculatedField({key: 'social_web_enabled', type: 'boolean', group: 'social_web', fn: settingsHelpers.isSocialWebEnabled.bind(settingsHelpers), dependents: ['social_web', 'labs']}));
|
|
111
113
|
|
|
112
114
|
// Web analytics
|
|
113
|
-
fields.push(new CalculatedField({key: 'web_analytics_enabled', type: 'boolean', group: 'analytics', fn: settingsHelpers.isWebAnalyticsEnabled.bind(settingsHelpers), dependents: ['web_analytics'
|
|
115
|
+
fields.push(new CalculatedField({key: 'web_analytics_enabled', type: 'boolean', group: 'analytics', fn: settingsHelpers.isWebAnalyticsEnabled.bind(settingsHelpers), dependents: ['web_analytics']}));
|
|
114
116
|
fields.push(new CalculatedField({key: 'web_analytics_configured', type: 'boolean', group: 'analytics', fn: settingsHelpers.isWebAnalyticsConfigured.bind(settingsHelpers), dependents: ['web_analytics']}));
|
|
115
117
|
|
|
116
118
|
return fields;
|
|
@@ -234,12 +234,6 @@ class SettingsHelpers {
|
|
|
234
234
|
return false;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
// Labs setting
|
|
238
|
-
if (!this.labs.isSet('ActivityPub')) {
|
|
239
|
-
debug('Social web is disabled in labs');
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
237
|
// Ghost (Pro) limits
|
|
244
238
|
if (this.limitService.isDisabled('limitSocialWeb')) {
|
|
245
239
|
debug('Social web is not available for Ghost (Pro) sites without a custom domain, or hosted on a subdirectory');
|
|
@@ -279,12 +273,6 @@ class SettingsHelpers {
|
|
|
279
273
|
return false;
|
|
280
274
|
}
|
|
281
275
|
|
|
282
|
-
// Labs setting
|
|
283
|
-
if (!this.labs.isSet('trafficAnalytics')) {
|
|
284
|
-
debug('Web analytics is disabled in labs');
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
276
|
// Check if web analytics can be configured (limit service and required config)
|
|
289
277
|
if (!this.isWebAnalyticsConfigured()) {
|
|
290
278
|
return false;
|
|
@@ -19,16 +19,15 @@ module.exports = function setupAdminApp() {
|
|
|
19
19
|
const adminApp = express('admin');
|
|
20
20
|
|
|
21
21
|
// Admin assets
|
|
22
|
-
// @TODO ensure this gets a local 404 error handler
|
|
23
|
-
const configMaxAge = config.get('caching:admin:maxAge');
|
|
24
22
|
// @NOTE: when we start working on HTTP/3 optimizations the immutable headers
|
|
25
23
|
// produced below should be split into separate 'Cache-Control' entry.
|
|
26
24
|
// For reference see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#validation_2
|
|
27
|
-
|
|
28
|
-
// is specified in seconds. See https://github.com/expressjs/serve-static/issues/150 for more context
|
|
25
|
+
|
|
29
26
|
adminApp.use('/assets', serveStatic(
|
|
30
27
|
path.join(config.get('paths').adminAssets, 'assets'), {
|
|
31
|
-
|
|
28
|
+
// @NOTE: the maxAge config passed below are in milliseconds and the config
|
|
29
|
+
// is specified in seconds. See https://github.com/expressjs/serve-static/issues/150 for more context
|
|
30
|
+
maxAge: config.get('caching:admin:maxAge') * 1000,
|
|
32
31
|
immutable: true,
|
|
33
32
|
fallthrough: false
|
|
34
33
|
}
|
|
@@ -94,4 +93,4 @@ module.exports = function setupAdminApp() {
|
|
|
94
93
|
debug('Admin setup end');
|
|
95
94
|
|
|
96
95
|
return adminApp;
|
|
97
|
-
};
|
|
96
|
+
};
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
const isString = require('lodash/isString');
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* @param {'public'|'private'} profile Use "private" if you do not want caching
|
|
12
|
+
* @param {'public'|'private'|'noCache'} profile Use "private" if you do not want caching
|
|
13
13
|
* @param {object} [options]
|
|
14
14
|
* @param {number} [options.maxAge] The max-age in seconds to use when profile is "public"
|
|
15
15
|
* @param {number} [options.staleWhileRevalidate] The stale-while-revalidate in seconds to use when profile is "public"
|
|
@@ -24,6 +24,7 @@ const cacheControl = (profile, options = {maxAge: 0}) => {
|
|
|
24
24
|
|
|
25
25
|
const profiles = {
|
|
26
26
|
public: publicOptions.filter(option => option).join(', '),
|
|
27
|
+
noCache: 'no-cache, max-age=0, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0',
|
|
27
28
|
private: 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'
|
|
28
29
|
};
|
|
29
30
|
|
package/core/shared/labs.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ghost",
|
|
3
|
-
"version": "6.0.0-alpha.
|
|
3
|
+
"version": "6.0.0-alpha.2",
|
|
4
4
|
"description": "The professional publishing platform",
|
|
5
5
|
"author": "Ghost Foundation",
|
|
6
6
|
"homepage": "https://ghost.org",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"@tryghost/helpers": "1.1.97",
|
|
87
87
|
"@tryghost/html-to-plaintext": "1.0.4",
|
|
88
88
|
"@tryghost/http-cache-utils": "0.1.20",
|
|
89
|
-
"@tryghost/i18n": "file:components/tryghost-i18n-6.0.0-alpha.
|
|
89
|
+
"@tryghost/i18n": "file:components/tryghost-i18n-6.0.0-alpha.2.tgz",
|
|
90
90
|
"@tryghost/image-transform": "1.4.6",
|
|
91
91
|
"@tryghost/job-manager": "1.0.3",
|
|
92
92
|
"@tryghost/kg-card-factory": "5.1.2",
|
|
@@ -135,9 +135,9 @@
|
|
|
135
135
|
"clsx": "2.1.1",
|
|
136
136
|
"cluster-key-slot": "1.1.2",
|
|
137
137
|
"common-tags": "1.8.2",
|
|
138
|
-
"compression": "1.8.
|
|
138
|
+
"compression": "1.8.1",
|
|
139
139
|
"connect-slashes": "1.4.0",
|
|
140
|
-
"cookie-session": "2.1.
|
|
140
|
+
"cookie-session": "2.1.1",
|
|
141
141
|
"cookies": "0.9.1",
|
|
142
142
|
"cors": "2.8.5",
|
|
143
143
|
"countries-and-timezones": "3.8.0",
|
|
@@ -154,9 +154,9 @@
|
|
|
154
154
|
"express-lazy-router": "1.0.6",
|
|
155
155
|
"express-query-boolean": "2.0.0",
|
|
156
156
|
"express-queue": "0.0.13",
|
|
157
|
-
"express-session": "1.18.
|
|
157
|
+
"express-session": "1.18.2",
|
|
158
158
|
"file-type": "16.5.4",
|
|
159
|
-
"form-data": "4.0.
|
|
159
|
+
"form-data": "4.0.4",
|
|
160
160
|
"fs-extra": "11.3.0",
|
|
161
161
|
"ghost-storage-base": "1.0.0",
|
|
162
162
|
"glob": "8.1.0",
|
|
@@ -195,7 +195,7 @@
|
|
|
195
195
|
"mime-types": "2.1.35",
|
|
196
196
|
"moment": "2.24.0",
|
|
197
197
|
"moment-timezone": "0.5.45",
|
|
198
|
-
"multer": "2.0.
|
|
198
|
+
"multer": "2.0.2",
|
|
199
199
|
"mysql2": "3.14.2",
|
|
200
200
|
"nconf": "0.13.0",
|
|
201
201
|
"node-fetch": "2.7.0",
|
|
@@ -232,7 +232,7 @@
|
|
|
232
232
|
"@types/bookshelf": "1.2.9",
|
|
233
233
|
"@types/common-tags": "1.8.4",
|
|
234
234
|
"@types/jsonwebtoken": "9.0.10",
|
|
235
|
-
"@types/node": "22.16.
|
|
235
|
+
"@types/node": "22.16.5",
|
|
236
236
|
"@types/node-jose": "1.1.13",
|
|
237
237
|
"@types/nodemailer": "6.4.17",
|
|
238
238
|
"@types/sinon": "17.0.4",
|
|
@@ -244,7 +244,7 @@
|
|
|
244
244
|
"detect-newline": "3.1.0",
|
|
245
245
|
"expect": "29.7.0",
|
|
246
246
|
"find-root": "1.1.0",
|
|
247
|
-
"form-data": "4.0.
|
|
247
|
+
"form-data": "4.0.4",
|
|
248
248
|
"html-minifier": "4.0.0",
|
|
249
249
|
"html-validate": "8.29.0",
|
|
250
250
|
"inquirer": "8.2.6",
|
|
@@ -273,7 +273,7 @@
|
|
|
273
273
|
"jackspeak": "2.3.6",
|
|
274
274
|
"moment": "2.24.0",
|
|
275
275
|
"moment-timezone": "0.5.45",
|
|
276
|
-
"@tryghost/i18n": "file:components/tryghost-i18n-6.0.0-alpha.
|
|
276
|
+
"@tryghost/i18n": "file:components/tryghost-i18n-6.0.0-alpha.2.tgz"
|
|
277
277
|
},
|
|
278
278
|
"nx": {
|
|
279
279
|
"targets": {
|