@fedify/fedify 2.2.0-dev.802 → 2.2.0-pr.695.16
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/{builder-DI1wKPDM.mjs → builder-7PVCiLiR.mjs} +7 -57
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{context-BGrYMSTk.d.ts → context-78ecvxf5.d.ts} +1 -143
- package/dist/{context-CMUd4wy0.d.cts → context-DYDPdoCb.d.cts} +1 -143
- package/dist/{context-Dk_tacqz.mjs → context-Juj6bdHC.mjs} +2 -17
- package/dist/{deno-Cw2dzrO3.mjs → deno-vxcWcxQS.mjs} +1 -1
- package/dist/{docloader-DgqPtQaA.mjs → docloader-D7q0-Xef.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -25
- package/dist/federation/handler.test.mjs +8 -369
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox.test.mjs +3 -3
- package/dist/federation/middleware.test.mjs +8 -510
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/webfinger.test.mjs +2 -2
- package/dist/{http-sBAA3RBV.js → http-D-MhhYUF.js} +1 -1
- package/dist/{http-B8Eh-Nuv.cjs → http-JxF7bG0o.cjs} +1 -1
- package/dist/{http-iFvJsgX5.mjs → http-RZPxDWq5.mjs} +2 -2
- package/dist/inbox-CmYvcSMM.mjs +179 -0
- package/dist/{key-hslhDRFM.mjs → key-CGx_dDkX.mjs} +1 -1
- package/dist/{kv-cache-BJGwpmdO.cjs → kv-cache-C2gdVgvb.cjs} +1 -1
- package/dist/{kv-cache-CPthE3cU.js → kv-cache-D84Mk0fZ.js} +1 -1
- package/dist/{ld-BDJhDECh.mjs → ld-wup-liFO.mjs} +3 -26
- package/dist/{middleware-CERbaJNL.mjs → middleware-BjVx-_bv.mjs} +180 -612
- package/dist/{middleware-gIhAvTk3.cjs → middleware-Bn75dPug.cjs} +365 -718
- package/dist/{middleware-uOgNgCF7.cjs → middleware-CXOVT4Ph.cjs} +1 -1
- package/dist/{middleware-D4erN-QW.js → middleware-RF-sUfTr.js} +368 -716
- package/dist/{middleware-_UvHRcH-.mjs → middleware-wdfeWjRJ.mjs} +1 -1
- package/dist/{mod-BcJHeuv1.d.cts → mod-CEohtXhV.d.cts} +1 -1
- package/dist/{mod-CJXfyw7v.d.ts → mod-CokIUYDr.d.ts} +1 -1
- package/dist/{mod-Cr3f-ACa.d.cts → mod-DoJBjjnO.d.cts} +1 -18
- package/dist/{mod-CR8soWa9.d.ts → mod-DvxszxXC.d.ts} +1 -18
- package/dist/mod.cjs +4 -6
- package/dist/mod.d.cts +5 -5
- package/dist/mod.d.ts +5 -5
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/handler.test.mjs +2 -2
- package/dist/{owner-B4-VE_g8.mjs → owner-q2mUMM9a.mjs} +2 -2
- package/dist/{proof-B6PfWGbc.mjs → proof--CpZsF_p.mjs} +3 -32
- package/dist/{proof-BqB_7rN2.js → proof-CirP9OSd.js} +2 -54
- package/dist/{proof-4H8fV7OE.cjs → proof-_Zyfqyce.cjs} +3 -61
- package/dist/{send-BIh8F_6u.mjs → send-CVJfx7bF.mjs} +2 -2
- package/dist/sig/http.test.mjs +2 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +2 -44
- package/dist/sig/mod.cjs +2 -4
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/proof.test.mjs +2 -46
- package/dist/testing/mod.d.mts +1 -149
- package/dist/testing/mod.mjs +2 -2
- package/dist/utils/docloader.test.mjs +2 -2
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +6 -6
- package/dist/activity-listener-Ck3JZ_hR.mjs +0 -40
|
@@ -2,23 +2,24 @@ import { Temporal } from "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { n as RouterError } from "./router-CrMLXoOr.mjs";
|
|
5
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
+
import { n as version, t as name } from "./deno-vxcWcxQS.mjs";
|
|
6
6
|
import { t as formatAcceptSignature } from "./accept-Dd__NiUL.mjs";
|
|
7
|
-
import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-
|
|
8
|
-
import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-
|
|
9
|
-
import { t as getAuthenticatedDocumentLoader } from "./docloader-
|
|
7
|
+
import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-CGx_dDkX.mjs";
|
|
8
|
+
import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-RZPxDWq5.mjs";
|
|
9
|
+
import { t as getAuthenticatedDocumentLoader } from "./docloader-D7q0-Xef.mjs";
|
|
10
10
|
import { n as kvCache } from "./kv-cache-B01V7s3h.mjs";
|
|
11
|
-
import { a as signJsonLd, i as
|
|
12
|
-
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-
|
|
13
|
-
import {
|
|
11
|
+
import { a as signJsonLd, i as hasSignature, o as verifyJsonLd, r as detachSignature } from "./ld-wup-liFO.mjs";
|
|
12
|
+
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-q2mUMM9a.mjs";
|
|
13
|
+
import { n as signObject, r as verifyObject } from "./proof--CpZsF_p.mjs";
|
|
14
14
|
import { t as getNodeInfo } from "./client-DEpOVgY1.mjs";
|
|
15
15
|
import { t as nodeInfoToJson } from "./types-DCP0WLdt.mjs";
|
|
16
|
-
import {
|
|
16
|
+
import { n as routeActivity } from "./inbox-CmYvcSMM.mjs";
|
|
17
|
+
import { t as FederationBuilderImpl } from "./builder-7PVCiLiR.mjs";
|
|
17
18
|
import { t as buildCollectionSynchronizationHeader } from "./collection-BD6-SZ6O.mjs";
|
|
18
19
|
import { t as KvKeyCache } from "./keycache-CCSwkQcY.mjs";
|
|
19
20
|
import { t as acceptsJsonLd } from "./negotiation-DnsfFF8I.mjs";
|
|
20
21
|
import { t as createExponentialBackoffPolicy } from "./retry-B_E3V_Dx.mjs";
|
|
21
|
-
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-
|
|
22
|
+
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-CVJfx7bF.mjs";
|
|
22
23
|
import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
23
24
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
24
25
|
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
|
|
@@ -152,144 +153,6 @@ function handleNodeInfoJrd(_request, context) {
|
|
|
152
153
|
return Promise.resolve(response);
|
|
153
154
|
}
|
|
154
155
|
//#endregion
|
|
155
|
-
//#region src/federation/inbox.ts
|
|
156
|
-
async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, tracerProvider, idempotencyStrategy }) {
|
|
157
|
-
const logger = getLogger([
|
|
158
|
-
"fedify",
|
|
159
|
-
"federation",
|
|
160
|
-
"inbox"
|
|
161
|
-
]);
|
|
162
|
-
let cacheKey = null;
|
|
163
|
-
if (activity.id != null) {
|
|
164
|
-
const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, getTypeId(activity).href);
|
|
165
|
-
const strategy = idempotencyStrategy ?? "per-inbox";
|
|
166
|
-
let keyString;
|
|
167
|
-
if (typeof strategy === "function") keyString = await strategy(inboxContext, activity);
|
|
168
|
-
else switch (strategy) {
|
|
169
|
-
case "global":
|
|
170
|
-
keyString = activity.id.href;
|
|
171
|
-
break;
|
|
172
|
-
case "per-origin":
|
|
173
|
-
keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
174
|
-
break;
|
|
175
|
-
case "per-inbox":
|
|
176
|
-
keyString = `${ctx.origin}\n${activity.id.href}\n${recipient == null ? "sharedInbox" : `inbox\n${recipient}`}`;
|
|
177
|
-
break;
|
|
178
|
-
default: keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
179
|
-
}
|
|
180
|
-
if (keyString != null) cacheKey = [...kvPrefixes.activityIdempotence, keyString];
|
|
181
|
-
}
|
|
182
|
-
if (cacheKey != null) {
|
|
183
|
-
if (await kv.get(cacheKey) === true) {
|
|
184
|
-
logger.debug("Activity {activityId} has already been processed.", {
|
|
185
|
-
activityId: activity.id?.href,
|
|
186
|
-
activity: json,
|
|
187
|
-
recipient
|
|
188
|
-
});
|
|
189
|
-
span.setStatus({
|
|
190
|
-
code: SpanStatusCode.UNSET,
|
|
191
|
-
message: `Activity ${activity.id?.href} has already been processed.`
|
|
192
|
-
});
|
|
193
|
-
return "alreadyProcessed";
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
if (activity.actorId == null) {
|
|
197
|
-
logger.error("Missing actor.", { activity: json });
|
|
198
|
-
span.setStatus({
|
|
199
|
-
code: SpanStatusCode.ERROR,
|
|
200
|
-
message: "Missing actor."
|
|
201
|
-
});
|
|
202
|
-
return "missingActor";
|
|
203
|
-
}
|
|
204
|
-
span.setAttribute("activitypub.actor.id", activity.actorId.href);
|
|
205
|
-
if (queue != null) {
|
|
206
|
-
const carrier = {};
|
|
207
|
-
propagation.inject(context.active(), carrier);
|
|
208
|
-
try {
|
|
209
|
-
await queue.enqueue({
|
|
210
|
-
type: "inbox",
|
|
211
|
-
id: crypto.randomUUID(),
|
|
212
|
-
baseUrl: ctx.origin,
|
|
213
|
-
activity: json,
|
|
214
|
-
identifier: recipient,
|
|
215
|
-
attempt: 0,
|
|
216
|
-
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
217
|
-
traceContext: carrier
|
|
218
|
-
});
|
|
219
|
-
} catch (error) {
|
|
220
|
-
logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
|
|
221
|
-
error,
|
|
222
|
-
activityId: activity.id?.href,
|
|
223
|
-
activity: json,
|
|
224
|
-
recipient
|
|
225
|
-
});
|
|
226
|
-
span.setStatus({
|
|
227
|
-
code: SpanStatusCode.ERROR,
|
|
228
|
-
message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
|
|
229
|
-
});
|
|
230
|
-
throw error;
|
|
231
|
-
}
|
|
232
|
-
logger.info("Activity {activityId} is enqueued.", {
|
|
233
|
-
activityId: activity.id?.href,
|
|
234
|
-
activity: json,
|
|
235
|
-
recipient
|
|
236
|
-
});
|
|
237
|
-
return "enqueued";
|
|
238
|
-
}
|
|
239
|
-
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
240
|
-
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
|
|
241
|
-
const dispatched = inboxListeners?.dispatchWithClass(activity);
|
|
242
|
-
if (dispatched == null) {
|
|
243
|
-
logger.error("Unsupported activity type:\n{activity}", {
|
|
244
|
-
activity: json,
|
|
245
|
-
recipient
|
|
246
|
-
});
|
|
247
|
-
span.setStatus({
|
|
248
|
-
code: SpanStatusCode.UNSET,
|
|
249
|
-
message: `Unsupported activity type: ${getTypeId(activity).href}`
|
|
250
|
-
});
|
|
251
|
-
span.end();
|
|
252
|
-
return "unsupportedActivity";
|
|
253
|
-
}
|
|
254
|
-
const { class: cls, listener } = dispatched;
|
|
255
|
-
span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
|
256
|
-
try {
|
|
257
|
-
await listener(inboxContextFactory(recipient, json, activity?.id?.href, getTypeId(activity).href), activity);
|
|
258
|
-
} catch (error) {
|
|
259
|
-
try {
|
|
260
|
-
await inboxErrorHandler?.(ctx, error);
|
|
261
|
-
} catch (error) {
|
|
262
|
-
logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
263
|
-
error,
|
|
264
|
-
activityId: activity.id?.href,
|
|
265
|
-
activity: json,
|
|
266
|
-
recipient
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
|
|
270
|
-
error,
|
|
271
|
-
activityId: activity.id?.href,
|
|
272
|
-
activity: json,
|
|
273
|
-
recipient
|
|
274
|
-
});
|
|
275
|
-
span.setStatus({
|
|
276
|
-
code: SpanStatusCode.ERROR,
|
|
277
|
-
message: String(error)
|
|
278
|
-
});
|
|
279
|
-
span.end();
|
|
280
|
-
return "error";
|
|
281
|
-
}
|
|
282
|
-
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
|
|
283
|
-
logger.info("Activity {activityId} has been processed.", {
|
|
284
|
-
activityId: activity.id?.href,
|
|
285
|
-
activity: json,
|
|
286
|
-
recipient
|
|
287
|
-
});
|
|
288
|
-
span.end();
|
|
289
|
-
return "success";
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
//#endregion
|
|
293
156
|
//#region src/federation/handler.ts
|
|
294
157
|
/**
|
|
295
158
|
* Handles an actor request.
|
|
@@ -362,8 +225,8 @@ async function handleObject(request, { values, context, objectDispatcher, author
|
|
|
362
225
|
* @param parameters The parameters for handling the collection.
|
|
363
226
|
* @returns A promise that resolves to an HTTP response.
|
|
364
227
|
*/
|
|
365
|
-
async function handleCollection(request, { name: name$
|
|
366
|
-
const spanName = name$
|
|
228
|
+
async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, onUnauthorized, onNotFound }) {
|
|
229
|
+
const spanName = name$2.trim().replace(/\s+/g, "_");
|
|
367
230
|
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
368
231
|
const tracer = tracerProvider.getTracer(name, version);
|
|
369
232
|
const cursor = new URL(request.url).searchParams.get("cursor");
|
|
@@ -405,7 +268,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
405
268
|
collection = new OrderedCollection({
|
|
406
269
|
id: baseUri,
|
|
407
270
|
totalItems: totalItems == null ? null : Number(totalItems),
|
|
408
|
-
items: filterCollectionItems(itemsOrResponse, name$
|
|
271
|
+
items: filterCollectionItems(itemsOrResponse, name$2, filterPredicate)
|
|
409
272
|
});
|
|
410
273
|
} else {
|
|
411
274
|
const lastCursor = await collectionCallbacks.lastCursor?.(context, identifier);
|
|
@@ -426,7 +289,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
426
289
|
} else {
|
|
427
290
|
const uri = new URL(baseUri);
|
|
428
291
|
uri.searchParams.set("cursor", cursor);
|
|
429
|
-
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$
|
|
292
|
+
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
|
|
430
293
|
kind: SpanKind.SERVER,
|
|
431
294
|
attributes: {
|
|
432
295
|
"activitypub.collection.id": uri.href,
|
|
@@ -470,7 +333,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
|
|
|
470
333
|
id: uri,
|
|
471
334
|
prev,
|
|
472
335
|
next,
|
|
473
|
-
items: filterCollectionItems(items, name$
|
|
336
|
+
items: filterCollectionItems(items, name$2, filterPredicate),
|
|
474
337
|
partOf
|
|
475
338
|
});
|
|
476
339
|
}
|
|
@@ -514,202 +377,6 @@ function filterCollectionItems(items, collectionName, filterPredicate) {
|
|
|
514
377
|
}
|
|
515
378
|
return result;
|
|
516
379
|
}
|
|
517
|
-
function summarizeJsonActivity(json) {
|
|
518
|
-
if (json == null || typeof json !== "object") return {};
|
|
519
|
-
const activity = json;
|
|
520
|
-
return {
|
|
521
|
-
activityId: typeof activity.id === "string" ? activity.id : void 0,
|
|
522
|
-
activityType: typeof activity.type === "string" ? activity.type : void 0
|
|
523
|
-
};
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* Handles an outbox POST request.
|
|
527
|
-
* @template TContextData The context data to pass to the context.
|
|
528
|
-
* @param request The HTTP request.
|
|
529
|
-
* @param parameters The parameters for handling the request.
|
|
530
|
-
* @returns A promise that resolves to an HTTP response.
|
|
531
|
-
* @since 2.2.0
|
|
532
|
-
*/
|
|
533
|
-
async function handleOutbox(request, { identifier, context: ctx, outboxContextFactory, actorDispatcher, authorizePredicate, outboxListeners, outboxErrorHandler, onUnauthorized, onNotFound }) {
|
|
534
|
-
const logger = getLogger([
|
|
535
|
-
"fedify",
|
|
536
|
-
"federation",
|
|
537
|
-
"outbox"
|
|
538
|
-
]);
|
|
539
|
-
if (request.bodyUsed) {
|
|
540
|
-
logger.error("Request body has already been read.", { identifier });
|
|
541
|
-
return new Response("Internal server error.", {
|
|
542
|
-
status: 500,
|
|
543
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
544
|
-
});
|
|
545
|
-
} else if (request.body?.locked) {
|
|
546
|
-
logger.error("Request body is locked.", { identifier });
|
|
547
|
-
return new Response("Internal server error.", {
|
|
548
|
-
status: 500,
|
|
549
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
550
|
-
});
|
|
551
|
-
}
|
|
552
|
-
if (actorDispatcher == null) {
|
|
553
|
-
logger.error("Actor dispatcher is not set.", { identifier });
|
|
554
|
-
return await onNotFound(request);
|
|
555
|
-
}
|
|
556
|
-
if (authorizePredicate != null) {
|
|
557
|
-
const authorizeContext = ctx.clone(ctx.data);
|
|
558
|
-
authorizeContext.request = request.clone();
|
|
559
|
-
const requestForUnauthorized = authorizeContext.request.clone();
|
|
560
|
-
if (!await authorizePredicate(authorizeContext, identifier)) return await onUnauthorized(requestForUnauthorized);
|
|
561
|
-
}
|
|
562
|
-
const actor = await actorDispatcher(ctx, identifier);
|
|
563
|
-
if (actor == null || actor instanceof Tombstone) {
|
|
564
|
-
logger.error("Actor {identifier} not found.", { identifier });
|
|
565
|
-
return await onNotFound(request);
|
|
566
|
-
}
|
|
567
|
-
const requestForParsing = request.clone();
|
|
568
|
-
let json;
|
|
569
|
-
try {
|
|
570
|
-
json = await requestForParsing.json();
|
|
571
|
-
} catch (error) {
|
|
572
|
-
logger.error("Failed to parse JSON:\n{error}", {
|
|
573
|
-
identifier,
|
|
574
|
-
error
|
|
575
|
-
});
|
|
576
|
-
const outboxContext = outboxContextFactory(identifier, null, void 0, "");
|
|
577
|
-
try {
|
|
578
|
-
await outboxErrorHandler?.(outboxContext, error);
|
|
579
|
-
} catch (error) {
|
|
580
|
-
logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
|
|
581
|
-
error,
|
|
582
|
-
identifier
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
return new Response("Invalid JSON.", {
|
|
586
|
-
status: 400,
|
|
587
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
let activity;
|
|
591
|
-
try {
|
|
592
|
-
activity = await Activity.fromJsonLd(json, ctx);
|
|
593
|
-
} catch (error) {
|
|
594
|
-
const summary = summarizeJsonActivity(json);
|
|
595
|
-
logger.error("Failed to parse activity:\n{error}", {
|
|
596
|
-
identifier,
|
|
597
|
-
...summary,
|
|
598
|
-
error
|
|
599
|
-
});
|
|
600
|
-
const outboxContext = outboxContextFactory(identifier, json, summary.activityId, summary.activityType ?? "");
|
|
601
|
-
try {
|
|
602
|
-
await outboxErrorHandler?.(outboxContext, error);
|
|
603
|
-
} catch (error) {
|
|
604
|
-
logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
|
|
605
|
-
error,
|
|
606
|
-
identifier,
|
|
607
|
-
...summary
|
|
608
|
-
});
|
|
609
|
-
}
|
|
610
|
-
return new Response("Invalid activity.", {
|
|
611
|
-
status: 400,
|
|
612
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
const outboxContext = outboxContextFactory(identifier, json, activity.id?.href, getTypeId(activity).href);
|
|
616
|
-
const expectedActorId = actor.id ?? ctx.getActorUri(identifier);
|
|
617
|
-
if (activity.actorIds.length < 1) {
|
|
618
|
-
const error = /* @__PURE__ */ new Error("The posted activity has no actor.");
|
|
619
|
-
logger.error("The posted activity has no actor for outbox {identifier}.", {
|
|
620
|
-
identifier,
|
|
621
|
-
activityId: activity.id?.href,
|
|
622
|
-
expectedActorId: expectedActorId.href
|
|
623
|
-
});
|
|
624
|
-
try {
|
|
625
|
-
await outboxErrorHandler?.(outboxContext, error);
|
|
626
|
-
} catch (error) {
|
|
627
|
-
logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
|
|
628
|
-
error,
|
|
629
|
-
activityId: activity.id?.href,
|
|
630
|
-
activityType: getTypeId(activity).href,
|
|
631
|
-
identifier
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
return new Response(error.message, {
|
|
635
|
-
status: 400,
|
|
636
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
if (!activity.actorIds.every((actorId) => actorId.href === expectedActorId.href)) {
|
|
640
|
-
const error = /* @__PURE__ */ new Error("The activity actor does not match the outbox owner.");
|
|
641
|
-
logger.error("The posted activity actor does not match outbox owner {identifier}.", {
|
|
642
|
-
identifier,
|
|
643
|
-
activityId: activity.id?.href,
|
|
644
|
-
expectedActorId: expectedActorId.href,
|
|
645
|
-
actorIds: activity.actorIds.map((actorId) => actorId.href)
|
|
646
|
-
});
|
|
647
|
-
try {
|
|
648
|
-
await outboxErrorHandler?.(outboxContext, error);
|
|
649
|
-
} catch (error) {
|
|
650
|
-
logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
|
|
651
|
-
error,
|
|
652
|
-
activityId: activity.id?.href,
|
|
653
|
-
activityType: getTypeId(activity).href,
|
|
654
|
-
identifier
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
|
-
return new Response(error.message, {
|
|
658
|
-
status: 400,
|
|
659
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
660
|
-
});
|
|
661
|
-
}
|
|
662
|
-
const dispatched = outboxListeners?.dispatchWithClass(activity);
|
|
663
|
-
if (dispatched == null) {
|
|
664
|
-
logger.debug("Unsupported activity type {activityType}.", {
|
|
665
|
-
identifier,
|
|
666
|
-
activityId: activity.id?.href,
|
|
667
|
-
activityType: getTypeId(activity).href
|
|
668
|
-
});
|
|
669
|
-
return new Response("", {
|
|
670
|
-
status: 202,
|
|
671
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
try {
|
|
675
|
-
await dispatched.listener(outboxContext, activity);
|
|
676
|
-
} catch (error) {
|
|
677
|
-
try {
|
|
678
|
-
await outboxErrorHandler?.(outboxContext, error);
|
|
679
|
-
} catch (error) {
|
|
680
|
-
logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
|
|
681
|
-
error,
|
|
682
|
-
activityId: activity.id?.href,
|
|
683
|
-
activityType: getTypeId(activity).href,
|
|
684
|
-
identifier
|
|
685
|
-
});
|
|
686
|
-
}
|
|
687
|
-
logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
|
|
688
|
-
error,
|
|
689
|
-
activityId: activity.id?.href,
|
|
690
|
-
activityType: getTypeId(activity).href,
|
|
691
|
-
identifier
|
|
692
|
-
});
|
|
693
|
-
return new Response("Internal server error.", {
|
|
694
|
-
status: 500,
|
|
695
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
696
|
-
});
|
|
697
|
-
}
|
|
698
|
-
if (!outboxContext.hasDeliveredActivity()) logger.warn("Outbox listener for {identifier} returned without delivering the posted activity; ctx.sendActivity() or ctx.forwardActivity() may have been skipped or resulted in no delivery.", {
|
|
699
|
-
identifier,
|
|
700
|
-
activityId: activity.id?.href,
|
|
701
|
-
activityType: getTypeId(activity).href
|
|
702
|
-
});
|
|
703
|
-
logger.info("Activity {activityId} has been processed in outbox listener.", {
|
|
704
|
-
activityId: activity.id?.href,
|
|
705
|
-
activityType: getTypeId(activity).href,
|
|
706
|
-
identifier
|
|
707
|
-
});
|
|
708
|
-
return new Response("", {
|
|
709
|
-
status: 202,
|
|
710
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
380
|
/**
|
|
714
381
|
* Handles an inbox request for ActivityPub activities.
|
|
715
382
|
* @template TContextData The context data to pass to the context.
|
|
@@ -1140,8 +807,8 @@ var CustomCollectionHandler = class {
|
|
|
1140
807
|
* @param CollectionPage The CollectionPage constructor.
|
|
1141
808
|
* @param filterPredicate Optional filter predicate for items.
|
|
1142
809
|
*/
|
|
1143
|
-
constructor(name$
|
|
1144
|
-
this.name = name$
|
|
810
|
+
constructor(name$1, values, context, callbacks, tracerProvider = trace.getTracerProvider(), Collection, CollectionPage, filterPredicate) {
|
|
811
|
+
this.name = name$1;
|
|
1145
812
|
this.values = values;
|
|
1146
813
|
this.context = context;
|
|
1147
814
|
this.callbacks = callbacks;
|
|
@@ -2500,37 +2167,16 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
2500
2167
|
onNotFound
|
|
2501
2168
|
});
|
|
2502
2169
|
}
|
|
2503
|
-
case "outbox":
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
identifier: route.values.identifier,
|
|
2514
|
-
context,
|
|
2515
|
-
outboxContextFactory: context.toOutboxContext.bind(context),
|
|
2516
|
-
actorDispatcher: this.actorCallbacks?.dispatcher,
|
|
2517
|
-
authorizePredicate: this.outboxAuthorizePredicate ?? this.outboxCallbacks?.authorizePredicate,
|
|
2518
|
-
outboxListeners: this.outboxListeners,
|
|
2519
|
-
outboxErrorHandler: this.outboxListenerErrorHandler,
|
|
2520
|
-
onUnauthorized,
|
|
2521
|
-
onNotFound
|
|
2522
|
-
});
|
|
2523
|
-
}
|
|
2524
|
-
return await handleCollection(request, {
|
|
2525
|
-
name: "outbox",
|
|
2526
|
-
identifier: route.values.identifier,
|
|
2527
|
-
uriGetter: context.getOutboxUri.bind(context),
|
|
2528
|
-
context,
|
|
2529
|
-
collectionCallbacks: this.outboxCallbacks,
|
|
2530
|
-
tracerProvider: this.tracerProvider,
|
|
2531
|
-
onUnauthorized,
|
|
2532
|
-
onNotFound
|
|
2533
|
-
});
|
|
2170
|
+
case "outbox": return await handleCollection(request, {
|
|
2171
|
+
name: "outbox",
|
|
2172
|
+
identifier: route.values.identifier,
|
|
2173
|
+
uriGetter: context.getOutboxUri.bind(context),
|
|
2174
|
+
context,
|
|
2175
|
+
collectionCallbacks: this.outboxCallbacks,
|
|
2176
|
+
tracerProvider: this.tracerProvider,
|
|
2177
|
+
onUnauthorized,
|
|
2178
|
+
onNotFound
|
|
2179
|
+
});
|
|
2534
2180
|
case "inbox":
|
|
2535
2181
|
if (request.method !== "POST") return await handleCollection(request, {
|
|
2536
2182
|
name: "inbox",
|
|
@@ -2700,16 +2346,6 @@ var ContextImpl = class ContextImpl {
|
|
|
2700
2346
|
invokedFromActorKeyPairsDispatcher: this.invokedFromActorKeyPairsDispatcher
|
|
2701
2347
|
});
|
|
2702
2348
|
}
|
|
2703
|
-
toOutboxContext(identifier, activity, activityId, activityType) {
|
|
2704
|
-
return new OutboxContextImpl(identifier, activity, activityId, activityType, {
|
|
2705
|
-
url: this.url,
|
|
2706
|
-
federation: this.federation,
|
|
2707
|
-
data: this.data,
|
|
2708
|
-
documentLoader: this.documentLoader,
|
|
2709
|
-
contextLoader: this.contextLoader,
|
|
2710
|
-
invokedFromActorKeyPairsDispatcher: this.invokedFromActorKeyPairsDispatcher
|
|
2711
|
-
});
|
|
2712
|
-
}
|
|
2713
2349
|
get hostname() {
|
|
2714
2350
|
return this.url.hostname;
|
|
2715
2351
|
}
|
|
@@ -2999,9 +2635,9 @@ var ContextImpl = class ContextImpl {
|
|
|
2999
2635
|
attributes: {
|
|
3000
2636
|
"activitypub.activity.type": getTypeId(activity).href,
|
|
3001
2637
|
"activitypub.activity.to": activity.toIds.map((to) => to.href),
|
|
3002
|
-
"activitypub.activity.cc": activity.
|
|
2638
|
+
"activitypub.activity.cc": activity.toIds.map((cc) => cc.href),
|
|
3003
2639
|
"activitypub.activity.bto": activity.btoIds.map((bto) => bto.href),
|
|
3004
|
-
"activitypub.activity.bcc": activity.
|
|
2640
|
+
"activitypub.activity.bcc": activity.toIds.map((bcc) => bcc.href)
|
|
3005
2641
|
}
|
|
3006
2642
|
}, async (span) => {
|
|
3007
2643
|
try {
|
|
@@ -3096,13 +2732,6 @@ var ContextImpl = class ContextImpl {
|
|
|
3096
2732
|
preferSharedInbox: options.preferSharedInbox,
|
|
3097
2733
|
excludeBaseUris: options.excludeBaseUris
|
|
3098
2734
|
});
|
|
3099
|
-
if (globalThis.Object.keys(inboxes).length < 1) {
|
|
3100
|
-
logger.debug("No inboxes found for activity {activityId}.", {
|
|
3101
|
-
activityId: activity.id?.href,
|
|
3102
|
-
activity
|
|
3103
|
-
});
|
|
3104
|
-
return false;
|
|
3105
|
-
}
|
|
3106
2735
|
logger.debug("Sending activity {activityId} to inboxes:\n{inboxes}", {
|
|
3107
2736
|
inboxes: globalThis.Object.keys(inboxes),
|
|
3108
2737
|
activityId: activity.id?.href,
|
|
@@ -3110,7 +2739,7 @@ var ContextImpl = class ContextImpl {
|
|
|
3110
2739
|
});
|
|
3111
2740
|
if (this.federation.fanoutQueue == null || options.immediate || options.fanout === "skip" || (options.fanout ?? "auto") === "auto" && globalThis.Object.keys(inboxes).length < FANOUT_THRESHOLD) {
|
|
3112
2741
|
await this.federation.sendActivity(keys, inboxes, activity, opts);
|
|
3113
|
-
return
|
|
2742
|
+
return;
|
|
3114
2743
|
}
|
|
3115
2744
|
const keyJwkPairs = await Promise.all(keys.map(async ({ keyId, privateKey }) => ({
|
|
3116
2745
|
keyId: keyId.href,
|
|
@@ -3139,7 +2768,6 @@ var ContextImpl = class ContextImpl {
|
|
|
3139
2768
|
};
|
|
3140
2769
|
if (!this.federation.manuallyStartQueue) this.federation._startQueueInternal(this.data);
|
|
3141
2770
|
await this.federation.fanoutQueue.enqueue(message, { orderingKey: options.orderingKey });
|
|
3142
|
-
return true;
|
|
3143
2771
|
}
|
|
3144
2772
|
async *getFollowers(identifier) {
|
|
3145
2773
|
if (this.federation.followersCallbacks == null) throw new Error("No followers collection dispatcher registered.");
|
|
@@ -3374,170 +3002,6 @@ var RequestContextImpl = class RequestContextImpl extends ContextImpl {
|
|
|
3374
3002
|
}
|
|
3375
3003
|
}
|
|
3376
3004
|
};
|
|
3377
|
-
function forwardActivity(ctx, loggerCategory, forwarder, recipients, options) {
|
|
3378
|
-
return ctx.tracerProvider.getTracer(name, version).startActiveSpan(ctx.federation.outboxQueue == null || options?.immediate ? `activitypub.${loggerCategory}` : "activitypub.fanout", {
|
|
3379
|
-
kind: ctx.federation.outboxQueue == null || options?.immediate ? SpanKind.CLIENT : SpanKind.PRODUCER,
|
|
3380
|
-
attributes: { "activitypub.activity.type": ctx.activityType }
|
|
3381
|
-
}, async (span) => {
|
|
3382
|
-
try {
|
|
3383
|
-
if (ctx.activityId != null) span.setAttribute("activitypub.activity.id", ctx.activityId);
|
|
3384
|
-
return await forwardActivityInternal(ctx, loggerCategory, forwarder, recipients, options);
|
|
3385
|
-
} catch (e) {
|
|
3386
|
-
span.setStatus({
|
|
3387
|
-
code: SpanStatusCode.ERROR,
|
|
3388
|
-
message: String(e)
|
|
3389
|
-
});
|
|
3390
|
-
throw e;
|
|
3391
|
-
} finally {
|
|
3392
|
-
span.end();
|
|
3393
|
-
}
|
|
3394
|
-
});
|
|
3395
|
-
}
|
|
3396
|
-
async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipients, options) {
|
|
3397
|
-
const logger = getLogger([
|
|
3398
|
-
"fedify",
|
|
3399
|
-
"federation",
|
|
3400
|
-
loggerCategory
|
|
3401
|
-
]);
|
|
3402
|
-
let keys;
|
|
3403
|
-
let identifier = null;
|
|
3404
|
-
if ("identifier" in forwarder || "username" in forwarder) {
|
|
3405
|
-
if ("identifier" in forwarder) identifier = forwarder.identifier;
|
|
3406
|
-
else {
|
|
3407
|
-
const username = forwarder.username;
|
|
3408
|
-
if (ctx.federation.actorCallbacks?.handleMapper == null) identifier = username;
|
|
3409
|
-
else {
|
|
3410
|
-
const mapped = await ctx.federation.actorCallbacks.handleMapper(ctx, username);
|
|
3411
|
-
if (mapped == null) throw new Error(`No actor found for the given username ${JSON.stringify(username)}.`);
|
|
3412
|
-
identifier = mapped;
|
|
3413
|
-
}
|
|
3414
|
-
}
|
|
3415
|
-
const actorKeyPairs = await ctx.getActorKeyPairs(identifier);
|
|
3416
|
-
if (actorKeyPairs.length < 1) throw new Error(`No key pair found for actor ${JSON.stringify(identifier)}.`);
|
|
3417
|
-
keys = actorKeyPairs.map((kp) => ({
|
|
3418
|
-
keyId: kp.keyId,
|
|
3419
|
-
privateKey: kp.privateKey
|
|
3420
|
-
}));
|
|
3421
|
-
} else if (Array.isArray(forwarder)) {
|
|
3422
|
-
if (forwarder.length < 1) throw new Error("The forwarder's key pairs are empty.");
|
|
3423
|
-
keys = forwarder;
|
|
3424
|
-
} else keys = [forwarder];
|
|
3425
|
-
if (!hasSignatureLike(ctx.activity)) {
|
|
3426
|
-
if (!hasProofLike(ctx.activity)) {
|
|
3427
|
-
if (options?.skipIfUnsigned) return false;
|
|
3428
|
-
logger.warn("The activity {activityId} is not signed; even if it is forwarded to other servers as is, it may not be accepted by them due to the lack of a signature/proof.", {
|
|
3429
|
-
activityId: ctx.activityId,
|
|
3430
|
-
activityType: ctx.activityType,
|
|
3431
|
-
identifier: identifier ?? void 0
|
|
3432
|
-
});
|
|
3433
|
-
}
|
|
3434
|
-
}
|
|
3435
|
-
if (recipients === "followers") {
|
|
3436
|
-
if (identifier == null) throw new Error("If recipients is \"followers\", forwarder must be an actor identifier or username.");
|
|
3437
|
-
const followers = [];
|
|
3438
|
-
for await (const recipient of ctx.getFollowers(identifier)) followers.push(recipient);
|
|
3439
|
-
recipients = followers;
|
|
3440
|
-
}
|
|
3441
|
-
const inboxes = extractInboxes({
|
|
3442
|
-
recipients: Array.isArray(recipients) ? recipients : [recipients],
|
|
3443
|
-
preferSharedInbox: options?.preferSharedInbox,
|
|
3444
|
-
excludeBaseUris: options?.excludeBaseUris
|
|
3445
|
-
});
|
|
3446
|
-
if (globalThis.Object.keys(inboxes).length < 1) {
|
|
3447
|
-
logger.debug("No inboxes found for activity {activityId}.", {
|
|
3448
|
-
activityId: ctx.activityId,
|
|
3449
|
-
activityType: ctx.activityType,
|
|
3450
|
-
identifier: identifier ?? void 0
|
|
3451
|
-
});
|
|
3452
|
-
return false;
|
|
3453
|
-
}
|
|
3454
|
-
logger.debug("Forwarding activity {activityId} to inboxes:\n{inboxes}", {
|
|
3455
|
-
inboxes: globalThis.Object.keys(inboxes),
|
|
3456
|
-
activityId: ctx.activityId,
|
|
3457
|
-
activity: ctx.activity
|
|
3458
|
-
});
|
|
3459
|
-
if (options?.immediate || ctx.federation.outboxQueue == null) {
|
|
3460
|
-
if (options?.immediate) logger.debug("Forwarding activity immediately without queue since immediate option is set.");
|
|
3461
|
-
else logger.debug("Forwarding activity immediately without queue since queue is not set.");
|
|
3462
|
-
const promises = [];
|
|
3463
|
-
for (const inbox in inboxes) promises.push(sendActivity({
|
|
3464
|
-
keys,
|
|
3465
|
-
activity: ctx.activity,
|
|
3466
|
-
activityId: ctx.activityId,
|
|
3467
|
-
activityType: ctx.activityType,
|
|
3468
|
-
inbox: new URL(inbox),
|
|
3469
|
-
sharedInbox: inboxes[inbox].sharedInbox,
|
|
3470
|
-
tracerProvider: ctx.tracerProvider,
|
|
3471
|
-
specDeterminer: new KvSpecDeterminer(ctx.federation.kv, ctx.federation.kvPrefixes.httpMessageSignaturesSpec, ctx.federation.firstKnock)
|
|
3472
|
-
}));
|
|
3473
|
-
await Promise.all(promises);
|
|
3474
|
-
return true;
|
|
3475
|
-
}
|
|
3476
|
-
logger.debug("Enqueuing activity {activityId} to forward later.", {
|
|
3477
|
-
activityId: ctx.activityId,
|
|
3478
|
-
activity: ctx.activity
|
|
3479
|
-
});
|
|
3480
|
-
if (!ctx.federation.manuallyStartQueue) ctx.federation._startQueueInternal(ctx.data);
|
|
3481
|
-
const keyJwkPairs = [];
|
|
3482
|
-
for (const { keyId, privateKey } of keys) {
|
|
3483
|
-
const privateKeyJwk = await exportJwk(privateKey);
|
|
3484
|
-
keyJwkPairs.push({
|
|
3485
|
-
keyId: keyId.href,
|
|
3486
|
-
privateKey: privateKeyJwk
|
|
3487
|
-
});
|
|
3488
|
-
}
|
|
3489
|
-
const carrier = {};
|
|
3490
|
-
propagation.inject(context.active(), carrier);
|
|
3491
|
-
const orderingKey = options?.orderingKey;
|
|
3492
|
-
const started = (/* @__PURE__ */ new Date()).toISOString();
|
|
3493
|
-
const messages = [];
|
|
3494
|
-
for (const inbox in inboxes) {
|
|
3495
|
-
const inboxUrl = new URL(inbox);
|
|
3496
|
-
const message = {
|
|
3497
|
-
type: "outbox",
|
|
3498
|
-
id: crypto.randomUUID(),
|
|
3499
|
-
baseUrl: ctx.origin,
|
|
3500
|
-
keys: keyJwkPairs,
|
|
3501
|
-
activity: ctx.activity,
|
|
3502
|
-
activityId: ctx.activityId,
|
|
3503
|
-
activityType: ctx.activityType,
|
|
3504
|
-
inbox,
|
|
3505
|
-
sharedInbox: inboxes[inbox].sharedInbox,
|
|
3506
|
-
actorIds: [...inboxes[inbox].actorIds],
|
|
3507
|
-
started,
|
|
3508
|
-
attempt: 0,
|
|
3509
|
-
headers: {},
|
|
3510
|
-
orderingKey: orderingKey == null ? void 0 : `${orderingKey}\n${inboxUrl.origin}`,
|
|
3511
|
-
traceContext: carrier
|
|
3512
|
-
};
|
|
3513
|
-
messages.push({
|
|
3514
|
-
message,
|
|
3515
|
-
orderingKey: message.orderingKey
|
|
3516
|
-
});
|
|
3517
|
-
}
|
|
3518
|
-
const { outboxQueue } = ctx.federation;
|
|
3519
|
-
if (outboxQueue.enqueueMany == null || orderingKey != null) {
|
|
3520
|
-
const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
|
|
3521
|
-
const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
3522
|
-
if (errors.length > 0) {
|
|
3523
|
-
logger.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
|
|
3524
|
-
activityId: ctx.activityId,
|
|
3525
|
-
errors
|
|
3526
|
-
});
|
|
3527
|
-
if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${ctx.activityId} to forward later.`);
|
|
3528
|
-
throw errors[0];
|
|
3529
|
-
}
|
|
3530
|
-
} else try {
|
|
3531
|
-
await outboxQueue.enqueueMany(messages.map((m) => m.message));
|
|
3532
|
-
} catch (error) {
|
|
3533
|
-
logger.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
|
|
3534
|
-
activityId: ctx.activityId,
|
|
3535
|
-
error
|
|
3536
|
-
});
|
|
3537
|
-
throw error;
|
|
3538
|
-
}
|
|
3539
|
-
return true;
|
|
3540
|
-
}
|
|
3541
3005
|
var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
3542
3006
|
recipient;
|
|
3543
3007
|
activity;
|
|
@@ -3561,40 +3025,13 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
|
3561
3025
|
});
|
|
3562
3026
|
}
|
|
3563
3027
|
forwardActivity(forwarder, recipients, options) {
|
|
3564
|
-
return
|
|
3565
|
-
|
|
3566
|
-
}
|
|
3567
|
-
var OutboxContextImpl = class OutboxContextImpl extends ContextImpl {
|
|
3568
|
-
#deliveryState;
|
|
3569
|
-
identifier;
|
|
3570
|
-
activity;
|
|
3571
|
-
activityId;
|
|
3572
|
-
activityType;
|
|
3573
|
-
constructor(identifier, activity, activityId, activityType, options, deliveryState = { delivered: false }) {
|
|
3574
|
-
super(options);
|
|
3575
|
-
this.#deliveryState = deliveryState;
|
|
3576
|
-
this.identifier = identifier;
|
|
3577
|
-
this.activity = activity;
|
|
3578
|
-
this.activityId = activityId;
|
|
3579
|
-
this.activityType = activityType;
|
|
3580
|
-
}
|
|
3581
|
-
hasDeliveredActivity() {
|
|
3582
|
-
return this.#deliveryState.delivered;
|
|
3583
|
-
}
|
|
3584
|
-
sendActivity(sender, recipients, activity, options = {}) {
|
|
3585
|
-
return this.tracerProvider.getTracer(name, version).startActiveSpan(this.federation.outboxQueue == null || options.immediate ? "activitypub.outbox" : "activitypub.fanout", {
|
|
3586
|
-
kind: this.federation.outboxQueue == null || options.immediate ? SpanKind.CLIENT : SpanKind.PRODUCER,
|
|
3587
|
-
attributes: {
|
|
3588
|
-
"activitypub.activity.type": getTypeId(activity).href,
|
|
3589
|
-
"activitypub.activity.to": activity.toIds.map((to) => to.href),
|
|
3590
|
-
"activitypub.activity.cc": activity.ccIds.map((cc) => cc.href),
|
|
3591
|
-
"activitypub.activity.bto": activity.btoIds.map((bto) => bto.href),
|
|
3592
|
-
"activitypub.activity.bcc": activity.bccIds.map((bcc) => bcc.href)
|
|
3593
|
-
}
|
|
3028
|
+
return this.tracerProvider.getTracer(name, version).startActiveSpan("activitypub.outbox", {
|
|
3029
|
+
kind: this.federation.outboxQueue == null || options?.immediate ? SpanKind.CLIENT : SpanKind.PRODUCER,
|
|
3030
|
+
attributes: { "activitypub.activity.type": this.activityType }
|
|
3594
3031
|
}, async (span) => {
|
|
3595
3032
|
try {
|
|
3596
|
-
if (
|
|
3597
|
-
|
|
3033
|
+
if (this.activityId != null) span.setAttribute("activitypub.activity.id", this.activityId);
|
|
3034
|
+
await this.forwardActivityInternal(forwarder, recipients, options);
|
|
3598
3035
|
} catch (e) {
|
|
3599
3036
|
span.setStatus({
|
|
3600
3037
|
code: SpanStatusCode.ERROR,
|
|
@@ -3606,20 +3043,151 @@ var OutboxContextImpl = class OutboxContextImpl extends ContextImpl {
|
|
|
3606
3043
|
}
|
|
3607
3044
|
});
|
|
3608
3045
|
}
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3046
|
+
async forwardActivityInternal(forwarder, recipients, options) {
|
|
3047
|
+
const logger = getLogger([
|
|
3048
|
+
"fedify",
|
|
3049
|
+
"federation",
|
|
3050
|
+
"inbox"
|
|
3051
|
+
]);
|
|
3052
|
+
let keys;
|
|
3053
|
+
let identifier = null;
|
|
3054
|
+
if ("identifier" in forwarder || "username" in forwarder) {
|
|
3055
|
+
if ("identifier" in forwarder) identifier = forwarder.identifier;
|
|
3056
|
+
else {
|
|
3057
|
+
const username = forwarder.username;
|
|
3058
|
+
if (this.federation.actorCallbacks?.handleMapper == null) identifier = username;
|
|
3059
|
+
else {
|
|
3060
|
+
const mapped = await this.federation.actorCallbacks.handleMapper(this, username);
|
|
3061
|
+
if (mapped == null) throw new Error(`No actor found for the given username ${JSON.stringify(username)}.`);
|
|
3062
|
+
identifier = mapped;
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
const actorKeyPairs = await this.getActorKeyPairs(identifier);
|
|
3066
|
+
if (actorKeyPairs.length < 1) throw new Error(`No key pair found for actor ${JSON.stringify(identifier)}.`);
|
|
3067
|
+
keys = actorKeyPairs.map((kp) => ({
|
|
3068
|
+
keyId: kp.keyId,
|
|
3069
|
+
privateKey: kp.privateKey
|
|
3070
|
+
}));
|
|
3071
|
+
} else if (Array.isArray(forwarder)) {
|
|
3072
|
+
if (forwarder.length < 1) throw new Error("The forwarder's key pairs are empty.");
|
|
3073
|
+
keys = forwarder;
|
|
3074
|
+
} else keys = [forwarder];
|
|
3075
|
+
if (!hasSignature(this.activity)) {
|
|
3076
|
+
let hasProof;
|
|
3077
|
+
try {
|
|
3078
|
+
hasProof = await (await Activity.fromJsonLd(this.activity, this)).getProof() != null;
|
|
3079
|
+
} catch {
|
|
3080
|
+
hasProof = false;
|
|
3081
|
+
}
|
|
3082
|
+
if (!hasProof) {
|
|
3083
|
+
if (options?.skipIfUnsigned) return;
|
|
3084
|
+
logger.warn("The received activity {activityId} is not signed; even if it is forwarded to other servers as is, it may not be accepted by them due to the lack of a signature/proof.");
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
if (recipients === "followers") {
|
|
3088
|
+
if (identifier == null) throw new Error("If recipients is \"followers\", forwarder must be an actor identifier or username.");
|
|
3089
|
+
const followers = [];
|
|
3090
|
+
for await (const recipient of this.getFollowers(identifier)) followers.push(recipient);
|
|
3091
|
+
recipients = followers;
|
|
3092
|
+
}
|
|
3093
|
+
const inboxes = extractInboxes({
|
|
3094
|
+
recipients: Array.isArray(recipients) ? recipients : [recipients],
|
|
3095
|
+
preferSharedInbox: options?.preferSharedInbox,
|
|
3096
|
+
excludeBaseUris: options?.excludeBaseUris
|
|
3612
3097
|
});
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3098
|
+
logger.debug("Forwarding activity {activityId} to inboxes:\n{inboxes}", {
|
|
3099
|
+
inboxes: globalThis.Object.keys(inboxes),
|
|
3100
|
+
activityId: this.activityId,
|
|
3101
|
+
activity: this.activity
|
|
3102
|
+
});
|
|
3103
|
+
if (options?.immediate || this.federation.outboxQueue == null) {
|
|
3104
|
+
if (options?.immediate) logger.debug("Forwarding activity immediately without queue since immediate option is set.");
|
|
3105
|
+
else logger.debug("Forwarding activity immediately without queue since queue is not set.");
|
|
3106
|
+
const promises = [];
|
|
3107
|
+
for (const inbox in inboxes) promises.push(sendActivity({
|
|
3108
|
+
keys,
|
|
3109
|
+
activity: this.activity,
|
|
3110
|
+
activityId: this.activityId,
|
|
3111
|
+
activityType: this.activityType,
|
|
3112
|
+
inbox: new URL(inbox),
|
|
3113
|
+
sharedInbox: inboxes[inbox].sharedInbox,
|
|
3114
|
+
tracerProvider: this.tracerProvider,
|
|
3115
|
+
specDeterminer: new KvSpecDeterminer(this.federation.kv, this.federation.kvPrefixes.httpMessageSignaturesSpec, this.federation.firstKnock)
|
|
3116
|
+
}));
|
|
3117
|
+
await Promise.all(promises);
|
|
3118
|
+
return;
|
|
3119
|
+
}
|
|
3120
|
+
logger.debug("Enqueuing activity {activityId} to forward later.", {
|
|
3121
|
+
activityId: this.activityId,
|
|
3122
|
+
activity: this.activity
|
|
3123
|
+
});
|
|
3124
|
+
const keyJwkPairs = [];
|
|
3125
|
+
for (const { keyId, privateKey } of keys) {
|
|
3126
|
+
const privateKeyJwk = await exportJwk(privateKey);
|
|
3127
|
+
keyJwkPairs.push({
|
|
3128
|
+
keyId: keyId.href,
|
|
3129
|
+
privateKey: privateKeyJwk
|
|
3130
|
+
});
|
|
3131
|
+
}
|
|
3132
|
+
const carrier = {};
|
|
3133
|
+
propagation.inject(context.active(), carrier);
|
|
3134
|
+
const orderingKey = options?.orderingKey;
|
|
3135
|
+
const messages = [];
|
|
3136
|
+
for (const inbox in inboxes) {
|
|
3137
|
+
const inboxUrl = new URL(inbox);
|
|
3138
|
+
const message = {
|
|
3139
|
+
type: "outbox",
|
|
3140
|
+
id: crypto.randomUUID(),
|
|
3141
|
+
baseUrl: this.origin,
|
|
3142
|
+
keys: keyJwkPairs,
|
|
3143
|
+
activity: this.activity,
|
|
3144
|
+
activityId: this.activityId,
|
|
3145
|
+
activityType: this.activityType,
|
|
3146
|
+
inbox,
|
|
3147
|
+
sharedInbox: inboxes[inbox].sharedInbox,
|
|
3148
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3149
|
+
attempt: 0,
|
|
3150
|
+
headers: {},
|
|
3151
|
+
orderingKey: orderingKey == null ? void 0 : `${orderingKey}\n${inboxUrl.origin}`,
|
|
3152
|
+
traceContext: carrier
|
|
3153
|
+
};
|
|
3154
|
+
messages.push({
|
|
3155
|
+
message,
|
|
3156
|
+
orderingKey: message.orderingKey
|
|
3157
|
+
});
|
|
3158
|
+
}
|
|
3159
|
+
const { outboxQueue } = this.federation;
|
|
3160
|
+
if (outboxQueue.enqueueMany == null) {
|
|
3161
|
+
const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
|
|
3162
|
+
const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
3163
|
+
if (errors.length > 0) {
|
|
3164
|
+
logger.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
|
|
3165
|
+
activityId: this.activityId,
|
|
3166
|
+
errors
|
|
3167
|
+
});
|
|
3168
|
+
if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${this.activityId} to forward later.`);
|
|
3169
|
+
throw errors[0];
|
|
3170
|
+
}
|
|
3171
|
+
} else if (orderingKey != null) {
|
|
3172
|
+
const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
|
|
3173
|
+
const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
3174
|
+
if (errors.length > 0) {
|
|
3175
|
+
logger.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
|
|
3176
|
+
activityId: this.activityId,
|
|
3177
|
+
errors
|
|
3178
|
+
});
|
|
3179
|
+
if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${this.activityId} to forward later.`);
|
|
3180
|
+
throw errors[0];
|
|
3181
|
+
}
|
|
3182
|
+
} else try {
|
|
3183
|
+
await outboxQueue.enqueueMany(messages.map((m) => m.message));
|
|
3184
|
+
} catch (error) {
|
|
3185
|
+
logger.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
|
|
3186
|
+
activityId: this.activityId,
|
|
3187
|
+
error
|
|
3188
|
+
});
|
|
3189
|
+
throw error;
|
|
3190
|
+
}
|
|
3623
3191
|
}
|
|
3624
3192
|
};
|
|
3625
3193
|
var KvSpecDeterminer = class {
|
|
@@ -3672,4 +3240,4 @@ function getRequestId(request) {
|
|
|
3672
3240
|
return `req_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
|
|
3673
3241
|
}
|
|
3674
3242
|
//#endregion
|
|
3675
|
-
export {
|
|
3243
|
+
export { autoIdAssigner as _, createFederation as a, handleCollection as c, handleObject as d, respondWithObject as f, actorDehydrator as g, handleNodeInfoJrd as h, KvSpecDeterminer as i, handleCustomCollection as l, handleNodeInfo as m, FederationImpl as n, handleWebFinger as o, respondWithObjectIfAcceptable as p, InboxContextImpl as r, handleActor as s, ContextImpl as t, handleInbox as u };
|