ghost 5.32.0 → 5.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/{tryghost-adapter-manager-5.32.0.tgz → tryghost-adapter-manager-5.33.0.tgz} +0 -0
- package/components/{tryghost-api-framework-5.32.0.tgz → tryghost-api-framework-5.33.0.tgz} +0 -0
- package/components/{tryghost-api-version-compatibility-service-5.32.0.tgz → tryghost-api-version-compatibility-service-5.33.0.tgz} +0 -0
- package/components/tryghost-audience-feedback-5.33.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.33.0.tgz +0 -0
- package/components/tryghost-constants-5.33.0.tgz +0 -0
- package/components/{tryghost-custom-theme-settings-service-5.32.0.tgz → tryghost-custom-theme-settings-service-5.33.0.tgz} +0 -0
- package/components/tryghost-data-generator-5.33.0.tgz +0 -0
- package/components/{tryghost-domain-events-5.32.0.tgz → tryghost-domain-events-5.33.0.tgz} +0 -0
- package/components/tryghost-dynamic-routing-events-5.33.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.33.0.tgz +0 -0
- package/components/{tryghost-email-analytics-service-5.32.0.tgz → tryghost-email-analytics-service-5.33.0.tgz} +0 -0
- package/components/tryghost-email-content-generator-5.33.0.tgz +0 -0
- package/components/tryghost-email-events-5.33.0.tgz +0 -0
- package/components/tryghost-email-service-5.33.0.tgz +0 -0
- package/components/{tryghost-email-suppression-list-5.32.0.tgz → tryghost-email-suppression-list-5.33.0.tgz} +0 -0
- package/components/{tryghost-express-dynamic-redirects-5.32.0.tgz → tryghost-express-dynamic-redirects-5.33.0.tgz} +0 -0
- package/components/{tryghost-extract-api-key-5.32.0.tgz → tryghost-extract-api-key-5.33.0.tgz} +0 -0
- package/components/tryghost-html-to-plaintext-5.33.0.tgz +0 -0
- package/components/tryghost-i18n-5.33.0.tgz +0 -0
- package/components/{tryghost-importer-revue-5.32.0.tgz → tryghost-importer-revue-5.33.0.tgz} +0 -0
- package/components/{tryghost-job-manager-5.32.0.tgz → tryghost-job-manager-5.33.0.tgz} +0 -0
- package/components/{tryghost-link-redirects-5.32.0.tgz → tryghost-link-redirects-5.33.0.tgz} +0 -0
- package/components/tryghost-link-replacer-5.33.0.tgz +0 -0
- package/components/{tryghost-link-tracking-5.32.0.tgz → tryghost-link-tracking-5.33.0.tgz} +0 -0
- package/components/{tryghost-magic-link-5.32.0.tgz → tryghost-magic-link-5.33.0.tgz} +0 -0
- package/components/tryghost-mailgun-client-5.33.0.tgz +0 -0
- package/components/{tryghost-member-attribution-5.32.0.tgz → tryghost-member-attribution-5.33.0.tgz} +0 -0
- package/components/tryghost-member-events-5.33.0.tgz +0 -0
- package/components/{tryghost-members-api-5.32.0.tgz → tryghost-members-api-5.33.0.tgz} +0 -0
- package/components/{tryghost-members-csv-5.32.0.tgz → tryghost-members-csv-5.33.0.tgz} +0 -0
- package/components/{tryghost-members-events-service-5.32.0.tgz → tryghost-members-events-service-5.33.0.tgz} +0 -0
- package/components/{tryghost-members-importer-5.32.0.tgz → tryghost-members-importer-5.33.0.tgz} +0 -0
- package/components/{tryghost-members-offers-5.32.0.tgz → tryghost-members-offers-5.33.0.tgz} +0 -0
- package/components/{tryghost-members-payments-5.32.0.tgz → tryghost-members-payments-5.33.0.tgz} +0 -0
- package/components/{tryghost-members-ssr-5.32.0.tgz → tryghost-members-ssr-5.33.0.tgz} +0 -0
- package/components/tryghost-members-stripe-service-5.33.0.tgz +0 -0
- package/components/{tryghost-minifier-5.32.0.tgz → tryghost-minifier-5.33.0.tgz} +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.33.0.tgz +0 -0
- package/components/{tryghost-mw-cache-control-5.32.0.tgz → tryghost-mw-cache-control-5.33.0.tgz} +0 -0
- package/components/{tryghost-mw-error-handler-5.32.0.tgz → tryghost-mw-error-handler-5.33.0.tgz} +0 -0
- package/components/tryghost-mw-session-from-token-5.33.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.33.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.33.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.33.0.tgz +0 -0
- package/components/{tryghost-package-json-5.32.0.tgz → tryghost-package-json-5.33.0.tgz} +0 -0
- package/components/tryghost-referrers-5.33.0.tgz +0 -0
- package/components/{tryghost-security-5.32.0.tgz → tryghost-security-5.33.0.tgz} +0 -0
- package/components/tryghost-session-service-5.33.0.tgz +0 -0
- package/components/{tryghost-settings-path-manager-5.32.0.tgz → tryghost-settings-path-manager-5.33.0.tgz} +0 -0
- package/components/tryghost-staff-service-5.33.0.tgz +0 -0
- package/components/{tryghost-stats-service-5.32.0.tgz → tryghost-stats-service-5.33.0.tgz} +0 -0
- package/components/{tryghost-tiers-5.32.0.tgz → tryghost-tiers-5.33.0.tgz} +0 -0
- package/components/{tryghost-update-check-service-5.32.0.tgz → tryghost-update-check-service-5.33.0.tgz} +0 -0
- package/components/{tryghost-verification-trigger-5.32.0.tgz → tryghost-verification-trigger-5.33.0.tgz} +0 -0
- package/components/{tryghost-version-notifications-data-service-5.32.0.tgz → tryghost-version-notifications-data-service-5.33.0.tgz} +0 -0
- package/components/tryghost-webmentions-5.33.0.tgz +0 -0
- package/core/built/admin/assets/{chunk.143.ad05239cc363254caed7.js → chunk.143.e158db597c343f4f132e.js} +23 -23
- package/core/built/admin/assets/{chunk.178.5260f900f09f859bf8ed.js → chunk.178.eaeb178bc4597cb87699.js} +4 -4
- package/core/built/admin/assets/{chunk.507.aa38cb08f25bae1d4e76.js → chunk.79.c3c2c05ea7ff7707fcad.js} +173 -167
- package/core/built/admin/assets/{chunk.47.f231a64fe3fbaba23b84.js → chunk.963.e47ead5abeca4cf69fed.js} +1003 -1009
- package/core/built/admin/assets/{chunk.47.f231a64fe3fbaba23b84.js.LICENSE.txt → chunk.963.e47ead5abeca4cf69fed.js.LICENSE.txt} +0 -0
- package/core/built/admin/assets/{ghost-4571ad33bd158ced8a9f5c13598fdb7f.js → ghost-86f1db1d00d5f0bc4f0cbdb39ff6c977.js} +135 -119
- package/core/built/admin/assets/{ghost-dark-fb05eb50e216469c5626356731afa42f.css → ghost-dark-ac0cb221eddc8652a0e7c263ed6513dc.css} +1 -1
- package/core/built/admin/assets/{ghost-721a7adc4ca642c88e4ac85e1cb8b385.css → ghost-fb9fb8adbcaf1603ad4006dd2d49e401.css} +1 -1
- package/core/built/admin/assets/img/stripe-3af79c77413d7e58d0322c1de5bfdb8e.svg +1 -0
- package/core/built/admin/index.html +5 -5
- package/core/cli/generate-data.js +3 -1
- package/core/frontend/services/sitemap/base-generator.js +16 -1
- package/core/frontend/services/sitemap/manager.js +6 -0
- package/core/server/services/email-analytics/jobs/index.js +3 -3
- package/core/server/services/email-service/wrapper.js +3 -1
- package/core/server/services/mentions/BookshelfMentionRepository.js +8 -0
- package/core/server/services/mentions/ResourceService.js +45 -0
- package/core/server/services/mentions/RoutingService.js +78 -0
- package/core/server/services/mentions/service.js +19 -32
- package/core/server/services/staff/index.js +4 -1
- package/core/server/services/url/Resources.js +33 -0
- package/core/server/services/url/Urls.js +3 -0
- package/core/server/services/url/config.js +6 -3
- package/core/shared/labs.js +1 -0
- package/package.json +110 -108
- package/yarn.lock +129 -129
- package/components/tryghost-audience-feedback-5.32.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.32.0.tgz +0 -0
- package/components/tryghost-constants-5.32.0.tgz +0 -0
- package/components/tryghost-data-generator-5.32.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.32.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.32.0.tgz +0 -0
- package/components/tryghost-email-events-5.32.0.tgz +0 -0
- package/components/tryghost-email-service-5.32.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.32.0.tgz +0 -0
- package/components/tryghost-i18n-5.32.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.32.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.32.0.tgz +0 -0
- package/components/tryghost-member-events-5.32.0.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.32.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.32.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.32.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.32.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.32.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.32.0.tgz +0 -0
- package/components/tryghost-referrers-5.32.0.tgz +0 -0
- package/components/tryghost-session-service-5.32.0.tgz +0 -0
- package/components/tryghost-staff-service-5.32.0.tgz +0 -0
- package/components/tryghost-webmentions-5.32.0.tgz +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400" style="enable-background:new 0 0 400 400" xml:space="preserve"><path style="fill-rule:evenodd;clip-rule:evenodd;fill:#635bff" d="M0 0h400v400H0z"/><path d="M184.4 155.5c0-9.4 7.7-13.1 20.5-13.1 18.4 0 41.6 5.6 60 15.5v-56.8C244.8 93.1 225 90 205 90c-49.1 0-81.7 25.6-81.7 68.4 0 66.7 91.9 56.1 91.9 84.9 0 11.1-9.7 14.7-23.2 14.7-20.1 0-45.7-8.2-66-19.3v57.5c22.5 9.7 45.2 13.8 66 13.8 50.3 0 84.9-24.9 84.9-68.2-.4-72-92.5-59.2-92.5-86.3z" style="fill-rule:evenodd;clip-rule:evenodd;fill:#fff"/></svg>
|
|
@@ -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%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%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.33%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%7D" />
|
|
12
12
|
|
|
13
13
|
<meta name="HandheldFriendly" content="True" />
|
|
14
14
|
<meta name="MobileOptimized" content="320" />
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
</style>
|
|
38
38
|
|
|
39
39
|
<link integrity="" rel="stylesheet" href="assets/vendor-3e6947aa681f0fb82b193090e520dc73.css">
|
|
40
|
-
<link integrity="" rel="stylesheet" href="assets/ghost-
|
|
40
|
+
<link integrity="" rel="stylesheet" href="assets/ghost-fb9fb8adbcaf1603ad4006dd2d49e401.css" title="light">
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
</head>
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
58
58
|
|
|
59
59
|
<script src="assets/vendor-0441964c34d58f2aacd5a04bbe240243.js"></script>
|
|
60
|
-
<script src="assets/chunk.
|
|
61
|
-
<script src="assets/chunk.143.
|
|
62
|
-
<script src="assets/ghost-
|
|
60
|
+
<script src="assets/chunk.963.e47ead5abeca4cf69fed.js"></script>
|
|
61
|
+
<script src="assets/chunk.143.e158db597c343f4f132e.js"></script>
|
|
62
|
+
<script src="assets/ghost-86f1db1d00d5f0bc4f0cbdb39ff6c977.js"></script>
|
|
63
63
|
</body>
|
|
64
64
|
</html>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const Command = require('./command');
|
|
2
2
|
const DataGenerator = require('@tryghost/data-generator');
|
|
3
|
+
const config = require('../shared/config');
|
|
3
4
|
|
|
4
5
|
module.exports = class REPL extends Command {
|
|
5
6
|
setup() {
|
|
@@ -39,7 +40,8 @@ module.exports = class REPL extends Command {
|
|
|
39
40
|
fatal: this.fatal,
|
|
40
41
|
debug: this.debug
|
|
41
42
|
},
|
|
42
|
-
modelQuantities: {}
|
|
43
|
+
modelQuantities: {},
|
|
44
|
+
baseUrl: config.getSiteUrl()
|
|
43
45
|
});
|
|
44
46
|
try {
|
|
45
47
|
await dataGenerator.importData();
|
|
@@ -55,7 +55,7 @@ class BaseSiteMapGenerator {
|
|
|
55
55
|
// Generate full xml
|
|
56
56
|
let sitemapXml = localUtils.getDeclarations() + xml(data);
|
|
57
57
|
|
|
58
|
-
// Perform url
|
|
58
|
+
// Perform url transformations
|
|
59
59
|
// - Necessary because sitemap data is supplied by the router which
|
|
60
60
|
// uses knex directly bypassing model-layer attribute transforms
|
|
61
61
|
sitemapXml = urlUtils.transformReadyToAbsolute(sitemapXml);
|
|
@@ -63,6 +63,15 @@ class BaseSiteMapGenerator {
|
|
|
63
63
|
return sitemapXml;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
updateURL(datum) {
|
|
67
|
+
const url = this.nodeLookup[datum.id]?.url[0].loc;
|
|
68
|
+
|
|
69
|
+
if (url) {
|
|
70
|
+
this.removeUrl(url, datum);
|
|
71
|
+
this.addUrl(url, datum);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
66
75
|
addUrl(url, datum) {
|
|
67
76
|
const node = this.createUrlNodeFromDatum(url, datum);
|
|
68
77
|
|
|
@@ -100,6 +109,12 @@ class BaseSiteMapGenerator {
|
|
|
100
109
|
}
|
|
101
110
|
}
|
|
102
111
|
|
|
112
|
+
/**
|
|
113
|
+
*
|
|
114
|
+
* @param {String} url
|
|
115
|
+
* @param {Object} datum
|
|
116
|
+
* @returns
|
|
117
|
+
*/
|
|
103
118
|
createUrlNodeFromDatum(url, datum) {
|
|
104
119
|
let node;
|
|
105
120
|
let imgNode;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const DomainEvents = require('@tryghost/domain-events');
|
|
2
|
+
const {URLResourceUpdatedEvent} = require('@tryghost/dynamic-routing-events');
|
|
1
3
|
const IndexMapGenerator = require('./index-generator');
|
|
2
4
|
const PagesMapGenerator = require('./page-generator');
|
|
3
5
|
const PostsMapGenerator = require('./post-generator');
|
|
@@ -29,6 +31,10 @@ class SiteMapManager {
|
|
|
29
31
|
}
|
|
30
32
|
});
|
|
31
33
|
|
|
34
|
+
DomainEvents.subscribe(URLResourceUpdatedEvent, (event) => {
|
|
35
|
+
this[event.data.resourceType].updateURL(event.data);
|
|
36
|
+
});
|
|
37
|
+
|
|
32
38
|
events.on('url.added', (obj) => {
|
|
33
39
|
this[obj.resource.config.type].addUrl(obj.url.absolute, obj.resource.data);
|
|
34
40
|
});
|
|
@@ -7,7 +7,7 @@ const jobsService = require('../../jobs');
|
|
|
7
7
|
let hasScheduled = false;
|
|
8
8
|
|
|
9
9
|
module.exports = {
|
|
10
|
-
async scheduleRecurringJobs() {
|
|
10
|
+
async scheduleRecurringJobs(skipEmailCheck = false) {
|
|
11
11
|
if (
|
|
12
12
|
!hasScheduled &&
|
|
13
13
|
config.get('emailAnalytics') &&
|
|
@@ -17,10 +17,10 @@ module.exports = {
|
|
|
17
17
|
// Don't register email analytics job if we have no emails,
|
|
18
18
|
// processor usage from many sites spinning up threads can be high.
|
|
19
19
|
// Mega service will re-run this scheduling task when an email is sent
|
|
20
|
-
const emailCount = await models.Email
|
|
20
|
+
const emailCount = skipEmailCheck ? 1 : (await models.Email
|
|
21
21
|
.where('created_at', '>', moment.utc().subtract(30, 'days').toDate())
|
|
22
22
|
.where('status', '<>', 'failed')
|
|
23
|
-
.count();
|
|
23
|
+
.count());
|
|
24
24
|
|
|
25
25
|
if (emailCount > 0) {
|
|
26
26
|
// use a random seconds value to avoid spikes to external APIs on the minute
|
|
@@ -35,6 +35,7 @@ class EmailServiceWrapper {
|
|
|
35
35
|
const linkTracking = require('../link-tracking');
|
|
36
36
|
const audienceFeedback = require('../audience-feedback');
|
|
37
37
|
const storageUtils = require('../../adapters/storage/utils');
|
|
38
|
+
const emailAnalyticsJobs = require('../email-analytics/jobs');
|
|
38
39
|
|
|
39
40
|
// capture errors from mailgun client and log them in sentry
|
|
40
41
|
const errorHandler = (error) => {
|
|
@@ -103,7 +104,8 @@ class EmailServiceWrapper {
|
|
|
103
104
|
emailSegmenter,
|
|
104
105
|
limitService,
|
|
105
106
|
membersRepository,
|
|
106
|
-
verificationTrigger: membersService.verificationTrigger
|
|
107
|
+
verificationTrigger: membersService.verificationTrigger,
|
|
108
|
+
emailAnalyticsJobs
|
|
107
109
|
});
|
|
108
110
|
|
|
109
111
|
this.controller = new EmailController(this.service, {
|
|
@@ -21,12 +21,17 @@ module.exports = class BookshelfMentionRepository {
|
|
|
21
21
|
/** @type {Object} */
|
|
22
22
|
#MentionModel;
|
|
23
23
|
|
|
24
|
+
/** @type {import('@tryghost/domain-events')} */
|
|
25
|
+
#DomainEvents;
|
|
26
|
+
|
|
24
27
|
/**
|
|
25
28
|
* @param {object} deps
|
|
26
29
|
* @param {object} deps.MentionModel Bookshelf Model
|
|
30
|
+
* @param {import('@tryghost/domain-events')} deps.DomainEvents
|
|
27
31
|
*/
|
|
28
32
|
constructor(deps) {
|
|
29
33
|
this.#MentionModel = deps.MentionModel;
|
|
34
|
+
this.#DomainEvents = deps.DomainEvents;
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
#modelToMention(model) {
|
|
@@ -113,5 +118,8 @@ module.exports = class BookshelfMentionRepository {
|
|
|
113
118
|
id: data.id
|
|
114
119
|
});
|
|
115
120
|
}
|
|
121
|
+
for (const event of mention.events) {
|
|
122
|
+
this.#DomainEvents.dispatch(event);
|
|
123
|
+
}
|
|
116
124
|
}
|
|
117
125
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const ObjectID = require('bson-objectid').default;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('@tryghost/webmentions/lib/MentionsAPI').IResourceService} IResourceService
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @implements {IResourceService}
|
|
9
|
+
*/
|
|
10
|
+
module.exports = class ResourceService {
|
|
11
|
+
/** @type {import('@tryghost/url-utils/lib/url-utils')} */
|
|
12
|
+
#urlUtils;
|
|
13
|
+
|
|
14
|
+
/** @type {import('../url')} */
|
|
15
|
+
#urlService;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {object} deps
|
|
19
|
+
* @param {import('@tryghost/url-utils/lib/url-utils')} deps.urlUtils
|
|
20
|
+
* @param {import('../url')} deps.urlService
|
|
21
|
+
*/
|
|
22
|
+
constructor(deps) {
|
|
23
|
+
this.#urlUtils = deps.urlUtils;
|
|
24
|
+
this.#urlService = deps.urlService;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {URL} url
|
|
29
|
+
* @returns {Promise<import('@tryghost/webmentions/lib/MentionsAPI').ResourceResult>}
|
|
30
|
+
*/
|
|
31
|
+
async getByURL(url) {
|
|
32
|
+
const path = this.#urlUtils.absoluteToRelative(url.href, {withoutSubdirectory: true});
|
|
33
|
+
const resource = this.#urlService.getResource(path);
|
|
34
|
+
if (resource?.config?.type === 'posts') {
|
|
35
|
+
return {
|
|
36
|
+
type: 'post',
|
|
37
|
+
id: ObjectID.createFromHexString(resource.data.id)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
type: null,
|
|
42
|
+
id: null
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('@tryghost/webmentions/lib/MentionsAPI').IRoutingService} IRoutingService
|
|
5
|
+
* @typedef {import('@tryghost/webmentions/lib/MentionsAPI').IResourceService} IResourceService
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {object} IUrlUtils
|
|
10
|
+
* @prop {() => string} getSiteUrl
|
|
11
|
+
* @prop {() => string} getSubdir
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @implements {IRoutingService}
|
|
16
|
+
*/
|
|
17
|
+
module.exports = class RoutingService {
|
|
18
|
+
/** @typedef {URL} */
|
|
19
|
+
#siteUrl;
|
|
20
|
+
|
|
21
|
+
/** @typedef {IResourceService} */
|
|
22
|
+
#resourceService;
|
|
23
|
+
|
|
24
|
+
/** @typedef {import('got')} */
|
|
25
|
+
#externalRequest;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {object} deps
|
|
29
|
+
* @param {URL} deps.siteUrl
|
|
30
|
+
* @param {IResourceService} deps.resourceService
|
|
31
|
+
* @param {import('got')} deps.externalRequest;
|
|
32
|
+
*/
|
|
33
|
+
constructor(deps) {
|
|
34
|
+
this.#siteUrl = deps.siteUrl;
|
|
35
|
+
this.#resourceService = deps.resourceService;
|
|
36
|
+
this.#externalRequest = deps.externalRequest;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {URL} url
|
|
41
|
+
*/
|
|
42
|
+
async pageExists(url) {
|
|
43
|
+
if (this.#siteUrl.origin !== url.origin) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const subdir = removeTrailingSlash(this.#siteUrl.pathname);
|
|
47
|
+
if (subdir && !url.pathname.startsWith(subdir)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const resource = await this.#resourceService.getByURL(url);
|
|
52
|
+
|
|
53
|
+
if (resource?.type !== null) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const response = await this.#externalRequest.head(url, {
|
|
59
|
+
followRedirect: false
|
|
60
|
+
});
|
|
61
|
+
if (response.statusCode < 400 && response.statusCode > 199) {
|
|
62
|
+
return true;
|
|
63
|
+
} else {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
} catch (err) {
|
|
67
|
+
logging.error(err);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {string} str
|
|
75
|
+
*/
|
|
76
|
+
function removeTrailingSlash(str) {
|
|
77
|
+
return str.replace(/\/$/, '');
|
|
78
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const ObjectID = require('bson-objectid').default;
|
|
2
1
|
const MentionController = require('./MentionController');
|
|
3
2
|
const WebmentionMetadata = require('./WebmentionMetadata');
|
|
4
3
|
const {
|
|
@@ -7,6 +6,8 @@ const {
|
|
|
7
6
|
MentionDiscoveryService
|
|
8
7
|
} = require('@tryghost/webmentions');
|
|
9
8
|
const BookshelfMentionRepository = require('./BookshelfMentionRepository');
|
|
9
|
+
const ResourceService = require('./ResourceService');
|
|
10
|
+
const RoutingService = require('./RoutingService');
|
|
10
11
|
const models = require('../../models');
|
|
11
12
|
const events = require('../../lib/common/events');
|
|
12
13
|
const externalRequest = require('../../../server/lib/request-external.js');
|
|
@@ -14,53 +15,39 @@ const urlUtils = require('../../../shared/url-utils');
|
|
|
14
15
|
const outputSerializerUrlUtil = require('../../../server/api/endpoints/utils/serializers/output/utils/url');
|
|
15
16
|
const labs = require('../../../shared/labs');
|
|
16
17
|
const urlService = require('../url');
|
|
18
|
+
const DomainEvents = require('@tryghost/domain-events');
|
|
17
19
|
|
|
18
20
|
function getPostUrl(post) {
|
|
19
21
|
const jsonModel = {};
|
|
20
22
|
outputSerializerUrlUtil.forPost(post.id, jsonModel, {options: {}});
|
|
21
23
|
return jsonModel.url;
|
|
22
24
|
}
|
|
25
|
+
|
|
23
26
|
module.exports = {
|
|
24
27
|
controller: new MentionController(),
|
|
25
28
|
async init() {
|
|
26
29
|
const repository = new BookshelfMentionRepository({
|
|
27
|
-
MentionModel: models.Mention
|
|
30
|
+
MentionModel: models.Mention,
|
|
31
|
+
DomainEvents
|
|
28
32
|
});
|
|
29
33
|
const webmentionMetadata = new WebmentionMetadata();
|
|
30
34
|
const discoveryService = new MentionDiscoveryService({externalRequest});
|
|
35
|
+
const resourceService = new ResourceService({
|
|
36
|
+
urlUtils,
|
|
37
|
+
urlService
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const routingService = new RoutingService({
|
|
41
|
+
siteUrl: new URL(urlUtils.getSiteUrl()),
|
|
42
|
+
resourceService,
|
|
43
|
+
externalRequest
|
|
44
|
+
});
|
|
45
|
+
|
|
31
46
|
const api = new MentionsAPI({
|
|
32
47
|
repository,
|
|
33
48
|
webmentionMetadata,
|
|
34
|
-
resourceService
|
|
35
|
-
|
|
36
|
-
const path = urlUtils.absoluteToRelative(url.href, {withoutSubdirectory: true});
|
|
37
|
-
const resource = urlService.getResource(path);
|
|
38
|
-
if (resource?.config?.type === 'posts') {
|
|
39
|
-
return {
|
|
40
|
-
type: 'post',
|
|
41
|
-
id: ObjectID.createFromHexString(resource.data.id)
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
return {
|
|
45
|
-
type: null,
|
|
46
|
-
id: null
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
routingService: {
|
|
51
|
-
async pageExists(url) {
|
|
52
|
-
const siteUrl = new URL(urlUtils.getSiteUrl());
|
|
53
|
-
if (siteUrl.origin !== url.origin) {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
const subdir = urlUtils.getSubdir();
|
|
57
|
-
if (subdir && !url.pathname.startsWith(subdir)) {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
49
|
+
resourceService,
|
|
50
|
+
routingService
|
|
64
51
|
});
|
|
65
52
|
|
|
66
53
|
this.controller.init({api});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const DomainEvents = require('@tryghost/domain-events');
|
|
2
|
+
const labs = require('../../../shared/labs');
|
|
3
|
+
|
|
2
4
|
class StaffServiceWrapper {
|
|
3
5
|
init() {
|
|
4
6
|
if (this.api) {
|
|
@@ -23,7 +25,8 @@ class StaffServiceWrapper {
|
|
|
23
25
|
settingsHelpers,
|
|
24
26
|
settingsCache,
|
|
25
27
|
urlUtils,
|
|
26
|
-
DomainEvents
|
|
28
|
+
DomainEvents,
|
|
29
|
+
labs
|
|
27
30
|
});
|
|
28
31
|
|
|
29
32
|
this.api.subscribeEvents();
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const debug = require('@tryghost/debug')('services:url:resources');
|
|
3
|
+
const DomainEvents = require('@tryghost/domain-events');
|
|
4
|
+
const {URLResourceUpdatedEvent} = require('@tryghost/dynamic-routing-events');
|
|
3
5
|
const Resource = require('./Resource');
|
|
4
6
|
const config = require('../../../shared/config');
|
|
5
7
|
const models = require('../../models');
|
|
@@ -272,6 +274,21 @@ class Resources {
|
|
|
272
274
|
}
|
|
273
275
|
}
|
|
274
276
|
|
|
277
|
+
/**
|
|
278
|
+
*
|
|
279
|
+
* @param {Object} model resource model
|
|
280
|
+
* @returns
|
|
281
|
+
*/
|
|
282
|
+
_containsRoutingAffectingChanges(model, ignoredProperties) {
|
|
283
|
+
if (model._changed && Object.keys(model._changed).length) {
|
|
284
|
+
return _.difference(Object.keys(model._changed), ignoredProperties).length !== 0;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// NOTE: returning true here as "_changed" property might not be available on attached/detached events
|
|
288
|
+
// assuming there were route affecting changes by default
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
|
|
275
292
|
/**
|
|
276
293
|
* @description Listener for "model updated" event.
|
|
277
294
|
*
|
|
@@ -297,6 +314,22 @@ class Resources {
|
|
|
297
314
|
|
|
298
315
|
const resourceConfig = _.find(this.resourcesConfig, {type: type});
|
|
299
316
|
|
|
317
|
+
// NOTE: check if any of the route-related fields were changed and only proceed if so
|
|
318
|
+
const ignoredProperties = [...resourceConfig.modelOptions.exclude, 'updated_at'];
|
|
319
|
+
if (!this._containsRoutingAffectingChanges(model, ignoredProperties)) {
|
|
320
|
+
const cachedResource = this.getByIdAndType(type, model.id);
|
|
321
|
+
|
|
322
|
+
if (cachedResource && model._changed && Object.keys(model._changed).includes('updated_at')) {
|
|
323
|
+
DomainEvents.dispatch(URLResourceUpdatedEvent.create(Object.assign(cachedResource.data, {
|
|
324
|
+
resourceType: cachedResource.config.type,
|
|
325
|
+
updated_at: model._changed.updated_at
|
|
326
|
+
})));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
debug('skipping _onResourceUpdated because only non-route-related properties changed');
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
|
|
300
333
|
// NOTE: synchronous handling for post and pages so that their URL is available without a delay
|
|
301
334
|
// for more context and future improvements check https://github.com/TryGhost/Ghost/issues/10360
|
|
302
335
|
if (['posts', 'pages'].includes(type)) {
|
|
@@ -32,6 +32,9 @@ class Urls {
|
|
|
32
32
|
/**
|
|
33
33
|
* @description Add a url to the system.
|
|
34
34
|
* @param {Object} options
|
|
35
|
+
* @param {import('./Resource')} options.resource - instance of the Resource class
|
|
36
|
+
* @param {string} options.generatorId
|
|
37
|
+
* @param {string} options.url
|
|
35
38
|
*/
|
|
36
39
|
add(options) {
|
|
37
40
|
const url = options.url;
|
|
@@ -31,7 +31,8 @@ module.exports = [
|
|
|
31
31
|
'twitter_title',
|
|
32
32
|
'twitter_description',
|
|
33
33
|
'custom_template',
|
|
34
|
-
'locale'
|
|
34
|
+
'locale',
|
|
35
|
+
'tiers'
|
|
35
36
|
],
|
|
36
37
|
withRelated: ['tags', 'authors'],
|
|
37
38
|
withRelatedPrimary: {
|
|
@@ -79,7 +80,8 @@ module.exports = [
|
|
|
79
80
|
'tags',
|
|
80
81
|
'authors',
|
|
81
82
|
'primary_tag',
|
|
82
|
-
'primary_author'
|
|
83
|
+
'primary_author',
|
|
84
|
+
'tiers'
|
|
83
85
|
],
|
|
84
86
|
filter: 'status:published+type:page'
|
|
85
87
|
},
|
|
@@ -126,7 +128,8 @@ module.exports = [
|
|
|
126
128
|
'accessibility',
|
|
127
129
|
'meta_title',
|
|
128
130
|
'meta_description',
|
|
129
|
-
'tour'
|
|
131
|
+
'tour',
|
|
132
|
+
'last_seen'
|
|
130
133
|
],
|
|
131
134
|
filter: 'visibility:public',
|
|
132
135
|
shouldHavePosts: {
|