@fedify/fedify 1.6.14 → 1.6.15
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/actor-C92GiVtr.js +146 -0
- package/dist/actor.js +34648 -5
- package/dist/{assert.js → assert-C-mZuSQl.js} +1 -1
- package/dist/{assert_instance_of.js → assert_instance_of-lS0Jr2iu.js} +1 -1
- package/dist/{assert_is_error.js → assert_is_error-CIYFACrT.js} +1 -1
- package/dist/{assert_not_equals.js → assert_not_equals-C1azCAB0.js} +1 -1
- package/dist/{assert_rejects.js → assert_rejects-Bkh5lA1a.js} +2 -2
- package/dist/{assert_throws.js → assert_throws-CmpfkWEM.js} +2 -2
- package/dist/authdocloader-D6yaLhIk.js +51 -0
- package/dist/authdocloader.js +2 -4
- package/dist/{builder.js → builder-BxA2MvN4.js} +5 -5
- package/dist/{client.js → client-CwG-8F8w.js} +2 -2
- package/dist/compat/transformers.test.js +30 -30
- package/dist/{context.js → context-p_zzA2HO.js} +4 -4
- package/dist/docloader-BHWMnBPo.js +4413 -0
- package/dist/docloader.js +61 -4
- package/dist/{esm.js → esm-BRXvTSrx.js} +1 -1
- package/dist/federation/builder.test.js +17 -17
- package/dist/federation/collection.test.js +8 -8
- package/dist/federation/handler.test.js +36 -36
- package/dist/federation/inbox.test.js +11 -11
- package/dist/federation/keycache.test.js +12 -12
- package/dist/federation/kv.test.js +8 -8
- package/dist/federation/middleware.test.js +39 -39
- package/dist/federation/mq.test.js +8 -8
- package/dist/federation/retry.test.js +5 -5
- package/dist/federation/router.test.js +9 -9
- package/dist/federation/send.test.js +23 -23
- package/dist/http-CDlvJGv-.js +780 -0
- package/dist/http.js +4 -5
- package/dist/{inbox.js → inbox-ABTFAUzE.js} +3 -3
- package/dist/key-B3u1GQO0.js +259 -0
- package/dist/key-CFRooZ-5.js +16 -0
- package/dist/key.js +1 -7
- package/dist/key2.js +2 -4
- package/dist/{keycache.js → keycache-cqA-vzFY.js} +1 -1
- package/dist/{keys.js → keys-Bf_ChvLf.js} +1 -1
- package/dist/{ld.js → ld-BqMJ-pvL.js} +4 -4
- package/dist/{lookup2.js → lookup-BjvH6B6r.js} +4 -4
- package/dist/lookup-DYr8tISP.js +129 -0
- package/dist/lookup.js +1 -3
- package/dist/middleware-BqxvDftt.js +32 -0
- package/dist/middleware-DrkMfeD5.js +2638 -0
- package/dist/middleware.js +1220 -362
- package/dist/middleware2.js +6 -21
- package/dist/nodeinfo/client.test.js +12 -12
- package/dist/nodeinfo/handler.test.js +34 -34
- package/dist/nodeinfo/semver.test.js +8 -8
- package/dist/nodeinfo/types.test.js +9 -9
- package/dist/{owner.js → owner-DmM37H3z.js} +3 -3
- package/dist/proof-DAyY3Emw.js +255 -0
- package/dist/proof.js +330 -8
- package/dist/runtime/authdocloader.test.js +20 -20
- package/dist/runtime/docloader.test.js +13 -13
- package/dist/runtime/key.test.js +15 -15
- package/dist/runtime/langstr.test.js +8 -8
- package/dist/runtime/multibase/multibase.test.js +8 -8
- package/dist/runtime/url.test.js +7 -7
- package/dist/{send.js → send-D8zkuKY4.js} +2 -2
- package/dist/sig/http.test.js +20 -20
- package/dist/sig/key.test.js +17 -17
- package/dist/sig/ld.test.js +18 -18
- package/dist/sig/owner.test.js +20 -20
- package/dist/sig/proof.test.js +19 -19
- package/dist/{std__assert.js → std__assert-BdP_WkD-.js} +1 -1
- package/dist/testing/docloader.test.js +8 -8
- package/dist/testing/mod.js +2 -2
- package/dist/{testing.js → testing-qaAD4B0t.js} +1 -1
- package/dist/types-CB_2uuCA.js +51 -0
- package/dist/types.js +397 -3
- package/dist/vocab/actor.test.js +16 -16
- package/dist/vocab/lookup.test.js +17 -17
- package/dist/vocab/type.test.js +9 -9
- package/dist/vocab/vocab.test.js +17 -17
- package/dist/vocab-Cj-MrEWI.js +34386 -0
- package/dist/vocab.js +133 -34351
- package/dist/webfinger/handler.test.js +34 -34
- package/dist/webfinger/lookup.test.js +11 -11
- package/dist/x/cfworkers.test.js +7 -7
- package/package.json +1 -1
- /package/dist/{assert_equals.js → assert_equals-Dy0MG_Zw.js} +0 -0
- /package/dist/{chunk.js → chunk-DvTpRkcT.js} +0 -0
- /package/dist/{collection.js → collection-XNLQhehO.js} +0 -0
- /package/dist/compat/{transformers.test.d.ts → transformers.test-DnJbd34u.d.ts} +0 -0
- /package/dist/{denokv.js → denokv-XeyH3cFs.js} +0 -0
- /package/dist/{docloader2.js → docloader-BDSHZfTJ.js} +0 -0
- /package/dist/federation/{builder.test.d.ts → builder.test-Bpt6NOZ6.d.ts} +0 -0
- /package/dist/federation/{collection.test.d.ts → collection.test-DKJ6JOZz.d.ts} +0 -0
- /package/dist/federation/{handler.test.d.ts → handler.test-BMT7uLC0.d.ts} +0 -0
- /package/dist/federation/{inbox.test.d.ts → inbox.test-Do6i02Qp.d.ts} +0 -0
- /package/dist/federation/{keycache.test.d.ts → keycache.test-BT83IPZY.d.ts} +0 -0
- /package/dist/federation/{kv.test.d.ts → kv.test-kFzzF2VN.d.ts} +0 -0
- /package/dist/federation/{middleware.test.d.ts → middleware.test-B1R4_e3-.d.ts} +0 -0
- /package/dist/federation/{mq.test.d.ts → mq.test-l79EQQOe.d.ts} +0 -0
- /package/dist/federation/{retry.test.d.ts → retry.test-BqS50VCX.d.ts} +0 -0
- /package/dist/federation/{router.test.d.ts → router.test-CYQl4po-.d.ts} +0 -0
- /package/dist/federation/{send.test.d.ts → send.test-COUnNUzv.d.ts} +0 -0
- /package/dist/{kv.js → kv-DosGYGwV.js} +0 -0
- /package/dist/{langstr.js → langstr-pFHBDU4y.js} +0 -0
- /package/dist/{multibase.js → multibase-DBcKTV2a.js} +0 -0
- /package/dist/nodeinfo/{client.test.d.ts → client.test-CZLe79hL.d.ts} +0 -0
- /package/dist/nodeinfo/{handler.test.d.ts → handler.test-B-EDZ_hK.d.ts} +0 -0
- /package/dist/nodeinfo/{semver.test.d.ts → semver.test-BEuuQSEM.d.ts} +0 -0
- /package/dist/nodeinfo/{types.test.d.ts → types.test-B5AT89WV.d.ts} +0 -0
- /package/dist/{retry.js → retry-BQet39_l.js} +0 -0
- /package/dist/{router.js → router-BuDkN4RQ.js} +0 -0
- /package/dist/runtime/{authdocloader.test.d.ts → authdocloader.test-hCRKzn9v.d.ts} +0 -0
- /package/dist/runtime/{docloader.test.d.ts → docloader.test-CVd7i_5h.d.ts} +0 -0
- /package/dist/runtime/{key.test.d.ts → key.test-DBsILYSD.d.ts} +0 -0
- /package/dist/runtime/{langstr.test.d.ts → langstr.test-CiKxuuRY.d.ts} +0 -0
- /package/dist/runtime/multibase/{multibase.test.d.ts → multibase.test-Brh6gPBP.d.ts} +0 -0
- /package/dist/runtime/{url.test.d.ts → url.test-DlRqkU2j.d.ts} +0 -0
- /package/dist/{semver.js → semver-D9d-VO-_.js} +0 -0
- /package/dist/sig/{http.test.d.ts → http.test-BpXNAWNI.d.ts} +0 -0
- /package/dist/sig/{key.test.d.ts → key.test-B2iLIugy.d.ts} +0 -0
- /package/dist/sig/{ld.test.d.ts → ld.test-D-cI70Gw.d.ts} +0 -0
- /package/dist/sig/{owner.test.d.ts → owner.test-B_YRjMPj.d.ts} +0 -0
- /package/dist/sig/{proof.test.d.ts → proof.test-BagEM_-4.d.ts} +0 -0
- /package/dist/testing/{docloader.test.d.ts → docloader.test-lrzf6sDZ.d.ts} +0 -0
- /package/dist/testing/{mod.d.ts → mod-3uM8ZvS7.d.ts} +0 -0
- /package/dist/{type.js → type-DFsmi-p1.js} +0 -0
- /package/dist/{url.js → url-BdNvnK9P.js} +0 -0
- /package/dist/vocab/{actor.test.d.ts → actor.test-ClC-iVWk.d.ts} +0 -0
- /package/dist/vocab/{lookup.test.d.ts → lookup.test-Cq1I-27w.d.ts} +0 -0
- /package/dist/vocab/{type.test.d.ts → type.test-bfFiYGcs.d.ts} +0 -0
- /package/dist/vocab/{vocab.test.d.ts → vocab.test-h-ZTisfu.d.ts} +0 -0
- /package/dist/webfinger/{handler.test.d.ts → handler.test-DiUeEDDD.d.ts} +0 -0
- /package/dist/webfinger/{lookup.test.d.ts → lookup.test-D9onm3U3.d.ts} +0 -0
- /package/dist/x/{cfworkers.test.d.ts → cfworkers.test-KXHlJ29z.d.ts} +0 -0
package/dist/middleware.js
CHANGED
|
@@ -1,301 +1,845 @@
|
|
|
1
1
|
|
|
2
2
|
import { Temporal } from "@js-temporal/polyfill";
|
|
3
3
|
import { URLPattern } from "urlpattern-polyfill";
|
|
4
|
-
globalThis.addEventListener = () => {};
|
|
5
4
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { t as nodeInfoToJson } from "./types.js";
|
|
10
|
-
import { _ as Object$1, b as OrderedCollectionPage, h as Multikey, m as Link, o as CryptographicKey, t as Activity, y as OrderedCollection } from "./vocab.js";
|
|
5
|
+
import { r as getDefaultActivityTransformers } from "./transformers.js";
|
|
6
|
+
import { d as name, f as version, i as getDocumentLoader, s as kvCache } from "./docloader.js";
|
|
7
|
+
import { G as Multikey, Tt as getTypeId, V as Link, X as OrderedCollectionPage, Y as OrderedCollection, q as Object$1, s as Activity, y as CryptographicKey } from "./actor.js";
|
|
11
8
|
import { t as lookupWebFinger } from "./lookup.js";
|
|
12
|
-
import { t as getTypeId } from "./type.js";
|
|
13
9
|
import { a as validateCryptoKey, i as importJwk, t as exportJwk } from "./key2.js";
|
|
14
|
-
import {
|
|
10
|
+
import { r as verifyRequest, t as doubleKnock } from "./http.js";
|
|
11
|
+
import { a as doesActorOwnKey, d as signJsonLd, f as verifyJsonLd, l as detachSignature, n as signObject, o as getKeyOwner, r as verifyObject, u as hasSignature } from "./proof.js";
|
|
12
|
+
import { n as getNodeInfo, t as nodeInfoToJson } from "./types.js";
|
|
15
13
|
import { t as getAuthenticatedDocumentLoader } from "./authdocloader.js";
|
|
16
|
-
import {
|
|
17
|
-
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner.js";
|
|
18
|
-
import { n as signObject, r as verifyObject } from "./proof.js";
|
|
19
|
-
import { n as traverseCollection, t as lookupObject } from "./lookup2.js";
|
|
20
|
-
import { n as routeActivity } from "./inbox.js";
|
|
21
|
-
import { t as FederationBuilderImpl } from "./builder.js";
|
|
22
|
-
import { t as buildCollectionSynchronizationHeader } from "./collection.js";
|
|
23
|
-
import { t as KvKeyCache } from "./keycache.js";
|
|
24
|
-
import { t as createExponentialBackoffPolicy } from "./retry.js";
|
|
25
|
-
import { n as sendActivity, t as extractInboxes } from "./send.js";
|
|
14
|
+
import { n as lookupObject, r as traverseCollection } from "./vocab.js";
|
|
26
15
|
import { getLogger, withContext } from "@logtape/logtape";
|
|
27
16
|
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
|
|
17
|
+
import { encodeHex } from "byte-encodings/hex";
|
|
18
|
+
import { cloneDeep } from "@es-toolkit/es-toolkit";
|
|
19
|
+
import { Router } from "uri-template-router";
|
|
20
|
+
import { parseTemplate } from "url-template";
|
|
28
21
|
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";
|
|
29
22
|
import { domainToASCII } from "node:url";
|
|
30
23
|
|
|
31
|
-
//#region
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
24
|
+
//#region federation/inbox.ts
|
|
25
|
+
var InboxListenerSet = class InboxListenerSet {
|
|
26
|
+
#listeners;
|
|
27
|
+
constructor() {
|
|
28
|
+
this.#listeners = /* @__PURE__ */ new Map();
|
|
29
|
+
}
|
|
30
|
+
clone() {
|
|
31
|
+
const clone = new InboxListenerSet();
|
|
32
|
+
clone.#listeners = new Map(this.#listeners);
|
|
33
|
+
return clone;
|
|
34
|
+
}
|
|
35
|
+
add(type, listener) {
|
|
36
|
+
if (this.#listeners.has(type)) throw new TypeError("Listener already set for this type.");
|
|
37
|
+
this.#listeners.set(type, listener);
|
|
38
|
+
}
|
|
39
|
+
dispatchWithClass(activity) {
|
|
40
|
+
let cls = activity.constructor;
|
|
41
|
+
const inboxListeners = this.#listeners;
|
|
42
|
+
if (inboxListeners == null) return null;
|
|
43
|
+
while (true) {
|
|
44
|
+
if (inboxListeners.has(cls)) break;
|
|
45
|
+
if (cls === Activity) return null;
|
|
46
|
+
cls = globalThis.Object.getPrototypeOf(cls);
|
|
47
|
+
}
|
|
48
|
+
const listener = inboxListeners.get(cls);
|
|
49
|
+
return {
|
|
50
|
+
class: cls,
|
|
51
|
+
listener
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
dispatch(activity) {
|
|
55
|
+
return this.dispatchWithClass(activity)?.listener ?? null;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, tracerProvider }) {
|
|
59
|
+
const logger$1 = getLogger([
|
|
60
|
+
"fedify",
|
|
61
|
+
"federation",
|
|
62
|
+
"inbox"
|
|
63
|
+
]);
|
|
64
|
+
const cacheKey = activity.id == null ? null : [
|
|
65
|
+
...kvPrefixes.activityIdempotence,
|
|
66
|
+
ctx.origin,
|
|
67
|
+
activity.id.href
|
|
68
|
+
];
|
|
69
|
+
if (cacheKey != null) {
|
|
70
|
+
if (await kv.get(cacheKey) === true) {
|
|
71
|
+
logger$1.debug("Activity {activityId} has already been processed.", {
|
|
72
|
+
activityId: activity.id?.href,
|
|
73
|
+
activity: json,
|
|
74
|
+
recipient
|
|
75
|
+
});
|
|
76
|
+
span.setStatus({
|
|
77
|
+
code: SpanStatusCode.UNSET,
|
|
78
|
+
message: `Activity ${activity.id?.href} has already been processed.`
|
|
79
|
+
});
|
|
80
|
+
return "alreadyProcessed";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (activity.actorId == null) {
|
|
84
|
+
logger$1.error("Missing actor.", { activity: json });
|
|
85
|
+
span.setStatus({
|
|
86
|
+
code: SpanStatusCode.ERROR,
|
|
87
|
+
message: "Missing actor."
|
|
88
|
+
});
|
|
89
|
+
return "missingActor";
|
|
90
|
+
}
|
|
91
|
+
span.setAttribute("activitypub.actor.id", activity.actorId.href);
|
|
92
|
+
if (queue != null) {
|
|
93
|
+
const carrier = {};
|
|
94
|
+
propagation.inject(context.active(), carrier);
|
|
95
|
+
try {
|
|
96
|
+
await queue.enqueue({
|
|
97
|
+
type: "inbox",
|
|
98
|
+
id: crypto.randomUUID(),
|
|
99
|
+
baseUrl: ctx.origin,
|
|
100
|
+
activity: json,
|
|
101
|
+
identifier: recipient,
|
|
102
|
+
attempt: 0,
|
|
103
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
104
|
+
traceContext: carrier
|
|
105
|
+
});
|
|
106
|
+
} catch (error) {
|
|
107
|
+
logger$1.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
|
|
108
|
+
error,
|
|
109
|
+
activityId: activity.id?.href,
|
|
110
|
+
activity: json,
|
|
111
|
+
recipient
|
|
112
|
+
});
|
|
113
|
+
span.setStatus({
|
|
114
|
+
code: SpanStatusCode.ERROR,
|
|
115
|
+
message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
|
|
116
|
+
});
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
logger$1.info("Activity {activityId} is enqueued.", {
|
|
120
|
+
activityId: activity.id?.href,
|
|
121
|
+
activity: json,
|
|
122
|
+
recipient
|
|
123
|
+
});
|
|
124
|
+
return "enqueued";
|
|
125
|
+
}
|
|
126
|
+
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
127
|
+
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span$1) => {
|
|
128
|
+
const dispatched = inboxListeners?.dispatchWithClass(activity);
|
|
129
|
+
if (dispatched == null) {
|
|
130
|
+
logger$1.error("Unsupported activity type:\n{activity}", {
|
|
131
|
+
activity: json,
|
|
132
|
+
recipient
|
|
133
|
+
});
|
|
134
|
+
span$1.setStatus({
|
|
135
|
+
code: SpanStatusCode.UNSET,
|
|
136
|
+
message: `Unsupported activity type: ${getTypeId(activity).href}`
|
|
137
|
+
});
|
|
138
|
+
span$1.end();
|
|
139
|
+
return "unsupportedActivity";
|
|
140
|
+
}
|
|
141
|
+
const { class: cls, listener } = dispatched;
|
|
142
|
+
span$1.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
|
143
|
+
try {
|
|
144
|
+
await listener(inboxContextFactory(recipient, json, activity?.id?.href, getTypeId(activity).href), activity);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
try {
|
|
147
|
+
await inboxErrorHandler?.(ctx, error);
|
|
148
|
+
} catch (error$1) {
|
|
149
|
+
logger$1.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
150
|
+
error: error$1,
|
|
151
|
+
activityId: activity.id?.href,
|
|
152
|
+
activity: json,
|
|
153
|
+
recipient
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
logger$1.error("Failed to process the incoming activity {activityId}:\n{error}", {
|
|
157
|
+
error,
|
|
158
|
+
activityId: activity.id?.href,
|
|
159
|
+
activity: json,
|
|
160
|
+
recipient
|
|
161
|
+
});
|
|
162
|
+
span$1.setStatus({
|
|
163
|
+
code: SpanStatusCode.ERROR,
|
|
164
|
+
message: String(error)
|
|
165
|
+
});
|
|
166
|
+
span$1.end();
|
|
167
|
+
return "error";
|
|
168
|
+
}
|
|
169
|
+
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
|
|
170
|
+
logger$1.info("Activity {activityId} has been processed.", {
|
|
171
|
+
activityId: activity.id?.href,
|
|
172
|
+
activity: json,
|
|
173
|
+
recipient
|
|
174
|
+
});
|
|
175
|
+
span$1.end();
|
|
176
|
+
return "success";
|
|
177
|
+
});
|
|
119
178
|
}
|
|
120
179
|
|
|
121
180
|
//#endregion
|
|
122
|
-
//#region
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const json = nodeInfoToJson(promise instanceof Promise ? await promise : promise);
|
|
133
|
-
return new Response(JSON.stringify(json), { headers: { "Content-Type": "application/json; profile=\"http://nodeinfo.diaspora.software/ns/schema/2.1#\"" } });
|
|
181
|
+
//#region federation/router.ts
|
|
182
|
+
function cloneInnerRouter(router) {
|
|
183
|
+
const clone = new Router();
|
|
184
|
+
clone.nid = router.nid;
|
|
185
|
+
clone.fsm = cloneDeep(router.fsm);
|
|
186
|
+
clone.routeSet = new Set(router.routeSet);
|
|
187
|
+
clone.templateRouteMap = new Map(router.templateRouteMap);
|
|
188
|
+
clone.valueRouteMap = new Map(router.valueRouteMap);
|
|
189
|
+
clone.hierarchy = cloneDeep(router.hierarchy);
|
|
190
|
+
return clone;
|
|
134
191
|
}
|
|
135
192
|
/**
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* @param request The request to handle.
|
|
139
|
-
* @param context The request context.
|
|
140
|
-
* @returns The response to the request.
|
|
193
|
+
* URL router and constructor based on URI Template
|
|
194
|
+
* ([RFC 6570](https://tools.ietf.org/html/rfc6570)).
|
|
141
195
|
*/
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
196
|
+
var Router$1 = class Router$1 {
|
|
197
|
+
#router;
|
|
198
|
+
#templates;
|
|
199
|
+
#templateStrings;
|
|
200
|
+
/**
|
|
201
|
+
* Whether to ignore trailing slashes when matching paths.
|
|
202
|
+
* @since 1.6.0
|
|
203
|
+
*/
|
|
204
|
+
trailingSlashInsensitive;
|
|
205
|
+
/**
|
|
206
|
+
* Create a new {@link Router}.
|
|
207
|
+
* @param options Options for the router.
|
|
208
|
+
*/
|
|
209
|
+
constructor(options = {}) {
|
|
210
|
+
this.#router = new Router();
|
|
211
|
+
this.#templates = {};
|
|
212
|
+
this.#templateStrings = {};
|
|
213
|
+
this.trailingSlashInsensitive = options.trailingSlashInsensitive ?? false;
|
|
214
|
+
}
|
|
215
|
+
clone() {
|
|
216
|
+
const clone = new Router$1({ trailingSlashInsensitive: this.trailingSlashInsensitive });
|
|
217
|
+
clone.#router = cloneInnerRouter(this.#router);
|
|
218
|
+
clone.#templates = { ...this.#templates };
|
|
219
|
+
clone.#templateStrings = { ...this.#templateStrings };
|
|
220
|
+
return clone;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Checks if a path name exists in the router.
|
|
224
|
+
* @param name The name of the path.
|
|
225
|
+
* @returns `true` if the path name exists, otherwise `false`.
|
|
226
|
+
*/
|
|
227
|
+
has(name$1) {
|
|
228
|
+
return name$1 in this.#templates;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Adds a new path rule to the router.
|
|
232
|
+
* @param template The path pattern.
|
|
233
|
+
* @param name The name of the path.
|
|
234
|
+
* @returns The names of the variables in the path pattern.
|
|
235
|
+
*/
|
|
236
|
+
add(template, name$1) {
|
|
237
|
+
if (!template.startsWith("/")) throw new RouterError("Path must start with a slash.");
|
|
238
|
+
const rule = this.#router.addTemplate(template, {}, name$1);
|
|
239
|
+
this.#templates[name$1] = parseTemplate(template);
|
|
240
|
+
this.#templateStrings[name$1] = template;
|
|
241
|
+
return new Set(rule.variables.map((v) => v.varname));
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Resolves a path name and values from a URL, if any match.
|
|
245
|
+
* @param url The URL to resolve.
|
|
246
|
+
* @returns The name of the path and its values, if any match. Otherwise,
|
|
247
|
+
* `null`.
|
|
248
|
+
*/
|
|
249
|
+
route(url) {
|
|
250
|
+
let match = this.#router.resolveURI(url);
|
|
251
|
+
if (match == null) {
|
|
252
|
+
if (!this.trailingSlashInsensitive) return null;
|
|
253
|
+
url = url.endsWith("/") ? url.replace(/\/+$/, "") : `${url}/`;
|
|
254
|
+
match = this.#router.resolveURI(url);
|
|
255
|
+
if (match == null) return null;
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
name: match.matchValue,
|
|
259
|
+
template: this.#templateStrings[match.matchValue],
|
|
260
|
+
values: match.params
|
|
261
|
+
};
|
|
152
262
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
263
|
+
/**
|
|
264
|
+
* Constructs a URL/path from a path name and values.
|
|
265
|
+
* @param name The name of the path.
|
|
266
|
+
* @param values The values to expand the path with.
|
|
267
|
+
* @returns The URL/path, if the name exists. Otherwise, `null`.
|
|
268
|
+
*/
|
|
269
|
+
build(name$1, values) {
|
|
270
|
+
if (name$1 in this.#templates) return this.#templates[name$1].expand(values);
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
};
|
|
160
274
|
/**
|
|
161
|
-
*
|
|
162
|
-
* object.*
|
|
163
|
-
*
|
|
164
|
-
* [public addressing]: https://www.w3.org/TR/activitypub/#public-addressing
|
|
165
|
-
*
|
|
166
|
-
* @since 0.7.0
|
|
275
|
+
* An error thrown by the {@link Router}.
|
|
167
276
|
*/
|
|
168
|
-
|
|
277
|
+
var RouterError = class extends Error {
|
|
278
|
+
/**
|
|
279
|
+
* Create a new {@link RouterError}.
|
|
280
|
+
* @param message The error message.
|
|
281
|
+
*/
|
|
282
|
+
constructor(message) {
|
|
283
|
+
super(message);
|
|
284
|
+
this.name = "RouterError";
|
|
285
|
+
}
|
|
286
|
+
};
|
|
169
287
|
|
|
170
288
|
//#endregion
|
|
171
|
-
//#region
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
289
|
+
//#region federation/builder.ts
|
|
290
|
+
var FederationBuilderImpl = class {
|
|
291
|
+
router;
|
|
292
|
+
actorCallbacks;
|
|
293
|
+
nodeInfoDispatcher;
|
|
294
|
+
objectCallbacks;
|
|
295
|
+
objectTypeIds;
|
|
296
|
+
inboxPath;
|
|
297
|
+
inboxCallbacks;
|
|
298
|
+
outboxCallbacks;
|
|
299
|
+
followingCallbacks;
|
|
300
|
+
followersCallbacks;
|
|
301
|
+
likedCallbacks;
|
|
302
|
+
featuredCallbacks;
|
|
303
|
+
featuredTagsCallbacks;
|
|
304
|
+
inboxListeners;
|
|
305
|
+
inboxErrorHandler;
|
|
306
|
+
sharedInboxKeyDispatcher;
|
|
307
|
+
constructor() {
|
|
308
|
+
this.router = new Router$1();
|
|
309
|
+
this.objectCallbacks = {};
|
|
310
|
+
this.objectTypeIds = {};
|
|
311
|
+
}
|
|
312
|
+
async build(options) {
|
|
313
|
+
const { FederationImpl: FederationImpl$1 } = await import("./middleware2.js");
|
|
314
|
+
const f = new FederationImpl$1(options);
|
|
315
|
+
const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
|
|
316
|
+
f.router = this.router.clone();
|
|
317
|
+
f.router.trailingSlashInsensitive = trailingSlashInsensitiveValue;
|
|
318
|
+
f._initializeRouter();
|
|
319
|
+
f.actorCallbacks = this.actorCallbacks == null ? void 0 : { ...this.actorCallbacks };
|
|
320
|
+
f.nodeInfoDispatcher = this.nodeInfoDispatcher;
|
|
321
|
+
f.objectCallbacks = { ...this.objectCallbacks };
|
|
322
|
+
f.objectTypeIds = { ...this.objectTypeIds };
|
|
323
|
+
f.inboxPath = this.inboxPath;
|
|
324
|
+
f.inboxCallbacks = this.inboxCallbacks == null ? void 0 : { ...this.inboxCallbacks };
|
|
325
|
+
f.outboxCallbacks = this.outboxCallbacks == null ? void 0 : { ...this.outboxCallbacks };
|
|
326
|
+
f.followingCallbacks = this.followingCallbacks == null ? void 0 : { ...this.followingCallbacks };
|
|
327
|
+
f.followersCallbacks = this.followersCallbacks == null ? void 0 : { ...this.followersCallbacks };
|
|
328
|
+
f.likedCallbacks = this.likedCallbacks == null ? void 0 : { ...this.likedCallbacks };
|
|
329
|
+
f.featuredCallbacks = this.featuredCallbacks == null ? void 0 : { ...this.featuredCallbacks };
|
|
330
|
+
f.featuredTagsCallbacks = this.featuredTagsCallbacks == null ? void 0 : { ...this.featuredTagsCallbacks };
|
|
331
|
+
f.inboxListeners = this.inboxListeners?.clone();
|
|
332
|
+
f.inboxErrorHandler = this.inboxErrorHandler;
|
|
333
|
+
f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
|
|
334
|
+
return f;
|
|
335
|
+
}
|
|
336
|
+
_getTracer() {
|
|
337
|
+
return trace.getTracer(name, version);
|
|
338
|
+
}
|
|
339
|
+
setActorDispatcher(path, dispatcher) {
|
|
340
|
+
if (this.router.has("actor")) throw new RouterError("Actor dispatcher already set.");
|
|
341
|
+
const variables = this.router.add(path, "actor");
|
|
342
|
+
if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for actor dispatcher must have one variable: {identifier}");
|
|
343
|
+
if (variables.has("handle")) getLogger([
|
|
344
|
+
"fedify",
|
|
345
|
+
"federation",
|
|
346
|
+
"actor"
|
|
347
|
+
]).warn("The {{handle}} variable in the actor dispatcher path is deprecated. Use {{identifier}} instead.");
|
|
348
|
+
const callbacks = { dispatcher: async (context$1, identifier) => {
|
|
349
|
+
const actor = await this._getTracer().startActiveSpan("activitypub.dispatch_actor", {
|
|
350
|
+
kind: SpanKind.SERVER,
|
|
351
|
+
attributes: { "fedify.actor.identifier": identifier }
|
|
352
|
+
}, async (span) => {
|
|
353
|
+
try {
|
|
354
|
+
const actor$1 = await dispatcher(context$1, identifier);
|
|
355
|
+
span.setAttribute("activitypub.actor.id", (actor$1?.id ?? context$1.getActorUri(identifier)).href);
|
|
356
|
+
if (actor$1 == null) span.setStatus({ code: SpanStatusCode.ERROR });
|
|
357
|
+
else span.setAttribute("activitypub.actor.type", getTypeId(actor$1).href);
|
|
358
|
+
return actor$1;
|
|
359
|
+
} catch (error) {
|
|
360
|
+
span.setStatus({
|
|
361
|
+
code: SpanStatusCode.ERROR,
|
|
362
|
+
message: String(error)
|
|
363
|
+
});
|
|
364
|
+
throw error;
|
|
365
|
+
} finally {
|
|
366
|
+
span.end();
|
|
367
|
+
}
|
|
195
368
|
});
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
369
|
+
if (actor == null) return null;
|
|
370
|
+
const logger$1 = getLogger([
|
|
371
|
+
"fedify",
|
|
372
|
+
"federation",
|
|
373
|
+
"actor"
|
|
374
|
+
]);
|
|
375
|
+
if (actor.id == null) logger$1.warn("Actor dispatcher returned an actor without an id property. Set the property with Context.getActorUri(identifier).");
|
|
376
|
+
else if (actor.id.href != context$1.getActorUri(identifier).href) logger$1.warn("Actor dispatcher returned an actor with an id property that does not match the actor URI. Set the property with Context.getActorUri(identifier).");
|
|
377
|
+
if (this.followingCallbacks != null && this.followingCallbacks.dispatcher != null) {
|
|
378
|
+
if (actor.followingId == null) logger$1.warn("You configured a following collection dispatcher, but the actor does not have a following property. Set the property with Context.getFollowingUri(identifier).");
|
|
379
|
+
else if (actor.followingId.href != context$1.getFollowingUri(identifier).href) logger$1.warn("You configured a following collection dispatcher, but the actor's following property does not match the following collection URI. Set the property with Context.getFollowingUri(identifier).");
|
|
380
|
+
}
|
|
381
|
+
if (this.followersCallbacks != null && this.followersCallbacks.dispatcher != null) {
|
|
382
|
+
if (actor.followersId == null) logger$1.warn("You configured a followers collection dispatcher, but the actor does not have a followers property. Set the property with Context.getFollowersUri(identifier).");
|
|
383
|
+
else if (actor.followersId.href != context$1.getFollowersUri(identifier).href) logger$1.warn("You configured a followers collection dispatcher, but the actor's followers property does not match the followers collection URI. Set the property with Context.getFollowersUri(identifier).");
|
|
384
|
+
}
|
|
385
|
+
if (this.outboxCallbacks != null && this.outboxCallbacks.dispatcher != null) {
|
|
386
|
+
if (actor?.outboxId == null) logger$1.warn("You configured an outbox collection dispatcher, but the actor does not have an outbox property. Set the property with Context.getOutboxUri(identifier).");
|
|
387
|
+
else if (actor.outboxId.href != context$1.getOutboxUri(identifier).href) logger$1.warn("You configured an outbox collection dispatcher, but the actor's outbox property does not match the outbox collection URI. Set the property with Context.getOutboxUri(identifier).");
|
|
388
|
+
}
|
|
389
|
+
if (this.likedCallbacks != null && this.likedCallbacks.dispatcher != null) {
|
|
390
|
+
if (actor?.likedId == null) logger$1.warn("You configured a liked collection dispatcher, but the actor does not have a liked property. Set the property with Context.getLikedUri(identifier).");
|
|
391
|
+
else if (actor.likedId.href != context$1.getLikedUri(identifier).href) logger$1.warn("You configured a liked collection dispatcher, but the actor's liked property does not match the liked collection URI. Set the property with Context.getLikedUri(identifier).");
|
|
392
|
+
}
|
|
393
|
+
if (this.featuredCallbacks != null && this.featuredCallbacks.dispatcher != null) {
|
|
394
|
+
if (actor?.featuredId == null) logger$1.warn("You configured a featured collection dispatcher, but the actor does not have a featured property. Set the property with Context.getFeaturedUri(identifier).");
|
|
395
|
+
else if (actor.featuredId.href != context$1.getFeaturedUri(identifier).href) logger$1.warn("You configured a featured collection dispatcher, but the actor's featured property does not match the featured collection URI. Set the property with Context.getFeaturedUri(identifier).");
|
|
396
|
+
}
|
|
397
|
+
if (this.featuredTagsCallbacks != null && this.featuredTagsCallbacks.dispatcher != null) {
|
|
398
|
+
if (actor?.featuredTagsId == null) logger$1.warn("You configured a featured tags collection dispatcher, but the actor does not have a featuredTags property. Set the property with Context.getFeaturedTagsUri(identifier).");
|
|
399
|
+
else if (actor.featuredTagsId.href != context$1.getFeaturedTagsUri(identifier).href) logger$1.warn("You configured a featured tags collection dispatcher, but the actor's featuredTags property does not match the featured tags collection URI. Set the property with Context.getFeaturedTagsUri(identifier).");
|
|
400
|
+
}
|
|
401
|
+
if (this.router.has("inbox")) {
|
|
402
|
+
if (actor.inboxId == null) logger$1.warn("You configured inbox listeners, but the actor does not have an inbox property. Set the property with Context.getInboxUri(identifier).");
|
|
403
|
+
else if (actor.inboxId.href != context$1.getInboxUri(identifier).href) logger$1.warn("You configured inbox listeners, but the actor's inbox property does not match the inbox URI. Set the property with Context.getInboxUri(identifier).");
|
|
404
|
+
if (actor.endpoints == null || actor.endpoints.sharedInbox == null) logger$1.warn("You configured inbox listeners, but the actor does not have a endpoints.sharedInbox property. Set the property with Context.getInboxUri().");
|
|
405
|
+
else if (actor.endpoints.sharedInbox.href != context$1.getInboxUri().href) logger$1.warn("You configured inbox listeners, but the actor's endpoints.sharedInbox property does not match the shared inbox URI. Set the property with Context.getInboxUri().");
|
|
406
|
+
}
|
|
407
|
+
if (callbacks.keyPairsDispatcher != null) {
|
|
408
|
+
if (actor.publicKeyId == null) logger$1.warn("You configured a key pairs dispatcher, but the actor does not have a publicKey property. Set the property with Context.getActorKeyPairs(identifier).");
|
|
409
|
+
if (actor.assertionMethodId == null) logger$1.warn("You configured a key pairs dispatcher, but the actor does not have an assertionMethod property. Set the property with Context.getActorKeyPairs(identifier).");
|
|
410
|
+
}
|
|
411
|
+
return actor;
|
|
412
|
+
} };
|
|
413
|
+
this.actorCallbacks = callbacks;
|
|
414
|
+
const setters = {
|
|
415
|
+
setKeyPairsDispatcher: (dispatcher$1) => {
|
|
416
|
+
callbacks.keyPairsDispatcher = (ctx, identifier) => this._getTracer().startActiveSpan("activitypub.dispatch_actor_key_pairs", {
|
|
417
|
+
kind: SpanKind.SERVER,
|
|
418
|
+
attributes: {
|
|
419
|
+
"activitypub.actor.id": ctx.getActorUri(identifier).href,
|
|
420
|
+
"fedify.actor.identifier": identifier
|
|
421
|
+
}
|
|
422
|
+
}, async (span) => {
|
|
423
|
+
try {
|
|
424
|
+
return await dispatcher$1(ctx, identifier);
|
|
425
|
+
} catch (e) {
|
|
426
|
+
span.setStatus({
|
|
427
|
+
code: SpanStatusCode.ERROR,
|
|
428
|
+
message: String(e)
|
|
429
|
+
});
|
|
430
|
+
throw e;
|
|
431
|
+
} finally {
|
|
432
|
+
span.end();
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
return setters;
|
|
436
|
+
},
|
|
437
|
+
mapHandle(mapper) {
|
|
438
|
+
callbacks.handleMapper = mapper;
|
|
439
|
+
return setters;
|
|
440
|
+
},
|
|
441
|
+
mapAlias(mapper) {
|
|
442
|
+
callbacks.aliasMapper = mapper;
|
|
443
|
+
return setters;
|
|
444
|
+
},
|
|
445
|
+
authorize(predicate) {
|
|
446
|
+
callbacks.authorizePredicate = predicate;
|
|
447
|
+
return setters;
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
return setters;
|
|
451
|
+
}
|
|
452
|
+
setNodeInfoDispatcher(path, dispatcher) {
|
|
453
|
+
if (this.router.has("nodeInfo")) throw new RouterError("NodeInfo dispatcher already set.");
|
|
454
|
+
if (this.router.add(path, "nodeInfo").size !== 0) throw new RouterError("Path for NodeInfo dispatcher must have no variables.");
|
|
455
|
+
this.nodeInfoDispatcher = dispatcher;
|
|
456
|
+
}
|
|
457
|
+
setObjectDispatcher(cls, path, dispatcher) {
|
|
458
|
+
const routeName = `object:${cls.typeId.href}`;
|
|
459
|
+
if (this.router.has(routeName)) throw new RouterError(`Object dispatcher for ${cls.name} already set.`);
|
|
460
|
+
const variables = this.router.add(path, routeName);
|
|
461
|
+
if (variables.size < 1) throw new RouterError("Path for object dispatcher must have at least one variable.");
|
|
462
|
+
const callbacks = {
|
|
463
|
+
dispatcher: (ctx, values) => {
|
|
464
|
+
return this._getTracer().startActiveSpan("activitypub.dispatch_object", {
|
|
465
|
+
kind: SpanKind.SERVER,
|
|
466
|
+
attributes: {
|
|
467
|
+
"fedify.object.type": cls.typeId.href,
|
|
468
|
+
...globalThis.Object.fromEntries(globalThis.Object.entries(values).map(([k, v]) => [`fedify.object.values.${k}`, v]))
|
|
469
|
+
}
|
|
470
|
+
}, async (span) => {
|
|
471
|
+
try {
|
|
472
|
+
const object = await dispatcher(ctx, values);
|
|
473
|
+
span.setAttribute("activitypub.object.id", (object?.id ?? ctx.getObjectUri(cls, values)).href);
|
|
474
|
+
if (object == null) span.setStatus({ code: SpanStatusCode.ERROR });
|
|
475
|
+
else span.setAttribute("activitypub.object.type", getTypeId(object).href);
|
|
476
|
+
return object;
|
|
477
|
+
} catch (e) {
|
|
478
|
+
span.setStatus({
|
|
479
|
+
code: SpanStatusCode.ERROR,
|
|
480
|
+
message: String(e)
|
|
481
|
+
});
|
|
482
|
+
throw e;
|
|
483
|
+
} finally {
|
|
484
|
+
span.end();
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
},
|
|
488
|
+
parameters: variables
|
|
489
|
+
};
|
|
490
|
+
this.objectCallbacks[cls.typeId.href] = callbacks;
|
|
491
|
+
this.objectTypeIds[cls.typeId.href] = cls;
|
|
492
|
+
const setters = { authorize(predicate) {
|
|
493
|
+
callbacks.authorizePredicate = predicate;
|
|
494
|
+
return setters;
|
|
495
|
+
} };
|
|
496
|
+
return setters;
|
|
497
|
+
}
|
|
498
|
+
setInboxDispatcher(path, dispatcher) {
|
|
499
|
+
if (this.inboxCallbacks != null) throw new RouterError("Inbox dispatcher already set.");
|
|
500
|
+
if (this.router.has("inbox")) {
|
|
501
|
+
if (this.inboxPath !== path) throw new RouterError("Inbox dispatcher path must match inbox listener path.");
|
|
502
|
+
} else {
|
|
503
|
+
const variables = this.router.add(path, "inbox");
|
|
504
|
+
if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for inbox dispatcher must have one variable: {identifier}");
|
|
505
|
+
if (variables.has("handle")) getLogger([
|
|
506
|
+
"fedify",
|
|
507
|
+
"federation",
|
|
508
|
+
"inbox"
|
|
509
|
+
]).warn("The {{handle}} variable in the inbox dispatcher path is deprecated. Use {{identifier}} instead.");
|
|
510
|
+
this.inboxPath = path;
|
|
199
511
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
512
|
+
const callbacks = { dispatcher };
|
|
513
|
+
this.inboxCallbacks = callbacks;
|
|
514
|
+
const setters = {
|
|
515
|
+
setCounter(counter) {
|
|
516
|
+
callbacks.counter = counter;
|
|
517
|
+
return setters;
|
|
518
|
+
},
|
|
519
|
+
setFirstCursor(cursor) {
|
|
520
|
+
callbacks.firstCursor = cursor;
|
|
521
|
+
return setters;
|
|
522
|
+
},
|
|
523
|
+
setLastCursor(cursor) {
|
|
524
|
+
callbacks.lastCursor = cursor;
|
|
525
|
+
return setters;
|
|
526
|
+
},
|
|
527
|
+
authorize(predicate) {
|
|
528
|
+
callbacks.authorizePredicate = predicate;
|
|
529
|
+
return setters;
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
return setters;
|
|
213
533
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
534
|
+
setOutboxDispatcher(path, dispatcher) {
|
|
535
|
+
if (this.router.has("outbox")) throw new RouterError("Outbox dispatcher already set.");
|
|
536
|
+
const variables = this.router.add(path, "outbox");
|
|
537
|
+
if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for outbox dispatcher must have one variable: {identifier}");
|
|
538
|
+
if (variables.has("handle")) getLogger([
|
|
539
|
+
"fedify",
|
|
540
|
+
"federation",
|
|
541
|
+
"outbox"
|
|
542
|
+
]).warn("The {{handle}} variable in the outbox dispatcher path is deprecated. Use {{identifier}} instead.");
|
|
543
|
+
const callbacks = { dispatcher };
|
|
544
|
+
this.outboxCallbacks = callbacks;
|
|
545
|
+
const setters = {
|
|
546
|
+
setCounter(counter) {
|
|
547
|
+
callbacks.counter = counter;
|
|
548
|
+
return setters;
|
|
549
|
+
},
|
|
550
|
+
setFirstCursor(cursor) {
|
|
551
|
+
callbacks.firstCursor = cursor;
|
|
552
|
+
return setters;
|
|
553
|
+
},
|
|
554
|
+
setLastCursor(cursor) {
|
|
555
|
+
callbacks.lastCursor = cursor;
|
|
556
|
+
return setters;
|
|
557
|
+
},
|
|
558
|
+
authorize(predicate) {
|
|
559
|
+
callbacks.authorizePredicate = predicate;
|
|
560
|
+
return setters;
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
return setters;
|
|
218
564
|
}
|
|
219
|
-
|
|
220
|
-
if (
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
|
|
565
|
+
setFollowingDispatcher(path, dispatcher) {
|
|
566
|
+
if (this.router.has("following")) throw new RouterError("Following collection dispatcher already set.");
|
|
567
|
+
const variables = this.router.add(path, "following");
|
|
568
|
+
if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for following collection dispatcher must have one variable: {identifier}");
|
|
569
|
+
if (variables.has("handle")) getLogger([
|
|
570
|
+
"fedify",
|
|
571
|
+
"federation",
|
|
572
|
+
"collection"
|
|
573
|
+
]).warn("The {{handle}} variable in the following collection dispatcher path is deprecated. Use {{identifier}} instead.");
|
|
574
|
+
const callbacks = { dispatcher };
|
|
575
|
+
this.followingCallbacks = callbacks;
|
|
576
|
+
const setters = {
|
|
577
|
+
setCounter(counter) {
|
|
578
|
+
callbacks.counter = counter;
|
|
579
|
+
return setters;
|
|
580
|
+
},
|
|
581
|
+
setFirstCursor(cursor) {
|
|
582
|
+
callbacks.firstCursor = cursor;
|
|
583
|
+
return setters;
|
|
584
|
+
},
|
|
585
|
+
setLastCursor(cursor) {
|
|
586
|
+
callbacks.lastCursor = cursor;
|
|
587
|
+
return setters;
|
|
588
|
+
},
|
|
589
|
+
authorize(predicate) {
|
|
590
|
+
callbacks.authorizePredicate = predicate;
|
|
591
|
+
return setters;
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
return setters;
|
|
230
595
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
596
|
+
setFollowersDispatcher(path, dispatcher) {
|
|
597
|
+
if (this.router.has("followers")) throw new RouterError("Followers collection dispatcher already set.");
|
|
598
|
+
const variables = this.router.add(path, "followers");
|
|
599
|
+
if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for followers collection dispatcher must have one variable: {identifier}");
|
|
600
|
+
if (variables.has("handle")) getLogger([
|
|
601
|
+
"fedify",
|
|
602
|
+
"federation",
|
|
603
|
+
"collection"
|
|
604
|
+
]).warn("The {{handle}} variable in the followers collection dispatcher path is deprecated. Use {{identifier}} instead.");
|
|
605
|
+
const callbacks = { dispatcher };
|
|
606
|
+
this.followersCallbacks = callbacks;
|
|
607
|
+
const setters = {
|
|
608
|
+
setCounter(counter) {
|
|
609
|
+
callbacks.counter = counter;
|
|
610
|
+
return setters;
|
|
611
|
+
},
|
|
612
|
+
setFirstCursor(cursor) {
|
|
613
|
+
callbacks.firstCursor = cursor;
|
|
614
|
+
return setters;
|
|
615
|
+
},
|
|
616
|
+
setLastCursor(cursor) {
|
|
617
|
+
callbacks.lastCursor = cursor;
|
|
618
|
+
return setters;
|
|
619
|
+
},
|
|
620
|
+
authorize(predicate) {
|
|
621
|
+
callbacks.authorizePredicate = predicate;
|
|
622
|
+
return setters;
|
|
247
623
|
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (identifier == null) return await onNotFound(request);
|
|
251
|
-
const actor = await actorDispatcher(context$1, identifier);
|
|
252
|
-
if (actor == null) {
|
|
253
|
-
logger.error("Actor {identifier} not found.", { identifier });
|
|
254
|
-
return await onNotFound(request);
|
|
624
|
+
};
|
|
625
|
+
return setters;
|
|
255
626
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
627
|
+
setLikedDispatcher(path, dispatcher) {
|
|
628
|
+
if (this.router.has("liked")) throw new RouterError("Liked collection dispatcher already set.");
|
|
629
|
+
const variables = this.router.add(path, "liked");
|
|
630
|
+
if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for liked collection dispatcher must have one variable: {identifier}");
|
|
631
|
+
if (variables.has("handle")) getLogger([
|
|
632
|
+
"fedify",
|
|
633
|
+
"federation",
|
|
634
|
+
"collection"
|
|
635
|
+
]).warn("The {{handle}} variable in the liked collection dispatcher path is deprecated. Use {{identifier}} instead.");
|
|
636
|
+
const callbacks = { dispatcher };
|
|
637
|
+
this.likedCallbacks = callbacks;
|
|
638
|
+
const setters = {
|
|
639
|
+
setCounter(counter) {
|
|
640
|
+
callbacks.counter = counter;
|
|
641
|
+
return setters;
|
|
642
|
+
},
|
|
643
|
+
setFirstCursor(cursor) {
|
|
644
|
+
callbacks.firstCursor = cursor;
|
|
645
|
+
return setters;
|
|
646
|
+
},
|
|
647
|
+
setLastCursor(cursor) {
|
|
648
|
+
callbacks.lastCursor = cursor;
|
|
649
|
+
return setters;
|
|
650
|
+
},
|
|
651
|
+
authorize(predicate) {
|
|
652
|
+
callbacks.authorizePredicate = predicate;
|
|
653
|
+
return setters;
|
|
654
|
+
}
|
|
275
655
|
};
|
|
276
|
-
|
|
277
|
-
|
|
656
|
+
return setters;
|
|
657
|
+
}
|
|
658
|
+
setFeaturedDispatcher(path, dispatcher) {
|
|
659
|
+
if (this.router.has("featured")) throw new RouterError("Featured collection dispatcher already set.");
|
|
660
|
+
const variables = this.router.add(path, "featured");
|
|
661
|
+
if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for featured collection dispatcher must have one variable: {identifier}");
|
|
662
|
+
if (variables.has("handle")) getLogger([
|
|
663
|
+
"fedify",
|
|
664
|
+
"federation",
|
|
665
|
+
"collection"
|
|
666
|
+
]).warn("The {{handle}} variable in the featured collection dispatcher path is deprecated. Use {{identifier}} instead.");
|
|
667
|
+
const callbacks = { dispatcher };
|
|
668
|
+
this.featuredCallbacks = callbacks;
|
|
669
|
+
const setters = {
|
|
670
|
+
setCounter(counter) {
|
|
671
|
+
callbacks.counter = counter;
|
|
672
|
+
return setters;
|
|
673
|
+
},
|
|
674
|
+
setFirstCursor(cursor) {
|
|
675
|
+
callbacks.firstCursor = cursor;
|
|
676
|
+
return setters;
|
|
677
|
+
},
|
|
678
|
+
setLastCursor(cursor) {
|
|
679
|
+
callbacks.lastCursor = cursor;
|
|
680
|
+
return setters;
|
|
681
|
+
},
|
|
682
|
+
authorize(predicate) {
|
|
683
|
+
callbacks.authorizePredicate = predicate;
|
|
684
|
+
return setters;
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
return setters;
|
|
688
|
+
}
|
|
689
|
+
setFeaturedTagsDispatcher(path, dispatcher) {
|
|
690
|
+
if (this.router.has("featuredTags")) throw new RouterError("Featured tags collection dispatcher already set.");
|
|
691
|
+
const variables = this.router.add(path, "featuredTags");
|
|
692
|
+
if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for featured tags collection dispatcher must have one variable: {identifier}");
|
|
693
|
+
if (variables.has("handle")) getLogger([
|
|
694
|
+
"fedify",
|
|
695
|
+
"federation",
|
|
696
|
+
"collection"
|
|
697
|
+
]).warn("The {{handle}} variable in the featured tags collection dispatcher path is deprecated. Use {{identifier}} instead.");
|
|
698
|
+
const callbacks = { dispatcher };
|
|
699
|
+
this.featuredTagsCallbacks = callbacks;
|
|
700
|
+
const setters = {
|
|
701
|
+
setCounter(counter) {
|
|
702
|
+
callbacks.counter = counter;
|
|
703
|
+
return setters;
|
|
704
|
+
},
|
|
705
|
+
setFirstCursor(cursor) {
|
|
706
|
+
callbacks.firstCursor = cursor;
|
|
707
|
+
return setters;
|
|
708
|
+
},
|
|
709
|
+
setLastCursor(cursor) {
|
|
710
|
+
callbacks.lastCursor = cursor;
|
|
711
|
+
return setters;
|
|
712
|
+
},
|
|
713
|
+
authorize(predicate) {
|
|
714
|
+
callbacks.authorizePredicate = predicate;
|
|
715
|
+
return setters;
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
return setters;
|
|
719
|
+
}
|
|
720
|
+
setInboxListeners(inboxPath, sharedInboxPath) {
|
|
721
|
+
if (this.inboxListeners != null) throw new RouterError("Inbox listeners already set.");
|
|
722
|
+
if (this.router.has("inbox")) {
|
|
723
|
+
if (this.inboxPath !== inboxPath) throw new RouterError("Inbox listener path must match inbox dispatcher path.");
|
|
724
|
+
} else {
|
|
725
|
+
const variables = this.router.add(inboxPath, "inbox");
|
|
726
|
+
if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for inbox must have one variable: {identifier}");
|
|
727
|
+
this.inboxPath = inboxPath;
|
|
728
|
+
if (variables.has("handle")) getLogger([
|
|
729
|
+
"fedify",
|
|
730
|
+
"federation",
|
|
731
|
+
"inbox"
|
|
732
|
+
]).warn("The {{handle}} variable in the inbox path is deprecated. Use {{identifier}} instead.");
|
|
733
|
+
}
|
|
734
|
+
if (sharedInboxPath != null) {
|
|
735
|
+
if (this.router.add(sharedInboxPath, "sharedInbox").size !== 0) throw new RouterError("Path for shared inbox must have no variables.");
|
|
736
|
+
}
|
|
737
|
+
const listeners = this.inboxListeners = new InboxListenerSet();
|
|
738
|
+
const setters = {
|
|
739
|
+
on(type, listener) {
|
|
740
|
+
listeners.add(type, listener);
|
|
741
|
+
return setters;
|
|
742
|
+
},
|
|
743
|
+
onError: (handler) => {
|
|
744
|
+
this.inboxErrorHandler = handler;
|
|
745
|
+
return setters;
|
|
746
|
+
},
|
|
747
|
+
setSharedKeyDispatcher: (dispatcher) => {
|
|
748
|
+
this.sharedInboxKeyDispatcher = dispatcher;
|
|
749
|
+
return setters;
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
return setters;
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
/**
|
|
756
|
+
* Creates a new {@link FederationBuilder} instance.
|
|
757
|
+
* @returns A new {@link FederationBuilder} instance.
|
|
758
|
+
* @since 1.6.0
|
|
759
|
+
*/
|
|
760
|
+
function createFederationBuilder() {
|
|
761
|
+
return new FederationBuilderImpl();
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
//#endregion
|
|
765
|
+
//#region federation/collection.ts
|
|
766
|
+
/**
|
|
767
|
+
* Calculates the [partial follower collection digest][1].
|
|
768
|
+
*
|
|
769
|
+
* [1]: https://w3id.org/fep/8fcf#partial-follower-collection-digest
|
|
770
|
+
* @param uris The URIs to calculate the digest. Duplicate URIs are ignored.
|
|
771
|
+
* @returns The digest.
|
|
772
|
+
*/
|
|
773
|
+
async function digest(uris) {
|
|
774
|
+
const processed = /* @__PURE__ */ new Set();
|
|
775
|
+
const encoder = new TextEncoder();
|
|
776
|
+
const result = new Uint8Array(32);
|
|
777
|
+
for (const uri of uris) {
|
|
778
|
+
const u = uri instanceof URL ? uri.href : uri;
|
|
779
|
+
if (processed.has(u)) continue;
|
|
780
|
+
processed.add(u);
|
|
781
|
+
const encoded = encoder.encode(u);
|
|
782
|
+
const digest$1 = new Uint8Array(await crypto.subtle.digest("SHA-256", encoded));
|
|
783
|
+
for (let i = 0; i < 32; i++) result[i] ^= digest$1[i];
|
|
784
|
+
}
|
|
785
|
+
return result;
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Builds [`Collection-Synchronization`][1] header content.
|
|
789
|
+
*
|
|
790
|
+
* [1]: https://w3id.org/fep/8fcf#the-collection-synchronization-http-header
|
|
791
|
+
*
|
|
792
|
+
* @param collectionId The sender's followers collection URI.
|
|
793
|
+
* @param actorIds The actor URIs to digest.
|
|
794
|
+
* @returns The header content.
|
|
795
|
+
*/
|
|
796
|
+
async function buildCollectionSynchronizationHeader(collectionId, actorIds) {
|
|
797
|
+
const [anyActorId] = actorIds;
|
|
798
|
+
const baseUrl = new URL(anyActorId);
|
|
799
|
+
const url = new URL(collectionId);
|
|
800
|
+
url.searchParams.set("base-url", `${baseUrl.origin}/`);
|
|
801
|
+
return `collectionId="${collectionId}", url="${url}", digest="${encodeHex(await digest(actorIds))}"`;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
//#endregion
|
|
805
|
+
//#region federation/keycache.ts
|
|
806
|
+
var KvKeyCache = class {
|
|
807
|
+
kv;
|
|
808
|
+
prefix;
|
|
809
|
+
options;
|
|
810
|
+
nullKeys;
|
|
811
|
+
constructor(kv, prefix, options = {}) {
|
|
812
|
+
this.kv = kv;
|
|
813
|
+
this.prefix = prefix;
|
|
814
|
+
this.nullKeys = /* @__PURE__ */ new Set();
|
|
815
|
+
this.options = options;
|
|
278
816
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (
|
|
817
|
+
async get(keyId) {
|
|
818
|
+
if (this.nullKeys.has(keyId.href)) return null;
|
|
819
|
+
const serialized = await this.kv.get([...this.prefix, keyId.href]);
|
|
820
|
+
if (serialized == null) return void 0;
|
|
821
|
+
try {
|
|
822
|
+
return await CryptographicKey.fromJsonLd(serialized, this.options);
|
|
823
|
+
} catch {
|
|
824
|
+
try {
|
|
825
|
+
return await Multikey.fromJsonLd(serialized, this.options);
|
|
826
|
+
} catch {
|
|
827
|
+
await this.kv.delete([...this.prefix, keyId.href]);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
283
831
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
832
|
+
async set(keyId, key) {
|
|
833
|
+
if (key == null) {
|
|
834
|
+
this.nullKeys.add(keyId.href);
|
|
835
|
+
await this.kv.delete([...this.prefix, keyId.href]);
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
this.nullKeys.delete(keyId.href);
|
|
839
|
+
const serialized = await key.toJsonLd(this.options);
|
|
840
|
+
await this.kv.set([...this.prefix, keyId.href], serialized);
|
|
288
841
|
}
|
|
289
|
-
|
|
290
|
-
subject: resourceUrl.href,
|
|
291
|
-
aliases,
|
|
292
|
-
links
|
|
293
|
-
};
|
|
294
|
-
return new Response(JSON.stringify(jrd), { headers: {
|
|
295
|
-
"Content-Type": "application/jrd+json",
|
|
296
|
-
"Access-Control-Allow-Origin": "*"
|
|
297
|
-
} });
|
|
298
|
-
}
|
|
842
|
+
};
|
|
299
843
|
|
|
300
844
|
//#endregion
|
|
301
845
|
//#region federation/negotiation.ts
|
|
@@ -364,18 +908,18 @@ function acceptsJsonLd(request) {
|
|
|
364
908
|
return types.includes("application/activity+json") || types.includes("application/ld+json") || types.includes("application/json");
|
|
365
909
|
}
|
|
366
910
|
async function handleActor(request, { identifier, context: context$1, actorDispatcher, authorizePredicate, onNotFound, onNotAcceptable, onUnauthorized }) {
|
|
367
|
-
const logger$
|
|
911
|
+
const logger$1 = getLogger([
|
|
368
912
|
"fedify",
|
|
369
913
|
"federation",
|
|
370
914
|
"actor"
|
|
371
915
|
]);
|
|
372
916
|
if (actorDispatcher == null) {
|
|
373
|
-
logger$
|
|
917
|
+
logger$1.debug("Actor dispatcher is not set.", { identifier });
|
|
374
918
|
return await onNotFound(request);
|
|
375
919
|
}
|
|
376
920
|
const actor = await actorDispatcher(context$1, identifier);
|
|
377
921
|
if (actor == null) {
|
|
378
|
-
logger$
|
|
922
|
+
logger$1.debug("Actor {identifier} not found.", { identifier });
|
|
379
923
|
return await onNotFound(request);
|
|
380
924
|
}
|
|
381
925
|
if (!acceptsJsonLd(request)) return await onNotAcceptable(request);
|
|
@@ -621,13 +1165,13 @@ async function handleInbox(request, options) {
|
|
|
621
1165
|
});
|
|
622
1166
|
}
|
|
623
1167
|
async function handleInboxInternal(request, { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, tracerProvider }, span) {
|
|
624
|
-
const logger$
|
|
1168
|
+
const logger$1 = getLogger([
|
|
625
1169
|
"fedify",
|
|
626
1170
|
"federation",
|
|
627
1171
|
"inbox"
|
|
628
1172
|
]);
|
|
629
1173
|
if (actorDispatcher == null) {
|
|
630
|
-
logger$
|
|
1174
|
+
logger$1.error("Actor dispatcher is not set.", { recipient });
|
|
631
1175
|
span.setStatus({
|
|
632
1176
|
code: SpanStatusCode.ERROR,
|
|
633
1177
|
message: "Actor dispatcher is not set."
|
|
@@ -635,7 +1179,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
|
|
|
635
1179
|
return await onNotFound(request);
|
|
636
1180
|
} else if (recipient != null) {
|
|
637
1181
|
if (await actorDispatcher(ctx, recipient) == null) {
|
|
638
|
-
logger$
|
|
1182
|
+
logger$1.error("Actor {recipient} not found.", { recipient });
|
|
639
1183
|
span.setStatus({
|
|
640
1184
|
code: SpanStatusCode.ERROR,
|
|
641
1185
|
message: `Actor ${recipient} not found.`
|
|
@@ -644,7 +1188,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
|
|
|
644
1188
|
}
|
|
645
1189
|
}
|
|
646
1190
|
if (request.bodyUsed) {
|
|
647
|
-
logger$
|
|
1191
|
+
logger$1.error("Request body has already been read.", { recipient });
|
|
648
1192
|
span.setStatus({
|
|
649
1193
|
code: SpanStatusCode.ERROR,
|
|
650
1194
|
message: "Request body has already been read."
|
|
@@ -654,7 +1198,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
|
|
|
654
1198
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
655
1199
|
});
|
|
656
1200
|
} else if (request.body?.locked) {
|
|
657
|
-
logger$
|
|
1201
|
+
logger$1.error("Request body is locked.", { recipient });
|
|
658
1202
|
span.setStatus({
|
|
659
1203
|
code: SpanStatusCode.ERROR,
|
|
660
1204
|
message: "Request body is locked."
|
|
@@ -668,14 +1212,14 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
|
|
|
668
1212
|
try {
|
|
669
1213
|
json = await request.clone().json();
|
|
670
1214
|
} catch (error) {
|
|
671
|
-
logger$
|
|
1215
|
+
logger$1.error("Failed to parse JSON:\n{error}", {
|
|
672
1216
|
recipient,
|
|
673
1217
|
error
|
|
674
1218
|
});
|
|
675
1219
|
try {
|
|
676
1220
|
await inboxErrorHandler?.(ctx, error);
|
|
677
1221
|
} catch (error$1) {
|
|
678
|
-
logger$
|
|
1222
|
+
logger$1.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
679
1223
|
error: error$1,
|
|
680
1224
|
activity: json,
|
|
681
1225
|
recipient
|
|
@@ -701,7 +1245,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
|
|
|
701
1245
|
});
|
|
702
1246
|
} catch (error) {
|
|
703
1247
|
if (error instanceof Error && error.name === "jsonld.SyntaxError") {
|
|
704
|
-
logger$
|
|
1248
|
+
logger$1.error("Failed to parse JSON-LD:\n{error}", {
|
|
705
1249
|
recipient,
|
|
706
1250
|
error
|
|
707
1251
|
});
|
|
@@ -715,13 +1259,13 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
|
|
|
715
1259
|
const jsonWithoutSig = detachSignature(json);
|
|
716
1260
|
let activity = null;
|
|
717
1261
|
if (ldSigVerified) {
|
|
718
|
-
logger$
|
|
1262
|
+
logger$1.debug("Linked Data Signatures are verified.", {
|
|
719
1263
|
recipient,
|
|
720
1264
|
json
|
|
721
1265
|
});
|
|
722
1266
|
activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
|
|
723
1267
|
} else {
|
|
724
|
-
logger$
|
|
1268
|
+
logger$1.debug("Linked Data Signatures are not verified.", {
|
|
725
1269
|
recipient,
|
|
726
1270
|
json
|
|
727
1271
|
});
|
|
@@ -733,7 +1277,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
|
|
|
733
1277
|
tracerProvider
|
|
734
1278
|
});
|
|
735
1279
|
} catch (error) {
|
|
736
|
-
logger$
|
|
1280
|
+
logger$1.error("Failed to parse activity:\n{error}", {
|
|
737
1281
|
recipient,
|
|
738
1282
|
activity: json,
|
|
739
1283
|
error
|
|
@@ -741,7 +1285,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
|
|
|
741
1285
|
try {
|
|
742
1286
|
await inboxErrorHandler?.(ctx, error);
|
|
743
1287
|
} catch (error$1) {
|
|
744
|
-
logger$
|
|
1288
|
+
logger$1.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
745
1289
|
error: error$1,
|
|
746
1290
|
activity: json,
|
|
747
1291
|
recipient
|
|
@@ -756,11 +1300,11 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
|
|
|
756
1300
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
757
1301
|
});
|
|
758
1302
|
}
|
|
759
|
-
if (activity == null) logger$
|
|
1303
|
+
if (activity == null) logger$1.debug("Object Integrity Proofs are not verified.", {
|
|
760
1304
|
recipient,
|
|
761
1305
|
activity: json
|
|
762
1306
|
});
|
|
763
|
-
else logger$
|
|
1307
|
+
else logger$1.debug("Object Integrity Proofs are verified.", {
|
|
764
1308
|
recipient,
|
|
765
1309
|
activity: json
|
|
766
1310
|
});
|
|
@@ -776,7 +1320,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
|
|
|
776
1320
|
tracerProvider
|
|
777
1321
|
});
|
|
778
1322
|
if (key == null) {
|
|
779
|
-
logger$
|
|
1323
|
+
logger$1.error("Failed to verify the request's HTTP Signatures.", { recipient });
|
|
780
1324
|
span.setStatus({
|
|
781
1325
|
code: SpanStatusCode.ERROR,
|
|
782
1326
|
message: `Failed to verify the request's HTTP Signatures.`
|
|
@@ -785,7 +1329,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
|
|
|
785
1329
|
status: 401,
|
|
786
1330
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
787
1331
|
});
|
|
788
|
-
} else logger$
|
|
1332
|
+
} else logger$1.debug("HTTP Signatures are verified.", { recipient });
|
|
789
1333
|
httpSigKey = key;
|
|
790
1334
|
}
|
|
791
1335
|
activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
|
|
@@ -793,7 +1337,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
|
|
|
793
1337
|
if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
|
|
794
1338
|
span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
|
|
795
1339
|
if (httpSigKey != null && !await doesActorOwnKey(activity, httpSigKey, ctx)) {
|
|
796
|
-
logger$
|
|
1340
|
+
logger$1.error("The signer ({keyId}) and the actor ({actorId}) do not match.", {
|
|
797
1341
|
activity: json,
|
|
798
1342
|
recipient,
|
|
799
1343
|
keyId: httpSigKey.id?.href,
|
|
@@ -874,6 +1418,320 @@ async function respondWithObjectIfAcceptable(object, request, options) {
|
|
|
874
1418
|
return response;
|
|
875
1419
|
}
|
|
876
1420
|
|
|
1421
|
+
//#endregion
|
|
1422
|
+
//#region nodeinfo/handler.ts
|
|
1423
|
+
/**
|
|
1424
|
+
* Handles a NodeInfo request. You would not typically call this function
|
|
1425
|
+
* directly, but instead use {@link Federation.handle} method.
|
|
1426
|
+
* @param request The NodeInfo request to handle.
|
|
1427
|
+
* @param parameters The parameters for handling the request.
|
|
1428
|
+
* @returns The response to the request.
|
|
1429
|
+
*/
|
|
1430
|
+
async function handleNodeInfo(_request, { context: context$1, nodeInfoDispatcher }) {
|
|
1431
|
+
const promise = nodeInfoDispatcher(context$1);
|
|
1432
|
+
const json = nodeInfoToJson(promise instanceof Promise ? await promise : promise);
|
|
1433
|
+
return new Response(JSON.stringify(json), { headers: { "Content-Type": "application/json; profile=\"http://nodeinfo.diaspora.software/ns/schema/2.1#\"" } });
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Handles a request to `/.well-known/nodeinfo`. You would not typically call
|
|
1437
|
+
* this function directly, but instead use {@link Federation.handle} method.
|
|
1438
|
+
* @param request The request to handle.
|
|
1439
|
+
* @param context The request context.
|
|
1440
|
+
* @returns The response to the request.
|
|
1441
|
+
*/
|
|
1442
|
+
function handleNodeInfoJrd(_request, context$1) {
|
|
1443
|
+
const links = [];
|
|
1444
|
+
try {
|
|
1445
|
+
links.push({
|
|
1446
|
+
rel: "http://nodeinfo.diaspora.software/ns/schema/2.1",
|
|
1447
|
+
href: context$1.getNodeInfoUri().href,
|
|
1448
|
+
type: "application/json; profile=\"http://nodeinfo.diaspora.software/ns/schema/2.1#\""
|
|
1449
|
+
});
|
|
1450
|
+
} catch (e) {
|
|
1451
|
+
if (!(e instanceof RouterError)) throw e;
|
|
1452
|
+
}
|
|
1453
|
+
const jrd = { links };
|
|
1454
|
+
const response = new Response(JSON.stringify(jrd), { headers: { "Content-Type": "application/jrd+json" } });
|
|
1455
|
+
return Promise.resolve(response);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
//#endregion
|
|
1459
|
+
//#region webfinger/handler.ts
|
|
1460
|
+
const logger = getLogger([
|
|
1461
|
+
"fedify",
|
|
1462
|
+
"webfinger",
|
|
1463
|
+
"server"
|
|
1464
|
+
]);
|
|
1465
|
+
/**
|
|
1466
|
+
* Handles a WebFinger request. You would not typically call this function
|
|
1467
|
+
* directly, but instead use {@link Federation.fetch} method.
|
|
1468
|
+
* @param request The WebFinger request to handle.
|
|
1469
|
+
* @param parameters The parameters for handling the request.
|
|
1470
|
+
* @returns The response to the request.
|
|
1471
|
+
*/
|
|
1472
|
+
async function handleWebFinger(request, options) {
|
|
1473
|
+
if (options.tracer == null) return await handleWebFingerInternal(request, options);
|
|
1474
|
+
return await options.tracer.startActiveSpan("webfinger.handle", { kind: SpanKind.SERVER }, async (span) => {
|
|
1475
|
+
try {
|
|
1476
|
+
const response = await handleWebFingerInternal(request, options);
|
|
1477
|
+
span.setStatus({ code: response.ok ? SpanStatusCode.UNSET : SpanStatusCode.ERROR });
|
|
1478
|
+
return response;
|
|
1479
|
+
} catch (error) {
|
|
1480
|
+
span.setStatus({
|
|
1481
|
+
code: SpanStatusCode.ERROR,
|
|
1482
|
+
message: String(error)
|
|
1483
|
+
});
|
|
1484
|
+
throw error;
|
|
1485
|
+
} finally {
|
|
1486
|
+
span.end();
|
|
1487
|
+
}
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1490
|
+
async function handleWebFingerInternal(request, { context: context$1, host, actorDispatcher, actorHandleMapper, actorAliasMapper, onNotFound, span }) {
|
|
1491
|
+
if (actorDispatcher == null) return await onNotFound(request);
|
|
1492
|
+
const resource = context$1.url.searchParams.get("resource");
|
|
1493
|
+
if (resource == null) return new Response("Missing resource parameter.", { status: 400 });
|
|
1494
|
+
span?.setAttribute("webfinger.resource", resource);
|
|
1495
|
+
let resourceUrl;
|
|
1496
|
+
try {
|
|
1497
|
+
resourceUrl = new URL(resource);
|
|
1498
|
+
} catch (e) {
|
|
1499
|
+
if (e instanceof TypeError) return new Response("Invalid resource URL.", { status: 400 });
|
|
1500
|
+
throw e;
|
|
1501
|
+
}
|
|
1502
|
+
span?.setAttribute("webfinger.resource.scheme", resourceUrl.protocol.replace(/:$/, ""));
|
|
1503
|
+
if (actorDispatcher == null) {
|
|
1504
|
+
logger.error("Actor dispatcher is not set.");
|
|
1505
|
+
return await onNotFound(request);
|
|
1506
|
+
}
|
|
1507
|
+
async function mapUsernameToIdentifier(username) {
|
|
1508
|
+
if (actorHandleMapper == null) {
|
|
1509
|
+
logger.error("No actor handle mapper is set; use the WebFinger username {username} as the actor's internal identifier.", { username });
|
|
1510
|
+
return username;
|
|
1511
|
+
}
|
|
1512
|
+
const identifier$1 = await actorHandleMapper(context$1, username);
|
|
1513
|
+
if (identifier$1 == null) {
|
|
1514
|
+
logger.error("Actor {username} not found.", { username });
|
|
1515
|
+
return null;
|
|
1516
|
+
}
|
|
1517
|
+
return identifier$1;
|
|
1518
|
+
}
|
|
1519
|
+
let identifier = null;
|
|
1520
|
+
const uriParsed = context$1.parseUri(resourceUrl);
|
|
1521
|
+
if (uriParsed?.type != "actor") {
|
|
1522
|
+
const match = /^acct:([^@]+)@([^@]+)$/.exec(resource);
|
|
1523
|
+
if (match == null) {
|
|
1524
|
+
const result = await actorAliasMapper?.(context$1, resourceUrl);
|
|
1525
|
+
if (result == null) return await onNotFound(request);
|
|
1526
|
+
if ("identifier" in result) identifier = result.identifier;
|
|
1527
|
+
else identifier = await mapUsernameToIdentifier(result.username);
|
|
1528
|
+
} else {
|
|
1529
|
+
const portMatch = /:\d+$/.exec(match[2]);
|
|
1530
|
+
const normalizedHost = portMatch == null ? domainToASCII(match[2].toLowerCase()) : domainToASCII(match[2].substring(0, portMatch.index).toLowerCase()) + portMatch[0];
|
|
1531
|
+
if (normalizedHost != context$1.url.host && normalizedHost != host) return await onNotFound(request);
|
|
1532
|
+
else {
|
|
1533
|
+
identifier = await mapUsernameToIdentifier(match[1]);
|
|
1534
|
+
resourceUrl = new URL(`acct:${match[1]}@${normalizedHost}`);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
} else identifier = uriParsed.identifier;
|
|
1538
|
+
if (identifier == null) return await onNotFound(request);
|
|
1539
|
+
const actor = await actorDispatcher(context$1, identifier);
|
|
1540
|
+
if (actor == null) {
|
|
1541
|
+
logger.error("Actor {identifier} not found.", { identifier });
|
|
1542
|
+
return await onNotFound(request);
|
|
1543
|
+
}
|
|
1544
|
+
const links = [{
|
|
1545
|
+
rel: "self",
|
|
1546
|
+
href: context$1.getActorUri(identifier).href,
|
|
1547
|
+
type: "application/activity+json"
|
|
1548
|
+
}];
|
|
1549
|
+
for (const url of actor.urls) if (url instanceof Link && url.href != null) links.push({
|
|
1550
|
+
rel: url.rel ?? "http://webfinger.net/rel/profile-page",
|
|
1551
|
+
href: url.href.href,
|
|
1552
|
+
type: url.mediaType == null ? void 0 : url.mediaType
|
|
1553
|
+
});
|
|
1554
|
+
else if (url instanceof URL) links.push({
|
|
1555
|
+
rel: "http://webfinger.net/rel/profile-page",
|
|
1556
|
+
href: url.href
|
|
1557
|
+
});
|
|
1558
|
+
for await (const image of actor.getIcons()) {
|
|
1559
|
+
if (image.url?.href == null) continue;
|
|
1560
|
+
const link = {
|
|
1561
|
+
rel: "http://webfinger.net/rel/avatar",
|
|
1562
|
+
href: image.url.href.toString()
|
|
1563
|
+
};
|
|
1564
|
+
if (image.mediaType != null) link.type = image.mediaType;
|
|
1565
|
+
links.push(link);
|
|
1566
|
+
}
|
|
1567
|
+
const aliases = [];
|
|
1568
|
+
if (resourceUrl.protocol != "acct:" && actor.preferredUsername != null) {
|
|
1569
|
+
aliases.push(`acct:${actor.preferredUsername}@${host ?? context$1.url.host}`);
|
|
1570
|
+
if (host != null && host !== context$1.url.host) aliases.push(`acct:${actor.preferredUsername}@${context$1.url.host}`);
|
|
1571
|
+
}
|
|
1572
|
+
if (resourceUrl.href !== context$1.getActorUri(identifier).href) aliases.push(context$1.getActorUri(identifier).href);
|
|
1573
|
+
if (resourceUrl.protocol === "acct:" && host != null && host !== context$1.url.host && !resourceUrl.href.endsWith(`@${host}`)) {
|
|
1574
|
+
const username = resourceUrl.href.replace(/^acct:/, "").replace(/@.*$/, "");
|
|
1575
|
+
aliases.push(`acct:${username}@${host}`);
|
|
1576
|
+
}
|
|
1577
|
+
const jrd = {
|
|
1578
|
+
subject: resourceUrl.href,
|
|
1579
|
+
aliases,
|
|
1580
|
+
links
|
|
1581
|
+
};
|
|
1582
|
+
return new Response(JSON.stringify(jrd), { headers: {
|
|
1583
|
+
"Content-Type": "application/jrd+json",
|
|
1584
|
+
"Access-Control-Allow-Origin": "*"
|
|
1585
|
+
} });
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
//#endregion
|
|
1589
|
+
//#region federation/retry.ts
|
|
1590
|
+
/**
|
|
1591
|
+
* Creates an exponential backoff retry policy. The delay between retries
|
|
1592
|
+
* starts at the `initialDelay` and is multiplied by the `factor` for each
|
|
1593
|
+
* subsequent retry, up to the `maxDelay`. The policy will give up after
|
|
1594
|
+
* `maxAttempts` attempts. The actual delay is randomized to avoid
|
|
1595
|
+
* synchronization (jitter).
|
|
1596
|
+
* @param options The options for the policy.
|
|
1597
|
+
* @returns The retry policy.
|
|
1598
|
+
* @since 0.12.0
|
|
1599
|
+
*/
|
|
1600
|
+
function createExponentialBackoffPolicy(options = {}) {
|
|
1601
|
+
const initialDelay = Temporal.Duration.from(options.initialDelay ?? { seconds: 1 });
|
|
1602
|
+
const maxDelay = Temporal.Duration.from(options.maxDelay ?? { hours: 12 });
|
|
1603
|
+
const maxAttempts = options.maxAttempts ?? 10;
|
|
1604
|
+
const factor = options.factor ?? 2;
|
|
1605
|
+
const jitter = options.jitter ?? true;
|
|
1606
|
+
return ({ attempts }) => {
|
|
1607
|
+
if (attempts >= maxAttempts) return null;
|
|
1608
|
+
let milliseconds = initialDelay.total("millisecond");
|
|
1609
|
+
milliseconds *= factor ** attempts;
|
|
1610
|
+
if (jitter) {
|
|
1611
|
+
milliseconds *= 1 + Math.random();
|
|
1612
|
+
milliseconds = Math.round(milliseconds);
|
|
1613
|
+
}
|
|
1614
|
+
const delay$1 = Temporal.Duration.from({ milliseconds });
|
|
1615
|
+
return Temporal.Duration.compare(delay$1, maxDelay) > 0 ? maxDelay : delay$1;
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
//#endregion
|
|
1620
|
+
//#region federation/send.ts
|
|
1621
|
+
/**
|
|
1622
|
+
* Extracts the inbox URLs from recipients.
|
|
1623
|
+
* @param parameters The parameters to extract the inboxes.
|
|
1624
|
+
* See also {@link ExtractInboxesParameters}.
|
|
1625
|
+
* @returns The inboxes as a map of inbox URL to actor URIs.
|
|
1626
|
+
*/
|
|
1627
|
+
function extractInboxes({ recipients, preferSharedInbox, excludeBaseUris }) {
|
|
1628
|
+
const inboxes = {};
|
|
1629
|
+
for (const recipient of recipients) {
|
|
1630
|
+
let inbox;
|
|
1631
|
+
let sharedInbox = false;
|
|
1632
|
+
if (preferSharedInbox && recipient.endpoints?.sharedInbox != null) {
|
|
1633
|
+
inbox = recipient.endpoints.sharedInbox;
|
|
1634
|
+
sharedInbox = true;
|
|
1635
|
+
} else inbox = recipient.inboxId;
|
|
1636
|
+
if (inbox != null && recipient.id != null) {
|
|
1637
|
+
if (excludeBaseUris != null && excludeBaseUris.some((u) => u.origin === inbox?.origin)) continue;
|
|
1638
|
+
inboxes[inbox.href] ??= {
|
|
1639
|
+
actorIds: /* @__PURE__ */ new Set(),
|
|
1640
|
+
sharedInbox
|
|
1641
|
+
};
|
|
1642
|
+
inboxes[inbox.href].actorIds.add(recipient.id.href);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
return inboxes;
|
|
1646
|
+
}
|
|
1647
|
+
/**
|
|
1648
|
+
* Sends an {@link Activity} to an inbox.
|
|
1649
|
+
*
|
|
1650
|
+
* @param parameters The parameters for sending the activity.
|
|
1651
|
+
* See also {@link SendActivityParameters}.
|
|
1652
|
+
* @throws {Error} If the activity fails to send.
|
|
1653
|
+
*/
|
|
1654
|
+
function sendActivity(options) {
|
|
1655
|
+
const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
|
|
1656
|
+
return tracerProvider.getTracer(name, version).startActiveSpan("activitypub.send_activity", {
|
|
1657
|
+
kind: SpanKind.CLIENT,
|
|
1658
|
+
attributes: { "activitypub.shared_inbox": options.sharedInbox ?? false }
|
|
1659
|
+
}, async (span) => {
|
|
1660
|
+
if (options.activityId != null) span.setAttribute("activitypub.activity.id", options.activityId);
|
|
1661
|
+
if (options.activityType != null) span.setAttribute("activitypub.activity.type", options.activityType);
|
|
1662
|
+
try {
|
|
1663
|
+
await sendActivityInternal({
|
|
1664
|
+
...options,
|
|
1665
|
+
tracerProvider
|
|
1666
|
+
});
|
|
1667
|
+
} catch (e) {
|
|
1668
|
+
span.setStatus({
|
|
1669
|
+
code: SpanStatusCode.ERROR,
|
|
1670
|
+
message: String(e)
|
|
1671
|
+
});
|
|
1672
|
+
throw e;
|
|
1673
|
+
} finally {
|
|
1674
|
+
span.end();
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
async function sendActivityInternal({ activity, activityId, keys, inbox, headers, specDeterminer, tracerProvider }) {
|
|
1679
|
+
const logger$1 = getLogger([
|
|
1680
|
+
"fedify",
|
|
1681
|
+
"federation",
|
|
1682
|
+
"outbox"
|
|
1683
|
+
]);
|
|
1684
|
+
headers = new Headers(headers);
|
|
1685
|
+
headers.set("Content-Type", "application/activity+json");
|
|
1686
|
+
const request = new Request(inbox, {
|
|
1687
|
+
method: "POST",
|
|
1688
|
+
headers,
|
|
1689
|
+
body: JSON.stringify(activity)
|
|
1690
|
+
});
|
|
1691
|
+
let rsaKey = null;
|
|
1692
|
+
for (const key of keys) if (key.privateKey.algorithm.name === "RSASSA-PKCS1-v1_5") {
|
|
1693
|
+
rsaKey = key;
|
|
1694
|
+
break;
|
|
1695
|
+
}
|
|
1696
|
+
if (rsaKey == null) logger$1.warn("No supported key found to sign the request to {inbox}. The request will be sent without a signature. In order to sign the request, at least one RSASSA-PKCS1-v1_5 key must be provided.", {
|
|
1697
|
+
inbox: inbox.href,
|
|
1698
|
+
keys: keys.map((pair) => ({
|
|
1699
|
+
keyId: pair.keyId.href,
|
|
1700
|
+
privateKey: pair.privateKey
|
|
1701
|
+
}))
|
|
1702
|
+
});
|
|
1703
|
+
let response;
|
|
1704
|
+
try {
|
|
1705
|
+
response = rsaKey == null ? await fetch(request) : await doubleKnock(request, rsaKey, {
|
|
1706
|
+
tracerProvider,
|
|
1707
|
+
specDeterminer
|
|
1708
|
+
});
|
|
1709
|
+
} catch (error) {
|
|
1710
|
+
logger$1.error("Failed to send activity {activityId} to {inbox}:\n{error}", {
|
|
1711
|
+
activityId,
|
|
1712
|
+
inbox: inbox.href,
|
|
1713
|
+
error
|
|
1714
|
+
});
|
|
1715
|
+
throw error;
|
|
1716
|
+
}
|
|
1717
|
+
if (!response.ok) {
|
|
1718
|
+
let error;
|
|
1719
|
+
try {
|
|
1720
|
+
error = await response.text();
|
|
1721
|
+
} catch (_) {
|
|
1722
|
+
error = "";
|
|
1723
|
+
}
|
|
1724
|
+
logger$1.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
|
|
1725
|
+
activityId,
|
|
1726
|
+
inbox: inbox.href,
|
|
1727
|
+
status: response.status,
|
|
1728
|
+
statusText: response.statusText,
|
|
1729
|
+
error
|
|
1730
|
+
});
|
|
1731
|
+
throw new Error(`Failed to send activity ${activityId} to ${inbox.href} (${response.status} ${response.statusText}):\n${error}`);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
|
|
877
1735
|
//#endregion
|
|
878
1736
|
//#region federation/middleware.ts
|
|
879
1737
|
/**
|
|
@@ -910,7 +1768,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
910
1768
|
tracerProvider;
|
|
911
1769
|
constructor(options) {
|
|
912
1770
|
super();
|
|
913
|
-
const logger$
|
|
1771
|
+
const logger$1 = getLogger(["fedify", "federation"]);
|
|
914
1772
|
this.kv = options.kv;
|
|
915
1773
|
this.kvPrefixes = {
|
|
916
1774
|
activityIdempotence: ["_fedify", "activityIdempotence"],
|
|
@@ -967,7 +1825,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
967
1825
|
if (options.documentLoader != null) {
|
|
968
1826
|
if (options.documentLoaderFactory != null) throw new TypeError("Cannot set both documentLoader and documentLoaderFactory options at a time; use documentLoaderFactory only.");
|
|
969
1827
|
this.documentLoaderFactory = () => options.documentLoader;
|
|
970
|
-
logger$
|
|
1828
|
+
logger$1.warn("The documentLoader option is deprecated; use documentLoaderFactory option instead.");
|
|
971
1829
|
} else this.documentLoaderFactory = options.documentLoaderFactory ?? ((opts) => {
|
|
972
1830
|
return kvCache({
|
|
973
1831
|
loader: getDocumentLoader({
|
|
@@ -981,7 +1839,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
981
1839
|
if (options.contextLoader != null) {
|
|
982
1840
|
if (options.contextLoaderFactory != null) throw new TypeError("Cannot set both contextLoader and contextLoaderFactory options at a time; use contextLoaderFactory only.");
|
|
983
1841
|
this.contextLoaderFactory = () => options.contextLoader;
|
|
984
|
-
logger$
|
|
1842
|
+
logger$1.warn("The contextLoader option is deprecated; use contextLoaderFactory option instead.");
|
|
985
1843
|
} else this.contextLoaderFactory = options.contextLoaderFactory ?? this.documentLoaderFactory;
|
|
986
1844
|
this.authenticatedDocumentLoaderFactory = options.authenticatedDocumentLoaderFactory ?? ((identity) => getAuthenticatedDocumentLoader(identity, {
|
|
987
1845
|
allowPrivateAddress,
|
|
@@ -1007,24 +1865,24 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1007
1865
|
}
|
|
1008
1866
|
async _startQueueInternal(ctxData, signal, queue) {
|
|
1009
1867
|
if (this.inboxQueue == null && this.outboxQueue == null) return;
|
|
1010
|
-
const logger$
|
|
1868
|
+
const logger$1 = getLogger([
|
|
1011
1869
|
"fedify",
|
|
1012
1870
|
"federation",
|
|
1013
1871
|
"queue"
|
|
1014
1872
|
]);
|
|
1015
1873
|
const promises = [];
|
|
1016
1874
|
if (this.inboxQueue != null && (queue == null || queue === "inbox") && !this.inboxQueueStarted) {
|
|
1017
|
-
logger$
|
|
1875
|
+
logger$1.debug("Starting an inbox task worker.");
|
|
1018
1876
|
this.inboxQueueStarted = true;
|
|
1019
1877
|
promises.push(this.inboxQueue.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
|
|
1020
1878
|
}
|
|
1021
1879
|
if (this.outboxQueue != null && this.outboxQueue !== this.inboxQueue && (queue == null || queue === "outbox") && !this.outboxQueueStarted) {
|
|
1022
|
-
logger$
|
|
1880
|
+
logger$1.debug("Starting an outbox task worker.");
|
|
1023
1881
|
this.outboxQueueStarted = true;
|
|
1024
1882
|
promises.push(this.outboxQueue.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
|
|
1025
1883
|
}
|
|
1026
1884
|
if (this.fanoutQueue != null && this.fanoutQueue !== this.inboxQueue && this.fanoutQueue !== this.outboxQueue && (queue == null || queue === "fanout") && !this.fanoutQueueStarted) {
|
|
1027
|
-
logger$
|
|
1885
|
+
logger$1.debug("Starting a fanout task worker.");
|
|
1028
1886
|
this.fanoutQueueStarted = true;
|
|
1029
1887
|
promises.push(this.fanoutQueue.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
|
|
1030
1888
|
}
|
|
@@ -1123,7 +1981,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1123
1981
|
});
|
|
1124
1982
|
}
|
|
1125
1983
|
async #listenOutboxMessage(_, message, span) {
|
|
1126
|
-
const logger$
|
|
1984
|
+
const logger$1 = getLogger([
|
|
1127
1985
|
"fedify",
|
|
1128
1986
|
"federation",
|
|
1129
1987
|
"outbox"
|
|
@@ -1172,34 +2030,34 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1172
2030
|
try {
|
|
1173
2031
|
this.onOutboxError?.(error, activity);
|
|
1174
2032
|
} catch (error$1) {
|
|
1175
|
-
logger$
|
|
2033
|
+
logger$1.error("An unexpected error occurred in onError handler:\n{error}", {
|
|
1176
2034
|
...logData,
|
|
1177
2035
|
error: error$1
|
|
1178
2036
|
});
|
|
1179
2037
|
}
|
|
1180
|
-
const delay = this.outboxRetryPolicy({
|
|
2038
|
+
const delay$1 = this.outboxRetryPolicy({
|
|
1181
2039
|
elapsedTime: Temporal.Instant.from(message.started).until(Temporal.Now.instant()),
|
|
1182
2040
|
attempts: message.attempt
|
|
1183
2041
|
});
|
|
1184
|
-
if (delay != null) {
|
|
1185
|
-
logger$
|
|
2042
|
+
if (delay$1 != null) {
|
|
2043
|
+
logger$1.error("Failed to send activity {activityId} to {inbox} (attempt #{attempt}); retry...:\n{error}", {
|
|
1186
2044
|
...logData,
|
|
1187
2045
|
error
|
|
1188
2046
|
});
|
|
1189
2047
|
await this.outboxQueue?.enqueue({
|
|
1190
2048
|
...message,
|
|
1191
2049
|
attempt: message.attempt + 1
|
|
1192
|
-
}, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
|
|
1193
|
-
} else logger$
|
|
2050
|
+
}, { delay: Temporal.Duration.compare(delay$1, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay$1 });
|
|
2051
|
+
} else logger$1.error("Failed to send activity {activityId} to {inbox} after {attempt} attempts; giving up:\n{error}", {
|
|
1194
2052
|
...logData,
|
|
1195
2053
|
error
|
|
1196
2054
|
});
|
|
1197
2055
|
return;
|
|
1198
2056
|
}
|
|
1199
|
-
logger$
|
|
2057
|
+
logger$1.info("Successfully sent activity {activityId} to {inbox}.", { ...logData });
|
|
1200
2058
|
}
|
|
1201
2059
|
async #listenInboxMessage(ctxData, message, span) {
|
|
1202
|
-
const logger$
|
|
2060
|
+
const logger$1 = getLogger([
|
|
1203
2061
|
"fedify",
|
|
1204
2062
|
"federation",
|
|
1205
2063
|
"inbox"
|
|
@@ -1221,7 +2079,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1221
2079
|
];
|
|
1222
2080
|
if (cacheKey != null) {
|
|
1223
2081
|
if (await this.kv.get(cacheKey) === true) {
|
|
1224
|
-
logger$
|
|
2082
|
+
logger$1.debug("Activity {activityId} has already been processed.", {
|
|
1225
2083
|
activityId: activity.id?.href,
|
|
1226
2084
|
activity: message.activity,
|
|
1227
2085
|
recipient: message.identifier
|
|
@@ -1232,7 +2090,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1232
2090
|
await this._getTracer().startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span$1) => {
|
|
1233
2091
|
const dispatched = this.inboxListeners?.dispatchWithClass(activity);
|
|
1234
2092
|
if (dispatched == null) {
|
|
1235
|
-
logger$
|
|
2093
|
+
logger$1.error("Unsupported activity type:\n{activity}", {
|
|
1236
2094
|
activityId: activity.id?.href,
|
|
1237
2095
|
activity: message.activity,
|
|
1238
2096
|
recipient: message.identifier,
|
|
@@ -1253,7 +2111,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1253
2111
|
try {
|
|
1254
2112
|
await this.inboxErrorHandler?.(context$1, error);
|
|
1255
2113
|
} catch (error$1) {
|
|
1256
|
-
logger$
|
|
2114
|
+
logger$1.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
1257
2115
|
error: error$1,
|
|
1258
2116
|
trial: message.attempt,
|
|
1259
2117
|
activityId: activity.id?.href,
|
|
@@ -1261,12 +2119,12 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1261
2119
|
recipient: message.identifier
|
|
1262
2120
|
});
|
|
1263
2121
|
}
|
|
1264
|
-
const delay = this.inboxRetryPolicy({
|
|
2122
|
+
const delay$1 = this.inboxRetryPolicy({
|
|
1265
2123
|
elapsedTime: Temporal.Instant.from(message.started).until(Temporal.Now.instant()),
|
|
1266
2124
|
attempts: message.attempt
|
|
1267
2125
|
});
|
|
1268
|
-
if (delay != null) {
|
|
1269
|
-
logger$
|
|
2126
|
+
if (delay$1 != null) {
|
|
2127
|
+
logger$1.error("Failed to process the incoming activity {activityId} (attempt #{attempt}); retry...:\n{error}", {
|
|
1270
2128
|
error,
|
|
1271
2129
|
attempt: message.attempt,
|
|
1272
2130
|
activityId: activity.id?.href,
|
|
@@ -1276,8 +2134,8 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1276
2134
|
await this.inboxQueue?.enqueue({
|
|
1277
2135
|
...message,
|
|
1278
2136
|
attempt: message.attempt + 1
|
|
1279
|
-
}, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
|
|
1280
|
-
} else logger$
|
|
2137
|
+
}, { delay: Temporal.Duration.compare(delay$1, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay$1 });
|
|
2138
|
+
} else logger$1.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
|
|
1281
2139
|
error,
|
|
1282
2140
|
activityId: activity.id?.href,
|
|
1283
2141
|
activity: message.activity,
|
|
@@ -1291,7 +2149,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1291
2149
|
return;
|
|
1292
2150
|
}
|
|
1293
2151
|
if (cacheKey != null) await this.kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
|
|
1294
|
-
logger$
|
|
2152
|
+
logger$1.info("Activity {activityId} has been processed.", {
|
|
1295
2153
|
activityId: activity.id?.href,
|
|
1296
2154
|
activity: message.activity,
|
|
1297
2155
|
recipient: message.identifier
|
|
@@ -1340,7 +2198,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1340
2198
|
};
|
|
1341
2199
|
}
|
|
1342
2200
|
async sendActivity(keys, inboxes, activity, options) {
|
|
1343
|
-
const logger$
|
|
2201
|
+
const logger$1 = getLogger([
|
|
1344
2202
|
"fedify",
|
|
1345
2203
|
"federation",
|
|
1346
2204
|
"outbox"
|
|
@@ -1374,7 +2232,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1374
2232
|
format: "compact",
|
|
1375
2233
|
contextLoader
|
|
1376
2234
|
});
|
|
1377
|
-
if (rsaKey == null) logger$
|
|
2235
|
+
if (rsaKey == null) logger$1.warn("No supported key found to create a Linked Data signature for the activity {activityId}. The activity will be sent without a Linked Data signature. In order to create a Linked Data signature, at least one RSASSA-PKCS1-v1_5 key must be provided.", {
|
|
1378
2236
|
activityId,
|
|
1379
2237
|
keys: keys.map((pair) => ({
|
|
1380
2238
|
keyId: pair.keyId.href,
|
|
@@ -1385,7 +2243,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1385
2243
|
contextLoader,
|
|
1386
2244
|
tracerProvider: this.tracerProvider
|
|
1387
2245
|
});
|
|
1388
|
-
if (!proofCreated) logger$
|
|
2246
|
+
if (!proofCreated) logger$1.warn("No supported key found to create a proof for the activity {activityId}. The activity will be sent without a proof. In order to create a proof, at least one Ed25519 key must be provided.", {
|
|
1389
2247
|
activityId,
|
|
1390
2248
|
keys: keys.map((pair) => ({
|
|
1391
2249
|
keyId: pair.keyId.href,
|
|
@@ -1393,11 +2251,11 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1393
2251
|
}))
|
|
1394
2252
|
});
|
|
1395
2253
|
if (immediate || this.outboxQueue == null) {
|
|
1396
|
-
if (immediate) logger$
|
|
2254
|
+
if (immediate) logger$1.debug("Sending activity immediately without queue since immediate option is set.", {
|
|
1397
2255
|
activityId: activity.id.href,
|
|
1398
2256
|
activity: jsonLd
|
|
1399
2257
|
});
|
|
1400
|
-
else logger$
|
|
2258
|
+
else logger$1.debug("Sending activity immediately without queue since queue is not set.", {
|
|
1401
2259
|
activityId: activity.id.href,
|
|
1402
2260
|
activity: jsonLd
|
|
1403
2261
|
});
|
|
@@ -1416,7 +2274,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1416
2274
|
await Promise.all(promises);
|
|
1417
2275
|
return;
|
|
1418
2276
|
}
|
|
1419
|
-
logger$
|
|
2277
|
+
logger$1.debug("Enqueuing activity {activityId} to send later.", {
|
|
1420
2278
|
activityId: activity.id.href,
|
|
1421
2279
|
activity: jsonLd
|
|
1422
2280
|
});
|
|
@@ -1455,7 +2313,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1455
2313
|
const promises = messages.map((m) => outboxQueue.enqueue(m));
|
|
1456
2314
|
const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
1457
2315
|
if (errors.length > 0) {
|
|
1458
|
-
logger$
|
|
2316
|
+
logger$1.error("Failed to enqueue activity {activityId} to send later: {errors}", {
|
|
1459
2317
|
activityId: activity.id.href,
|
|
1460
2318
|
errors
|
|
1461
2319
|
});
|
|
@@ -1465,7 +2323,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1465
2323
|
} else try {
|
|
1466
2324
|
await outboxQueue.enqueueMany(messages);
|
|
1467
2325
|
} catch (error) {
|
|
1468
|
-
logger$
|
|
2326
|
+
logger$1.error("Failed to enqueue activity {activityId} to send later: {error}", {
|
|
1469
2327
|
activityId: activity.id.href,
|
|
1470
2328
|
error
|
|
1471
2329
|
});
|
|
@@ -1482,7 +2340,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1482
2340
|
[ATTR_URL_FULL]: request.url
|
|
1483
2341
|
}
|
|
1484
2342
|
}, async (span) => {
|
|
1485
|
-
const logger$
|
|
2343
|
+
const logger$1 = getLogger([
|
|
1486
2344
|
"fedify",
|
|
1487
2345
|
"federation",
|
|
1488
2346
|
"http"
|
|
@@ -1501,7 +2359,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1501
2359
|
message: `${error}`
|
|
1502
2360
|
});
|
|
1503
2361
|
span.end();
|
|
1504
|
-
logger$
|
|
2362
|
+
logger$1.error("An error occurred while serving request {method} {url}: {error}", {
|
|
1505
2363
|
method: request.method,
|
|
1506
2364
|
url: request.url,
|
|
1507
2365
|
error
|
|
@@ -1525,9 +2383,9 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1525
2383
|
url: request.url,
|
|
1526
2384
|
status: response.status
|
|
1527
2385
|
};
|
|
1528
|
-
if (response.status >= 500) logger$
|
|
1529
|
-
else if (response.status >= 400) logger$
|
|
1530
|
-
else logger$
|
|
2386
|
+
if (response.status >= 500) logger$1.error(logTpl, values);
|
|
2387
|
+
else if (response.status >= 400) logger$1.warn(logTpl, values);
|
|
2388
|
+
else logger$1.info(logTpl, values);
|
|
1531
2389
|
return response;
|
|
1532
2390
|
});
|
|
1533
2391
|
});
|
|
@@ -1844,13 +2702,13 @@ var ContextImpl = class ContextImpl {
|
|
|
1844
2702
|
if (uri == null) return null;
|
|
1845
2703
|
if (uri.origin !== this.origin && uri.origin !== this.canonicalOrigin) return null;
|
|
1846
2704
|
const route = this.federation.router.route(uri.pathname);
|
|
1847
|
-
const logger$
|
|
2705
|
+
const logger$1 = getLogger(["fedify", "federation"]);
|
|
1848
2706
|
if (route == null) return null;
|
|
1849
2707
|
else if (route.name === "sharedInbox") return {
|
|
1850
2708
|
type: "inbox",
|
|
1851
2709
|
identifier: void 0,
|
|
1852
2710
|
get handle() {
|
|
1853
|
-
logger$
|
|
2711
|
+
logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
|
|
1854
2712
|
}
|
|
1855
2713
|
};
|
|
1856
2714
|
const identifier = "identifier" in route.values ? route.values.identifier : route.values.handle;
|
|
@@ -1858,7 +2716,7 @@ var ContextImpl = class ContextImpl {
|
|
|
1858
2716
|
type: "actor",
|
|
1859
2717
|
identifier,
|
|
1860
2718
|
get handle() {
|
|
1861
|
-
logger$
|
|
2719
|
+
logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
|
|
1862
2720
|
return identifier;
|
|
1863
2721
|
}
|
|
1864
2722
|
};
|
|
@@ -1874,7 +2732,7 @@ var ContextImpl = class ContextImpl {
|
|
|
1874
2732
|
type: "inbox",
|
|
1875
2733
|
identifier,
|
|
1876
2734
|
get handle() {
|
|
1877
|
-
logger$
|
|
2735
|
+
logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
|
|
1878
2736
|
return identifier;
|
|
1879
2737
|
}
|
|
1880
2738
|
};
|
|
@@ -1882,7 +2740,7 @@ var ContextImpl = class ContextImpl {
|
|
|
1882
2740
|
type: "outbox",
|
|
1883
2741
|
identifier,
|
|
1884
2742
|
get handle() {
|
|
1885
|
-
logger$
|
|
2743
|
+
logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
|
|
1886
2744
|
return identifier;
|
|
1887
2745
|
}
|
|
1888
2746
|
};
|
|
@@ -1890,7 +2748,7 @@ var ContextImpl = class ContextImpl {
|
|
|
1890
2748
|
type: "following",
|
|
1891
2749
|
identifier,
|
|
1892
2750
|
get handle() {
|
|
1893
|
-
logger$
|
|
2751
|
+
logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
|
|
1894
2752
|
return identifier;
|
|
1895
2753
|
}
|
|
1896
2754
|
};
|
|
@@ -1898,7 +2756,7 @@ var ContextImpl = class ContextImpl {
|
|
|
1898
2756
|
type: "followers",
|
|
1899
2757
|
identifier,
|
|
1900
2758
|
get handle() {
|
|
1901
|
-
logger$
|
|
2759
|
+
logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
|
|
1902
2760
|
return identifier;
|
|
1903
2761
|
}
|
|
1904
2762
|
};
|
|
@@ -1906,7 +2764,7 @@ var ContextImpl = class ContextImpl {
|
|
|
1906
2764
|
type: "liked",
|
|
1907
2765
|
identifier,
|
|
1908
2766
|
get handle() {
|
|
1909
|
-
logger$
|
|
2767
|
+
logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
|
|
1910
2768
|
return identifier;
|
|
1911
2769
|
}
|
|
1912
2770
|
};
|
|
@@ -1914,7 +2772,7 @@ var ContextImpl = class ContextImpl {
|
|
|
1914
2772
|
type: "featured",
|
|
1915
2773
|
identifier,
|
|
1916
2774
|
get handle() {
|
|
1917
|
-
logger$
|
|
2775
|
+
logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
|
|
1918
2776
|
return identifier;
|
|
1919
2777
|
}
|
|
1920
2778
|
};
|
|
@@ -1922,19 +2780,19 @@ var ContextImpl = class ContextImpl {
|
|
|
1922
2780
|
type: "featuredTags",
|
|
1923
2781
|
identifier,
|
|
1924
2782
|
get handle() {
|
|
1925
|
-
logger$
|
|
2783
|
+
logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
|
|
1926
2784
|
return identifier;
|
|
1927
2785
|
}
|
|
1928
2786
|
};
|
|
1929
2787
|
return null;
|
|
1930
2788
|
}
|
|
1931
2789
|
async getActorKeyPairs(identifier) {
|
|
1932
|
-
const logger$
|
|
2790
|
+
const logger$1 = getLogger([
|
|
1933
2791
|
"fedify",
|
|
1934
2792
|
"federation",
|
|
1935
2793
|
"actor"
|
|
1936
2794
|
]);
|
|
1937
|
-
if (this.invokedFromActorKeyPairsDispatcher != null) logger$
|
|
2795
|
+
if (this.invokedFromActorKeyPairsDispatcher != null) logger$1.warn("Context.getActorKeyPairs({getActorKeyPairsIdentifier}) method is invoked from the actor key pairs dispatcher ({actorKeyPairsDispatcherIdentifier}); this may cause an infinite loop.", {
|
|
1938
2796
|
getActorKeyPairsIdentifier: identifier,
|
|
1939
2797
|
actorKeyPairsDispatcherIdentifier: this.invokedFromActorKeyPairsDispatcher.identifier
|
|
1940
2798
|
});
|
|
@@ -1942,7 +2800,7 @@ var ContextImpl = class ContextImpl {
|
|
|
1942
2800
|
try {
|
|
1943
2801
|
keyPairs = await this.getKeyPairsFromIdentifier(identifier);
|
|
1944
2802
|
} catch (_) {
|
|
1945
|
-
logger$
|
|
2803
|
+
logger$1.warn("No actor key pairs dispatcher registered.");
|
|
1946
2804
|
return [];
|
|
1947
2805
|
}
|
|
1948
2806
|
const owner = this.getActorUri(identifier);
|
|
@@ -1966,7 +2824,7 @@ var ContextImpl = class ContextImpl {
|
|
|
1966
2824
|
return result;
|
|
1967
2825
|
}
|
|
1968
2826
|
async getKeyPairsFromIdentifier(identifier) {
|
|
1969
|
-
const logger$
|
|
2827
|
+
const logger$1 = getLogger([
|
|
1970
2828
|
"fedify",
|
|
1971
2829
|
"federation",
|
|
1972
2830
|
"actor"
|
|
@@ -1977,7 +2835,7 @@ var ContextImpl = class ContextImpl {
|
|
|
1977
2835
|
handle: identifier
|
|
1978
2836
|
});
|
|
1979
2837
|
if (path == null) {
|
|
1980
|
-
logger$
|
|
2838
|
+
logger$1.warn("No actor dispatcher registered.");
|
|
1981
2839
|
return [];
|
|
1982
2840
|
}
|
|
1983
2841
|
const actorUri = new URL(path, this.canonicalOrigin);
|
|
@@ -1985,7 +2843,7 @@ var ContextImpl = class ContextImpl {
|
|
|
1985
2843
|
...this,
|
|
1986
2844
|
invokedFromActorKeyPairsDispatcher: { identifier }
|
|
1987
2845
|
}), identifier);
|
|
1988
|
-
if (keyPairs.length < 1) logger$
|
|
2846
|
+
if (keyPairs.length < 1) logger$1.warn("No key pairs found for actor {identifier}.", { identifier });
|
|
1989
2847
|
let i = 0;
|
|
1990
2848
|
const result = [];
|
|
1991
2849
|
for (const keyPair of keyPairs) {
|
|
@@ -2100,7 +2958,7 @@ var ContextImpl = class ContextImpl {
|
|
|
2100
2958
|
});
|
|
2101
2959
|
}
|
|
2102
2960
|
async sendActivityInternal(sender, recipients, activity, options, span) {
|
|
2103
|
-
const logger$
|
|
2961
|
+
const logger$1 = getLogger([
|
|
2104
2962
|
"fedify",
|
|
2105
2963
|
"federation",
|
|
2106
2964
|
"outbox"
|
|
@@ -2114,7 +2972,7 @@ var ContextImpl = class ContextImpl {
|
|
|
2114
2972
|
if ("username" in sender) username = sender.username;
|
|
2115
2973
|
else {
|
|
2116
2974
|
username = sender.handle;
|
|
2117
|
-
logger$
|
|
2975
|
+
logger$1.warn("The \"handle\" property for the sender parameter is deprecated; use \"identifier\" or \"username\" instead.", { sender });
|
|
2118
2976
|
}
|
|
2119
2977
|
if (this.federation.actorCallbacks?.handleMapper == null) identifier = username;
|
|
2120
2978
|
else {
|
|
@@ -2151,7 +3009,7 @@ var ContextImpl = class ContextImpl {
|
|
|
2151
3009
|
for (const activityTransformer of this.federation.activityTransformers) activity = activityTransformer(activity, this);
|
|
2152
3010
|
span?.setAttribute("activitypub.activity.id", activity?.id?.href ?? "");
|
|
2153
3011
|
if (activity.actorId == null) {
|
|
2154
|
-
logger$
|
|
3012
|
+
logger$1.error("Activity {activityId} to send does not have an actor.", {
|
|
2155
3013
|
activity,
|
|
2156
3014
|
activityId: activity?.id?.href
|
|
2157
3015
|
});
|
|
@@ -2162,7 +3020,7 @@ var ContextImpl = class ContextImpl {
|
|
|
2162
3020
|
preferSharedInbox: options.preferSharedInbox,
|
|
2163
3021
|
excludeBaseUris: options.excludeBaseUris
|
|
2164
3022
|
});
|
|
2165
|
-
logger$
|
|
3023
|
+
logger$1.debug("Sending activity {activityId} to inboxes:\n{inboxes}", {
|
|
2166
3024
|
inboxes: globalThis.Object.keys(inboxes),
|
|
2167
3025
|
activityId: activity.id?.href,
|
|
2168
3026
|
activity
|
|
@@ -2248,7 +3106,7 @@ var ContextImpl = class ContextImpl {
|
|
|
2248
3106
|
});
|
|
2249
3107
|
}
|
|
2250
3108
|
async routeActivityInternal(recipient, activity, options = {}, span) {
|
|
2251
|
-
const logger$
|
|
3109
|
+
const logger$1 = getLogger([
|
|
2252
3110
|
"fedify",
|
|
2253
3111
|
"federation",
|
|
2254
3112
|
"inbox"
|
|
@@ -2262,12 +3120,12 @@ var ContextImpl = class ContextImpl {
|
|
|
2262
3120
|
tracerProvider: options.tracerProvider ?? this.tracerProvider,
|
|
2263
3121
|
keyCache
|
|
2264
3122
|
}) == null) {
|
|
2265
|
-
logger$
|
|
3123
|
+
logger$1.debug("Object Integrity Proofs are not verified.", {
|
|
2266
3124
|
recipient,
|
|
2267
3125
|
activity: json
|
|
2268
3126
|
});
|
|
2269
3127
|
if (activity.id == null) {
|
|
2270
|
-
logger$
|
|
3128
|
+
logger$1.debug("Activity is missing an ID; unable to fetch.", {
|
|
2271
3129
|
recipient,
|
|
2272
3130
|
activity: json
|
|
2273
3131
|
});
|
|
@@ -2275,26 +3133,26 @@ var ContextImpl = class ContextImpl {
|
|
|
2275
3133
|
}
|
|
2276
3134
|
const fetched = await this.lookupObject(activity.id, options);
|
|
2277
3135
|
if (fetched == null) {
|
|
2278
|
-
logger$
|
|
3136
|
+
logger$1.debug("Failed to fetch the remote activity object {activityId}.", {
|
|
2279
3137
|
recipient,
|
|
2280
3138
|
activity: json,
|
|
2281
3139
|
activityId: activity.id.href
|
|
2282
3140
|
});
|
|
2283
3141
|
return false;
|
|
2284
3142
|
} else if (!(fetched instanceof Activity)) {
|
|
2285
|
-
logger$
|
|
3143
|
+
logger$1.debug("Fetched object is not an Activity.", {
|
|
2286
3144
|
recipient,
|
|
2287
3145
|
activity: await fetched.toJsonLd({ contextLoader })
|
|
2288
3146
|
});
|
|
2289
3147
|
return false;
|
|
2290
3148
|
} else if (fetched.id?.href !== activity.id.href) {
|
|
2291
|
-
logger$
|
|
3149
|
+
logger$1.debug("Fetched activity object has a different ID; failed to verify.", {
|
|
2292
3150
|
recipient,
|
|
2293
3151
|
activity: await fetched.toJsonLd({ contextLoader })
|
|
2294
3152
|
});
|
|
2295
3153
|
return false;
|
|
2296
3154
|
} else if (fetched.actorIds.length < 1) {
|
|
2297
|
-
logger$
|
|
3155
|
+
logger$1.debug("Fetched activity object is missing an actor; unable to verify.", {
|
|
2298
3156
|
recipient,
|
|
2299
3157
|
activity: await fetched.toJsonLd({ contextLoader })
|
|
2300
3158
|
});
|
|
@@ -2302,15 +3160,15 @@ var ContextImpl = class ContextImpl {
|
|
|
2302
3160
|
}
|
|
2303
3161
|
const activityId = fetched.id;
|
|
2304
3162
|
if (!fetched.actorIds.every((actor) => actor.origin === activityId.origin)) {
|
|
2305
|
-
logger$
|
|
3163
|
+
logger$1.debug("Fetched activity object has actors from different origins; unable to verify.", {
|
|
2306
3164
|
recipient,
|
|
2307
3165
|
activity: await fetched.toJsonLd({ contextLoader })
|
|
2308
3166
|
});
|
|
2309
3167
|
return false;
|
|
2310
3168
|
}
|
|
2311
|
-
logger$
|
|
3169
|
+
logger$1.debug("Successfully fetched the remote activity object {activityId}; ignore the original activity and use the fetched one, which is trustworthy.");
|
|
2312
3170
|
activity = fetched;
|
|
2313
|
-
} else logger$
|
|
3171
|
+
} else logger$1.debug("Object Integrity Proofs are verified.", {
|
|
2314
3172
|
recipient,
|
|
2315
3173
|
activity: json
|
|
2316
3174
|
});
|
|
@@ -2454,7 +3312,7 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
|
2454
3312
|
});
|
|
2455
3313
|
}
|
|
2456
3314
|
async forwardActivityInternal(forwarder, recipients, options) {
|
|
2457
|
-
const logger$
|
|
3315
|
+
const logger$1 = getLogger([
|
|
2458
3316
|
"fedify",
|
|
2459
3317
|
"federation",
|
|
2460
3318
|
"inbox"
|
|
@@ -2468,7 +3326,7 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
|
2468
3326
|
if ("username" in forwarder) username = forwarder.username;
|
|
2469
3327
|
else {
|
|
2470
3328
|
username = forwarder.handle;
|
|
2471
|
-
logger$
|
|
3329
|
+
logger$1.warn("The \"handle\" property for the forwarder parameter is deprecated; use \"identifier\" or \"username\" instead.", { forwarder });
|
|
2472
3330
|
}
|
|
2473
3331
|
if (this.federation.actorCallbacks?.handleMapper == null) identifier = username;
|
|
2474
3332
|
else {
|
|
@@ -2492,7 +3350,7 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
|
2492
3350
|
}
|
|
2493
3351
|
if (!hasProof) {
|
|
2494
3352
|
if (options?.skipIfUnsigned) return;
|
|
2495
|
-
logger$
|
|
3353
|
+
logger$1.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.");
|
|
2496
3354
|
}
|
|
2497
3355
|
}
|
|
2498
3356
|
if (recipients === "followers") {
|
|
@@ -2506,14 +3364,14 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
|
2506
3364
|
preferSharedInbox: options?.preferSharedInbox,
|
|
2507
3365
|
excludeBaseUris: options?.excludeBaseUris
|
|
2508
3366
|
});
|
|
2509
|
-
logger$
|
|
3367
|
+
logger$1.debug("Forwarding activity {activityId} to inboxes:\n{inboxes}", {
|
|
2510
3368
|
inboxes: globalThis.Object.keys(inboxes),
|
|
2511
3369
|
activityId: this.activityId,
|
|
2512
3370
|
activity: this.activity
|
|
2513
3371
|
});
|
|
2514
3372
|
if (options?.immediate || this.federation.outboxQueue == null) {
|
|
2515
|
-
if (options?.immediate) logger$
|
|
2516
|
-
else logger$
|
|
3373
|
+
if (options?.immediate) logger$1.debug("Forwarding activity immediately without queue since immediate option is set.");
|
|
3374
|
+
else logger$1.debug("Forwarding activity immediately without queue since queue is not set.");
|
|
2517
3375
|
const promises = [];
|
|
2518
3376
|
for (const inbox in inboxes) promises.push(sendActivity({
|
|
2519
3377
|
keys,
|
|
@@ -2528,7 +3386,7 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
|
2528
3386
|
await Promise.all(promises);
|
|
2529
3387
|
return;
|
|
2530
3388
|
}
|
|
2531
|
-
logger$
|
|
3389
|
+
logger$1.debug("Enqueuing activity {activityId} to forward later.", {
|
|
2532
3390
|
activityId: this.activityId,
|
|
2533
3391
|
activity: this.activity
|
|
2534
3392
|
});
|
|
@@ -2566,7 +3424,7 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
|
2566
3424
|
const promises = messages.map((m) => outboxQueue.enqueue(m));
|
|
2567
3425
|
const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
2568
3426
|
if (errors.length > 0) {
|
|
2569
|
-
logger$
|
|
3427
|
+
logger$1.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
|
|
2570
3428
|
activityId: this.activityId,
|
|
2571
3429
|
errors
|
|
2572
3430
|
});
|
|
@@ -2576,7 +3434,7 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
|
|
|
2576
3434
|
} else try {
|
|
2577
3435
|
await outboxQueue.enqueueMany(messages);
|
|
2578
3436
|
} catch (error) {
|
|
2579
|
-
logger$
|
|
3437
|
+
logger$1.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
|
|
2580
3438
|
activityId: this.activityId,
|
|
2581
3439
|
error
|
|
2582
3440
|
});
|
|
@@ -2635,4 +3493,4 @@ function getRequestId(request) {
|
|
|
2635
3493
|
}
|
|
2636
3494
|
|
|
2637
3495
|
//#endregion
|
|
2638
|
-
export {
|
|
3496
|
+
export { createFederation as a, respondWithObjectIfAcceptable as c, createFederationBuilder as d, Router$1 as f, KvSpecDeterminer as i, buildCollectionSynchronizationHeader as l, FederationImpl as n, createExponentialBackoffPolicy as o, RouterError as p, InboxContextImpl as r, respondWithObject as s, ContextImpl as t, digest as u };
|