@fedify/relay 2.0.0-pr.471.1 → 2.0.0-pr.471.1920
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/dist/mod.cjs +222 -7
- package/dist/mod.js +223 -8
- package/package.json +7 -5
package/dist/mod.cjs
CHANGED
|
@@ -120,21 +120,21 @@ var MastodonRelay = class {
|
|
|
120
120
|
const isPublicFollow = follow.objectId.href === "https://www.w3.org/ns/activitystreams#Public";
|
|
121
121
|
if (!isPublicFollow && parsed?.type !== "actor") return;
|
|
122
122
|
const relayActorUri = ctx.getActorUri(RELAY_SERVER_ACTOR);
|
|
123
|
-
const
|
|
124
|
-
if (
|
|
123
|
+
const follower = await follow.getActor(ctx);
|
|
124
|
+
if (follower == null || follower.id == null || follower.preferredUsername == null || follower.inboxId == null) return;
|
|
125
125
|
let approved = false;
|
|
126
|
-
if (this.#subscriptionHandler) approved = await this.#subscriptionHandler(ctx,
|
|
126
|
+
if (this.#subscriptionHandler) approved = await this.#subscriptionHandler(ctx, follower);
|
|
127
127
|
if (approved) {
|
|
128
128
|
const followers = await options.kv.get(["followers"]) ?? [];
|
|
129
129
|
followers.push(follow.id.href);
|
|
130
130
|
await options.kv.set(["followers"], followers);
|
|
131
|
-
await options.kv.set(["follower", follow.id.href], await
|
|
132
|
-
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR },
|
|
131
|
+
await options.kv.set(["follower", follow.id.href], await follower.toJsonLd());
|
|
132
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new __fedify_fedify_vocab.Accept({
|
|
133
133
|
id: new URL(`#accepts`, relayActorUri),
|
|
134
134
|
actor: relayActorUri,
|
|
135
135
|
object: follow
|
|
136
136
|
}));
|
|
137
|
-
} else await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR },
|
|
137
|
+
} else await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new __fedify_fedify_vocab.Reject({
|
|
138
138
|
id: new URL(`#rejects`, relayActorUri),
|
|
139
139
|
actor: relayActorUri,
|
|
140
140
|
object: follow
|
|
@@ -210,7 +210,222 @@ var LitePubRelay = class {
|
|
|
210
210
|
#subscriptionHandler;
|
|
211
211
|
constructor(options) {
|
|
212
212
|
this.#options = options;
|
|
213
|
-
this.#federation = (0, __fedify_fedify.createFederation)({
|
|
213
|
+
this.#federation = options.federation ?? (0, __fedify_fedify.createFederation)({
|
|
214
|
+
kv: options.kv,
|
|
215
|
+
queue: options.queue,
|
|
216
|
+
documentLoaderFactory: options.documentLoaderFactory,
|
|
217
|
+
authenticatedDocumentLoaderFactory: options.authenticatedDocumentLoaderFactory
|
|
218
|
+
});
|
|
219
|
+
this.#federation.setActorDispatcher("/users/{identifier}", async (ctx, identifier) => {
|
|
220
|
+
if (identifier !== RELAY_SERVER_ACTOR) return null;
|
|
221
|
+
const keys = await ctx.getActorKeyPairs(identifier);
|
|
222
|
+
return new __fedify_fedify_vocab.Application({
|
|
223
|
+
id: ctx.getActorUri(identifier),
|
|
224
|
+
preferredUsername: identifier,
|
|
225
|
+
name: "ActivityPub Relay",
|
|
226
|
+
summary: "LitePub-compatible ActivityPub relay server",
|
|
227
|
+
inbox: ctx.getInboxUri(),
|
|
228
|
+
followers: ctx.getFollowersUri(identifier),
|
|
229
|
+
following: ctx.getFollowingUri(identifier),
|
|
230
|
+
url: ctx.getActorUri(identifier),
|
|
231
|
+
publicKey: keys[0].cryptographicKey,
|
|
232
|
+
assertionMethods: keys.map((k) => k.multikey)
|
|
233
|
+
});
|
|
234
|
+
}).setKeyPairsDispatcher(async (_ctx, identifier) => {
|
|
235
|
+
if (identifier !== RELAY_SERVER_ACTOR) return [];
|
|
236
|
+
const rsaPairJson = await options.kv.get([
|
|
237
|
+
"keypair",
|
|
238
|
+
"rsa",
|
|
239
|
+
identifier
|
|
240
|
+
]);
|
|
241
|
+
const ed25519PairJson = await options.kv.get([
|
|
242
|
+
"keypair",
|
|
243
|
+
"ed25519",
|
|
244
|
+
identifier
|
|
245
|
+
]);
|
|
246
|
+
if (rsaPairJson == null || ed25519PairJson == null) {
|
|
247
|
+
const rsaPair$1 = await (0, __fedify_fedify.generateCryptoKeyPair)("RSASSA-PKCS1-v1_5");
|
|
248
|
+
const ed25519Pair$1 = await (0, __fedify_fedify.generateCryptoKeyPair)("Ed25519");
|
|
249
|
+
await options.kv.set([
|
|
250
|
+
"keypair",
|
|
251
|
+
"rsa",
|
|
252
|
+
identifier
|
|
253
|
+
], {
|
|
254
|
+
privateKey: await (0, __fedify_fedify.exportJwk)(rsaPair$1.privateKey),
|
|
255
|
+
publicKey: await (0, __fedify_fedify.exportJwk)(rsaPair$1.publicKey)
|
|
256
|
+
});
|
|
257
|
+
await options.kv.set([
|
|
258
|
+
"keypair",
|
|
259
|
+
"ed25519",
|
|
260
|
+
identifier
|
|
261
|
+
], {
|
|
262
|
+
privateKey: await (0, __fedify_fedify.exportJwk)(ed25519Pair$1.privateKey),
|
|
263
|
+
publicKey: await (0, __fedify_fedify.exportJwk)(ed25519Pair$1.publicKey)
|
|
264
|
+
});
|
|
265
|
+
return [rsaPair$1, ed25519Pair$1];
|
|
266
|
+
}
|
|
267
|
+
const rsaPair = {
|
|
268
|
+
privateKey: await (0, __fedify_fedify.importJwk)(rsaPairJson.privateKey, "private"),
|
|
269
|
+
publicKey: await (0, __fedify_fedify.importJwk)(rsaPairJson.publicKey, "public")
|
|
270
|
+
};
|
|
271
|
+
const ed25519Pair = {
|
|
272
|
+
privateKey: await (0, __fedify_fedify.importJwk)(ed25519PairJson.privateKey, "private"),
|
|
273
|
+
publicKey: await (0, __fedify_fedify.importJwk)(ed25519PairJson.publicKey, "public")
|
|
274
|
+
};
|
|
275
|
+
return [rsaPair, ed25519Pair];
|
|
276
|
+
});
|
|
277
|
+
this.#federation.setFollowingDispatcher("/users/{identifier}/following", (_ctx, _identifier) => {
|
|
278
|
+
return { items: [] };
|
|
279
|
+
}).setCounter((_ctx, _identifier) => 0);
|
|
280
|
+
this.#federation.setFollowersDispatcher("/users/{identifier}/followers", async (_ctx, identifier) => {
|
|
281
|
+
if (identifier !== RELAY_SERVER_ACTOR) return null;
|
|
282
|
+
const followers = await options.kv.get(["followers"]) ?? [];
|
|
283
|
+
const actors = [];
|
|
284
|
+
for (const followerId of followers) {
|
|
285
|
+
const follower = await options.kv.get(["follower", followerId]);
|
|
286
|
+
if (!follower) continue;
|
|
287
|
+
const actor = await __fedify_fedify_vocab.Object.fromJsonLd(follower.actor);
|
|
288
|
+
if (!(0, __fedify_fedify_vocab.isActor)(actor)) continue;
|
|
289
|
+
actors.push(actor);
|
|
290
|
+
}
|
|
291
|
+
return { items: actors };
|
|
292
|
+
});
|
|
293
|
+
this.#federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(__fedify_fedify_vocab.Follow, async (ctx, follow) => {
|
|
294
|
+
if (follow.id == null || follow.objectId == null) return;
|
|
295
|
+
const parsed = ctx.parseUri(follow.objectId);
|
|
296
|
+
const isPublicFollow = follow.objectId.href === "https://www.w3.org/ns/activitystreams#Public";
|
|
297
|
+
if (!isPublicFollow && parsed?.type !== "actor") return;
|
|
298
|
+
const relayActorUri = ctx.getActorUri(RELAY_SERVER_ACTOR);
|
|
299
|
+
const follower = await follow.getActor(ctx);
|
|
300
|
+
if (follower == null || follower.id == null || follower.preferredUsername == null || follower.inboxId == null) return;
|
|
301
|
+
const existingFollow = await options.kv.get(["follower", follower.id.href]);
|
|
302
|
+
if (existingFollow?.state === "pending") return;
|
|
303
|
+
let subscriptionApproved = false;
|
|
304
|
+
if (this.#subscriptionHandler) subscriptionApproved = await this.#subscriptionHandler(ctx, follower);
|
|
305
|
+
if (subscriptionApproved) {
|
|
306
|
+
await options.kv.set(["follower", follower.id.href], {
|
|
307
|
+
"actor": await follower.toJsonLd(),
|
|
308
|
+
"state": "pending"
|
|
309
|
+
});
|
|
310
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new __fedify_fedify_vocab.Accept({
|
|
311
|
+
id: new URL(`#accepts`, relayActorUri),
|
|
312
|
+
actor: relayActorUri,
|
|
313
|
+
object: follow
|
|
314
|
+
}));
|
|
315
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new __fedify_fedify_vocab.Follow({
|
|
316
|
+
actor: relayActorUri,
|
|
317
|
+
object: follower.id,
|
|
318
|
+
to: follower.id
|
|
319
|
+
}));
|
|
320
|
+
} else await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new __fedify_fedify_vocab.Reject({
|
|
321
|
+
id: new URL(`#rejects`, relayActorUri),
|
|
322
|
+
actor: relayActorUri,
|
|
323
|
+
object: follow
|
|
324
|
+
}));
|
|
325
|
+
}).on(__fedify_fedify_vocab.Accept, async (ctx, accept) => {
|
|
326
|
+
const follow = await accept.getObject({
|
|
327
|
+
crossOrigin: "trust",
|
|
328
|
+
...ctx
|
|
329
|
+
});
|
|
330
|
+
if (!(follow instanceof __fedify_fedify_vocab.Follow)) return;
|
|
331
|
+
const follower = follow.actorId;
|
|
332
|
+
if (follower == null) return;
|
|
333
|
+
const following = await accept.getActor();
|
|
334
|
+
if (!(0, __fedify_fedify_vocab.isActor)(following) || !following.id) return;
|
|
335
|
+
const parsed = ctx.parseUri(follower);
|
|
336
|
+
if (parsed == null || parsed.type !== "actor") return;
|
|
337
|
+
const followerData = await options.kv.get(["follower", following.id.href]);
|
|
338
|
+
if (followerData == null) return;
|
|
339
|
+
const updatedFollowerData = {
|
|
340
|
+
...followerData,
|
|
341
|
+
status: "accepted"
|
|
342
|
+
};
|
|
343
|
+
await options.kv.set(["follower", following.id.href], updatedFollowerData);
|
|
344
|
+
const followers = await options.kv.get(["followers"]) ?? [];
|
|
345
|
+
followers.push(following.id.href);
|
|
346
|
+
await options.kv.set(["followers"], followers);
|
|
347
|
+
}).on(__fedify_fedify_vocab.Undo, async (ctx, undo) => {
|
|
348
|
+
const activity = await undo.getObject({
|
|
349
|
+
crossOrigin: "trust",
|
|
350
|
+
...ctx
|
|
351
|
+
});
|
|
352
|
+
if (activity instanceof __fedify_fedify_vocab.Follow) {
|
|
353
|
+
if (activity.id == null || activity.actorId == null) return;
|
|
354
|
+
const followers = await options.kv.get(["followers"]) ?? [];
|
|
355
|
+
const updatedFollowers = followers.filter((id) => id !== activity.actorId?.href);
|
|
356
|
+
await options.kv.set(["followers"], updatedFollowers);
|
|
357
|
+
options.kv.delete(["follower", activity.actorId?.href]);
|
|
358
|
+
} else console.warn("Unsupported object type ({type}) for Undo activity: {object}", {
|
|
359
|
+
type: activity?.constructor.name,
|
|
360
|
+
object: activity
|
|
361
|
+
});
|
|
362
|
+
}).on(__fedify_fedify_vocab.Create, async (ctx, create) => {
|
|
363
|
+
const sender = await create.getActor(ctx);
|
|
364
|
+
const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
|
|
365
|
+
const announce = new __fedify_fedify_vocab.Announce({
|
|
366
|
+
id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
|
|
367
|
+
actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
|
|
368
|
+
object: create.objectId,
|
|
369
|
+
to: __fedify_fedify_vocab.PUBLIC_COLLECTION,
|
|
370
|
+
published: Temporal.Now.instant()
|
|
371
|
+
});
|
|
372
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
|
|
373
|
+
excludeBaseUris,
|
|
374
|
+
preferSharedInbox: true
|
|
375
|
+
});
|
|
376
|
+
}).on(__fedify_fedify_vocab.Update, async (ctx, update) => {
|
|
377
|
+
const sender = await update.getActor(ctx);
|
|
378
|
+
const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
|
|
379
|
+
const announce = new __fedify_fedify_vocab.Announce({
|
|
380
|
+
id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
|
|
381
|
+
actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
|
|
382
|
+
object: update.objectId,
|
|
383
|
+
to: __fedify_fedify_vocab.PUBLIC_COLLECTION
|
|
384
|
+
});
|
|
385
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
|
|
386
|
+
excludeBaseUris,
|
|
387
|
+
preferSharedInbox: true
|
|
388
|
+
});
|
|
389
|
+
}).on(__fedify_fedify_vocab.Move, async (ctx, move) => {
|
|
390
|
+
const sender = await move.getActor(ctx);
|
|
391
|
+
const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
|
|
392
|
+
const announce = new __fedify_fedify_vocab.Announce({
|
|
393
|
+
id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
|
|
394
|
+
actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
|
|
395
|
+
object: move.objectId,
|
|
396
|
+
to: __fedify_fedify_vocab.PUBLIC_COLLECTION
|
|
397
|
+
});
|
|
398
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
|
|
399
|
+
excludeBaseUris,
|
|
400
|
+
preferSharedInbox: true
|
|
401
|
+
});
|
|
402
|
+
}).on(__fedify_fedify_vocab.Delete, async (ctx, deleteActivity) => {
|
|
403
|
+
const sender = await deleteActivity.getActor(ctx);
|
|
404
|
+
const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
|
|
405
|
+
const announce = new __fedify_fedify_vocab.Announce({
|
|
406
|
+
id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
|
|
407
|
+
actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
|
|
408
|
+
object: deleteActivity.objectId,
|
|
409
|
+
to: __fedify_fedify_vocab.PUBLIC_COLLECTION
|
|
410
|
+
});
|
|
411
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
|
|
412
|
+
excludeBaseUris,
|
|
413
|
+
preferSharedInbox: true
|
|
414
|
+
});
|
|
415
|
+
}).on(__fedify_fedify_vocab.Announce, async (ctx, announceActivity) => {
|
|
416
|
+
const sender = await announceActivity.getActor(ctx);
|
|
417
|
+
const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
|
|
418
|
+
const announce = new __fedify_fedify_vocab.Announce({
|
|
419
|
+
id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
|
|
420
|
+
actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
|
|
421
|
+
object: announceActivity.objectId,
|
|
422
|
+
to: __fedify_fedify_vocab.PUBLIC_COLLECTION
|
|
423
|
+
});
|
|
424
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
|
|
425
|
+
excludeBaseUris,
|
|
426
|
+
preferSharedInbox: true
|
|
427
|
+
});
|
|
428
|
+
});
|
|
214
429
|
}
|
|
215
430
|
get domain() {
|
|
216
431
|
return this.#options.domain || "localhost";
|
package/dist/mod.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createFederation, exportJwk, generateCryptoKeyPair, importJwk } from "@fedify/fedify";
|
|
2
|
-
import { Accept, Create, Delete, Follow, Move, Object as Object$1, Reject, Service, Undo, Update, isActor } from "@fedify/fedify/vocab";
|
|
2
|
+
import { Accept, Announce, Application, Create, Delete, Follow, Move, Object as Object$1, PUBLIC_COLLECTION, Reject, Service, Undo, Update, isActor } from "@fedify/fedify/vocab";
|
|
3
3
|
|
|
4
4
|
//#region src/relay.ts
|
|
5
5
|
const RELAY_SERVER_ACTOR = "relay";
|
|
@@ -97,21 +97,21 @@ var MastodonRelay = class {
|
|
|
97
97
|
const isPublicFollow = follow.objectId.href === "https://www.w3.org/ns/activitystreams#Public";
|
|
98
98
|
if (!isPublicFollow && parsed?.type !== "actor") return;
|
|
99
99
|
const relayActorUri = ctx.getActorUri(RELAY_SERVER_ACTOR);
|
|
100
|
-
const
|
|
101
|
-
if (
|
|
100
|
+
const follower = await follow.getActor(ctx);
|
|
101
|
+
if (follower == null || follower.id == null || follower.preferredUsername == null || follower.inboxId == null) return;
|
|
102
102
|
let approved = false;
|
|
103
|
-
if (this.#subscriptionHandler) approved = await this.#subscriptionHandler(ctx,
|
|
103
|
+
if (this.#subscriptionHandler) approved = await this.#subscriptionHandler(ctx, follower);
|
|
104
104
|
if (approved) {
|
|
105
105
|
const followers = await options.kv.get(["followers"]) ?? [];
|
|
106
106
|
followers.push(follow.id.href);
|
|
107
107
|
await options.kv.set(["followers"], followers);
|
|
108
|
-
await options.kv.set(["follower", follow.id.href], await
|
|
109
|
-
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR },
|
|
108
|
+
await options.kv.set(["follower", follow.id.href], await follower.toJsonLd());
|
|
109
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new Accept({
|
|
110
110
|
id: new URL(`#accepts`, relayActorUri),
|
|
111
111
|
actor: relayActorUri,
|
|
112
112
|
object: follow
|
|
113
113
|
}));
|
|
114
|
-
} else await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR },
|
|
114
|
+
} else await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new Reject({
|
|
115
115
|
id: new URL(`#rejects`, relayActorUri),
|
|
116
116
|
actor: relayActorUri,
|
|
117
117
|
object: follow
|
|
@@ -187,7 +187,222 @@ var LitePubRelay = class {
|
|
|
187
187
|
#subscriptionHandler;
|
|
188
188
|
constructor(options) {
|
|
189
189
|
this.#options = options;
|
|
190
|
-
this.#federation = createFederation({
|
|
190
|
+
this.#federation = options.federation ?? createFederation({
|
|
191
|
+
kv: options.kv,
|
|
192
|
+
queue: options.queue,
|
|
193
|
+
documentLoaderFactory: options.documentLoaderFactory,
|
|
194
|
+
authenticatedDocumentLoaderFactory: options.authenticatedDocumentLoaderFactory
|
|
195
|
+
});
|
|
196
|
+
this.#federation.setActorDispatcher("/users/{identifier}", async (ctx, identifier) => {
|
|
197
|
+
if (identifier !== RELAY_SERVER_ACTOR) return null;
|
|
198
|
+
const keys = await ctx.getActorKeyPairs(identifier);
|
|
199
|
+
return new Application({
|
|
200
|
+
id: ctx.getActorUri(identifier),
|
|
201
|
+
preferredUsername: identifier,
|
|
202
|
+
name: "ActivityPub Relay",
|
|
203
|
+
summary: "LitePub-compatible ActivityPub relay server",
|
|
204
|
+
inbox: ctx.getInboxUri(),
|
|
205
|
+
followers: ctx.getFollowersUri(identifier),
|
|
206
|
+
following: ctx.getFollowingUri(identifier),
|
|
207
|
+
url: ctx.getActorUri(identifier),
|
|
208
|
+
publicKey: keys[0].cryptographicKey,
|
|
209
|
+
assertionMethods: keys.map((k) => k.multikey)
|
|
210
|
+
});
|
|
211
|
+
}).setKeyPairsDispatcher(async (_ctx, identifier) => {
|
|
212
|
+
if (identifier !== RELAY_SERVER_ACTOR) return [];
|
|
213
|
+
const rsaPairJson = await options.kv.get([
|
|
214
|
+
"keypair",
|
|
215
|
+
"rsa",
|
|
216
|
+
identifier
|
|
217
|
+
]);
|
|
218
|
+
const ed25519PairJson = await options.kv.get([
|
|
219
|
+
"keypair",
|
|
220
|
+
"ed25519",
|
|
221
|
+
identifier
|
|
222
|
+
]);
|
|
223
|
+
if (rsaPairJson == null || ed25519PairJson == null) {
|
|
224
|
+
const rsaPair$1 = await generateCryptoKeyPair("RSASSA-PKCS1-v1_5");
|
|
225
|
+
const ed25519Pair$1 = await generateCryptoKeyPair("Ed25519");
|
|
226
|
+
await options.kv.set([
|
|
227
|
+
"keypair",
|
|
228
|
+
"rsa",
|
|
229
|
+
identifier
|
|
230
|
+
], {
|
|
231
|
+
privateKey: await exportJwk(rsaPair$1.privateKey),
|
|
232
|
+
publicKey: await exportJwk(rsaPair$1.publicKey)
|
|
233
|
+
});
|
|
234
|
+
await options.kv.set([
|
|
235
|
+
"keypair",
|
|
236
|
+
"ed25519",
|
|
237
|
+
identifier
|
|
238
|
+
], {
|
|
239
|
+
privateKey: await exportJwk(ed25519Pair$1.privateKey),
|
|
240
|
+
publicKey: await exportJwk(ed25519Pair$1.publicKey)
|
|
241
|
+
});
|
|
242
|
+
return [rsaPair$1, ed25519Pair$1];
|
|
243
|
+
}
|
|
244
|
+
const rsaPair = {
|
|
245
|
+
privateKey: await importJwk(rsaPairJson.privateKey, "private"),
|
|
246
|
+
publicKey: await importJwk(rsaPairJson.publicKey, "public")
|
|
247
|
+
};
|
|
248
|
+
const ed25519Pair = {
|
|
249
|
+
privateKey: await importJwk(ed25519PairJson.privateKey, "private"),
|
|
250
|
+
publicKey: await importJwk(ed25519PairJson.publicKey, "public")
|
|
251
|
+
};
|
|
252
|
+
return [rsaPair, ed25519Pair];
|
|
253
|
+
});
|
|
254
|
+
this.#federation.setFollowingDispatcher("/users/{identifier}/following", (_ctx, _identifier) => {
|
|
255
|
+
return { items: [] };
|
|
256
|
+
}).setCounter((_ctx, _identifier) => 0);
|
|
257
|
+
this.#federation.setFollowersDispatcher("/users/{identifier}/followers", async (_ctx, identifier) => {
|
|
258
|
+
if (identifier !== RELAY_SERVER_ACTOR) return null;
|
|
259
|
+
const followers = await options.kv.get(["followers"]) ?? [];
|
|
260
|
+
const actors = [];
|
|
261
|
+
for (const followerId of followers) {
|
|
262
|
+
const follower = await options.kv.get(["follower", followerId]);
|
|
263
|
+
if (!follower) continue;
|
|
264
|
+
const actor = await Object$1.fromJsonLd(follower.actor);
|
|
265
|
+
if (!isActor(actor)) continue;
|
|
266
|
+
actors.push(actor);
|
|
267
|
+
}
|
|
268
|
+
return { items: actors };
|
|
269
|
+
});
|
|
270
|
+
this.#federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Follow, async (ctx, follow) => {
|
|
271
|
+
if (follow.id == null || follow.objectId == null) return;
|
|
272
|
+
const parsed = ctx.parseUri(follow.objectId);
|
|
273
|
+
const isPublicFollow = follow.objectId.href === "https://www.w3.org/ns/activitystreams#Public";
|
|
274
|
+
if (!isPublicFollow && parsed?.type !== "actor") return;
|
|
275
|
+
const relayActorUri = ctx.getActorUri(RELAY_SERVER_ACTOR);
|
|
276
|
+
const follower = await follow.getActor(ctx);
|
|
277
|
+
if (follower == null || follower.id == null || follower.preferredUsername == null || follower.inboxId == null) return;
|
|
278
|
+
const existingFollow = await options.kv.get(["follower", follower.id.href]);
|
|
279
|
+
if (existingFollow?.state === "pending") return;
|
|
280
|
+
let subscriptionApproved = false;
|
|
281
|
+
if (this.#subscriptionHandler) subscriptionApproved = await this.#subscriptionHandler(ctx, follower);
|
|
282
|
+
if (subscriptionApproved) {
|
|
283
|
+
await options.kv.set(["follower", follower.id.href], {
|
|
284
|
+
"actor": await follower.toJsonLd(),
|
|
285
|
+
"state": "pending"
|
|
286
|
+
});
|
|
287
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new Accept({
|
|
288
|
+
id: new URL(`#accepts`, relayActorUri),
|
|
289
|
+
actor: relayActorUri,
|
|
290
|
+
object: follow
|
|
291
|
+
}));
|
|
292
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new Follow({
|
|
293
|
+
actor: relayActorUri,
|
|
294
|
+
object: follower.id,
|
|
295
|
+
to: follower.id
|
|
296
|
+
}));
|
|
297
|
+
} else await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new Reject({
|
|
298
|
+
id: new URL(`#rejects`, relayActorUri),
|
|
299
|
+
actor: relayActorUri,
|
|
300
|
+
object: follow
|
|
301
|
+
}));
|
|
302
|
+
}).on(Accept, async (ctx, accept) => {
|
|
303
|
+
const follow = await accept.getObject({
|
|
304
|
+
crossOrigin: "trust",
|
|
305
|
+
...ctx
|
|
306
|
+
});
|
|
307
|
+
if (!(follow instanceof Follow)) return;
|
|
308
|
+
const follower = follow.actorId;
|
|
309
|
+
if (follower == null) return;
|
|
310
|
+
const following = await accept.getActor();
|
|
311
|
+
if (!isActor(following) || !following.id) return;
|
|
312
|
+
const parsed = ctx.parseUri(follower);
|
|
313
|
+
if (parsed == null || parsed.type !== "actor") return;
|
|
314
|
+
const followerData = await options.kv.get(["follower", following.id.href]);
|
|
315
|
+
if (followerData == null) return;
|
|
316
|
+
const updatedFollowerData = {
|
|
317
|
+
...followerData,
|
|
318
|
+
status: "accepted"
|
|
319
|
+
};
|
|
320
|
+
await options.kv.set(["follower", following.id.href], updatedFollowerData);
|
|
321
|
+
const followers = await options.kv.get(["followers"]) ?? [];
|
|
322
|
+
followers.push(following.id.href);
|
|
323
|
+
await options.kv.set(["followers"], followers);
|
|
324
|
+
}).on(Undo, async (ctx, undo) => {
|
|
325
|
+
const activity = await undo.getObject({
|
|
326
|
+
crossOrigin: "trust",
|
|
327
|
+
...ctx
|
|
328
|
+
});
|
|
329
|
+
if (activity instanceof Follow) {
|
|
330
|
+
if (activity.id == null || activity.actorId == null) return;
|
|
331
|
+
const followers = await options.kv.get(["followers"]) ?? [];
|
|
332
|
+
const updatedFollowers = followers.filter((id) => id !== activity.actorId?.href);
|
|
333
|
+
await options.kv.set(["followers"], updatedFollowers);
|
|
334
|
+
options.kv.delete(["follower", activity.actorId?.href]);
|
|
335
|
+
} else console.warn("Unsupported object type ({type}) for Undo activity: {object}", {
|
|
336
|
+
type: activity?.constructor.name,
|
|
337
|
+
object: activity
|
|
338
|
+
});
|
|
339
|
+
}).on(Create, async (ctx, create) => {
|
|
340
|
+
const sender = await create.getActor(ctx);
|
|
341
|
+
const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
|
|
342
|
+
const announce = new Announce({
|
|
343
|
+
id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
|
|
344
|
+
actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
|
|
345
|
+
object: create.objectId,
|
|
346
|
+
to: PUBLIC_COLLECTION,
|
|
347
|
+
published: Temporal.Now.instant()
|
|
348
|
+
});
|
|
349
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
|
|
350
|
+
excludeBaseUris,
|
|
351
|
+
preferSharedInbox: true
|
|
352
|
+
});
|
|
353
|
+
}).on(Update, async (ctx, update) => {
|
|
354
|
+
const sender = await update.getActor(ctx);
|
|
355
|
+
const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
|
|
356
|
+
const announce = new Announce({
|
|
357
|
+
id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
|
|
358
|
+
actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
|
|
359
|
+
object: update.objectId,
|
|
360
|
+
to: PUBLIC_COLLECTION
|
|
361
|
+
});
|
|
362
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
|
|
363
|
+
excludeBaseUris,
|
|
364
|
+
preferSharedInbox: true
|
|
365
|
+
});
|
|
366
|
+
}).on(Move, async (ctx, move) => {
|
|
367
|
+
const sender = await move.getActor(ctx);
|
|
368
|
+
const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
|
|
369
|
+
const announce = new Announce({
|
|
370
|
+
id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
|
|
371
|
+
actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
|
|
372
|
+
object: move.objectId,
|
|
373
|
+
to: PUBLIC_COLLECTION
|
|
374
|
+
});
|
|
375
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
|
|
376
|
+
excludeBaseUris,
|
|
377
|
+
preferSharedInbox: true
|
|
378
|
+
});
|
|
379
|
+
}).on(Delete, async (ctx, deleteActivity) => {
|
|
380
|
+
const sender = await deleteActivity.getActor(ctx);
|
|
381
|
+
const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
|
|
382
|
+
const announce = new Announce({
|
|
383
|
+
id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
|
|
384
|
+
actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
|
|
385
|
+
object: deleteActivity.objectId,
|
|
386
|
+
to: PUBLIC_COLLECTION
|
|
387
|
+
});
|
|
388
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
|
|
389
|
+
excludeBaseUris,
|
|
390
|
+
preferSharedInbox: true
|
|
391
|
+
});
|
|
392
|
+
}).on(Announce, async (ctx, announceActivity) => {
|
|
393
|
+
const sender = await announceActivity.getActor(ctx);
|
|
394
|
+
const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
|
|
395
|
+
const announce = new Announce({
|
|
396
|
+
id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
|
|
397
|
+
actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
|
|
398
|
+
object: announceActivity.objectId,
|
|
399
|
+
to: PUBLIC_COLLECTION
|
|
400
|
+
});
|
|
401
|
+
await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
|
|
402
|
+
excludeBaseUris,
|
|
403
|
+
preferSharedInbox: true
|
|
404
|
+
});
|
|
405
|
+
});
|
|
191
406
|
}
|
|
192
407
|
get domain() {
|
|
193
408
|
return this.#options.domain || "localhost";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/relay",
|
|
3
|
-
"version": "2.0.0-pr.471.
|
|
3
|
+
"version": "2.0.0-pr.471.1920+38258210",
|
|
4
4
|
"description": "ActivityPub relay support for Fedify",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Fedify",
|
|
@@ -48,17 +48,19 @@
|
|
|
48
48
|
"package.json"
|
|
49
49
|
],
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@fedify/fedify": "^2.0.0-pr.471.
|
|
51
|
+
"@fedify/fedify": "^2.0.0-pr.471.1920+38258210"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"tsdown": "^0.12.9",
|
|
55
|
-
"typescript": "^5.9.3"
|
|
55
|
+
"typescript": "^5.9.3",
|
|
56
|
+
"@fedify/testing": "^2.0.0-pr.471.1920+38258210",
|
|
57
|
+
"@fedify/vocab-runtime": "^2.0.0-pr.471.1920+38258210"
|
|
56
58
|
},
|
|
57
59
|
"scripts": {
|
|
58
60
|
"build": "deno task codegen && tsdown",
|
|
59
61
|
"prepublish": "deno task codegen && tsdown",
|
|
60
|
-
"test": "deno task codegen && tsdown &&
|
|
61
|
-
"test:bun": "deno task codegen &&
|
|
62
|
+
"test": "deno task codegen && tsdown && node --test",
|
|
63
|
+
"test:bun": "deno task codegen && tsdown && bun test --timeout 60000",
|
|
62
64
|
"test:cfworkers": "deno task codegen && wrangler deploy --dry-run --outdir src/cfworkers && node --import=tsx src/cfworkers/client.ts"
|
|
63
65
|
}
|
|
64
66
|
}
|