mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-06 22:40:14 -05:00
Supported delivering Activities to a Collection of Actors
ref https://linear.app/tryghost/issue/MOM-126 Now that we're setting the recipient of our Create Activites to the Followers Collection, we need to actually dereference it and pull out all the inboxes. This is all done over the network at the moment, but we'll start storing this information locally when we've got the DB wired up.
This commit is contained in:
parent
603891645d
commit
d15858e16a
4 changed files with 97 additions and 4 deletions
|
@ -14,7 +14,7 @@ describe('TheWorld', 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 service = new TheWorld(new URL('https://base.com'), console);
|
||||
|
||||
const actor = Actor.create({
|
||||
username: 'Testing'
|
||||
|
@ -47,5 +47,69 @@ describe('TheWorld', function () {
|
|||
assert(actorFetch.isDone(), 'Expected actor to be fetched');
|
||||
assert(activityDelivery.isDone(), 'Expected activity to be delivered');
|
||||
});
|
||||
|
||||
it('Can deliver the activity to the inboxes of a collection of actors', async function () {
|
||||
const service = new TheWorld(new URL('https://base.com'), console);
|
||||
|
||||
const actor = Actor.create({
|
||||
username: 'Testing'
|
||||
});
|
||||
|
||||
const followers = new URI('https://main.ghost.org/activitypub/followers/deadbeefdeadbeefdeadbeef');
|
||||
|
||||
const activity = new Activity({
|
||||
type: 'Create',
|
||||
activity: null,
|
||||
actor: actor.actorId,
|
||||
object: {
|
||||
id: new URI('https://main.ghost.org/hello-world'),
|
||||
type: 'Note',
|
||||
content: '<p> Hello, world. </p>'
|
||||
},
|
||||
to: followers
|
||||
});
|
||||
|
||||
nock('https://main.ghost.org')
|
||||
.get('/activitypub/followers/deadbeefdeadbeefdeadbeef')
|
||||
.reply(200, {
|
||||
'@context': '',
|
||||
type: 'Collection',
|
||||
totalItems: 3,
|
||||
items: [
|
||||
'https://main.ghost.org/activitypub/actor/deadbeefdeadbeefdeadbeef',
|
||||
{
|
||||
id: 'https://main.ghost.org/activitypub/actor/beefdeadbeefdeadbeefdead'
|
||||
},
|
||||
{
|
||||
invalid: true
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
nock('https://main.ghost.org')
|
||||
.get('/activitypub/actor/deadbeefdeadbeefdeadbeef')
|
||||
.reply(200, {
|
||||
inbox: 'https://main.ghost.org/activitypub/inbox/deadbeefdeadbeefdeadbeef'
|
||||
});
|
||||
|
||||
nock('https://main.ghost.org')
|
||||
.get('/activitypub/actor/beefdeadbeefdeadbeefdead')
|
||||
.reply(200, {
|
||||
inbox: 'https://main.ghost.org/activitypub/inbox/beefdeadbeefdeadbeefdead'
|
||||
});
|
||||
|
||||
const firstActivityDelivery = nock('https://main.ghost.org')
|
||||
.post('/activitypub/inbox/deadbeefdeadbeefdeadbeef')
|
||||
.reply(201, {});
|
||||
|
||||
const secondActivityDelivery = nock('https://main.ghost.org')
|
||||
.post('/activitypub/inbox/beefdeadbeefdeadbeefdead')
|
||||
.reply(201, {});
|
||||
|
||||
await service.deliverActivity(activity, actor);
|
||||
|
||||
assert(firstActivityDelivery.isDone(), 'Expected activity to be delivered');
|
||||
assert(secondActivityDelivery.isDone(), 'Expected activity to be delivered');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,7 +4,8 @@ import {Actor} from './actor.entity';
|
|||
|
||||
export class TheWorld {
|
||||
constructor(
|
||||
@Inject('ActivityPubBaseURL') private readonly url: URL
|
||||
@Inject('ActivityPubBaseURL') private readonly url: URL,
|
||||
@Inject('logger') private readonly logger: Console
|
||||
) {}
|
||||
|
||||
async deliverActivity(activity: Activity, actor: Actor): Promise<void> {
|
||||
|
@ -15,6 +16,26 @@ export class TheWorld {
|
|||
const inbox = new URL(data.inbox);
|
||||
await this.sendActivity(inbox, activity, actor);
|
||||
}
|
||||
|
||||
if ('type' in data && data.type === 'Collection') {
|
||||
if ('items' in data && Array.isArray(data.items)) {
|
||||
for (const item of data.items) {
|
||||
let url;
|
||||
if (typeof item === 'string') {
|
||||
url = new URL(item);
|
||||
} else if ('id' in item && typeof item.id === 'string') {
|
||||
url = new URL(item.id);
|
||||
}
|
||||
if (url) {
|
||||
const fetchedActor = await this.fetchForActor(url.href, actor);
|
||||
if ('inbox' in fetchedActor && typeof fetchedActor.inbox === 'string') {
|
||||
const inbox = new URL(fetchedActor.inbox);
|
||||
await this.sendActivity(inbox, activity, actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,7 +48,11 @@ export class TheWorld {
|
|||
body: JSON.stringify(activity.getJSONLD(this.url))
|
||||
});
|
||||
const signedRequest = await from.sign(request, this.url);
|
||||
await fetch(signedRequest);
|
||||
try {
|
||||
await fetch(signedRequest);
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
private async getRecipients(activity: Activity): Promise<URL[]>{
|
||||
|
|
|
@ -21,6 +21,10 @@ describe('ActivityPubController', function () {
|
|||
const moduleRef = await Test.createTestingModule({
|
||||
controllers: [ActivityPubController],
|
||||
providers: [
|
||||
{
|
||||
provide: 'logger',
|
||||
useValue: console
|
||||
},
|
||||
{
|
||||
provide: 'ActivityPubBaseURL',
|
||||
useValue: new URL('https://example.com')
|
||||
|
|
|
@ -17,7 +17,7 @@ describe('ActivityListener', function () {
|
|||
calledWith.push([activity, actor]);
|
||||
}
|
||||
}
|
||||
const listener = new ActivityListener(new MockTheWorld(new URL('https://example.com')));
|
||||
const listener = new ActivityListener(new MockTheWorld(new URL('https://example.com'), console));
|
||||
|
||||
const actor = Actor.create({
|
||||
username: 'Testing'
|
||||
|
|
Loading…
Reference in a new issue