From e14d2e662bf7d514478d08d936efa4fc734cf0a2 Mon Sep 17 00:00:00 2001 From: "Fabien \"egg\" O'Carroll" Date: Fri, 17 Feb 2023 18:53:06 +0700 Subject: [PATCH] Wired up verification to MentionsAPI refs https://github.com/TryGhost/Team/issues/2550 Whilst this isn't ideal making multiple requests for the same site it's a first step towards verification properties, and can be refactored to improve performance later. With the current volume of incoming Webmentions we've seen so far this shouldn't be a problem. --- .../services/mentions/WebmentionRequest.js | 20 ++++++++++ .../core/server/services/mentions/service.js | 3 ++ ghost/webmentions/lib/MentionsAPI.js | 16 +++++++- ghost/webmentions/test/MentionsAPI.test.js | 38 ++++++++++++++----- 4 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 ghost/core/core/server/services/mentions/WebmentionRequest.js diff --git a/ghost/core/core/server/services/mentions/WebmentionRequest.js b/ghost/core/core/server/services/mentions/WebmentionRequest.js new file mode 100644 index 0000000000..1d50a3b349 --- /dev/null +++ b/ghost/core/core/server/services/mentions/WebmentionRequest.js @@ -0,0 +1,20 @@ +const logging = require('@tryghost/logging'); +const oembedService = require('../oembed'); + +module.exports = class WebmentionRequest { + /** + * @param {URL} url + * @returns {Promise<{html: string}>} + */ + async fetch(url) { + try { + const data = await oembedService.fetchPageHtml(url.href); + return { + html: data.body + }; + } catch (err) { + logging.warn(err); + return null; + } + } +}; diff --git a/ghost/core/core/server/services/mentions/service.js b/ghost/core/core/server/services/mentions/service.js index 714d805d4e..0485b7a9a8 100644 --- a/ghost/core/core/server/services/mentions/service.js +++ b/ghost/core/core/server/services/mentions/service.js @@ -1,5 +1,6 @@ const MentionController = require('./MentionController'); const WebmentionMetadata = require('./WebmentionMetadata'); +const WebmentionRequest = require('./WebmentionRequest'); const { MentionsAPI, MentionSendingService, @@ -32,6 +33,7 @@ module.exports = { DomainEvents }); const webmentionMetadata = new WebmentionMetadata(); + const webmentionRequest = new WebmentionRequest(); const discoveryService = new MentionDiscoveryService({externalRequest}); const resourceService = new ResourceService({ urlUtils, @@ -47,6 +49,7 @@ module.exports = { const api = new MentionsAPI({ repository, webmentionMetadata, + webmentionRequest, resourceService, routingService }); diff --git a/ghost/webmentions/lib/MentionsAPI.js b/ghost/webmentions/lib/MentionsAPI.js index a5f510ebdf..0bb57f7fee 100644 --- a/ghost/webmentions/lib/MentionsAPI.js +++ b/ghost/webmentions/lib/MentionsAPI.js @@ -72,6 +72,11 @@ const Mention = require('./Mention'); * @prop {(url: URL) => Promise} fetch */ +/** + * @typedef {object} IWebmentionRequest + * @prop {(url: URL) => Promise<{html: string}>} fetch + */ + module.exports = class MentionsAPI { /** @type {IMentionRepository} */ #repository; @@ -81,6 +86,8 @@ module.exports = class MentionsAPI { #routingService; /** @type {IWebmentionMetadata} */ #webmentionMetadata; + /** @type {IWebmentionRequest} */ + #webmentionRequest; /** * @param {object} deps @@ -88,12 +95,14 @@ module.exports = class MentionsAPI { * @param {IResourceService} deps.resourceService * @param {IRoutingService} deps.routingService * @param {IWebmentionMetadata} deps.webmentionMetadata + * @param {IWebmentionRequest} deps.webmentionRequest */ constructor(deps) { this.#repository = deps.repository; this.#resourceService = deps.resourceService; this.#routingService = deps.routingService; this.#webmentionMetadata = deps.webmentionMetadata; + this.#webmentionRequest = deps.webmentionRequest; } /** @@ -187,8 +196,13 @@ module.exports = class MentionsAPI { sourceFeaturedImage: metadata.image }); } - await this.#repository.save(mention); + const responseBody = await this.#webmentionRequest.fetch(webmention.source); + if (responseBody?.html) { + mention.verify(responseBody.html); + } + + await this.#repository.save(mention); return mention; } }; diff --git a/ghost/webmentions/test/MentionsAPI.test.js b/ghost/webmentions/test/MentionsAPI.test.js index 499e99d008..7204133690 100644 --- a/ghost/webmentions/test/MentionsAPI.test.js +++ b/ghost/webmentions/test/MentionsAPI.test.js @@ -31,6 +31,14 @@ const mockWebmentionMetadata = { } }; +const mockWebmentionRequest = { + async fetch() { + return { + html: `

Some HTML and a mentioned url

` + }; + } +}; + function addMinutes(date, minutes) { date.setMinutes(date.getMinutes() + minutes); @@ -48,7 +56,8 @@ describe('MentionsAPI', function () { repository, routingService: mockRoutingService, resourceService: mockResourceService, - webmentionMetadata: mockWebmentionMetadata + webmentionMetadata: mockWebmentionMetadata, + webmentionRequest: mockWebmentionRequest }); const mention = await api.processWebmention({ @@ -73,7 +82,8 @@ describe('MentionsAPI', function () { repository, routingService: mockRoutingService, resourceService: mockResourceService, - webmentionMetadata: mockWebmentionMetadata + webmentionMetadata: mockWebmentionMetadata, + webmentionRequest: mockWebmentionRequest }); const mention = await api.processWebmention({ @@ -97,7 +107,8 @@ describe('MentionsAPI', function () { repository, routingService: mockRoutingService, resourceService: mockResourceService, - webmentionMetadata: mockWebmentionMetadata + webmentionMetadata: mockWebmentionMetadata, + webmentionRequest: mockWebmentionRequest }); const mentionOne = await api.processWebmention({ @@ -129,7 +140,8 @@ describe('MentionsAPI', function () { repository, routingService: mockRoutingService, resourceService: mockResourceService, - webmentionMetadata: mockWebmentionMetadata + webmentionMetadata: mockWebmentionMetadata, + webmentionRequest: mockWebmentionRequest }); const mentionOne = await api.processWebmention({ @@ -165,7 +177,8 @@ describe('MentionsAPI', function () { repository, routingService: mockRoutingService, resourceService: mockResourceService, - webmentionMetadata: mockWebmentionMetadata + webmentionMetadata: mockWebmentionMetadata, + webmentionRequest: mockWebmentionRequest }); const mentionOne = await api.processWebmention({ @@ -201,7 +214,8 @@ describe('MentionsAPI', function () { repository, routingService: mockRoutingService, resourceService: mockResourceService, - webmentionMetadata: mockWebmentionMetadata + webmentionMetadata: mockWebmentionMetadata, + webmentionRequest: mockWebmentionRequest }); const mentionOne = await api.processWebmention({ @@ -238,7 +252,8 @@ describe('MentionsAPI', function () { } }, resourceService: mockResourceService, - webmentionMetadata: mockWebmentionMetadata + webmentionMetadata: mockWebmentionMetadata, + webmentionRequest: mockWebmentionRequest }); let errored = false; @@ -268,7 +283,8 @@ describe('MentionsAPI', function () { }; } }, - webmentionMetadata: mockWebmentionMetadata + webmentionMetadata: mockWebmentionMetadata, + webmentionRequest: mockWebmentionRequest }); const mention = await api.processWebmention({ @@ -301,7 +317,8 @@ describe('MentionsAPI', function () { }; } }, - webmentionMetadata: mockWebmentionMetadata + webmentionMetadata: mockWebmentionMetadata, + webmentionRequest: mockWebmentionRequest }); checkFirstMention: { @@ -352,7 +369,8 @@ describe('MentionsAPI', function () { fetch: sinon.stub() .onFirstCall().resolves(mockWebmentionMetadata.fetch()) .onSecondCall().rejects() - } + }, + webmentionRequest: mockWebmentionRequest }); checkFirstMention: {