fedipod-server 0.14.0 → 0.15.0
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/lib/client/masto/index.mjs +6 -1
- package/lib/core/intake/activities.mjs +10 -2
- package/lib/core/intake/index.mjs +1 -1
- package/lib/core/intake/notes.mjs +4 -2
- package/lib/core/social.mjs +17 -6
- package/lib/core/wire.mjs +2 -1
- package/lib/gateway/front-core.mjs +22 -3
- package/lib/pod/inbox.mjs +4 -1
- package/lib/pod/root.mjs +27 -0
- package/lib/pod/transport.mjs +7 -2
- package/lib/pod/urls.mjs +14 -0
- package/package.json +1 -1
- package/web/app/README.md +1 -1
- package/web/app/agent.mjs +21 -5
- package/web/app/boot.mjs +80 -20
- package/web/app/dist/boot.js +187 -49
- package/web/app/dist/boot.js.map +3 -3
- package/web/app/dist/sw.js +46 -22
- package/web/app/dist/sw.js.map +2 -2
- package/web/app/index.html +46 -8
- package/web/app/keys-browser.mjs +8 -3
- package/web/app/signup.mjs +68 -40
- package/web/app/site/boot.js +187 -49
- package/web/app/site/index.html +46 -8
- package/web/app/site/sw.js +46 -22
package/web/app/dist/sw.js
CHANGED
|
@@ -141,6 +141,13 @@ function apUrls(remotePod, root, { publicBase = null } = {}) {
|
|
|
141
141
|
}
|
|
142
142
|
return urls;
|
|
143
143
|
}
|
|
144
|
+
function podBaseOfWebId(webId) {
|
|
145
|
+
const u = new URL(webId);
|
|
146
|
+
u.hash = "";
|
|
147
|
+
u.search = "";
|
|
148
|
+
const dir = u.pathname.replace(/profile\/card$/u, "").replace(/[^/]*$/u, "");
|
|
149
|
+
return `${u.origin}${dir.endsWith("/") ? dir : dir + "/"}`;
|
|
150
|
+
}
|
|
144
151
|
function webfingerHost(podUrl) {
|
|
145
152
|
const u = new URL(podUrl);
|
|
146
153
|
return u.pathname === "/" ? u.host : null;
|
|
@@ -9651,9 +9658,10 @@ function hostMeta(base) {
|
|
|
9651
9658
|
</XRD>
|
|
9652
9659
|
`;
|
|
9653
9660
|
}
|
|
9654
|
-
function jrd({ handle: handle7, host, actor }) {
|
|
9661
|
+
function jrd({ handle: handle7, host, actor, aliases = [] }) {
|
|
9655
9662
|
return {
|
|
9656
9663
|
subject: `acct:${handle7}@${host}`,
|
|
9664
|
+
...aliases.length ? { aliases } : {},
|
|
9657
9665
|
links: [{ rel: "self", type: "application/activity+json", href: actor }]
|
|
9658
9666
|
};
|
|
9659
9667
|
}
|
|
@@ -57393,7 +57401,7 @@ async function onUndo(intake, activity, actor, { trusted = false } = {}) {
|
|
|
57393
57401
|
await intake.republish({ followers: true });
|
|
57394
57402
|
intake.log(`unfollowed by ${actor}`);
|
|
57395
57403
|
}
|
|
57396
|
-
async function onCreate(intake, activity, actor) {
|
|
57404
|
+
async function onCreate(intake, activity, actor, { trusted = false } = {}) {
|
|
57397
57405
|
const objectId = typeof activity.object === "string" ? activity.object : activity.object?.id;
|
|
57398
57406
|
if (!objectId) return "Create without object id";
|
|
57399
57407
|
if (intake.store.isBlocked(objectId)) return `blocked domain (${objectId})`;
|
|
@@ -57402,7 +57410,8 @@ async function onCreate(intake, activity, actor) {
|
|
|
57402
57410
|
if (!intake.concernsUs(envelope, actor)) return `not addressed to us (${objectId})`;
|
|
57403
57411
|
const ingested = intake.store.getStatuses().some((x) => x.noteId === objectId && (x.kind === "timeline" || x.kind === "mention"));
|
|
57404
57412
|
if (!ingested) {
|
|
57405
|
-
const
|
|
57413
|
+
const inline = trusted && typeof activity.object === "object" && activity.object?.id === objectId ? activity.object : null;
|
|
57414
|
+
const rejected = await intake.ingestNote(objectId, actor, { inline });
|
|
57406
57415
|
if (rejected) return rejected;
|
|
57407
57416
|
}
|
|
57408
57417
|
if (intake.config.kind === "group") await intake.amplify(objectId, { activity });
|
|
@@ -57616,8 +57625,8 @@ function referencesOurObject(intake, activity) {
|
|
|
57616
57625
|
if (refs.some((r) => r.startsWith(intake.urls.notes))) return true;
|
|
57617
57626
|
return intake.config.kind === "group" && refs.some((r) => intake.store.getStatuses().some((s) => s.noteId === r));
|
|
57618
57627
|
}
|
|
57619
|
-
async function ingestNote(intake, objectId, actor, { via } = {}) {
|
|
57620
|
-
const note = await intake.fetchAP(objectId);
|
|
57628
|
+
async function ingestNote(intake, objectId, actor, { via, inline = null } = {}) {
|
|
57629
|
+
const note = inline || await intake.fetchAP(objectId);
|
|
57621
57630
|
if (!note) return `object fetch failed (${objectId})`;
|
|
57622
57631
|
if (note.id !== objectId || !isContentType(note.type)) return `object not verifiable content (${objectId}, ${note.type})`;
|
|
57623
57632
|
const { attachmentsOf: attachmentsOf3, titledContent: titledContent2 } = await Promise.resolve().then(() => (init_wire(), wire_exports));
|
|
@@ -63143,7 +63152,7 @@ var Intake = class {
|
|
|
63143
63152
|
case "Undo":
|
|
63144
63153
|
return this.onUndo(activity, actor, { trusted });
|
|
63145
63154
|
case "Create":
|
|
63146
|
-
return this.onCreate(activity, actor);
|
|
63155
|
+
return this.onCreate(activity, actor, { trusted });
|
|
63147
63156
|
case "Accept":
|
|
63148
63157
|
return this.onAccept(activity, actor, { trusted });
|
|
63149
63158
|
case "Like":
|
|
@@ -64122,7 +64131,7 @@ __export(social_exports, {
|
|
|
64122
64131
|
});
|
|
64123
64132
|
init_node_crypto();
|
|
64124
64133
|
init_wire();
|
|
64125
|
-
async function lookupWebFinger(acct) {
|
|
64134
|
+
async function lookupWebFinger(acct, { fetchImpl = null } = {}) {
|
|
64126
64135
|
const clean = String(acct || "").replace(/^acct:/, "");
|
|
64127
64136
|
const at = clean.lastIndexOf("@");
|
|
64128
64137
|
if (at < 1) return null;
|
|
@@ -64130,9 +64139,8 @@ async function lookupWebFinger(acct) {
|
|
|
64130
64139
|
if (!host || /[/\\?#]/.test(host)) return null;
|
|
64131
64140
|
const { safeFetch: safeFetch2, readCapped: readCapped3 } = await Promise.resolve().then(() => (init_safefetch(), safefetch_exports));
|
|
64132
64141
|
const url = `https://${host}/.well-known/webfinger?resource=${encodeURIComponent(`acct:${clean}`)}`;
|
|
64133
|
-
const
|
|
64134
|
-
|
|
64135
|
-
}).catch(() => null);
|
|
64142
|
+
const headers = { accept: "application/jrd+json, application/json" };
|
|
64143
|
+
const res = await (fetchImpl ? fetchImpl(url, { headers }) : safeFetch2(url, { headers })).catch(() => null);
|
|
64136
64144
|
if (!res || res.status >= 400) return null;
|
|
64137
64145
|
let jrd2;
|
|
64138
64146
|
try {
|
|
@@ -64168,13 +64176,15 @@ async function resolveHandle(agent2, handle7) {
|
|
|
64168
64176
|
const clean = String(handle7 || "").replace(/^@/, "");
|
|
64169
64177
|
if (!/^[^@]+@[^@]+$/.test(clean)) throw new Error("handle must look like user@host");
|
|
64170
64178
|
if (agent2.store.isBlocked("https://" + clean.split("@")[1] + "/")) throw new Error("domain is blocked");
|
|
64171
|
-
const
|
|
64179
|
+
const remote = agent2.intake?.deliverer?.signedFetch ? (u, i) => agent2.intake.deliverer.signedFetch(u, i) : null;
|
|
64180
|
+
const lookup2 = (a) => lookupWebFinger(a, { fetchImpl: remote });
|
|
64181
|
+
const jrd2 = await lookup2("acct:" + clean);
|
|
64172
64182
|
const self2 = selfLink(jrd2);
|
|
64173
64183
|
if (!self2?.href) throw new Error(`webfinger found no actor for ${clean}`);
|
|
64174
64184
|
const doc = await agent2.intake.fetchAP(self2.href);
|
|
64175
64185
|
if (!doc?.id) throw new Error(`actor document unusable for ${clean}`);
|
|
64176
64186
|
if (agent2.store.isBlocked(doc.id)) throw new Error("actor is blocked");
|
|
64177
|
-
await confirmDelegation(clean.slice(clean.lastIndexOf("@") + 1), doc);
|
|
64187
|
+
await confirmDelegation(clean.slice(clean.lastIndexOf("@") + 1), doc, lookup2);
|
|
64178
64188
|
await cacheCounts(agent2, doc);
|
|
64179
64189
|
return doc;
|
|
64180
64190
|
}
|
|
@@ -66229,8 +66239,11 @@ var MastoApi = class _MastoApi {
|
|
|
66229
66239
|
});
|
|
66230
66240
|
return this._push;
|
|
66231
66241
|
}
|
|
66242
|
+
// The host in the owner's own address: the gateway's when the identity is
|
|
66243
|
+
// fronted (its documents still live on the pod, but its name does not).
|
|
66232
66244
|
get host() {
|
|
66233
|
-
|
|
66245
|
+
if (!this.urls) return "unconfigured.invalid";
|
|
66246
|
+
return new URL(this.urls.publicHome || this.urls.base).host;
|
|
66234
66247
|
}
|
|
66235
66248
|
// Where the live feed is, as the CLIENT must address it: this agent's own
|
|
66236
66249
|
// origin, taken from the request, not the pod's host. An instance document
|
|
@@ -66844,9 +66857,10 @@ var PodTransport = class {
|
|
|
66844
66857
|
return serialize(doc, g, url, "text/turtle");
|
|
66845
66858
|
}
|
|
66846
66859
|
async setAcl(targetUrl, publicModes, opts = {}) {
|
|
66847
|
-
const
|
|
66860
|
+
const podTarget = this.toPod ? this.toPod(targetUrl) : targetUrl;
|
|
66861
|
+
const url = await this.aclUrlFor(podTarget);
|
|
66848
66862
|
if (!await this.aclWritable(url)) return null;
|
|
66849
|
-
return this.put(url, this.aclDoc(
|
|
66863
|
+
return this.put(url, this.aclDoc(podTarget, publicModes, { ...opts, aclUrl: url }), "text/turtle");
|
|
66850
66864
|
}
|
|
66851
66865
|
// Child documents of an LDP container (URLs under it, excluding aux docs).
|
|
66852
66866
|
// Revalidated: the inbox is polled every couple of minutes and is usually
|
|
@@ -67164,14 +67178,16 @@ async function cacheOpenedKeys(actorUrl, keysRecord) {
|
|
|
67164
67178
|
return keys;
|
|
67165
67179
|
}
|
|
67166
67180
|
var keyCacheKey = (actorUrl) => `signing-keys:${actorUrl}`;
|
|
67181
|
+
var podActorOf = (urls) => urls.toPod ? urls.toPod(urls.actor) : urls.actor;
|
|
67167
67182
|
async function loadKeysFromPod(remote, urls) {
|
|
67168
|
-
const
|
|
67183
|
+
const podActor = podActorOf(urls);
|
|
67184
|
+
const cached = await kvGet(keyCacheKey(podActor)).catch(() => null);
|
|
67169
67185
|
if (isOpenedKey(cached)) return fromCache(cached);
|
|
67170
|
-
if (cached?.rsa?.privatePem) return cacheOpenedKeys(
|
|
67186
|
+
if (cached?.rsa?.privatePem) return cacheOpenedKeys(podActor, cached);
|
|
67171
67187
|
const doc = await readWrappedKeys(remote, urls);
|
|
67172
67188
|
if (isKeyEnvelope(doc)) throw new KeyPasswordNeeded();
|
|
67173
67189
|
if (!doc || !doc.rsa) throw new Error("no signing key on the pod \u2014 sign up did not finish");
|
|
67174
|
-
return cacheOpenedKeys(
|
|
67190
|
+
return cacheOpenedKeys(podActor, doc);
|
|
67175
67191
|
}
|
|
67176
67192
|
|
|
67177
67193
|
// lib/core/deliver.mjs
|
|
@@ -69600,6 +69616,7 @@ var AcctFeed = class {
|
|
|
69600
69616
|
};
|
|
69601
69617
|
|
|
69602
69618
|
// web/app/agent.mjs
|
|
69619
|
+
init_urls();
|
|
69603
69620
|
var originAuthorities = (host) => ({
|
|
69604
69621
|
set: /* @__PURE__ */ new Set([String(host || "").toLowerCase()]),
|
|
69605
69622
|
has(authority) {
|
|
@@ -69724,7 +69741,7 @@ var BrowserAgent = class _BrowserAgent {
|
|
|
69724
69741
|
if (oidc) {
|
|
69725
69742
|
session = { fetch: (u, i) => oidc.fetch(u, i) };
|
|
69726
69743
|
webId = oidc.webId;
|
|
69727
|
-
remotePod =
|
|
69744
|
+
remotePod = podBaseOfWebId(webId);
|
|
69728
69745
|
} else {
|
|
69729
69746
|
const dpop = await makeDpopSession(credential);
|
|
69730
69747
|
session = { fetch: (u, i) => dpop.fetch(u, i) };
|
|
@@ -69742,8 +69759,11 @@ var BrowserAgent = class _BrowserAgent {
|
|
|
69742
69759
|
const cfg = config || this.store.getConfig();
|
|
69743
69760
|
if (!cfg) throw new Error("no account config on this pod \u2014 sign up first");
|
|
69744
69761
|
this.store.setConfig({ ...this.store.getConfig() || {}, ...cfg, root });
|
|
69745
|
-
const keys = keysRecord ? await importSigningKey(keysRecord) : await loadKeysFromPod(this.remote, this.urls);
|
|
69746
69762
|
config = this.store.getConfig();
|
|
69763
|
+
const publicBase = config.gateway?.frontActor ? config.gateway.frontActor.replace(/ap\/actor\/?$/, "") : null;
|
|
69764
|
+
this.urls = apUrls2(remotePod, root, { publicBase });
|
|
69765
|
+
if (this.urls.toPod) this.remote.setUrlMap(this.urls.toPod);
|
|
69766
|
+
const keys = keysRecord ? await importSigningKey(keysRecord) : await loadKeysFromPod(this.remote, this.urls);
|
|
69747
69767
|
this.deliverer = new RelayDeliverer({
|
|
69748
69768
|
passive: true,
|
|
69749
69769
|
store: this.store,
|
|
@@ -69764,7 +69784,11 @@ var BrowserAgent = class _BrowserAgent {
|
|
|
69764
69784
|
deliverer: this.deliverer,
|
|
69765
69785
|
publicKeyPem: keys.rsaPublicPem,
|
|
69766
69786
|
assertionKey: null,
|
|
69767
|
-
log: this.log
|
|
69787
|
+
log: this.log,
|
|
69788
|
+
// Who a post names, resolved — the same lookup the installed agent
|
|
69789
|
+
// gives its publisher. Without it no mention from the browser ever
|
|
69790
|
+
// resolved: a direct message went to nobody, a mention notified no one.
|
|
69791
|
+
resolveMention: (h) => resolveHandle(this, h)
|
|
69768
69792
|
});
|
|
69769
69793
|
this.atproto = new BrowserAtproto({ store: this.store, actorId: this.urls.actor, log: this.log });
|
|
69770
69794
|
this.publisher.atproto = this.atproto;
|
|
@@ -69864,7 +69888,7 @@ var BrowserAgent = class _BrowserAgent {
|
|
|
69864
69888
|
const rec = await generateKeys();
|
|
69865
69889
|
rec.mintedFor = this.urls.actor;
|
|
69866
69890
|
await writeWrappedKeys(this.remote, this.urls, await wrapKeys(rec, password));
|
|
69867
|
-
const keys = await cacheOpenedKeys(this.urls
|
|
69891
|
+
const keys = await cacheOpenedKeys(podActorOf(this.urls), rec);
|
|
69868
69892
|
this.publisher.publicKeyPem = keys.rsaPublicPem;
|
|
69869
69893
|
this.deliverer.rsaPrivate = keys.rsaPrivate;
|
|
69870
69894
|
await this.publisher.publishProfile();
|