mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-24 11:22:19 +03:00
Added service for delivering activities
ref https://linear.app/tryghost/issue/MOM-120 This will allow us to deliver Follow activities to other sites
This commit is contained in:
parent
4d24bdbccb
commit
3a56b79a8c
@ -0,0 +1,51 @@
|
|||||||
|
import assert from 'assert';
|
||||||
|
import {Activity} from './activity.entity';
|
||||||
|
import {Actor} from './actor.entity';
|
||||||
|
import {TheWorld} from './tell-the-world.service';
|
||||||
|
import {URI} from './uri.object';
|
||||||
|
import nock from 'nock';
|
||||||
|
|
||||||
|
describe('TheWorld', function () {
|
||||||
|
describe('deliverActivity', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
nock.disableNetConnect();
|
||||||
|
});
|
||||||
|
afterEach(function () {
|
||||||
|
nock.enableNetConnect();
|
||||||
|
});
|
||||||
|
it('Can deliver the activity to the inbox of the desired recipient', async function () {
|
||||||
|
const service = new TheWorld(new URL('https://base.com'));
|
||||||
|
|
||||||
|
const actor = Actor.create({
|
||||||
|
username: 'Testing'
|
||||||
|
});
|
||||||
|
|
||||||
|
const toFollow = new URI('https://main.ghost.org/activitypub/actor/deadbeefdeadbeefdeadbeef');
|
||||||
|
|
||||||
|
const activity = new Activity({
|
||||||
|
type: 'Follow',
|
||||||
|
activity: null,
|
||||||
|
actor: actor.actorId,
|
||||||
|
object: {
|
||||||
|
id: toFollow
|
||||||
|
},
|
||||||
|
to: toFollow
|
||||||
|
});
|
||||||
|
|
||||||
|
const actorFetch = nock('https://main.ghost.org')
|
||||||
|
.get('/activitypub/actor/deadbeefdeadbeefdeadbeef')
|
||||||
|
.reply(200, {
|
||||||
|
inbox: 'https://main.ghost.org/activitypub/inbox/deadbeefdeadbeefdeadbeef'
|
||||||
|
});
|
||||||
|
|
||||||
|
const activityDelivery = nock('https://main.ghost.org')
|
||||||
|
.post('/activitypub/inbox/deadbeefdeadbeefdeadbeef')
|
||||||
|
.reply(201, {});
|
||||||
|
|
||||||
|
await service.deliverActivity(activity, actor);
|
||||||
|
|
||||||
|
assert(actorFetch.isDone(), 'Expected actor to be fetched');
|
||||||
|
assert(activityDelivery.isDone(), 'Expected activity to be delivered');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
61
ghost/ghost/src/core/activitypub/tell-the-world.service.ts
Normal file
61
ghost/ghost/src/core/activitypub/tell-the-world.service.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import {Inject} from '@nestjs/common';
|
||||||
|
import {Activity} from './activity.entity';
|
||||||
|
import {Actor} from './actor.entity';
|
||||||
|
|
||||||
|
export class TheWorld {
|
||||||
|
constructor(
|
||||||
|
@Inject('ActivityPubBaseURL') private readonly url: URL
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async deliverActivity(activity: Activity, actor: Actor): Promise<void> {
|
||||||
|
const recipients = await this.getRecipients(activity);
|
||||||
|
for (const recipient of recipients) {
|
||||||
|
const data = await this.fetchForActor(recipient.href, actor);
|
||||||
|
if ('inbox' in data && typeof data.inbox === 'string') {
|
||||||
|
const inbox = new URL(data.inbox);
|
||||||
|
await this.sendActivity(inbox, activity, actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async sendActivity(to: URL, activity: Activity, from: Actor) {
|
||||||
|
const request = new Request(to.href, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/ld+json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(activity.getJSONLD(this.url))
|
||||||
|
});
|
||||||
|
const signedRequest = await from.sign(request, this.url);
|
||||||
|
await fetch(signedRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getRecipients(activity: Activity): Promise<URL[]>{
|
||||||
|
const json = activity.getJSONLD(this.url);
|
||||||
|
const recipients = [];
|
||||||
|
if (json.to) {
|
||||||
|
recipients.push(new URL(json.to));
|
||||||
|
}
|
||||||
|
return recipients;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchForActor(uri: string, actor: Actor) {
|
||||||
|
const request = new Request(uri, {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/ld+json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const signedRequest = await actor.sign(request, this.url);
|
||||||
|
|
||||||
|
const result = await fetch(signedRequest);
|
||||||
|
|
||||||
|
const json = await result.json();
|
||||||
|
|
||||||
|
if (typeof json !== 'object' || json === null) {
|
||||||
|
throw new Error('Could not read data');
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user