@fedify/fedify 2.2.0-pr.695.16 → 2.2.0-pr.697.17
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/activity-listener-Ck3JZ_hR.mjs +40 -0
- package/dist/{builder-7PVCiLiR.mjs → builder-0VkYL-Be.mjs} +57 -7
- 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-78ecvxf5.d.ts → context-BGrYMSTk.d.ts} +143 -1
- package/dist/{context-DYDPdoCb.d.cts → context-CMUd4wy0.d.cts} +143 -1
- package/dist/{context-Juj6bdHC.mjs → context-Dk_tacqz.mjs} +17 -2
- package/dist/{deno-vxcWcxQS.mjs → deno-ZTLy21O_.mjs} +1 -1
- package/dist/{docloader-D7q0-Xef.mjs → docloader-qLB9fFVV.mjs} +2 -2
- package/dist/federation/builder.test.mjs +25 -1
- package/dist/federation/handler.test.mjs +369 -8
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox.test.mjs +3 -3
- package/dist/federation/middleware.test.mjs +510 -8
- 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-JxF7bG0o.cjs → http-BLuuf-Rt.cjs} +1 -1
- package/dist/{http-RZPxDWq5.mjs → http-CFmrJbuT.mjs} +2 -2
- package/dist/{http-D-MhhYUF.js → http-CqkZgsp_.js} +1 -1
- package/dist/{key-CGx_dDkX.mjs → key-DVFCI5om.mjs} +1 -1
- package/dist/{kv-cache-C2gdVgvb.cjs → kv-cache-BlXew67e.cjs} +1 -1
- package/dist/{kv-cache-D84Mk0fZ.js → kv-cache-Dgsvz3PC.js} +1 -1
- package/dist/{ld-wup-liFO.mjs → ld-N69KBAlI.mjs} +26 -3
- package/dist/{middleware-Bn75dPug.cjs → middleware-B0BqhA1u.cjs} +676 -323
- package/dist/{middleware-RF-sUfTr.js → middleware-BpSfJf2S.js} +670 -322
- package/dist/{middleware-CXOVT4Ph.cjs → middleware-CUe96Oxe.cjs} +1 -1
- package/dist/{middleware-wdfeWjRJ.mjs → middleware-DDXN5eBU.mjs} +1 -1
- package/dist/{middleware-BjVx-_bv.mjs → middleware-DbCHS-GH.mjs} +612 -180
- package/dist/{mod-CEohtXhV.d.cts → mod-BcJHeuv1.d.cts} +1 -1
- package/dist/{mod-CokIUYDr.d.ts → mod-CJXfyw7v.d.ts} +1 -1
- package/dist/{mod-DvxszxXC.d.ts → mod-CR8soWa9.d.ts} +18 -1
- package/dist/{mod-DoJBjjnO.d.cts → mod-Cr3f-ACa.d.cts} +18 -1
- package/dist/mod.cjs +6 -4
- 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-q2mUMM9a.mjs → owner-DGdkXeF5.mjs} +2 -2
- package/dist/{proof-CirP9OSd.js → proof-BtFhWZuV.js} +54 -2
- package/dist/{proof--CpZsF_p.mjs → proof-C50gFNxj.mjs} +32 -3
- package/dist/{proof-_Zyfqyce.cjs → proof-DwRcU3OF.cjs} +61 -3
- package/dist/{send-CVJfx7bF.mjs → send-BlHiNsZI.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 +44 -2
- package/dist/sig/mod.cjs +4 -2
- 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 +46 -2
- package/dist/testing/mod.d.mts +149 -1
- 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 +5 -5
- package/dist/inbox-CmYvcSMM.mjs +0 -179
|
@@ -2,10 +2,10 @@ import { Temporal } from "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
import { t as __exportAll } from "./chunk-nlSIicah.js";
|
|
4
4
|
import { r as getDefaultActivityTransformers } from "./transformers-ve6e2xcg.js";
|
|
5
|
-
import { _ as version, a as verifyRequestDetailed, d as validateCryptoKey, f as formatAcceptSignature, g as name, i as verifyRequest, n as parseRfc9421SignatureInput, o as exportJwk, t as doubleKnock, u as importJwk } from "./http-
|
|
6
|
-
import {
|
|
5
|
+
import { _ as version, a as verifyRequestDetailed, d as validateCryptoKey, f as formatAcceptSignature, g as name, i as verifyRequest, n as parseRfc9421SignatureInput, o as exportJwk, t as doubleKnock, u as importJwk } from "./http-CqkZgsp_.js";
|
|
6
|
+
import { d as hasSignatureLike, f as signJsonLd, i as verifyObject, n as hasProofLike, o as doesActorOwnKey, p as verifyJsonLd, r as signObject, s as getKeyOwner, u as detachSignature } from "./proof-BtFhWZuV.js";
|
|
7
7
|
import { n as getNodeInfo, t as nodeInfoToJson } from "./types-hvL8ElAs.js";
|
|
8
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-
|
|
8
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-Dgsvz3PC.js";
|
|
9
9
|
import { getLogger, withContext } from "@logtape/logtape";
|
|
10
10
|
import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
11
11
|
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
|
|
@@ -17,14 +17,15 @@ import { FetchError, getDocumentLoader } from "@fedify/vocab-runtime";
|
|
|
17
17
|
import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_HEADER, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
|
|
18
18
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
19
19
|
import { domainToASCII } from "node:url";
|
|
20
|
-
//#region src/federation/
|
|
21
|
-
var
|
|
20
|
+
//#region src/federation/activity-listener.ts
|
|
21
|
+
var ActivityListenerSet = class {
|
|
22
22
|
#listeners;
|
|
23
23
|
constructor() {
|
|
24
24
|
this.#listeners = /* @__PURE__ */ new Map();
|
|
25
25
|
}
|
|
26
26
|
clone() {
|
|
27
|
-
const
|
|
27
|
+
const Clone = this.constructor;
|
|
28
|
+
const clone = new Clone();
|
|
28
29
|
clone.#listeners = new Map(this.#listeners);
|
|
29
30
|
return clone;
|
|
30
31
|
}
|
|
@@ -34,14 +35,13 @@ var InboxListenerSet = class InboxListenerSet {
|
|
|
34
35
|
}
|
|
35
36
|
dispatchWithClass(activity) {
|
|
36
37
|
let cls = activity.constructor;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
while (true) {
|
|
40
|
-
if (inboxListeners.has(cls)) break;
|
|
38
|
+
while (cls != null) {
|
|
39
|
+
if (this.#listeners.has(cls)) break;
|
|
41
40
|
if (cls === Activity) return null;
|
|
42
41
|
cls = globalThis.Object.getPrototypeOf(cls);
|
|
43
42
|
}
|
|
44
|
-
|
|
43
|
+
if (cls == null) return null;
|
|
44
|
+
const listener = this.#listeners.get(cls);
|
|
45
45
|
return {
|
|
46
46
|
class: cls,
|
|
47
47
|
listener
|
|
@@ -51,142 +51,6 @@ var InboxListenerSet = class InboxListenerSet {
|
|
|
51
51
|
return this.dispatchWithClass(activity)?.listener ?? null;
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
|
-
async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, tracerProvider, idempotencyStrategy }) {
|
|
55
|
-
const logger = getLogger([
|
|
56
|
-
"fedify",
|
|
57
|
-
"federation",
|
|
58
|
-
"inbox"
|
|
59
|
-
]);
|
|
60
|
-
let cacheKey = null;
|
|
61
|
-
if (activity.id != null) {
|
|
62
|
-
const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, getTypeId(activity).href);
|
|
63
|
-
const strategy = idempotencyStrategy ?? "per-inbox";
|
|
64
|
-
let keyString;
|
|
65
|
-
if (typeof strategy === "function") keyString = await strategy(inboxContext, activity);
|
|
66
|
-
else switch (strategy) {
|
|
67
|
-
case "global":
|
|
68
|
-
keyString = activity.id.href;
|
|
69
|
-
break;
|
|
70
|
-
case "per-origin":
|
|
71
|
-
keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
72
|
-
break;
|
|
73
|
-
case "per-inbox":
|
|
74
|
-
keyString = `${ctx.origin}\n${activity.id.href}\n${recipient == null ? "sharedInbox" : `inbox\n${recipient}`}`;
|
|
75
|
-
break;
|
|
76
|
-
default: keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
77
|
-
}
|
|
78
|
-
if (keyString != null) cacheKey = [...kvPrefixes.activityIdempotence, keyString];
|
|
79
|
-
}
|
|
80
|
-
if (cacheKey != null) {
|
|
81
|
-
if (await kv.get(cacheKey) === true) {
|
|
82
|
-
logger.debug("Activity {activityId} has already been processed.", {
|
|
83
|
-
activityId: activity.id?.href,
|
|
84
|
-
activity: json,
|
|
85
|
-
recipient
|
|
86
|
-
});
|
|
87
|
-
span.setStatus({
|
|
88
|
-
code: SpanStatusCode.UNSET,
|
|
89
|
-
message: `Activity ${activity.id?.href} has already been processed.`
|
|
90
|
-
});
|
|
91
|
-
return "alreadyProcessed";
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
if (activity.actorId == null) {
|
|
95
|
-
logger.error("Missing actor.", { activity: json });
|
|
96
|
-
span.setStatus({
|
|
97
|
-
code: SpanStatusCode.ERROR,
|
|
98
|
-
message: "Missing actor."
|
|
99
|
-
});
|
|
100
|
-
return "missingActor";
|
|
101
|
-
}
|
|
102
|
-
span.setAttribute("activitypub.actor.id", activity.actorId.href);
|
|
103
|
-
if (queue != null) {
|
|
104
|
-
const carrier = {};
|
|
105
|
-
propagation.inject(context.active(), carrier);
|
|
106
|
-
try {
|
|
107
|
-
await queue.enqueue({
|
|
108
|
-
type: "inbox",
|
|
109
|
-
id: crypto.randomUUID(),
|
|
110
|
-
baseUrl: ctx.origin,
|
|
111
|
-
activity: json,
|
|
112
|
-
identifier: recipient,
|
|
113
|
-
attempt: 0,
|
|
114
|
-
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
115
|
-
traceContext: carrier
|
|
116
|
-
});
|
|
117
|
-
} catch (error) {
|
|
118
|
-
logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
|
|
119
|
-
error,
|
|
120
|
-
activityId: activity.id?.href,
|
|
121
|
-
activity: json,
|
|
122
|
-
recipient
|
|
123
|
-
});
|
|
124
|
-
span.setStatus({
|
|
125
|
-
code: SpanStatusCode.ERROR,
|
|
126
|
-
message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
|
|
127
|
-
});
|
|
128
|
-
throw error;
|
|
129
|
-
}
|
|
130
|
-
logger.info("Activity {activityId} is enqueued.", {
|
|
131
|
-
activityId: activity.id?.href,
|
|
132
|
-
activity: json,
|
|
133
|
-
recipient
|
|
134
|
-
});
|
|
135
|
-
return "enqueued";
|
|
136
|
-
}
|
|
137
|
-
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
138
|
-
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
|
|
139
|
-
const dispatched = inboxListeners?.dispatchWithClass(activity);
|
|
140
|
-
if (dispatched == null) {
|
|
141
|
-
logger.error("Unsupported activity type:\n{activity}", {
|
|
142
|
-
activity: json,
|
|
143
|
-
recipient
|
|
144
|
-
});
|
|
145
|
-
span.setStatus({
|
|
146
|
-
code: SpanStatusCode.UNSET,
|
|
147
|
-
message: `Unsupported activity type: ${getTypeId(activity).href}`
|
|
148
|
-
});
|
|
149
|
-
span.end();
|
|
150
|
-
return "unsupportedActivity";
|
|
151
|
-
}
|
|
152
|
-
const { class: cls, listener } = dispatched;
|
|
153
|
-
span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
|
154
|
-
try {
|
|
155
|
-
await listener(inboxContextFactory(recipient, json, activity?.id?.href, getTypeId(activity).href), activity);
|
|
156
|
-
} catch (error) {
|
|
157
|
-
try {
|
|
158
|
-
await inboxErrorHandler?.(ctx, error);
|
|
159
|
-
} catch (error) {
|
|
160
|
-
logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
161
|
-
error,
|
|
162
|
-
activityId: activity.id?.href,
|
|
163
|
-
activity: json,
|
|
164
|
-
recipient
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
|
|
168
|
-
error,
|
|
169
|
-
activityId: activity.id?.href,
|
|
170
|
-
activity: json,
|
|
171
|
-
recipient
|
|
172
|
-
});
|
|
173
|
-
span.setStatus({
|
|
174
|
-
code: SpanStatusCode.ERROR,
|
|
175
|
-
message: String(error)
|
|
176
|
-
});
|
|
177
|
-
span.end();
|
|
178
|
-
return "error";
|
|
179
|
-
}
|
|
180
|
-
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
|
|
181
|
-
logger.info("Activity {activityId} has been processed.", {
|
|
182
|
-
activityId: activity.id?.href,
|
|
183
|
-
activity: json,
|
|
184
|
-
recipient
|
|
185
|
-
});
|
|
186
|
-
span.end();
|
|
187
|
-
return "success";
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
54
|
//#endregion
|
|
191
55
|
//#region src/federation/router.ts
|
|
192
56
|
function cloneInnerRouter(router) {
|
|
@@ -296,6 +160,17 @@ var RouterError = class extends Error {
|
|
|
296
160
|
};
|
|
297
161
|
//#endregion
|
|
298
162
|
//#region src/federation/builder.ts
|
|
163
|
+
function validateSingleIdentifierVariablePath(path, errorMessage) {
|
|
164
|
+
const operatorMatches = globalThis.Array.from(path.matchAll(/{([+#./;?&]?)([A-Za-z_][A-Za-z0-9_]*)}/g));
|
|
165
|
+
if (operatorMatches.length !== 1 || operatorMatches[0]?.[2] !== "identifier") throw new RouterError(errorMessage);
|
|
166
|
+
if (operatorMatches.some((match) => [
|
|
167
|
+
"?",
|
|
168
|
+
"&",
|
|
169
|
+
"#"
|
|
170
|
+
].includes(match[1]) && match[2] === "identifier")) throw new RouterError(errorMessage);
|
|
171
|
+
const variables = new Router$1().add(path, "outbox");
|
|
172
|
+
if (variables.size !== 1 || !variables.has("identifier")) throw new RouterError(errorMessage);
|
|
173
|
+
}
|
|
299
174
|
var FederationBuilderImpl = class {
|
|
300
175
|
router;
|
|
301
176
|
actorCallbacks;
|
|
@@ -304,6 +179,7 @@ var FederationBuilderImpl = class {
|
|
|
304
179
|
objectCallbacks;
|
|
305
180
|
objectTypeIds;
|
|
306
181
|
inboxPath;
|
|
182
|
+
outboxPath;
|
|
307
183
|
inboxCallbacks;
|
|
308
184
|
outboxCallbacks;
|
|
309
185
|
followingCallbacks;
|
|
@@ -312,7 +188,10 @@ var FederationBuilderImpl = class {
|
|
|
312
188
|
featuredCallbacks;
|
|
313
189
|
featuredTagsCallbacks;
|
|
314
190
|
inboxListeners;
|
|
191
|
+
outboxListeners;
|
|
315
192
|
inboxErrorHandler;
|
|
193
|
+
outboxListenerErrorHandler;
|
|
194
|
+
outboxAuthorizePredicate;
|
|
316
195
|
sharedInboxKeyDispatcher;
|
|
317
196
|
unverifiedActivityHandler;
|
|
318
197
|
outboxPermanentFailureHandler;
|
|
@@ -343,6 +222,7 @@ var FederationBuilderImpl = class {
|
|
|
343
222
|
f.objectCallbacks = { ...this.objectCallbacks };
|
|
344
223
|
f.objectTypeIds = { ...this.objectTypeIds };
|
|
345
224
|
f.inboxPath = this.inboxPath;
|
|
225
|
+
f.outboxPath = this.outboxPath;
|
|
346
226
|
f.inboxCallbacks = this.inboxCallbacks == null ? void 0 : { ...this.inboxCallbacks };
|
|
347
227
|
f.outboxCallbacks = this.outboxCallbacks == null ? void 0 : { ...this.outboxCallbacks };
|
|
348
228
|
f.followingCallbacks = this.followingCallbacks == null ? void 0 : { ...this.followingCallbacks };
|
|
@@ -351,7 +231,10 @@ var FederationBuilderImpl = class {
|
|
|
351
231
|
f.featuredCallbacks = this.featuredCallbacks == null ? void 0 : { ...this.featuredCallbacks };
|
|
352
232
|
f.featuredTagsCallbacks = this.featuredTagsCallbacks == null ? void 0 : { ...this.featuredTagsCallbacks };
|
|
353
233
|
f.inboxListeners = this.inboxListeners?.clone();
|
|
234
|
+
f.outboxListeners = this.outboxListeners?.clone();
|
|
354
235
|
f.inboxErrorHandler = this.inboxErrorHandler;
|
|
236
|
+
f.outboxListenerErrorHandler = this.outboxListenerErrorHandler;
|
|
237
|
+
f.outboxAuthorizePredicate = this.outboxAuthorizePredicate;
|
|
355
238
|
f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
|
|
356
239
|
f.unverifiedActivityHandler = this.unverifiedActivityHandler;
|
|
357
240
|
f.outboxPermanentFailureHandler = this.outboxPermanentFailureHandler;
|
|
@@ -551,9 +434,14 @@ var FederationBuilderImpl = class {
|
|
|
551
434
|
return setters;
|
|
552
435
|
}
|
|
553
436
|
setOutboxDispatcher(path, dispatcher) {
|
|
554
|
-
if (this.
|
|
555
|
-
|
|
556
|
-
|
|
437
|
+
if (this.outboxCallbacks != null) throw new RouterError("Outbox dispatcher already set.");
|
|
438
|
+
if (this.router.has("outbox")) {
|
|
439
|
+
if (this.outboxPath !== path) throw new RouterError("Outbox dispatcher path must match outbox listener path.");
|
|
440
|
+
} else {
|
|
441
|
+
validateSingleIdentifierVariablePath(path, "Path for outbox dispatcher must have one variable: {identifier}");
|
|
442
|
+
this.router.add(path, "outbox");
|
|
443
|
+
this.outboxPath = path;
|
|
444
|
+
}
|
|
557
445
|
const callbacks = { dispatcher };
|
|
558
446
|
this.outboxCallbacks = callbacks;
|
|
559
447
|
const setters = {
|
|
@@ -576,6 +464,32 @@ var FederationBuilderImpl = class {
|
|
|
576
464
|
};
|
|
577
465
|
return setters;
|
|
578
466
|
}
|
|
467
|
+
setOutboxListeners(outboxPath) {
|
|
468
|
+
if (this.outboxListeners != null) throw new RouterError("Outbox listeners already set.");
|
|
469
|
+
if (this.router.has("outbox")) {
|
|
470
|
+
if (this.outboxPath !== outboxPath) throw new RouterError("Outbox listener path must match outbox dispatcher path.");
|
|
471
|
+
} else {
|
|
472
|
+
validateSingleIdentifierVariablePath(outboxPath, "Path for outbox must have one variable: {identifier}");
|
|
473
|
+
this.router.add(outboxPath, "outbox");
|
|
474
|
+
this.outboxPath = outboxPath;
|
|
475
|
+
}
|
|
476
|
+
const listeners = this.outboxListeners = new ActivityListenerSet();
|
|
477
|
+
const setters = {
|
|
478
|
+
on(type, listener) {
|
|
479
|
+
listeners.add(type, listener);
|
|
480
|
+
return setters;
|
|
481
|
+
},
|
|
482
|
+
onError: (handler) => {
|
|
483
|
+
this.outboxListenerErrorHandler = handler;
|
|
484
|
+
return setters;
|
|
485
|
+
},
|
|
486
|
+
authorize: (predicate) => {
|
|
487
|
+
this.outboxAuthorizePredicate = predicate;
|
|
488
|
+
return setters;
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
return setters;
|
|
492
|
+
}
|
|
579
493
|
setFollowingDispatcher(path, dispatcher) {
|
|
580
494
|
if (this.router.has("following")) throw new RouterError("Following collection dispatcher already set.");
|
|
581
495
|
const variables = this.router.add(path, "following");
|
|
@@ -718,7 +632,7 @@ var FederationBuilderImpl = class {
|
|
|
718
632
|
if (sharedInboxPath != null) {
|
|
719
633
|
if (this.router.add(sharedInboxPath, "sharedInbox").size !== 0) throw new RouterError("Path for shared inbox must have no variables.");
|
|
720
634
|
}
|
|
721
|
-
const listeners = this.inboxListeners = new
|
|
635
|
+
const listeners = this.inboxListeners = new ActivityListenerSet();
|
|
722
636
|
const setters = {
|
|
723
637
|
on(type, listener) {
|
|
724
638
|
listeners.add(type, listener);
|
|
@@ -855,6 +769,144 @@ async function buildCollectionSynchronizationHeader(collectionId, actorIds) {
|
|
|
855
769
|
return `collectionId="${collectionId}", url="${url}", digest="${encodeHex(await digest(actorIds))}"`;
|
|
856
770
|
}
|
|
857
771
|
//#endregion
|
|
772
|
+
//#region src/federation/inbox.ts
|
|
773
|
+
async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, tracerProvider, idempotencyStrategy }) {
|
|
774
|
+
const logger = getLogger([
|
|
775
|
+
"fedify",
|
|
776
|
+
"federation",
|
|
777
|
+
"inbox"
|
|
778
|
+
]);
|
|
779
|
+
let cacheKey = null;
|
|
780
|
+
if (activity.id != null) {
|
|
781
|
+
const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, getTypeId(activity).href);
|
|
782
|
+
const strategy = idempotencyStrategy ?? "per-inbox";
|
|
783
|
+
let keyString;
|
|
784
|
+
if (typeof strategy === "function") keyString = await strategy(inboxContext, activity);
|
|
785
|
+
else switch (strategy) {
|
|
786
|
+
case "global":
|
|
787
|
+
keyString = activity.id.href;
|
|
788
|
+
break;
|
|
789
|
+
case "per-origin":
|
|
790
|
+
keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
791
|
+
break;
|
|
792
|
+
case "per-inbox":
|
|
793
|
+
keyString = `${ctx.origin}\n${activity.id.href}\n${recipient == null ? "sharedInbox" : `inbox\n${recipient}`}`;
|
|
794
|
+
break;
|
|
795
|
+
default: keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
796
|
+
}
|
|
797
|
+
if (keyString != null) cacheKey = [...kvPrefixes.activityIdempotence, keyString];
|
|
798
|
+
}
|
|
799
|
+
if (cacheKey != null) {
|
|
800
|
+
if (await kv.get(cacheKey) === true) {
|
|
801
|
+
logger.debug("Activity {activityId} has already been processed.", {
|
|
802
|
+
activityId: activity.id?.href,
|
|
803
|
+
activity: json,
|
|
804
|
+
recipient
|
|
805
|
+
});
|
|
806
|
+
span.setStatus({
|
|
807
|
+
code: SpanStatusCode.UNSET,
|
|
808
|
+
message: `Activity ${activity.id?.href} has already been processed.`
|
|
809
|
+
});
|
|
810
|
+
return "alreadyProcessed";
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
if (activity.actorId == null) {
|
|
814
|
+
logger.error("Missing actor.", { activity: json });
|
|
815
|
+
span.setStatus({
|
|
816
|
+
code: SpanStatusCode.ERROR,
|
|
817
|
+
message: "Missing actor."
|
|
818
|
+
});
|
|
819
|
+
return "missingActor";
|
|
820
|
+
}
|
|
821
|
+
span.setAttribute("activitypub.actor.id", activity.actorId.href);
|
|
822
|
+
if (queue != null) {
|
|
823
|
+
const carrier = {};
|
|
824
|
+
propagation.inject(context.active(), carrier);
|
|
825
|
+
try {
|
|
826
|
+
await queue.enqueue({
|
|
827
|
+
type: "inbox",
|
|
828
|
+
id: crypto.randomUUID(),
|
|
829
|
+
baseUrl: ctx.origin,
|
|
830
|
+
activity: json,
|
|
831
|
+
identifier: recipient,
|
|
832
|
+
attempt: 0,
|
|
833
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
834
|
+
traceContext: carrier
|
|
835
|
+
});
|
|
836
|
+
} catch (error) {
|
|
837
|
+
logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
|
|
838
|
+
error,
|
|
839
|
+
activityId: activity.id?.href,
|
|
840
|
+
activity: json,
|
|
841
|
+
recipient
|
|
842
|
+
});
|
|
843
|
+
span.setStatus({
|
|
844
|
+
code: SpanStatusCode.ERROR,
|
|
845
|
+
message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
|
|
846
|
+
});
|
|
847
|
+
throw error;
|
|
848
|
+
}
|
|
849
|
+
logger.info("Activity {activityId} is enqueued.", {
|
|
850
|
+
activityId: activity.id?.href,
|
|
851
|
+
activity: json,
|
|
852
|
+
recipient
|
|
853
|
+
});
|
|
854
|
+
return "enqueued";
|
|
855
|
+
}
|
|
856
|
+
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
857
|
+
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
|
|
858
|
+
const dispatched = inboxListeners?.dispatchWithClass(activity);
|
|
859
|
+
if (dispatched == null) {
|
|
860
|
+
logger.error("Unsupported activity type:\n{activity}", {
|
|
861
|
+
activity: json,
|
|
862
|
+
recipient
|
|
863
|
+
});
|
|
864
|
+
span.setStatus({
|
|
865
|
+
code: SpanStatusCode.UNSET,
|
|
866
|
+
message: `Unsupported activity type: ${getTypeId(activity).href}`
|
|
867
|
+
});
|
|
868
|
+
span.end();
|
|
869
|
+
return "unsupportedActivity";
|
|
870
|
+
}
|
|
871
|
+
const { class: cls, listener } = dispatched;
|
|
872
|
+
span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
|
873
|
+
try {
|
|
874
|
+
await listener(inboxContextFactory(recipient, json, activity?.id?.href, getTypeId(activity).href), activity);
|
|
875
|
+
} catch (error) {
|
|
876
|
+
try {
|
|
877
|
+
await inboxErrorHandler?.(ctx, error);
|
|
878
|
+
} catch (error) {
|
|
879
|
+
logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
880
|
+
error,
|
|
881
|
+
activityId: activity.id?.href,
|
|
882
|
+
activity: json,
|
|
883
|
+
recipient
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
|
|
887
|
+
error,
|
|
888
|
+
activityId: activity.id?.href,
|
|
889
|
+
activity: json,
|
|
890
|
+
recipient
|
|
891
|
+
});
|
|
892
|
+
span.setStatus({
|
|
893
|
+
code: SpanStatusCode.ERROR,
|
|
894
|
+
message: String(error)
|
|
895
|
+
});
|
|
896
|
+
span.end();
|
|
897
|
+
return "error";
|
|
898
|
+
}
|
|
899
|
+
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
|
|
900
|
+
logger.info("Activity {activityId} has been processed.", {
|
|
901
|
+
activityId: activity.id?.href,
|
|
902
|
+
activity: json,
|
|
903
|
+
recipient
|
|
904
|
+
});
|
|
905
|
+
span.end();
|
|
906
|
+
return "success";
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
//#endregion
|
|
858
910
|
//#region src/federation/keycache.ts
|
|
859
911
|
var KvKeyCache = class {
|
|
860
912
|
kv;
|
|
@@ -1082,8 +1134,8 @@ async function handleObject(request, { values, context, objectDispatcher, author
|
|
|
1082
1134
|
* @param parameters The parameters for handling the collection.
|
|
1083
1135
|
* @returns A promise that resolves to an HTTP response.
|
|
1084
1136
|
*/
|
|
1085
|
-
async function handleCollection(request, { name: name$
|
|
1086
|
-
const spanName = name$
|
|
1137
|
+
async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, onUnauthorized, onNotFound }) {
|
|
1138
|
+
const spanName = name$1.trim().replace(/\s+/g, "_");
|
|
1087
1139
|
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
1088
1140
|
const tracer = tracerProvider.getTracer(name, version);
|
|
1089
1141
|
const cursor = new URL(request.url).searchParams.get("cursor");
|
|
@@ -1125,7 +1177,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
|
|
|
1125
1177
|
collection = new OrderedCollection({
|
|
1126
1178
|
id: baseUri,
|
|
1127
1179
|
totalItems: totalItems == null ? null : Number(totalItems),
|
|
1128
|
-
items: filterCollectionItems(itemsOrResponse, name$
|
|
1180
|
+
items: filterCollectionItems(itemsOrResponse, name$1, filterPredicate)
|
|
1129
1181
|
});
|
|
1130
1182
|
} else {
|
|
1131
1183
|
const lastCursor = await collectionCallbacks.lastCursor?.(context, identifier);
|
|
@@ -1146,7 +1198,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
|
|
|
1146
1198
|
} else {
|
|
1147
1199
|
const uri = new URL(baseUri);
|
|
1148
1200
|
uri.searchParams.set("cursor", cursor);
|
|
1149
|
-
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$
|
|
1201
|
+
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
|
|
1150
1202
|
kind: SpanKind.SERVER,
|
|
1151
1203
|
attributes: {
|
|
1152
1204
|
"activitypub.collection.id": uri.href,
|
|
@@ -1190,7 +1242,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
|
|
|
1190
1242
|
id: uri,
|
|
1191
1243
|
prev,
|
|
1192
1244
|
next,
|
|
1193
|
-
items: filterCollectionItems(items, name$
|
|
1245
|
+
items: filterCollectionItems(items, name$1, filterPredicate),
|
|
1194
1246
|
partOf
|
|
1195
1247
|
});
|
|
1196
1248
|
}
|
|
@@ -1230,9 +1282,205 @@ function filterCollectionItems(items, collectionName, filterPredicate) {
|
|
|
1230
1282
|
}
|
|
1231
1283
|
continue;
|
|
1232
1284
|
}
|
|
1233
|
-
result.push(mappedItem);
|
|
1285
|
+
result.push(mappedItem);
|
|
1286
|
+
}
|
|
1287
|
+
return result;
|
|
1288
|
+
}
|
|
1289
|
+
function summarizeJsonActivity(json) {
|
|
1290
|
+
if (json == null || typeof json !== "object") return {};
|
|
1291
|
+
const activity = json;
|
|
1292
|
+
return {
|
|
1293
|
+
activityId: typeof activity.id === "string" ? activity.id : void 0,
|
|
1294
|
+
activityType: typeof activity.type === "string" ? activity.type : void 0
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Handles an outbox POST request.
|
|
1299
|
+
* @template TContextData The context data to pass to the context.
|
|
1300
|
+
* @param request The HTTP request.
|
|
1301
|
+
* @param parameters The parameters for handling the request.
|
|
1302
|
+
* @returns A promise that resolves to an HTTP response.
|
|
1303
|
+
* @since 2.2.0
|
|
1304
|
+
*/
|
|
1305
|
+
async function handleOutbox(request, { identifier, context: ctx, outboxContextFactory, actorDispatcher, authorizePredicate, outboxListeners, outboxErrorHandler, onUnauthorized, onNotFound }) {
|
|
1306
|
+
const logger = getLogger([
|
|
1307
|
+
"fedify",
|
|
1308
|
+
"federation",
|
|
1309
|
+
"outbox"
|
|
1310
|
+
]);
|
|
1311
|
+
if (request.bodyUsed) {
|
|
1312
|
+
logger.error("Request body has already been read.", { identifier });
|
|
1313
|
+
return new Response("Internal server error.", {
|
|
1314
|
+
status: 500,
|
|
1315
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1316
|
+
});
|
|
1317
|
+
} else if (request.body?.locked) {
|
|
1318
|
+
logger.error("Request body is locked.", { identifier });
|
|
1319
|
+
return new Response("Internal server error.", {
|
|
1320
|
+
status: 500,
|
|
1321
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
if (actorDispatcher == null) {
|
|
1325
|
+
logger.error("Actor dispatcher is not set.", { identifier });
|
|
1326
|
+
return await onNotFound(request);
|
|
1327
|
+
}
|
|
1328
|
+
if (authorizePredicate != null) {
|
|
1329
|
+
const authorizeContext = ctx.clone(ctx.data);
|
|
1330
|
+
authorizeContext.request = request.clone();
|
|
1331
|
+
const requestForUnauthorized = authorizeContext.request.clone();
|
|
1332
|
+
if (!await authorizePredicate(authorizeContext, identifier)) return await onUnauthorized(requestForUnauthorized);
|
|
1333
|
+
}
|
|
1334
|
+
const actor = await actorDispatcher(ctx, identifier);
|
|
1335
|
+
if (actor == null || actor instanceof Tombstone) {
|
|
1336
|
+
logger.error("Actor {identifier} not found.", { identifier });
|
|
1337
|
+
return await onNotFound(request);
|
|
1338
|
+
}
|
|
1339
|
+
const requestForParsing = request.clone();
|
|
1340
|
+
let json;
|
|
1341
|
+
try {
|
|
1342
|
+
json = await requestForParsing.json();
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
logger.error("Failed to parse JSON:\n{error}", {
|
|
1345
|
+
identifier,
|
|
1346
|
+
error
|
|
1347
|
+
});
|
|
1348
|
+
const outboxContext = outboxContextFactory(identifier, null, void 0, "");
|
|
1349
|
+
try {
|
|
1350
|
+
await outboxErrorHandler?.(outboxContext, error);
|
|
1351
|
+
} catch (error) {
|
|
1352
|
+
logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
|
|
1353
|
+
error,
|
|
1354
|
+
identifier
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
return new Response("Invalid JSON.", {
|
|
1358
|
+
status: 400,
|
|
1359
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
let activity;
|
|
1363
|
+
try {
|
|
1364
|
+
activity = await Activity.fromJsonLd(json, ctx);
|
|
1365
|
+
} catch (error) {
|
|
1366
|
+
const summary = summarizeJsonActivity(json);
|
|
1367
|
+
logger.error("Failed to parse activity:\n{error}", {
|
|
1368
|
+
identifier,
|
|
1369
|
+
...summary,
|
|
1370
|
+
error
|
|
1371
|
+
});
|
|
1372
|
+
const outboxContext = outboxContextFactory(identifier, json, summary.activityId, summary.activityType ?? "");
|
|
1373
|
+
try {
|
|
1374
|
+
await outboxErrorHandler?.(outboxContext, error);
|
|
1375
|
+
} catch (error) {
|
|
1376
|
+
logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
|
|
1377
|
+
error,
|
|
1378
|
+
identifier,
|
|
1379
|
+
...summary
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
return new Response("Invalid activity.", {
|
|
1383
|
+
status: 400,
|
|
1384
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
const outboxContext = outboxContextFactory(identifier, json, activity.id?.href, getTypeId(activity).href);
|
|
1388
|
+
const expectedActorId = actor.id ?? ctx.getActorUri(identifier);
|
|
1389
|
+
if (activity.actorIds.length < 1) {
|
|
1390
|
+
const error = /* @__PURE__ */ new Error("The posted activity has no actor.");
|
|
1391
|
+
logger.error("The posted activity has no actor for outbox {identifier}.", {
|
|
1392
|
+
identifier,
|
|
1393
|
+
activityId: activity.id?.href,
|
|
1394
|
+
expectedActorId: expectedActorId.href
|
|
1395
|
+
});
|
|
1396
|
+
try {
|
|
1397
|
+
await outboxErrorHandler?.(outboxContext, error);
|
|
1398
|
+
} catch (error) {
|
|
1399
|
+
logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
|
|
1400
|
+
error,
|
|
1401
|
+
activityId: activity.id?.href,
|
|
1402
|
+
activityType: getTypeId(activity).href,
|
|
1403
|
+
identifier
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
return new Response(error.message, {
|
|
1407
|
+
status: 400,
|
|
1408
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1409
|
+
});
|
|
1234
1410
|
}
|
|
1235
|
-
|
|
1411
|
+
if (!activity.actorIds.every((actorId) => actorId.href === expectedActorId.href)) {
|
|
1412
|
+
const error = /* @__PURE__ */ new Error("The activity actor does not match the outbox owner.");
|
|
1413
|
+
logger.error("The posted activity actor does not match outbox owner {identifier}.", {
|
|
1414
|
+
identifier,
|
|
1415
|
+
activityId: activity.id?.href,
|
|
1416
|
+
expectedActorId: expectedActorId.href,
|
|
1417
|
+
actorIds: activity.actorIds.map((actorId) => actorId.href)
|
|
1418
|
+
});
|
|
1419
|
+
try {
|
|
1420
|
+
await outboxErrorHandler?.(outboxContext, error);
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
|
|
1423
|
+
error,
|
|
1424
|
+
activityId: activity.id?.href,
|
|
1425
|
+
activityType: getTypeId(activity).href,
|
|
1426
|
+
identifier
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
return new Response(error.message, {
|
|
1430
|
+
status: 400,
|
|
1431
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
const dispatched = outboxListeners?.dispatchWithClass(activity);
|
|
1435
|
+
if (dispatched == null) {
|
|
1436
|
+
logger.debug("Unsupported activity type {activityType}.", {
|
|
1437
|
+
identifier,
|
|
1438
|
+
activityId: activity.id?.href,
|
|
1439
|
+
activityType: getTypeId(activity).href
|
|
1440
|
+
});
|
|
1441
|
+
return new Response("", {
|
|
1442
|
+
status: 202,
|
|
1443
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
try {
|
|
1447
|
+
await dispatched.listener(outboxContext, activity);
|
|
1448
|
+
} catch (error) {
|
|
1449
|
+
try {
|
|
1450
|
+
await outboxErrorHandler?.(outboxContext, error);
|
|
1451
|
+
} catch (error) {
|
|
1452
|
+
logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
|
|
1453
|
+
error,
|
|
1454
|
+
activityId: activity.id?.href,
|
|
1455
|
+
activityType: getTypeId(activity).href,
|
|
1456
|
+
identifier
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
|
|
1460
|
+
error,
|
|
1461
|
+
activityId: activity.id?.href,
|
|
1462
|
+
activityType: getTypeId(activity).href,
|
|
1463
|
+
identifier
|
|
1464
|
+
});
|
|
1465
|
+
return new Response("Internal server error.", {
|
|
1466
|
+
status: 500,
|
|
1467
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
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.", {
|
|
1471
|
+
identifier,
|
|
1472
|
+
activityId: activity.id?.href,
|
|
1473
|
+
activityType: getTypeId(activity).href
|
|
1474
|
+
});
|
|
1475
|
+
logger.info("Activity {activityId} has been processed in outbox listener.", {
|
|
1476
|
+
activityId: activity.id?.href,
|
|
1477
|
+
activityType: getTypeId(activity).href,
|
|
1478
|
+
identifier
|
|
1479
|
+
});
|
|
1480
|
+
return new Response("", {
|
|
1481
|
+
status: 202,
|
|
1482
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1483
|
+
});
|
|
1236
1484
|
}
|
|
1237
1485
|
/**
|
|
1238
1486
|
* Handles an inbox request for ActivityPub activities.
|
|
@@ -1664,8 +1912,8 @@ var CustomCollectionHandler = class {
|
|
|
1664
1912
|
* @param CollectionPage The CollectionPage constructor.
|
|
1665
1913
|
* @param filterPredicate Optional filter predicate for items.
|
|
1666
1914
|
*/
|
|
1667
|
-
constructor(name$
|
|
1668
|
-
this.name = name$
|
|
1915
|
+
constructor(name$2, values, context, callbacks, tracerProvider = trace.getTracerProvider(), Collection, CollectionPage, filterPredicate) {
|
|
1916
|
+
this.name = name$2;
|
|
1669
1917
|
this.values = values;
|
|
1670
1918
|
this.context = context;
|
|
1671
1919
|
this.callbacks = callbacks;
|
|
@@ -2467,6 +2715,7 @@ var middleware_exports = /* @__PURE__ */ __exportAll({
|
|
|
2467
2715
|
FederationImpl: () => FederationImpl,
|
|
2468
2716
|
InboxContextImpl: () => InboxContextImpl,
|
|
2469
2717
|
KvSpecDeterminer: () => KvSpecDeterminer,
|
|
2718
|
+
OutboxContextImpl: () => OutboxContextImpl,
|
|
2470
2719
|
createFederation: () => createFederation
|
|
2471
2720
|
});
|
|
2472
2721
|
/**
|
|
@@ -3281,16 +3530,37 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3281
3530
|
onNotFound
|
|
3282
3531
|
});
|
|
3283
3532
|
}
|
|
3284
|
-
case "outbox":
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3533
|
+
case "outbox":
|
|
3534
|
+
if (request.method === "POST") {
|
|
3535
|
+
if (this.outboxListeners == null) return new Response("Method not allowed.", {
|
|
3536
|
+
status: 405,
|
|
3537
|
+
headers: {
|
|
3538
|
+
Allow: "GET, HEAD",
|
|
3539
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
3540
|
+
}
|
|
3541
|
+
});
|
|
3542
|
+
return await handleOutbox(request, {
|
|
3543
|
+
identifier: route.values.identifier,
|
|
3544
|
+
context,
|
|
3545
|
+
outboxContextFactory: context.toOutboxContext.bind(context),
|
|
3546
|
+
actorDispatcher: this.actorCallbacks?.dispatcher,
|
|
3547
|
+
authorizePredicate: this.outboxAuthorizePredicate ?? this.outboxCallbacks?.authorizePredicate,
|
|
3548
|
+
outboxListeners: this.outboxListeners,
|
|
3549
|
+
outboxErrorHandler: this.outboxListenerErrorHandler,
|
|
3550
|
+
onUnauthorized,
|
|
3551
|
+
onNotFound
|
|
3552
|
+
});
|
|
3553
|
+
}
|
|
3554
|
+
return await handleCollection(request, {
|
|
3555
|
+
name: "outbox",
|
|
3556
|
+
identifier: route.values.identifier,
|
|
3557
|
+
uriGetter: context.getOutboxUri.bind(context),
|
|
3558
|
+
context,
|
|
3559
|
+
collectionCallbacks: this.outboxCallbacks,
|
|
3560
|
+
tracerProvider: this.tracerProvider,
|
|
3561
|
+
onUnauthorized,
|
|
3562
|
+
onNotFound
|
|
3563
|
+
});
|
|
3294
3564
|
case "inbox":
|
|
3295
3565
|
if (request.method !== "POST") return await handleCollection(request, {
|
|
3296
3566
|
name: "inbox",
|
|
@@ -3460,6 +3730,16 @@ var ContextImpl = class ContextImpl {
|
|
|
3460
3730
|
invokedFromActorKeyPairsDispatcher: this.invokedFromActorKeyPairsDispatcher
|
|
3461
3731
|
});
|
|
3462
3732
|
}
|
|
3733
|
+
toOutboxContext(identifier, activity, activityId, activityType) {
|
|
3734
|
+
return new OutboxContextImpl(identifier, activity, activityId, activityType, {
|
|
3735
|
+
url: this.url,
|
|
3736
|
+
federation: this.federation,
|
|
3737
|
+
data: this.data,
|
|
3738
|
+
documentLoader: this.documentLoader,
|
|
3739
|
+
contextLoader: this.contextLoader,
|
|
3740
|
+
invokedFromActorKeyPairsDispatcher: this.invokedFromActorKeyPairsDispatcher
|
|
3741
|
+
});
|
|
3742
|
+
}
|
|
3463
3743
|
get hostname() {
|
|
3464
3744
|
return this.url.hostname;
|
|
3465
3745
|
}
|
|
@@ -3749,9 +4029,9 @@ var ContextImpl = class ContextImpl {
|
|
|
3749
4029
|
attributes: {
|
|
3750
4030
|
"activitypub.activity.type": getTypeId(activity).href,
|
|
3751
4031
|
"activitypub.activity.to": activity.toIds.map((to) => to.href),
|
|
3752
|
-
"activitypub.activity.cc": activity.
|
|
4032
|
+
"activitypub.activity.cc": activity.ccIds.map((cc) => cc.href),
|
|
3753
4033
|
"activitypub.activity.bto": activity.btoIds.map((bto) => bto.href),
|
|
3754
|
-
"activitypub.activity.bcc": activity.
|
|
4034
|
+
"activitypub.activity.bcc": activity.bccIds.map((bcc) => bcc.href)
|
|
3755
4035
|
}
|
|
3756
4036
|
}, async (span) => {
|
|
3757
4037
|
try {
|
|
@@ -3846,6 +4126,13 @@ var ContextImpl = class ContextImpl {
|
|
|
3846
4126
|
preferSharedInbox: options.preferSharedInbox,
|
|
3847
4127
|
excludeBaseUris: options.excludeBaseUris
|
|
3848
4128
|
});
|
|
4129
|
+
if (globalThis.Object.keys(inboxes).length < 1) {
|
|
4130
|
+
logger.debug("No inboxes found for activity {activityId}.", {
|
|
4131
|
+
activityId: activity.id?.href,
|
|
4132
|
+
activity
|
|
4133
|
+
});
|
|
4134
|
+
return false;
|
|
4135
|
+
}
|
|
3849
4136
|
logger.debug("Sending activity {activityId} to inboxes:\n{inboxes}", {
|
|
3850
4137
|
inboxes: globalThis.Object.keys(inboxes),
|
|
3851
4138
|
activityId: activity.id?.href,
|
|
@@ -3853,7 +4140,7 @@ var ContextImpl = class ContextImpl {
|
|
|
3853
4140
|
});
|
|
3854
4141
|
if (this.federation.fanoutQueue == null || options.immediate || options.fanout === "skip" || (options.fanout ?? "auto") === "auto" && globalThis.Object.keys(inboxes).length < FANOUT_THRESHOLD) {
|
|
3855
4142
|
await this.federation.sendActivity(keys, inboxes, activity, opts);
|
|
3856
|
-
return;
|
|
4143
|
+
return true;
|
|
3857
4144
|
}
|
|
3858
4145
|
const keyJwkPairs = await Promise.all(keys.map(async ({ keyId, privateKey }) => ({
|
|
3859
4146
|
keyId: keyId.href,
|
|
@@ -3882,6 +4169,7 @@ var ContextImpl = class ContextImpl {
|
|
|
3882
4169
|
};
|
|
3883
4170
|
if (!this.federation.manuallyStartQueue) this.federation._startQueueInternal(this.data);
|
|
3884
4171
|
await this.federation.fanoutQueue.enqueue(message, { orderingKey: options.orderingKey });
|
|
4172
|
+
return true;
|
|
3885
4173
|
}
|
|
3886
4174
|
async *getFollowers(identifier) {
|
|
3887
4175
|
if (this.federation.followersCallbacks == null) throw new Error("No followers collection dispatcher registered.");
|
|
@@ -4116,6 +4404,170 @@ var RequestContextImpl = class RequestContextImpl extends ContextImpl {
|
|
|
4116
4404
|
}
|
|
4117
4405
|
}
|
|
4118
4406
|
};
|
|
4407
|
+
function forwardActivity(ctx, loggerCategory, forwarder, recipients, options) {
|
|
4408
|
+
return ctx.tracerProvider.getTracer(name, version).startActiveSpan(ctx.federation.outboxQueue == null || options?.immediate ? `activitypub.${loggerCategory}` : "activitypub.fanout", {
|
|
4409
|
+
kind: ctx.federation.outboxQueue == null || options?.immediate ? SpanKind.CLIENT : SpanKind.PRODUCER,
|
|
4410
|
+
attributes: { "activitypub.activity.type": ctx.activityType }
|
|
4411
|
+
}, async (span) => {
|
|
4412
|
+
try {
|
|
4413
|
+
if (ctx.activityId != null) span.setAttribute("activitypub.activity.id", ctx.activityId);
|
|
4414
|
+
return await forwardActivityInternal(ctx, loggerCategory, forwarder, recipients, options);
|
|
4415
|
+
} catch (e) {
|
|
4416
|
+
span.setStatus({
|
|
4417
|
+
code: SpanStatusCode.ERROR,
|
|
4418
|
+
message: String(e)
|
|
4419
|
+
});
|
|
4420
|
+
throw e;
|
|
4421
|
+
} finally {
|
|
4422
|
+
span.end();
|
|
4423
|
+
}
|
|
4424
|
+
});
|
|
4425
|
+
}
|
|
4426
|
+
async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipients, options) {
|
|
4427
|
+
const logger = getLogger([
|
|
4428
|
+
"fedify",
|
|
4429
|
+
"federation",
|
|
4430
|
+
loggerCategory
|
|
4431
|
+
]);
|
|
4432
|
+
let keys;
|
|
4433
|
+
let identifier = null;
|
|
4434
|
+
if ("identifier" in forwarder || "username" in forwarder) {
|
|
4435
|
+
if ("identifier" in forwarder) identifier = forwarder.identifier;
|
|
4436
|
+
else {
|
|
4437
|
+
const username = forwarder.username;
|
|
4438
|
+
if (ctx.federation.actorCallbacks?.handleMapper == null) identifier = username;
|
|
4439
|
+
else {
|
|
4440
|
+
const mapped = await ctx.federation.actorCallbacks.handleMapper(ctx, username);
|
|
4441
|
+
if (mapped == null) throw new Error(`No actor found for the given username ${JSON.stringify(username)}.`);
|
|
4442
|
+
identifier = mapped;
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
const actorKeyPairs = await ctx.getActorKeyPairs(identifier);
|
|
4446
|
+
if (actorKeyPairs.length < 1) throw new Error(`No key pair found for actor ${JSON.stringify(identifier)}.`);
|
|
4447
|
+
keys = actorKeyPairs.map((kp) => ({
|
|
4448
|
+
keyId: kp.keyId,
|
|
4449
|
+
privateKey: kp.privateKey
|
|
4450
|
+
}));
|
|
4451
|
+
} else if (Array.isArray(forwarder)) {
|
|
4452
|
+
if (forwarder.length < 1) throw new Error("The forwarder's key pairs are empty.");
|
|
4453
|
+
keys = forwarder;
|
|
4454
|
+
} else keys = [forwarder];
|
|
4455
|
+
if (!hasSignatureLike(ctx.activity)) {
|
|
4456
|
+
if (!hasProofLike(ctx.activity)) {
|
|
4457
|
+
if (options?.skipIfUnsigned) return false;
|
|
4458
|
+
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.", {
|
|
4459
|
+
activityId: ctx.activityId,
|
|
4460
|
+
activityType: ctx.activityType,
|
|
4461
|
+
identifier: identifier ?? void 0
|
|
4462
|
+
});
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
if (recipients === "followers") {
|
|
4466
|
+
if (identifier == null) throw new Error("If recipients is \"followers\", forwarder must be an actor identifier or username.");
|
|
4467
|
+
const followers = [];
|
|
4468
|
+
for await (const recipient of ctx.getFollowers(identifier)) followers.push(recipient);
|
|
4469
|
+
recipients = followers;
|
|
4470
|
+
}
|
|
4471
|
+
const inboxes = extractInboxes({
|
|
4472
|
+
recipients: Array.isArray(recipients) ? recipients : [recipients],
|
|
4473
|
+
preferSharedInbox: options?.preferSharedInbox,
|
|
4474
|
+
excludeBaseUris: options?.excludeBaseUris
|
|
4475
|
+
});
|
|
4476
|
+
if (globalThis.Object.keys(inboxes).length < 1) {
|
|
4477
|
+
logger.debug("No inboxes found for activity {activityId}.", {
|
|
4478
|
+
activityId: ctx.activityId,
|
|
4479
|
+
activityType: ctx.activityType,
|
|
4480
|
+
identifier: identifier ?? void 0
|
|
4481
|
+
});
|
|
4482
|
+
return false;
|
|
4483
|
+
}
|
|
4484
|
+
logger.debug("Forwarding activity {activityId} to inboxes:\n{inboxes}", {
|
|
4485
|
+
inboxes: globalThis.Object.keys(inboxes),
|
|
4486
|
+
activityId: ctx.activityId,
|
|
4487
|
+
activity: ctx.activity
|
|
4488
|
+
});
|
|
4489
|
+
if (options?.immediate || ctx.federation.outboxQueue == null) {
|
|
4490
|
+
if (options?.immediate) logger.debug("Forwarding activity immediately without queue since immediate option is set.");
|
|
4491
|
+
else logger.debug("Forwarding activity immediately without queue since queue is not set.");
|
|
4492
|
+
const promises = [];
|
|
4493
|
+
for (const inbox in inboxes) promises.push(sendActivity({
|
|
4494
|
+
keys,
|
|
4495
|
+
activity: ctx.activity,
|
|
4496
|
+
activityId: ctx.activityId,
|
|
4497
|
+
activityType: ctx.activityType,
|
|
4498
|
+
inbox: new URL(inbox),
|
|
4499
|
+
sharedInbox: inboxes[inbox].sharedInbox,
|
|
4500
|
+
tracerProvider: ctx.tracerProvider,
|
|
4501
|
+
specDeterminer: new KvSpecDeterminer(ctx.federation.kv, ctx.federation.kvPrefixes.httpMessageSignaturesSpec, ctx.federation.firstKnock)
|
|
4502
|
+
}));
|
|
4503
|
+
await Promise.all(promises);
|
|
4504
|
+
return true;
|
|
4505
|
+
}
|
|
4506
|
+
logger.debug("Enqueuing activity {activityId} to forward later.", {
|
|
4507
|
+
activityId: ctx.activityId,
|
|
4508
|
+
activity: ctx.activity
|
|
4509
|
+
});
|
|
4510
|
+
if (!ctx.federation.manuallyStartQueue) ctx.federation._startQueueInternal(ctx.data);
|
|
4511
|
+
const keyJwkPairs = [];
|
|
4512
|
+
for (const { keyId, privateKey } of keys) {
|
|
4513
|
+
const privateKeyJwk = await exportJwk(privateKey);
|
|
4514
|
+
keyJwkPairs.push({
|
|
4515
|
+
keyId: keyId.href,
|
|
4516
|
+
privateKey: privateKeyJwk
|
|
4517
|
+
});
|
|
4518
|
+
}
|
|
4519
|
+
const carrier = {};
|
|
4520
|
+
propagation.inject(context.active(), carrier);
|
|
4521
|
+
const orderingKey = options?.orderingKey;
|
|
4522
|
+
const started = (/* @__PURE__ */ new Date()).toISOString();
|
|
4523
|
+
const messages = [];
|
|
4524
|
+
for (const inbox in inboxes) {
|
|
4525
|
+
const inboxUrl = new URL(inbox);
|
|
4526
|
+
const message = {
|
|
4527
|
+
type: "outbox",
|
|
4528
|
+
id: crypto.randomUUID(),
|
|
4529
|
+
baseUrl: ctx.origin,
|
|
4530
|
+
keys: keyJwkPairs,
|
|
4531
|
+
activity: ctx.activity,
|
|
4532
|
+
activityId: ctx.activityId,
|
|
4533
|
+
activityType: ctx.activityType,
|
|
4534
|
+
inbox,
|
|
4535
|
+
sharedInbox: inboxes[inbox].sharedInbox,
|
|
4536
|
+
actorIds: [...inboxes[inbox].actorIds],
|
|
4537
|
+
started,
|
|
4538
|
+
attempt: 0,
|
|
4539
|
+
headers: {},
|
|
4540
|
+
orderingKey: orderingKey == null ? void 0 : `${orderingKey}\n${inboxUrl.origin}`,
|
|
4541
|
+
traceContext: carrier
|
|
4542
|
+
};
|
|
4543
|
+
messages.push({
|
|
4544
|
+
message,
|
|
4545
|
+
orderingKey: message.orderingKey
|
|
4546
|
+
});
|
|
4547
|
+
}
|
|
4548
|
+
const { outboxQueue } = ctx.federation;
|
|
4549
|
+
if (outboxQueue.enqueueMany == null || orderingKey != null) {
|
|
4550
|
+
const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
|
|
4551
|
+
const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
4552
|
+
if (errors.length > 0) {
|
|
4553
|
+
logger.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
|
|
4554
|
+
activityId: ctx.activityId,
|
|
4555
|
+
errors
|
|
4556
|
+
});
|
|
4557
|
+
if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${ctx.activityId} to forward later.`);
|
|
4558
|
+
throw errors[0];
|
|
4559
|
+
}
|
|
4560
|
+
} else try {
|
|
4561
|
+
await outboxQueue.enqueueMany(messages.map((m) => m.message));
|
|
4562
|
+
} catch (error) {
|
|
4563
|
+
logger.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
|
|
4564
|
+
activityId: ctx.activityId,
|
|
4565
|
+
error
|
|
4566
|
+
});
|
|
4567
|
+
throw error;
|
|
4568
|
+
}
|
|
4569
|
+
return true;
|
|
4570
|
+
}
|
|
4119
4571
|
var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
4120
4572
|
recipient;
|
|
4121
4573
|
activity;
|
|
@@ -4139,13 +4591,40 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
|
4139
4591
|
});
|
|
4140
4592
|
}
|
|
4141
4593
|
forwardActivity(forwarder, recipients, options) {
|
|
4142
|
-
return this
|
|
4143
|
-
|
|
4144
|
-
|
|
4594
|
+
return forwardActivity(this, "inbox", forwarder, recipients, options).then(() => void 0);
|
|
4595
|
+
}
|
|
4596
|
+
};
|
|
4597
|
+
var OutboxContextImpl = class OutboxContextImpl extends ContextImpl {
|
|
4598
|
+
#deliveryState;
|
|
4599
|
+
identifier;
|
|
4600
|
+
activity;
|
|
4601
|
+
activityId;
|
|
4602
|
+
activityType;
|
|
4603
|
+
constructor(identifier, activity, activityId, activityType, options, deliveryState = { delivered: false }) {
|
|
4604
|
+
super(options);
|
|
4605
|
+
this.#deliveryState = deliveryState;
|
|
4606
|
+
this.identifier = identifier;
|
|
4607
|
+
this.activity = activity;
|
|
4608
|
+
this.activityId = activityId;
|
|
4609
|
+
this.activityType = activityType;
|
|
4610
|
+
}
|
|
4611
|
+
hasDeliveredActivity() {
|
|
4612
|
+
return this.#deliveryState.delivered;
|
|
4613
|
+
}
|
|
4614
|
+
sendActivity(sender, recipients, activity, options = {}) {
|
|
4615
|
+
return this.tracerProvider.getTracer(name, version).startActiveSpan(this.federation.outboxQueue == null || options.immediate ? "activitypub.outbox" : "activitypub.fanout", {
|
|
4616
|
+
kind: this.federation.outboxQueue == null || options.immediate ? SpanKind.CLIENT : SpanKind.PRODUCER,
|
|
4617
|
+
attributes: {
|
|
4618
|
+
"activitypub.activity.type": getTypeId(activity).href,
|
|
4619
|
+
"activitypub.activity.to": activity.toIds.map((to) => to.href),
|
|
4620
|
+
"activitypub.activity.cc": activity.ccIds.map((cc) => cc.href),
|
|
4621
|
+
"activitypub.activity.bto": activity.btoIds.map((bto) => bto.href),
|
|
4622
|
+
"activitypub.activity.bcc": activity.bccIds.map((bcc) => bcc.href)
|
|
4623
|
+
}
|
|
4145
4624
|
}, async (span) => {
|
|
4146
4625
|
try {
|
|
4147
|
-
if (
|
|
4148
|
-
await this.
|
|
4626
|
+
if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
|
|
4627
|
+
if (await this.sendActivityInternal(sender, recipients, activity, options, span)) this.#deliveryState.delivered = true;
|
|
4149
4628
|
} catch (e) {
|
|
4150
4629
|
span.setStatus({
|
|
4151
4630
|
code: SpanStatusCode.ERROR,
|
|
@@ -4157,151 +4636,20 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
|
4157
4636
|
}
|
|
4158
4637
|
});
|
|
4159
4638
|
}
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
"federation",
|
|
4164
|
-
"inbox"
|
|
4165
|
-
]);
|
|
4166
|
-
let keys;
|
|
4167
|
-
let identifier = null;
|
|
4168
|
-
if ("identifier" in forwarder || "username" in forwarder) {
|
|
4169
|
-
if ("identifier" in forwarder) identifier = forwarder.identifier;
|
|
4170
|
-
else {
|
|
4171
|
-
const username = forwarder.username;
|
|
4172
|
-
if (this.federation.actorCallbacks?.handleMapper == null) identifier = username;
|
|
4173
|
-
else {
|
|
4174
|
-
const mapped = await this.federation.actorCallbacks.handleMapper(this, username);
|
|
4175
|
-
if (mapped == null) throw new Error(`No actor found for the given username ${JSON.stringify(username)}.`);
|
|
4176
|
-
identifier = mapped;
|
|
4177
|
-
}
|
|
4178
|
-
}
|
|
4179
|
-
const actorKeyPairs = await this.getActorKeyPairs(identifier);
|
|
4180
|
-
if (actorKeyPairs.length < 1) throw new Error(`No key pair found for actor ${JSON.stringify(identifier)}.`);
|
|
4181
|
-
keys = actorKeyPairs.map((kp) => ({
|
|
4182
|
-
keyId: kp.keyId,
|
|
4183
|
-
privateKey: kp.privateKey
|
|
4184
|
-
}));
|
|
4185
|
-
} else if (Array.isArray(forwarder)) {
|
|
4186
|
-
if (forwarder.length < 1) throw new Error("The forwarder's key pairs are empty.");
|
|
4187
|
-
keys = forwarder;
|
|
4188
|
-
} else keys = [forwarder];
|
|
4189
|
-
if (!hasSignature(this.activity)) {
|
|
4190
|
-
let hasProof;
|
|
4191
|
-
try {
|
|
4192
|
-
hasProof = await (await Activity.fromJsonLd(this.activity, this)).getProof() != null;
|
|
4193
|
-
} catch {
|
|
4194
|
-
hasProof = false;
|
|
4195
|
-
}
|
|
4196
|
-
if (!hasProof) {
|
|
4197
|
-
if (options?.skipIfUnsigned) return;
|
|
4198
|
-
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.");
|
|
4199
|
-
}
|
|
4200
|
-
}
|
|
4201
|
-
if (recipients === "followers") {
|
|
4202
|
-
if (identifier == null) throw new Error("If recipients is \"followers\", forwarder must be an actor identifier or username.");
|
|
4203
|
-
const followers = [];
|
|
4204
|
-
for await (const recipient of this.getFollowers(identifier)) followers.push(recipient);
|
|
4205
|
-
recipients = followers;
|
|
4206
|
-
}
|
|
4207
|
-
const inboxes = extractInboxes({
|
|
4208
|
-
recipients: Array.isArray(recipients) ? recipients : [recipients],
|
|
4209
|
-
preferSharedInbox: options?.preferSharedInbox,
|
|
4210
|
-
excludeBaseUris: options?.excludeBaseUris
|
|
4211
|
-
});
|
|
4212
|
-
logger.debug("Forwarding activity {activityId} to inboxes:\n{inboxes}", {
|
|
4213
|
-
inboxes: globalThis.Object.keys(inboxes),
|
|
4214
|
-
activityId: this.activityId,
|
|
4215
|
-
activity: this.activity
|
|
4216
|
-
});
|
|
4217
|
-
if (options?.immediate || this.federation.outboxQueue == null) {
|
|
4218
|
-
if (options?.immediate) logger.debug("Forwarding activity immediately without queue since immediate option is set.");
|
|
4219
|
-
else logger.debug("Forwarding activity immediately without queue since queue is not set.");
|
|
4220
|
-
const promises = [];
|
|
4221
|
-
for (const inbox in inboxes) promises.push(sendActivity({
|
|
4222
|
-
keys,
|
|
4223
|
-
activity: this.activity,
|
|
4224
|
-
activityId: this.activityId,
|
|
4225
|
-
activityType: this.activityType,
|
|
4226
|
-
inbox: new URL(inbox),
|
|
4227
|
-
sharedInbox: inboxes[inbox].sharedInbox,
|
|
4228
|
-
tracerProvider: this.tracerProvider,
|
|
4229
|
-
specDeterminer: new KvSpecDeterminer(this.federation.kv, this.federation.kvPrefixes.httpMessageSignaturesSpec, this.federation.firstKnock)
|
|
4230
|
-
}));
|
|
4231
|
-
await Promise.all(promises);
|
|
4232
|
-
return;
|
|
4233
|
-
}
|
|
4234
|
-
logger.debug("Enqueuing activity {activityId} to forward later.", {
|
|
4235
|
-
activityId: this.activityId,
|
|
4236
|
-
activity: this.activity
|
|
4639
|
+
forwardActivity(forwarder, recipients, options) {
|
|
4640
|
+
return forwardActivity(this, "outbox", forwarder, recipients, options).then((delivered) => {
|
|
4641
|
+
if (delivered) this.#deliveryState.delivered = true;
|
|
4237
4642
|
});
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
const orderingKey = options?.orderingKey;
|
|
4249
|
-
const messages = [];
|
|
4250
|
-
for (const inbox in inboxes) {
|
|
4251
|
-
const inboxUrl = new URL(inbox);
|
|
4252
|
-
const message = {
|
|
4253
|
-
type: "outbox",
|
|
4254
|
-
id: crypto.randomUUID(),
|
|
4255
|
-
baseUrl: this.origin,
|
|
4256
|
-
keys: keyJwkPairs,
|
|
4257
|
-
activity: this.activity,
|
|
4258
|
-
activityId: this.activityId,
|
|
4259
|
-
activityType: this.activityType,
|
|
4260
|
-
inbox,
|
|
4261
|
-
sharedInbox: inboxes[inbox].sharedInbox,
|
|
4262
|
-
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4263
|
-
attempt: 0,
|
|
4264
|
-
headers: {},
|
|
4265
|
-
orderingKey: orderingKey == null ? void 0 : `${orderingKey}\n${inboxUrl.origin}`,
|
|
4266
|
-
traceContext: carrier
|
|
4267
|
-
};
|
|
4268
|
-
messages.push({
|
|
4269
|
-
message,
|
|
4270
|
-
orderingKey: message.orderingKey
|
|
4271
|
-
});
|
|
4272
|
-
}
|
|
4273
|
-
const { outboxQueue } = this.federation;
|
|
4274
|
-
if (outboxQueue.enqueueMany == null) {
|
|
4275
|
-
const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
|
|
4276
|
-
const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
4277
|
-
if (errors.length > 0) {
|
|
4278
|
-
logger.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
|
|
4279
|
-
activityId: this.activityId,
|
|
4280
|
-
errors
|
|
4281
|
-
});
|
|
4282
|
-
if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${this.activityId} to forward later.`);
|
|
4283
|
-
throw errors[0];
|
|
4284
|
-
}
|
|
4285
|
-
} else if (orderingKey != null) {
|
|
4286
|
-
const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
|
|
4287
|
-
const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
4288
|
-
if (errors.length > 0) {
|
|
4289
|
-
logger.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
|
|
4290
|
-
activityId: this.activityId,
|
|
4291
|
-
errors
|
|
4292
|
-
});
|
|
4293
|
-
if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${this.activityId} to forward later.`);
|
|
4294
|
-
throw errors[0];
|
|
4295
|
-
}
|
|
4296
|
-
} else try {
|
|
4297
|
-
await outboxQueue.enqueueMany(messages.map((m) => m.message));
|
|
4298
|
-
} catch (error) {
|
|
4299
|
-
logger.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
|
|
4300
|
-
activityId: this.activityId,
|
|
4301
|
-
error
|
|
4302
|
-
});
|
|
4303
|
-
throw error;
|
|
4304
|
-
}
|
|
4643
|
+
}
|
|
4644
|
+
clone(data) {
|
|
4645
|
+
return new OutboxContextImpl(this.identifier, this.activity, this.activityId, this.activityType, {
|
|
4646
|
+
url: this.url,
|
|
4647
|
+
federation: this.federation,
|
|
4648
|
+
data,
|
|
4649
|
+
documentLoader: this.documentLoader,
|
|
4650
|
+
contextLoader: this.contextLoader,
|
|
4651
|
+
invokedFromActorKeyPairsDispatcher: this.invokedFromActorKeyPairsDispatcher
|
|
4652
|
+
}, this.#deliveryState);
|
|
4305
4653
|
}
|
|
4306
4654
|
};
|
|
4307
4655
|
var KvSpecDeterminer = class {
|