@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 @@ const { Temporal } = require("@js-temporal/polyfill");
|
|
|
2
2
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
3
3
|
require("./chunk-DDcVe30Y.cjs");
|
|
4
4
|
const require_transformers = require("./transformers-NeAONrAq.cjs");
|
|
5
|
-
const require_http = require("./http-
|
|
6
|
-
const require_proof = require("./proof-
|
|
5
|
+
const require_http = require("./http-BLuuf-Rt.cjs");
|
|
6
|
+
const require_proof = require("./proof-DwRcU3OF.cjs");
|
|
7
7
|
const require_types = require("./types-KC4QAoxe.cjs");
|
|
8
|
-
const require_kv_cache = require("./kv-cache-
|
|
8
|
+
const require_kv_cache = require("./kv-cache-BlXew67e.cjs");
|
|
9
9
|
let _logtape_logtape = require("@logtape/logtape");
|
|
10
10
|
let _fedify_vocab = require("@fedify/vocab");
|
|
11
11
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
@@ -17,14 +17,15 @@ let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
|
|
|
17
17
|
let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
|
|
18
18
|
let _fedify_webfinger = require("@fedify/webfinger");
|
|
19
19
|
let node_url = require("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 === _fedify_vocab.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 = (0, _logtape_logtape.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, (0, _fedify_vocab.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: _opentelemetry_api.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: _opentelemetry_api.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
|
-
_opentelemetry_api.propagation.inject(_opentelemetry_api.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: _opentelemetry_api.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 ?? _opentelemetry_api.trace.getTracerProvider();
|
|
138
|
-
return await tracerProvider.getTracer(require_http.name, require_http.version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: _opentelemetry_api.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: _opentelemetry_api.SpanStatusCode.UNSET,
|
|
147
|
-
message: `Unsupported activity type: ${(0, _fedify_vocab.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, (0, _fedify_vocab.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: _opentelemetry_api.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().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;
|
|
@@ -331,7 +210,7 @@ var FederationBuilderImpl = class {
|
|
|
331
210
|
this.collectionTypeIds = {};
|
|
332
211
|
}
|
|
333
212
|
async build(options) {
|
|
334
|
-
const { FederationImpl } = await Promise.resolve().then(() => require("./middleware-
|
|
213
|
+
const { FederationImpl } = await Promise.resolve().then(() => require("./middleware-CUe96Oxe.cjs"));
|
|
335
214
|
const f = new FederationImpl(options);
|
|
336
215
|
const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
|
|
337
216
|
f.router = this.router.clone();
|
|
@@ -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="${(0, byte_encodings_hex.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 = (0, _logtape_logtape.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, (0, _fedify_vocab.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: _opentelemetry_api.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: _opentelemetry_api.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
|
+
_opentelemetry_api.propagation.inject(_opentelemetry_api.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: _opentelemetry_api.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 ?? _opentelemetry_api.trace.getTracerProvider();
|
|
857
|
+
return await tracerProvider.getTracer(require_http.name, require_http.version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: _opentelemetry_api.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: _opentelemetry_api.SpanStatusCode.UNSET,
|
|
866
|
+
message: `Unsupported activity type: ${(0, _fedify_vocab.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, (0, _fedify_vocab.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: _opentelemetry_api.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 ?? _opentelemetry_api.trace.getTracerProvider();
|
|
1088
1140
|
const tracer = tracerProvider.getTracer(require_http.name, require_http.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 _fedify_vocab.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: _opentelemetry_api.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 = (0, _logtape_logtape.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 _fedify_vocab.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 _fedify_vocab.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, (0, _fedify_vocab.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: (0, _fedify_vocab.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: (0, _fedify_vocab.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: (0, _fedify_vocab.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: (0, _fedify_vocab.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: (0, _fedify_vocab.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: (0, _fedify_vocab.getTypeId)(activity).href
|
|
1474
|
+
});
|
|
1475
|
+
logger.info("Activity {activityId} has been processed in outbox listener.", {
|
|
1476
|
+
activityId: activity.id?.href,
|
|
1477
|
+
activityType: (0, _fedify_vocab.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 = _opentelemetry_api.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;
|
|
@@ -3274,16 +3522,37 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3274
3522
|
onNotFound
|
|
3275
3523
|
});
|
|
3276
3524
|
}
|
|
3277
|
-
case "outbox":
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3525
|
+
case "outbox":
|
|
3526
|
+
if (request.method === "POST") {
|
|
3527
|
+
if (this.outboxListeners == null) return new Response("Method not allowed.", {
|
|
3528
|
+
status: 405,
|
|
3529
|
+
headers: {
|
|
3530
|
+
Allow: "GET, HEAD",
|
|
3531
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
3532
|
+
}
|
|
3533
|
+
});
|
|
3534
|
+
return await handleOutbox(request, {
|
|
3535
|
+
identifier: route.values.identifier,
|
|
3536
|
+
context,
|
|
3537
|
+
outboxContextFactory: context.toOutboxContext.bind(context),
|
|
3538
|
+
actorDispatcher: this.actorCallbacks?.dispatcher,
|
|
3539
|
+
authorizePredicate: this.outboxAuthorizePredicate ?? this.outboxCallbacks?.authorizePredicate,
|
|
3540
|
+
outboxListeners: this.outboxListeners,
|
|
3541
|
+
outboxErrorHandler: this.outboxListenerErrorHandler,
|
|
3542
|
+
onUnauthorized,
|
|
3543
|
+
onNotFound
|
|
3544
|
+
});
|
|
3545
|
+
}
|
|
3546
|
+
return await handleCollection(request, {
|
|
3547
|
+
name: "outbox",
|
|
3548
|
+
identifier: route.values.identifier,
|
|
3549
|
+
uriGetter: context.getOutboxUri.bind(context),
|
|
3550
|
+
context,
|
|
3551
|
+
collectionCallbacks: this.outboxCallbacks,
|
|
3552
|
+
tracerProvider: this.tracerProvider,
|
|
3553
|
+
onUnauthorized,
|
|
3554
|
+
onNotFound
|
|
3555
|
+
});
|
|
3287
3556
|
case "inbox":
|
|
3288
3557
|
if (request.method !== "POST") return await handleCollection(request, {
|
|
3289
3558
|
name: "inbox",
|
|
@@ -3453,6 +3722,16 @@ var ContextImpl = class ContextImpl {
|
|
|
3453
3722
|
invokedFromActorKeyPairsDispatcher: this.invokedFromActorKeyPairsDispatcher
|
|
3454
3723
|
});
|
|
3455
3724
|
}
|
|
3725
|
+
toOutboxContext(identifier, activity, activityId, activityType) {
|
|
3726
|
+
return new OutboxContextImpl(identifier, activity, activityId, activityType, {
|
|
3727
|
+
url: this.url,
|
|
3728
|
+
federation: this.federation,
|
|
3729
|
+
data: this.data,
|
|
3730
|
+
documentLoader: this.documentLoader,
|
|
3731
|
+
contextLoader: this.contextLoader,
|
|
3732
|
+
invokedFromActorKeyPairsDispatcher: this.invokedFromActorKeyPairsDispatcher
|
|
3733
|
+
});
|
|
3734
|
+
}
|
|
3456
3735
|
get hostname() {
|
|
3457
3736
|
return this.url.hostname;
|
|
3458
3737
|
}
|
|
@@ -3742,9 +4021,9 @@ var ContextImpl = class ContextImpl {
|
|
|
3742
4021
|
attributes: {
|
|
3743
4022
|
"activitypub.activity.type": (0, _fedify_vocab.getTypeId)(activity).href,
|
|
3744
4023
|
"activitypub.activity.to": activity.toIds.map((to) => to.href),
|
|
3745
|
-
"activitypub.activity.cc": activity.
|
|
4024
|
+
"activitypub.activity.cc": activity.ccIds.map((cc) => cc.href),
|
|
3746
4025
|
"activitypub.activity.bto": activity.btoIds.map((bto) => bto.href),
|
|
3747
|
-
"activitypub.activity.bcc": activity.
|
|
4026
|
+
"activitypub.activity.bcc": activity.bccIds.map((bcc) => bcc.href)
|
|
3748
4027
|
}
|
|
3749
4028
|
}, async (span) => {
|
|
3750
4029
|
try {
|
|
@@ -3839,6 +4118,13 @@ var ContextImpl = class ContextImpl {
|
|
|
3839
4118
|
preferSharedInbox: options.preferSharedInbox,
|
|
3840
4119
|
excludeBaseUris: options.excludeBaseUris
|
|
3841
4120
|
});
|
|
4121
|
+
if (globalThis.Object.keys(inboxes).length < 1) {
|
|
4122
|
+
logger.debug("No inboxes found for activity {activityId}.", {
|
|
4123
|
+
activityId: activity.id?.href,
|
|
4124
|
+
activity
|
|
4125
|
+
});
|
|
4126
|
+
return false;
|
|
4127
|
+
}
|
|
3842
4128
|
logger.debug("Sending activity {activityId} to inboxes:\n{inboxes}", {
|
|
3843
4129
|
inboxes: globalThis.Object.keys(inboxes),
|
|
3844
4130
|
activityId: activity.id?.href,
|
|
@@ -3846,7 +4132,7 @@ var ContextImpl = class ContextImpl {
|
|
|
3846
4132
|
});
|
|
3847
4133
|
if (this.federation.fanoutQueue == null || options.immediate || options.fanout === "skip" || (options.fanout ?? "auto") === "auto" && globalThis.Object.keys(inboxes).length < FANOUT_THRESHOLD) {
|
|
3848
4134
|
await this.federation.sendActivity(keys, inboxes, activity, opts);
|
|
3849
|
-
return;
|
|
4135
|
+
return true;
|
|
3850
4136
|
}
|
|
3851
4137
|
const keyJwkPairs = await Promise.all(keys.map(async ({ keyId, privateKey }) => ({
|
|
3852
4138
|
keyId: keyId.href,
|
|
@@ -3875,6 +4161,7 @@ var ContextImpl = class ContextImpl {
|
|
|
3875
4161
|
};
|
|
3876
4162
|
if (!this.federation.manuallyStartQueue) this.federation._startQueueInternal(this.data);
|
|
3877
4163
|
await this.federation.fanoutQueue.enqueue(message, { orderingKey: options.orderingKey });
|
|
4164
|
+
return true;
|
|
3878
4165
|
}
|
|
3879
4166
|
async *getFollowers(identifier) {
|
|
3880
4167
|
if (this.federation.followersCallbacks == null) throw new Error("No followers collection dispatcher registered.");
|
|
@@ -4109,6 +4396,170 @@ var RequestContextImpl = class RequestContextImpl extends ContextImpl {
|
|
|
4109
4396
|
}
|
|
4110
4397
|
}
|
|
4111
4398
|
};
|
|
4399
|
+
function forwardActivity(ctx, loggerCategory, forwarder, recipients, options) {
|
|
4400
|
+
return ctx.tracerProvider.getTracer(require_http.name, require_http.version).startActiveSpan(ctx.federation.outboxQueue == null || options?.immediate ? `activitypub.${loggerCategory}` : "activitypub.fanout", {
|
|
4401
|
+
kind: ctx.federation.outboxQueue == null || options?.immediate ? _opentelemetry_api.SpanKind.CLIENT : _opentelemetry_api.SpanKind.PRODUCER,
|
|
4402
|
+
attributes: { "activitypub.activity.type": ctx.activityType }
|
|
4403
|
+
}, async (span) => {
|
|
4404
|
+
try {
|
|
4405
|
+
if (ctx.activityId != null) span.setAttribute("activitypub.activity.id", ctx.activityId);
|
|
4406
|
+
return await forwardActivityInternal(ctx, loggerCategory, forwarder, recipients, options);
|
|
4407
|
+
} catch (e) {
|
|
4408
|
+
span.setStatus({
|
|
4409
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
4410
|
+
message: String(e)
|
|
4411
|
+
});
|
|
4412
|
+
throw e;
|
|
4413
|
+
} finally {
|
|
4414
|
+
span.end();
|
|
4415
|
+
}
|
|
4416
|
+
});
|
|
4417
|
+
}
|
|
4418
|
+
async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipients, options) {
|
|
4419
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
4420
|
+
"fedify",
|
|
4421
|
+
"federation",
|
|
4422
|
+
loggerCategory
|
|
4423
|
+
]);
|
|
4424
|
+
let keys;
|
|
4425
|
+
let identifier = null;
|
|
4426
|
+
if ("identifier" in forwarder || "username" in forwarder) {
|
|
4427
|
+
if ("identifier" in forwarder) identifier = forwarder.identifier;
|
|
4428
|
+
else {
|
|
4429
|
+
const username = forwarder.username;
|
|
4430
|
+
if (ctx.federation.actorCallbacks?.handleMapper == null) identifier = username;
|
|
4431
|
+
else {
|
|
4432
|
+
const mapped = await ctx.federation.actorCallbacks.handleMapper(ctx, username);
|
|
4433
|
+
if (mapped == null) throw new Error(`No actor found for the given username ${JSON.stringify(username)}.`);
|
|
4434
|
+
identifier = mapped;
|
|
4435
|
+
}
|
|
4436
|
+
}
|
|
4437
|
+
const actorKeyPairs = await ctx.getActorKeyPairs(identifier);
|
|
4438
|
+
if (actorKeyPairs.length < 1) throw new Error(`No key pair found for actor ${JSON.stringify(identifier)}.`);
|
|
4439
|
+
keys = actorKeyPairs.map((kp) => ({
|
|
4440
|
+
keyId: kp.keyId,
|
|
4441
|
+
privateKey: kp.privateKey
|
|
4442
|
+
}));
|
|
4443
|
+
} else if (Array.isArray(forwarder)) {
|
|
4444
|
+
if (forwarder.length < 1) throw new Error("The forwarder's key pairs are empty.");
|
|
4445
|
+
keys = forwarder;
|
|
4446
|
+
} else keys = [forwarder];
|
|
4447
|
+
if (!require_proof.hasSignatureLike(ctx.activity)) {
|
|
4448
|
+
if (!require_proof.hasProofLike(ctx.activity)) {
|
|
4449
|
+
if (options?.skipIfUnsigned) return false;
|
|
4450
|
+
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.", {
|
|
4451
|
+
activityId: ctx.activityId,
|
|
4452
|
+
activityType: ctx.activityType,
|
|
4453
|
+
identifier: identifier ?? void 0
|
|
4454
|
+
});
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
if (recipients === "followers") {
|
|
4458
|
+
if (identifier == null) throw new Error("If recipients is \"followers\", forwarder must be an actor identifier or username.");
|
|
4459
|
+
const followers = [];
|
|
4460
|
+
for await (const recipient of ctx.getFollowers(identifier)) followers.push(recipient);
|
|
4461
|
+
recipients = followers;
|
|
4462
|
+
}
|
|
4463
|
+
const inboxes = extractInboxes({
|
|
4464
|
+
recipients: Array.isArray(recipients) ? recipients : [recipients],
|
|
4465
|
+
preferSharedInbox: options?.preferSharedInbox,
|
|
4466
|
+
excludeBaseUris: options?.excludeBaseUris
|
|
4467
|
+
});
|
|
4468
|
+
if (globalThis.Object.keys(inboxes).length < 1) {
|
|
4469
|
+
logger.debug("No inboxes found for activity {activityId}.", {
|
|
4470
|
+
activityId: ctx.activityId,
|
|
4471
|
+
activityType: ctx.activityType,
|
|
4472
|
+
identifier: identifier ?? void 0
|
|
4473
|
+
});
|
|
4474
|
+
return false;
|
|
4475
|
+
}
|
|
4476
|
+
logger.debug("Forwarding activity {activityId} to inboxes:\n{inboxes}", {
|
|
4477
|
+
inboxes: globalThis.Object.keys(inboxes),
|
|
4478
|
+
activityId: ctx.activityId,
|
|
4479
|
+
activity: ctx.activity
|
|
4480
|
+
});
|
|
4481
|
+
if (options?.immediate || ctx.federation.outboxQueue == null) {
|
|
4482
|
+
if (options?.immediate) logger.debug("Forwarding activity immediately without queue since immediate option is set.");
|
|
4483
|
+
else logger.debug("Forwarding activity immediately without queue since queue is not set.");
|
|
4484
|
+
const promises = [];
|
|
4485
|
+
for (const inbox in inboxes) promises.push(sendActivity({
|
|
4486
|
+
keys,
|
|
4487
|
+
activity: ctx.activity,
|
|
4488
|
+
activityId: ctx.activityId,
|
|
4489
|
+
activityType: ctx.activityType,
|
|
4490
|
+
inbox: new URL(inbox),
|
|
4491
|
+
sharedInbox: inboxes[inbox].sharedInbox,
|
|
4492
|
+
tracerProvider: ctx.tracerProvider,
|
|
4493
|
+
specDeterminer: new KvSpecDeterminer(ctx.federation.kv, ctx.federation.kvPrefixes.httpMessageSignaturesSpec, ctx.federation.firstKnock)
|
|
4494
|
+
}));
|
|
4495
|
+
await Promise.all(promises);
|
|
4496
|
+
return true;
|
|
4497
|
+
}
|
|
4498
|
+
logger.debug("Enqueuing activity {activityId} to forward later.", {
|
|
4499
|
+
activityId: ctx.activityId,
|
|
4500
|
+
activity: ctx.activity
|
|
4501
|
+
});
|
|
4502
|
+
if (!ctx.federation.manuallyStartQueue) ctx.federation._startQueueInternal(ctx.data);
|
|
4503
|
+
const keyJwkPairs = [];
|
|
4504
|
+
for (const { keyId, privateKey } of keys) {
|
|
4505
|
+
const privateKeyJwk = await require_http.exportJwk(privateKey);
|
|
4506
|
+
keyJwkPairs.push({
|
|
4507
|
+
keyId: keyId.href,
|
|
4508
|
+
privateKey: privateKeyJwk
|
|
4509
|
+
});
|
|
4510
|
+
}
|
|
4511
|
+
const carrier = {};
|
|
4512
|
+
_opentelemetry_api.propagation.inject(_opentelemetry_api.context.active(), carrier);
|
|
4513
|
+
const orderingKey = options?.orderingKey;
|
|
4514
|
+
const started = (/* @__PURE__ */ new Date()).toISOString();
|
|
4515
|
+
const messages = [];
|
|
4516
|
+
for (const inbox in inboxes) {
|
|
4517
|
+
const inboxUrl = new URL(inbox);
|
|
4518
|
+
const message = {
|
|
4519
|
+
type: "outbox",
|
|
4520
|
+
id: crypto.randomUUID(),
|
|
4521
|
+
baseUrl: ctx.origin,
|
|
4522
|
+
keys: keyJwkPairs,
|
|
4523
|
+
activity: ctx.activity,
|
|
4524
|
+
activityId: ctx.activityId,
|
|
4525
|
+
activityType: ctx.activityType,
|
|
4526
|
+
inbox,
|
|
4527
|
+
sharedInbox: inboxes[inbox].sharedInbox,
|
|
4528
|
+
actorIds: [...inboxes[inbox].actorIds],
|
|
4529
|
+
started,
|
|
4530
|
+
attempt: 0,
|
|
4531
|
+
headers: {},
|
|
4532
|
+
orderingKey: orderingKey == null ? void 0 : `${orderingKey}\n${inboxUrl.origin}`,
|
|
4533
|
+
traceContext: carrier
|
|
4534
|
+
};
|
|
4535
|
+
messages.push({
|
|
4536
|
+
message,
|
|
4537
|
+
orderingKey: message.orderingKey
|
|
4538
|
+
});
|
|
4539
|
+
}
|
|
4540
|
+
const { outboxQueue } = ctx.federation;
|
|
4541
|
+
if (outboxQueue.enqueueMany == null || orderingKey != null) {
|
|
4542
|
+
const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
|
|
4543
|
+
const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
4544
|
+
if (errors.length > 0) {
|
|
4545
|
+
logger.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
|
|
4546
|
+
activityId: ctx.activityId,
|
|
4547
|
+
errors
|
|
4548
|
+
});
|
|
4549
|
+
if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${ctx.activityId} to forward later.`);
|
|
4550
|
+
throw errors[0];
|
|
4551
|
+
}
|
|
4552
|
+
} else try {
|
|
4553
|
+
await outboxQueue.enqueueMany(messages.map((m) => m.message));
|
|
4554
|
+
} catch (error) {
|
|
4555
|
+
logger.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
|
|
4556
|
+
activityId: ctx.activityId,
|
|
4557
|
+
error
|
|
4558
|
+
});
|
|
4559
|
+
throw error;
|
|
4560
|
+
}
|
|
4561
|
+
return true;
|
|
4562
|
+
}
|
|
4112
4563
|
var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
4113
4564
|
recipient;
|
|
4114
4565
|
activity;
|
|
@@ -4132,13 +4583,40 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
|
4132
4583
|
});
|
|
4133
4584
|
}
|
|
4134
4585
|
forwardActivity(forwarder, recipients, options) {
|
|
4135
|
-
return this
|
|
4136
|
-
|
|
4137
|
-
|
|
4586
|
+
return forwardActivity(this, "inbox", forwarder, recipients, options).then(() => void 0);
|
|
4587
|
+
}
|
|
4588
|
+
};
|
|
4589
|
+
var OutboxContextImpl = class OutboxContextImpl extends ContextImpl {
|
|
4590
|
+
#deliveryState;
|
|
4591
|
+
identifier;
|
|
4592
|
+
activity;
|
|
4593
|
+
activityId;
|
|
4594
|
+
activityType;
|
|
4595
|
+
constructor(identifier, activity, activityId, activityType, options, deliveryState = { delivered: false }) {
|
|
4596
|
+
super(options);
|
|
4597
|
+
this.#deliveryState = deliveryState;
|
|
4598
|
+
this.identifier = identifier;
|
|
4599
|
+
this.activity = activity;
|
|
4600
|
+
this.activityId = activityId;
|
|
4601
|
+
this.activityType = activityType;
|
|
4602
|
+
}
|
|
4603
|
+
hasDeliveredActivity() {
|
|
4604
|
+
return this.#deliveryState.delivered;
|
|
4605
|
+
}
|
|
4606
|
+
sendActivity(sender, recipients, activity, options = {}) {
|
|
4607
|
+
return this.tracerProvider.getTracer(require_http.name, require_http.version).startActiveSpan(this.federation.outboxQueue == null || options.immediate ? "activitypub.outbox" : "activitypub.fanout", {
|
|
4608
|
+
kind: this.federation.outboxQueue == null || options.immediate ? _opentelemetry_api.SpanKind.CLIENT : _opentelemetry_api.SpanKind.PRODUCER,
|
|
4609
|
+
attributes: {
|
|
4610
|
+
"activitypub.activity.type": (0, _fedify_vocab.getTypeId)(activity).href,
|
|
4611
|
+
"activitypub.activity.to": activity.toIds.map((to) => to.href),
|
|
4612
|
+
"activitypub.activity.cc": activity.ccIds.map((cc) => cc.href),
|
|
4613
|
+
"activitypub.activity.bto": activity.btoIds.map((bto) => bto.href),
|
|
4614
|
+
"activitypub.activity.bcc": activity.bccIds.map((bcc) => bcc.href)
|
|
4615
|
+
}
|
|
4138
4616
|
}, async (span) => {
|
|
4139
4617
|
try {
|
|
4140
|
-
if (
|
|
4141
|
-
await this.
|
|
4618
|
+
if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
|
|
4619
|
+
if (await this.sendActivityInternal(sender, recipients, activity, options, span)) this.#deliveryState.delivered = true;
|
|
4142
4620
|
} catch (e) {
|
|
4143
4621
|
span.setStatus({
|
|
4144
4622
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
@@ -4150,151 +4628,20 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
|
4150
4628
|
}
|
|
4151
4629
|
});
|
|
4152
4630
|
}
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
"federation",
|
|
4157
|
-
"inbox"
|
|
4158
|
-
]);
|
|
4159
|
-
let keys;
|
|
4160
|
-
let identifier = null;
|
|
4161
|
-
if ("identifier" in forwarder || "username" in forwarder) {
|
|
4162
|
-
if ("identifier" in forwarder) identifier = forwarder.identifier;
|
|
4163
|
-
else {
|
|
4164
|
-
const username = forwarder.username;
|
|
4165
|
-
if (this.federation.actorCallbacks?.handleMapper == null) identifier = username;
|
|
4166
|
-
else {
|
|
4167
|
-
const mapped = await this.federation.actorCallbacks.handleMapper(this, username);
|
|
4168
|
-
if (mapped == null) throw new Error(`No actor found for the given username ${JSON.stringify(username)}.`);
|
|
4169
|
-
identifier = mapped;
|
|
4170
|
-
}
|
|
4171
|
-
}
|
|
4172
|
-
const actorKeyPairs = await this.getActorKeyPairs(identifier);
|
|
4173
|
-
if (actorKeyPairs.length < 1) throw new Error(`No key pair found for actor ${JSON.stringify(identifier)}.`);
|
|
4174
|
-
keys = actorKeyPairs.map((kp) => ({
|
|
4175
|
-
keyId: kp.keyId,
|
|
4176
|
-
privateKey: kp.privateKey
|
|
4177
|
-
}));
|
|
4178
|
-
} else if (Array.isArray(forwarder)) {
|
|
4179
|
-
if (forwarder.length < 1) throw new Error("The forwarder's key pairs are empty.");
|
|
4180
|
-
keys = forwarder;
|
|
4181
|
-
} else keys = [forwarder];
|
|
4182
|
-
if (!require_proof.hasSignature(this.activity)) {
|
|
4183
|
-
let hasProof;
|
|
4184
|
-
try {
|
|
4185
|
-
hasProof = await (await _fedify_vocab.Activity.fromJsonLd(this.activity, this)).getProof() != null;
|
|
4186
|
-
} catch {
|
|
4187
|
-
hasProof = false;
|
|
4188
|
-
}
|
|
4189
|
-
if (!hasProof) {
|
|
4190
|
-
if (options?.skipIfUnsigned) return;
|
|
4191
|
-
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.");
|
|
4192
|
-
}
|
|
4193
|
-
}
|
|
4194
|
-
if (recipients === "followers") {
|
|
4195
|
-
if (identifier == null) throw new Error("If recipients is \"followers\", forwarder must be an actor identifier or username.");
|
|
4196
|
-
const followers = [];
|
|
4197
|
-
for await (const recipient of this.getFollowers(identifier)) followers.push(recipient);
|
|
4198
|
-
recipients = followers;
|
|
4199
|
-
}
|
|
4200
|
-
const inboxes = extractInboxes({
|
|
4201
|
-
recipients: Array.isArray(recipients) ? recipients : [recipients],
|
|
4202
|
-
preferSharedInbox: options?.preferSharedInbox,
|
|
4203
|
-
excludeBaseUris: options?.excludeBaseUris
|
|
4204
|
-
});
|
|
4205
|
-
logger.debug("Forwarding activity {activityId} to inboxes:\n{inboxes}", {
|
|
4206
|
-
inboxes: globalThis.Object.keys(inboxes),
|
|
4207
|
-
activityId: this.activityId,
|
|
4208
|
-
activity: this.activity
|
|
4209
|
-
});
|
|
4210
|
-
if (options?.immediate || this.federation.outboxQueue == null) {
|
|
4211
|
-
if (options?.immediate) logger.debug("Forwarding activity immediately without queue since immediate option is set.");
|
|
4212
|
-
else logger.debug("Forwarding activity immediately without queue since queue is not set.");
|
|
4213
|
-
const promises = [];
|
|
4214
|
-
for (const inbox in inboxes) promises.push(sendActivity({
|
|
4215
|
-
keys,
|
|
4216
|
-
activity: this.activity,
|
|
4217
|
-
activityId: this.activityId,
|
|
4218
|
-
activityType: this.activityType,
|
|
4219
|
-
inbox: new URL(inbox),
|
|
4220
|
-
sharedInbox: inboxes[inbox].sharedInbox,
|
|
4221
|
-
tracerProvider: this.tracerProvider,
|
|
4222
|
-
specDeterminer: new KvSpecDeterminer(this.federation.kv, this.federation.kvPrefixes.httpMessageSignaturesSpec, this.federation.firstKnock)
|
|
4223
|
-
}));
|
|
4224
|
-
await Promise.all(promises);
|
|
4225
|
-
return;
|
|
4226
|
-
}
|
|
4227
|
-
logger.debug("Enqueuing activity {activityId} to forward later.", {
|
|
4228
|
-
activityId: this.activityId,
|
|
4229
|
-
activity: this.activity
|
|
4631
|
+
forwardActivity(forwarder, recipients, options) {
|
|
4632
|
+
return forwardActivity(this, "outbox", forwarder, recipients, options).then((delivered) => {
|
|
4633
|
+
if (delivered) this.#deliveryState.delivered = true;
|
|
4230
4634
|
});
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
const orderingKey = options?.orderingKey;
|
|
4242
|
-
const messages = [];
|
|
4243
|
-
for (const inbox in inboxes) {
|
|
4244
|
-
const inboxUrl = new URL(inbox);
|
|
4245
|
-
const message = {
|
|
4246
|
-
type: "outbox",
|
|
4247
|
-
id: crypto.randomUUID(),
|
|
4248
|
-
baseUrl: this.origin,
|
|
4249
|
-
keys: keyJwkPairs,
|
|
4250
|
-
activity: this.activity,
|
|
4251
|
-
activityId: this.activityId,
|
|
4252
|
-
activityType: this.activityType,
|
|
4253
|
-
inbox,
|
|
4254
|
-
sharedInbox: inboxes[inbox].sharedInbox,
|
|
4255
|
-
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4256
|
-
attempt: 0,
|
|
4257
|
-
headers: {},
|
|
4258
|
-
orderingKey: orderingKey == null ? void 0 : `${orderingKey}\n${inboxUrl.origin}`,
|
|
4259
|
-
traceContext: carrier
|
|
4260
|
-
};
|
|
4261
|
-
messages.push({
|
|
4262
|
-
message,
|
|
4263
|
-
orderingKey: message.orderingKey
|
|
4264
|
-
});
|
|
4265
|
-
}
|
|
4266
|
-
const { outboxQueue } = this.federation;
|
|
4267
|
-
if (outboxQueue.enqueueMany == null) {
|
|
4268
|
-
const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
|
|
4269
|
-
const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
4270
|
-
if (errors.length > 0) {
|
|
4271
|
-
logger.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
|
|
4272
|
-
activityId: this.activityId,
|
|
4273
|
-
errors
|
|
4274
|
-
});
|
|
4275
|
-
if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${this.activityId} to forward later.`);
|
|
4276
|
-
throw errors[0];
|
|
4277
|
-
}
|
|
4278
|
-
} else if (orderingKey != null) {
|
|
4279
|
-
const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
|
|
4280
|
-
const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
4281
|
-
if (errors.length > 0) {
|
|
4282
|
-
logger.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
|
|
4283
|
-
activityId: this.activityId,
|
|
4284
|
-
errors
|
|
4285
|
-
});
|
|
4286
|
-
if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${this.activityId} to forward later.`);
|
|
4287
|
-
throw errors[0];
|
|
4288
|
-
}
|
|
4289
|
-
} else try {
|
|
4290
|
-
await outboxQueue.enqueueMany(messages.map((m) => m.message));
|
|
4291
|
-
} catch (error) {
|
|
4292
|
-
logger.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
|
|
4293
|
-
activityId: this.activityId,
|
|
4294
|
-
error
|
|
4295
|
-
});
|
|
4296
|
-
throw error;
|
|
4297
|
-
}
|
|
4635
|
+
}
|
|
4636
|
+
clone(data) {
|
|
4637
|
+
return new OutboxContextImpl(this.identifier, this.activity, this.activityId, this.activityType, {
|
|
4638
|
+
url: this.url,
|
|
4639
|
+
federation: this.federation,
|
|
4640
|
+
data,
|
|
4641
|
+
documentLoader: this.documentLoader,
|
|
4642
|
+
contextLoader: this.contextLoader,
|
|
4643
|
+
invokedFromActorKeyPairsDispatcher: this.invokedFromActorKeyPairsDispatcher
|
|
4644
|
+
}, this.#deliveryState);
|
|
4298
4645
|
}
|
|
4299
4646
|
};
|
|
4300
4647
|
var KvSpecDeterminer = class {
|
|
@@ -4371,6 +4718,12 @@ Object.defineProperty(exports, "KvSpecDeterminer", {
|
|
|
4371
4718
|
return KvSpecDeterminer;
|
|
4372
4719
|
}
|
|
4373
4720
|
});
|
|
4721
|
+
Object.defineProperty(exports, "OutboxContextImpl", {
|
|
4722
|
+
enumerable: true,
|
|
4723
|
+
get: function() {
|
|
4724
|
+
return OutboxContextImpl;
|
|
4725
|
+
}
|
|
4726
|
+
});
|
|
4374
4727
|
Object.defineProperty(exports, "Router", {
|
|
4375
4728
|
enumerable: true,
|
|
4376
4729
|
get: function() {
|