fedipod-server 0.18.0 → 0.19.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/c2s.mjs +19 -5
- package/lib/client/masto/index.mjs +9 -1
- package/lib/client/masto/render.mjs +5 -3
- package/lib/client/masto/statuses.mjs +7 -4
- package/lib/client/oidc-auth.mjs +10 -3
- package/lib/core/as2.mjs +1 -1
- package/lib/core/deliver.mjs +24 -1
- package/lib/core/intake/activities.mjs +54 -0
- package/lib/core/intake/index.mjs +2 -0
- package/lib/core/intake/notes.mjs +7 -0
- package/lib/core/publisher/collections.mjs +7 -4
- package/lib/core/publisher/index.mjs +33 -25
- package/lib/core/publisher/notes.mjs +76 -11
- package/lib/core/publisher/questions.mjs +10 -7
- package/lib/core/store.mjs +9 -0
- package/lib/core/wire.mjs +52 -8
- package/lib/device/admin/static.mjs +2 -2
- package/lib/device/admin/surface.mjs +3 -2
- package/lib/gateway/front-core.mjs +9 -3
- package/lib/gateway/httpsig.mjs +7 -3
- package/package.json +1 -1
- package/run-agent.mjs +2 -0
- package/web/app/agent.mjs +2 -0
- package/web/app/dist/sw.js +284 -69
- package/web/app/dist/sw.js.map +3 -3
package/web/app/dist/sw.js
CHANGED
|
@@ -9589,6 +9589,7 @@ __export(wire_exports, {
|
|
|
9589
9589
|
AS_CTX: () => AS_CTX,
|
|
9590
9590
|
DEFAULT_ROOT: () => DEFAULT_ROOT,
|
|
9591
9591
|
FOLLOWERS_PAGE_SIZE: () => FOLLOWERS_PAGE_SIZE,
|
|
9592
|
+
HASHTAG_RE: () => HASHTAG_RE,
|
|
9592
9593
|
MENTION_RE: () => MENTION_RE,
|
|
9593
9594
|
OUTBOX_PAGE_SIZE: () => OUTBOX_PAGE_SIZE,
|
|
9594
9595
|
PUBLIC: () => PUBLIC,
|
|
@@ -9615,6 +9616,7 @@ __export(wire_exports, {
|
|
|
9615
9616
|
followersPageId: () => followersPageId,
|
|
9616
9617
|
followersPaging: () => followersPaging,
|
|
9617
9618
|
followsNeedApproval: () => followsNeedApproval,
|
|
9619
|
+
hashtagsIn: () => hashtagsIn,
|
|
9618
9620
|
hostMeta: () => hostMeta,
|
|
9619
9621
|
jrd: () => jrd,
|
|
9620
9622
|
likeActivity: () => likeActivity,
|
|
@@ -9627,11 +9629,13 @@ __export(wire_exports, {
|
|
|
9627
9629
|
orderedCollection: () => orderedCollection,
|
|
9628
9630
|
outboxHead: () => outboxHead,
|
|
9629
9631
|
outboxItemId: () => outboxItemId,
|
|
9632
|
+
outboxLocalItem: () => outboxLocalItem,
|
|
9630
9633
|
outboxPage: () => outboxPage,
|
|
9631
9634
|
outboxPageCount: () => outboxPageCount,
|
|
9632
9635
|
outboxPageId: () => outboxPageId,
|
|
9633
9636
|
outboxPages: () => outboxPages,
|
|
9634
9637
|
outboxPaging: () => outboxPaging,
|
|
9638
|
+
outboxWireItem: () => outboxWireItem,
|
|
9635
9639
|
pageItems: () => pageItems,
|
|
9636
9640
|
profilePageHtml: () => profilePageHtml,
|
|
9637
9641
|
publicHandle: () => publicHandle,
|
|
@@ -9639,6 +9643,7 @@ __export(wire_exports, {
|
|
|
9639
9643
|
rejectActivity: () => rejectActivity,
|
|
9640
9644
|
repliesId: () => repliesId,
|
|
9641
9645
|
sanitizeHtml: () => sanitizeHtml,
|
|
9646
|
+
tagHref: () => tagHref,
|
|
9642
9647
|
titledContent: () => titledContent,
|
|
9643
9648
|
tombstoneDoc: () => tombstoneDoc,
|
|
9644
9649
|
undoActivity: () => undoActivity,
|
|
@@ -9926,7 +9931,7 @@ function outboxPage(outboxId, n, items) {
|
|
|
9926
9931
|
id: outboxPageId(outboxId, n),
|
|
9927
9932
|
type: "OrderedCollectionPage",
|
|
9928
9933
|
partOf: outboxId,
|
|
9929
|
-
orderedItems: items,
|
|
9934
|
+
orderedItems: items.map(outboxWireItem),
|
|
9930
9935
|
...n > 1 ? { next: outboxPageId(outboxId, n - 1) } : {}
|
|
9931
9936
|
};
|
|
9932
9937
|
}
|
|
@@ -10046,8 +10051,27 @@ function attachmentsOf(note) {
|
|
|
10046
10051
|
function mentionsIn(text) {
|
|
10047
10052
|
return [...new Set(String(text).match(MENTION_RE) || [])].map((m) => m.slice(1));
|
|
10048
10053
|
}
|
|
10049
|
-
function
|
|
10054
|
+
function hashtagsIn(text) {
|
|
10055
|
+
const seen = /* @__PURE__ */ new Map();
|
|
10056
|
+
for (const m of String(text).matchAll(HASHTAG_RE)) {
|
|
10057
|
+
const key = m[2].toLowerCase();
|
|
10058
|
+
if (!seen.has(key)) seen.set(key, m[2]);
|
|
10059
|
+
}
|
|
10060
|
+
return [...seen.values()];
|
|
10061
|
+
}
|
|
10062
|
+
function tagHref(urls, name) {
|
|
10063
|
+
return urls.profileHtml + "?tag=" + encodeURIComponent(String(name).toLowerCase());
|
|
10064
|
+
}
|
|
10065
|
+
function contentHtml(text, mentions = [], hashtags = []) {
|
|
10050
10066
|
let html = String(text).replace(/[&<>]/g, (c) => HTML_ESCAPES[c]);
|
|
10067
|
+
if (hashtags.length) {
|
|
10068
|
+
const byName = new Map(hashtags.filter((h) => h?.name && h?.href).map((h) => [h.name.toLowerCase(), h.href]));
|
|
10069
|
+
html = html.replace(HASHTAG_RE, (all, lead, name) => {
|
|
10070
|
+
const href = byName.get(name.toLowerCase());
|
|
10071
|
+
if (!href) return all;
|
|
10072
|
+
return `${lead}<a href="${String(href).replace(/[&<>"]/g, (c) => HTML_ESCAPES[c] || """)}" class="mention hashtag" rel="tag">#<span>${name}</span></a>`;
|
|
10073
|
+
});
|
|
10074
|
+
}
|
|
10051
10075
|
for (const m of mentions) {
|
|
10052
10076
|
if (!m?.handle || !m?.actor) continue;
|
|
10053
10077
|
const href = String(m.page || m.actor).replace(/[&<>"]/g, (c) => HTML_ESCAPES[c] || """);
|
|
@@ -10075,28 +10099,35 @@ function noteDoc({
|
|
|
10075
10099
|
mentions = [],
|
|
10076
10100
|
visibility = "public",
|
|
10077
10101
|
summary = null,
|
|
10102
|
+
sensitive = false,
|
|
10078
10103
|
updated = null,
|
|
10079
|
-
container = null
|
|
10104
|
+
container = null,
|
|
10105
|
+
also = []
|
|
10080
10106
|
}) {
|
|
10081
10107
|
const id = (container || urls.notes) + slug;
|
|
10082
10108
|
const who = mentions.map((m) => m.actor);
|
|
10083
|
-
const
|
|
10109
|
+
const extra = visibility === "direct" ? [] : also.filter((a) => a && !who.includes(a));
|
|
10110
|
+
const addressed = addressing(urls, visibility, [...who, ...extra]);
|
|
10111
|
+
const hashtags = hashtagsIn(content).map((name) => ({ name, href: tagHref(urls, name) }));
|
|
10084
10112
|
const note = {
|
|
10085
10113
|
"@context": AS_CTX,
|
|
10086
10114
|
id,
|
|
10087
10115
|
type: "Note",
|
|
10088
10116
|
attributedTo: urls.actor,
|
|
10089
|
-
content: contentHtml(content, mentions),
|
|
10117
|
+
content: contentHtml(content, mentions, hashtags),
|
|
10090
10118
|
published,
|
|
10091
10119
|
to: addressed.to,
|
|
10092
10120
|
cc: addressed.cc,
|
|
10093
10121
|
replies: repliesId(id)
|
|
10094
10122
|
};
|
|
10095
10123
|
if (summary) note.summary = summary;
|
|
10124
|
+
if (summary || sensitive) note.sensitive = true;
|
|
10096
10125
|
if (updated) note.updated = updated;
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10126
|
+
const tag = [
|
|
10127
|
+
...mentions.map((m) => ({ type: "Mention", href: m.actor, name: "@" + m.handle })),
|
|
10128
|
+
...hashtags.map((h) => ({ type: "Hashtag", href: h.href, name: "#" + h.name }))
|
|
10129
|
+
];
|
|
10130
|
+
if (tag.length) note.tag = tag;
|
|
10100
10131
|
if (inReplyTo) note.inReplyTo = inReplyTo;
|
|
10101
10132
|
if (attachments?.length) {
|
|
10102
10133
|
note.attachment = attachments.map((a) => ({
|
|
@@ -10274,7 +10305,7 @@ function addRemoveActivity({ urls, type, object, target, serial }) {
|
|
|
10274
10305
|
target
|
|
10275
10306
|
};
|
|
10276
10307
|
}
|
|
10277
|
-
var import_sanitize_html, AS_CTX, SEC_CTX, PUBLIC, DEFAULT_ROOT, assertionKeyId, OUTBOX_PAGE_SIZE, outboxPageId, outboxPageCount, outboxItemId, FOLLOWERS_PAGE_SIZE, followersPageId, followersPageCount, ALLOWED_TAGS, ALLOWED_ATTRS, MAX_ATTACHMENTS, MAX_ATTACHMENT_URL, attachmentUrl, HTML_ESCAPES, MENTION_RE;
|
|
10308
|
+
var import_sanitize_html, AS_CTX, SEC_CTX, PUBLIC, DEFAULT_ROOT, assertionKeyId, OUTBOX_PAGE_SIZE, outboxPageId, outboxPageCount, outboxItemId, outboxWireItem, outboxLocalItem, FOLLOWERS_PAGE_SIZE, followersPageId, followersPageCount, ALLOWED_TAGS, ALLOWED_ATTRS, MAX_ATTACHMENTS, MAX_ATTACHMENT_URL, attachmentUrl, HTML_ESCAPES, MENTION_RE, HASHTAG_RE;
|
|
10278
10309
|
var init_wire = __esm({
|
|
10279
10310
|
"lib/core/wire.mjs"() {
|
|
10280
10311
|
init_urls();
|
|
@@ -10289,6 +10320,8 @@ var init_wire = __esm({
|
|
|
10289
10320
|
outboxPageId = (outboxId, n) => `${outboxId}-${n}`;
|
|
10290
10321
|
outboxPageCount = (total) => Math.max(1, Math.ceil(total / OUTBOX_PAGE_SIZE));
|
|
10291
10322
|
outboxItemId = (i) => typeof i === "string" ? i : i?.id || null;
|
|
10323
|
+
outboxWireItem = (i) => typeof i === "string" ? createActivityId(i) : i;
|
|
10324
|
+
outboxLocalItem = (i) => typeof i === "string" && i.endsWith("-create") ? i.slice(0, -"-create".length) : i;
|
|
10292
10325
|
FOLLOWERS_PAGE_SIZE = 20;
|
|
10293
10326
|
followersPageId = (id, n) => `${id}-${n}`;
|
|
10294
10327
|
followersPageCount = (total) => Math.max(1, Math.ceil(total / FOLLOWERS_PAGE_SIZE));
|
|
@@ -10329,6 +10362,7 @@ var init_wire = __esm({
|
|
|
10329
10362
|
};
|
|
10330
10363
|
HTML_ESCAPES = { "&": "&", "<": "<", ">": ">" };
|
|
10331
10364
|
MENTION_RE = /@([A-Za-z0-9_.-]+)@([A-Za-z0-9.-]+\.[A-Za-z]{2,}(?::\d+)?)/g;
|
|
10365
|
+
HASHTAG_RE = /(^|[^\/\w)])#([\p{L}\p{N}_]*[\p{L}_][\p{L}\p{N}_]*)/gu;
|
|
10332
10366
|
}
|
|
10333
10367
|
});
|
|
10334
10368
|
|
|
@@ -32650,11 +32684,12 @@ async function verifyHttpSignature(request, { documentLoader, keyCache, timeWind
|
|
|
32650
32684
|
} catch {
|
|
32651
32685
|
result = null;
|
|
32652
32686
|
}
|
|
32687
|
+
const method = request.headers.has("signature-input") ? "rfc9421" : "draft-cavage";
|
|
32653
32688
|
if (result?.verified) {
|
|
32654
32689
|
const key = result.key;
|
|
32655
32690
|
return {
|
|
32656
32691
|
verified: true,
|
|
32657
|
-
method
|
|
32692
|
+
method,
|
|
32658
32693
|
keyId: key.id?.href ?? null,
|
|
32659
32694
|
actor: key.ownerId?.href ?? null,
|
|
32660
32695
|
reason: null,
|
|
@@ -32675,7 +32710,7 @@ async function verifyHttpSignature(request, { documentLoader, keyCache, timeWind
|
|
|
32675
32710
|
if (kind === "keyFetchError") {
|
|
32676
32711
|
return {
|
|
32677
32712
|
verified: false,
|
|
32678
|
-
method
|
|
32713
|
+
method,
|
|
32679
32714
|
keyId: null,
|
|
32680
32715
|
actor: null,
|
|
32681
32716
|
reason: "key-unfetchable",
|
|
@@ -32684,7 +32719,7 @@ async function verifyHttpSignature(request, { documentLoader, keyCache, timeWind
|
|
|
32684
32719
|
}
|
|
32685
32720
|
return {
|
|
32686
32721
|
verified: false,
|
|
32687
|
-
method
|
|
32722
|
+
method,
|
|
32688
32723
|
keyId: null,
|
|
32689
32724
|
actor: null,
|
|
32690
32725
|
reason: "bad-signature",
|
|
@@ -34481,6 +34516,14 @@ var PodStore = class {
|
|
|
34481
34516
|
// — not from the pod, not from the RDF. Unverified entries are dropped first
|
|
34482
34517
|
// now, so a flood can only ever evict itself, and a stranger's genuine
|
|
34483
34518
|
// favourite still shows up, which is what recording them at all is for.
|
|
34519
|
+
// What an Undo unsays: the favourite or boost it names, matched by a
|
|
34520
|
+
// predicate on what was recorded. Returns how many went.
|
|
34521
|
+
removeNotifications(pred) {
|
|
34522
|
+
const all = this.getNotifications();
|
|
34523
|
+
const kept = all.filter((n) => !pred(n));
|
|
34524
|
+
if (kept.length !== all.length) this.write("notifications.json", kept);
|
|
34525
|
+
return all.length - kept.length;
|
|
34526
|
+
}
|
|
34484
34527
|
addNotification(n) {
|
|
34485
34528
|
const all = this.getNotifications();
|
|
34486
34529
|
const id = node_crypto_default.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0, 16);
|
|
@@ -45433,8 +45476,9 @@ async function publishOutbox(publisher, outbox, { acls = false, force = false }
|
|
|
45433
45476
|
);
|
|
45434
45477
|
return wrote;
|
|
45435
45478
|
}
|
|
45436
|
-
function readPublishedOutbox(publisher) {
|
|
45437
|
-
|
|
45479
|
+
async function readPublishedOutbox(publisher) {
|
|
45480
|
+
const items = await readPublished(publisher.remote, publisher.urls);
|
|
45481
|
+
return Array.isArray(items) ? items.map(outboxLocalItem) : items;
|
|
45438
45482
|
}
|
|
45439
45483
|
async function publishFollowers(publisher, actors, { acls = false, force = false } = {}) {
|
|
45440
45484
|
const { urls } = publisher;
|
|
@@ -55941,6 +55985,35 @@ async function mentionsFor(publisher, content, inReplyTo) {
|
|
|
55941
55985
|
}
|
|
55942
55986
|
return mentions;
|
|
55943
55987
|
}
|
|
55988
|
+
async function replyTarget(publisher, inReplyTo, mentions = [], visibility = "public") {
|
|
55989
|
+
if (!inReplyTo || visibility === "direct" || !publisher.resolveActor) return null;
|
|
55990
|
+
const parent = publisher.store.getStatuses().find((s) => s.noteId === inReplyTo);
|
|
55991
|
+
let actor = parent?.actor || null;
|
|
55992
|
+
if (!parent) {
|
|
55993
|
+
const doc2 = await publisher.resolveActor(inReplyTo).catch(() => null);
|
|
55994
|
+
const by = doc2?.attributedTo;
|
|
55995
|
+
actor = typeof by === "string" ? by : (Array.isArray(by) ? by[0] : by)?.id || null;
|
|
55996
|
+
if (typeof actor === "object") actor = actor?.id || null;
|
|
55997
|
+
}
|
|
55998
|
+
if (!actor || actor === publisher.urls.actor) return null;
|
|
55999
|
+
if (mentions.some((m) => m.actor === actor)) return null;
|
|
56000
|
+
if (publisher.store.isBlocked?.(actor)) return null;
|
|
56001
|
+
const doc = await publisher.resolveActor(actor).catch(() => null);
|
|
56002
|
+
const inbox = doc?.endpoints?.sharedInbox || doc?.inbox || null;
|
|
56003
|
+
if (!inbox) publisher.log(`reply: no inbox found for ${actor} \u2014 they get it only through followers`);
|
|
56004
|
+
return { actor, inbox };
|
|
56005
|
+
}
|
|
56006
|
+
async function inboxesFor(publisher, actorIds = []) {
|
|
56007
|
+
const out = [];
|
|
56008
|
+
for (const id of [...new Set(actorIds.filter((a) => typeof a === "string" && a && a !== publisher.urls.actor))]) {
|
|
56009
|
+
if (!publisher.resolveActor) break;
|
|
56010
|
+
const doc = await publisher.resolveActor(id).catch(() => null);
|
|
56011
|
+
const inbox = doc?.endpoints?.sharedInbox || doc?.inbox;
|
|
56012
|
+
if (inbox) out.push(inbox);
|
|
56013
|
+
else publisher.log(`addressed actor ${id} has no inbox the agent can find \u2014 not delivered to`);
|
|
56014
|
+
}
|
|
56015
|
+
return out;
|
|
56016
|
+
}
|
|
55944
56017
|
function assertDirectAddressed(content, mentions) {
|
|
55945
56018
|
const wanted = [...new Set(mentionsIn(content))];
|
|
55946
56019
|
const missing = wanted.filter((h) => !mentions.some((m) => m.handle === h));
|
|
@@ -55966,7 +56039,7 @@ function rowContent(obj) {
|
|
|
55966
56039
|
const esc = (v) => String(v).replace(/[&<>"]/gu, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
55967
56040
|
return `<p><a href="${esc(obj?.id || "")}">${esc(obj?.type || "object")}</a></p>`;
|
|
55968
56041
|
}
|
|
55969
|
-
async function publishNote(publisher, content, { inReplyTo, attachments, visibility = "public", spoilerText = null, slug: wanted = null } = {}) {
|
|
56042
|
+
async function publishNote(publisher, content, { inReplyTo, attachments, visibility = "public", spoilerText = null, sensitive = false, slug: wanted = null, also = [], deliverTo = [] } = {}) {
|
|
55970
56043
|
const { urls } = publisher;
|
|
55971
56044
|
const priv = visibility === "private" || visibility === "direct";
|
|
55972
56045
|
if (priv) {
|
|
@@ -55977,6 +56050,7 @@ async function publishNote(publisher, content, { inReplyTo, attachments, visibil
|
|
|
55977
56050
|
const slug = await slugFor(publisher, priv ? urls.privateNotes : urls.notes, wanted, published);
|
|
55978
56051
|
const mentions = await publisher._mentionsFor(content, inReplyTo);
|
|
55979
56052
|
if (visibility === "direct") assertDirectAddressed(content, mentions);
|
|
56053
|
+
const reply = await replyTarget(publisher, inReplyTo, mentions, visibility);
|
|
55980
56054
|
const note = noteDoc({
|
|
55981
56055
|
urls,
|
|
55982
56056
|
slug,
|
|
@@ -55987,8 +56061,11 @@ async function publishNote(publisher, content, { inReplyTo, attachments, visibil
|
|
|
55987
56061
|
mentions,
|
|
55988
56062
|
visibility,
|
|
55989
56063
|
summary: spoilerText,
|
|
55990
|
-
|
|
56064
|
+
sensitive,
|
|
56065
|
+
container: priv ? urls.privateNotes : urls.notes,
|
|
56066
|
+
also: [...also, ...reply ? [reply.actor] : []]
|
|
55991
56067
|
});
|
|
56068
|
+
const addressedInboxes = await inboxesFor(publisher, [...also, ...deliverTo]);
|
|
55992
56069
|
await write4(publisher.remote, note.id, note);
|
|
55993
56070
|
await writeEmptyReplies(
|
|
55994
56071
|
publisher.remote,
|
|
@@ -56007,8 +56084,9 @@ async function publishNote(publisher, content, { inReplyTo, attachments, visibil
|
|
|
56007
56084
|
text: content,
|
|
56008
56085
|
visibility,
|
|
56009
56086
|
...spoilerText ? { spoiler: spoilerText } : {},
|
|
56087
|
+
...note.sensitive ? { sensitive: true } : {},
|
|
56010
56088
|
...attachments?.length ? { attachments } : {},
|
|
56011
|
-
...note
|
|
56089
|
+
...rowTags(note)
|
|
56012
56090
|
});
|
|
56013
56091
|
if (await publisher.store.commit?.() === false) publisher.log(`post published but its timeline row was refused: ${note.id}`);
|
|
56014
56092
|
const create = createActivity(note, urls);
|
|
@@ -56016,7 +56094,9 @@ async function publishNote(publisher, content, { inReplyTo, attachments, visibil
|
|
|
56016
56094
|
const contacts = publisher.store.getContacts();
|
|
56017
56095
|
const inboxes = [...new Set([
|
|
56018
56096
|
...visibility === "direct" ? [] : contacts.followers.map((f) => f.sharedInbox || f.inbox),
|
|
56019
|
-
...mentions.map((m) => m.inbox)
|
|
56097
|
+
...mentions.map((m) => m.inbox),
|
|
56098
|
+
reply?.inbox,
|
|
56099
|
+
...addressedInboxes
|
|
56020
56100
|
].filter(Boolean))];
|
|
56021
56101
|
await publisher.deliverer.deliverToAll(inboxes, create);
|
|
56022
56102
|
publisher.log(`note published: ${note.id} \u2192 ${inboxes.length} inbox(es)`);
|
|
@@ -56035,7 +56115,7 @@ async function publishNote(publisher, content, { inReplyTo, attachments, visibil
|
|
|
56035
56115
|
}
|
|
56036
56116
|
return note;
|
|
56037
56117
|
}
|
|
56038
|
-
async function publishObject(publisher, object, { visibility = "public", slug: wanted = null } = {}) {
|
|
56118
|
+
async function publishObject(publisher, object, { visibility = "public", slug: wanted = null, also = [], deliverTo = [] } = {}) {
|
|
56039
56119
|
const { urls } = publisher;
|
|
56040
56120
|
const priv = visibility === "private" || visibility === "direct";
|
|
56041
56121
|
if (priv) {
|
|
@@ -56047,11 +56127,13 @@ async function publishObject(publisher, object, { visibility = "public", slug: w
|
|
|
56047
56127
|
const pod = publisher.config?.remotePod || urls.base;
|
|
56048
56128
|
const ownId = typeof object.id === "string" && /^https?:\/\//u.test(object.id) && object.id.startsWith(pod) ? object.id : null;
|
|
56049
56129
|
const name = await slugFor(publisher, container, wanted, published);
|
|
56050
|
-
const addressed = addressing(urls, visibility);
|
|
56130
|
+
const addressed = addressing(urls, visibility, visibility === "direct" ? also : []);
|
|
56131
|
+
if (visibility !== "direct") addressed.cc = [.../* @__PURE__ */ new Set([...addressed.cc, ...also])];
|
|
56051
56132
|
let doc = null;
|
|
56052
56133
|
const id = ownId || container + name;
|
|
56053
56134
|
if (!ownId) {
|
|
56054
|
-
|
|
56135
|
+
const { bto, bcc, ...sent } = object;
|
|
56136
|
+
doc = { ...sent, id, attributedTo: urls.actor, published: object.published || published, to: addressed.to, cc: addressed.cc };
|
|
56055
56137
|
if (!doc["@context"]) doc["@context"] = AS_CTX;
|
|
56056
56138
|
if (typeof doc.content === "string") doc.content = sanitizeHtml(doc.content);
|
|
56057
56139
|
await write4(publisher.remote, id, doc);
|
|
@@ -56081,12 +56163,21 @@ async function publishObject(publisher, object, { visibility = "public", slug: w
|
|
|
56081
56163
|
};
|
|
56082
56164
|
await writeCreate(publisher.remote, create.id, create);
|
|
56083
56165
|
const contacts = publisher.store.getContacts();
|
|
56084
|
-
const inboxes =
|
|
56166
|
+
const inboxes = [...new Set([
|
|
56167
|
+
...visibility === "direct" ? [] : contacts.followers.map((f) => f.sharedInbox || f.inbox),
|
|
56168
|
+
...await inboxesFor(publisher, [...also, ...deliverTo])
|
|
56169
|
+
].filter(Boolean))];
|
|
56085
56170
|
await publisher.deliverer.deliverToAll(inboxes, create);
|
|
56086
56171
|
publisher.log(`${row.type || "object"} published: ${id} \u2192 ${inboxes.length} inbox(es)`);
|
|
56087
56172
|
return { id, createId, copied: !ownId };
|
|
56088
56173
|
}
|
|
56089
|
-
|
|
56174
|
+
function rowTags(note) {
|
|
56175
|
+
const tags = [].concat(note.tag || []);
|
|
56176
|
+
const mentions = tags.filter((t) => t.type === "Mention").map((t) => ({ href: t.href, name: t.name }));
|
|
56177
|
+
const hashtags = tags.filter((t) => t.type === "Hashtag").map((t) => ({ name: String(t.name).replace(/^#/, ""), url: t.href }));
|
|
56178
|
+
return { ...mentions.length ? { mentions } : {}, ...hashtags.length ? { tags: hashtags } : {} };
|
|
56179
|
+
}
|
|
56180
|
+
async function updateNote(publisher, s, { content, spoilerText = null, sensitive = null, attachments = null } = {}) {
|
|
56090
56181
|
const { urls } = publisher;
|
|
56091
56182
|
const updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
56092
56183
|
const inText = new Set(mentionsIn(content));
|
|
@@ -56103,6 +56194,7 @@ async function updateNote(publisher, s, { content, spoilerText = null, attachmen
|
|
|
56103
56194
|
const atts = attachments ?? s.attachments ?? [];
|
|
56104
56195
|
const container = String(s.noteId).startsWith(urls.privateNotes) ? urls.privateNotes : urls.notes;
|
|
56105
56196
|
const slug = s.slug || String(s.noteId).slice(container.length);
|
|
56197
|
+
const reply = await replyTarget(publisher, s.inReplyTo, mentions, s.visibility || "public");
|
|
56106
56198
|
const note = noteDoc({
|
|
56107
56199
|
urls,
|
|
56108
56200
|
slug,
|
|
@@ -56113,8 +56205,10 @@ async function updateNote(publisher, s, { content, spoilerText = null, attachmen
|
|
|
56113
56205
|
mentions,
|
|
56114
56206
|
visibility: s.visibility || "public",
|
|
56115
56207
|
summary: spoilerText,
|
|
56208
|
+
sensitive: sensitive ?? !!s.sensitive,
|
|
56116
56209
|
updated,
|
|
56117
|
-
container
|
|
56210
|
+
container,
|
|
56211
|
+
also: reply ? [reply.actor] : []
|
|
56118
56212
|
});
|
|
56119
56213
|
await write4(publisher.remote, note.id, note);
|
|
56120
56214
|
await writeCreate(publisher.remote, createActivityId(note.id), createActivity(note, urls));
|
|
@@ -56123,14 +56217,17 @@ async function updateNote(publisher, s, { content, spoilerText = null, attachmen
|
|
|
56123
56217
|
text: content,
|
|
56124
56218
|
editedAt: updated,
|
|
56125
56219
|
spoiler: spoilerText || void 0,
|
|
56220
|
+
sensitive: note.sensitive || void 0,
|
|
56126
56221
|
attachments: atts.length ? atts : void 0,
|
|
56127
|
-
mentions: note
|
|
56222
|
+
mentions: rowTags(note).mentions,
|
|
56223
|
+
tags: rowTags(note).tags
|
|
56128
56224
|
});
|
|
56129
56225
|
const update = updateActivity(note, urls);
|
|
56130
56226
|
const contacts = publisher.store.getContacts();
|
|
56131
56227
|
const inboxes = [...new Set([
|
|
56132
56228
|
...s.visibility === "direct" ? [] : contacts.followers.map((f) => f.sharedInbox || f.inbox),
|
|
56133
|
-
...mentions.map((m) => m.inbox)
|
|
56229
|
+
...mentions.map((m) => m.inbox),
|
|
56230
|
+
reply?.inbox
|
|
56134
56231
|
].filter(Boolean))];
|
|
56135
56232
|
await publisher.deliverer.deliverToAll(inboxes, update);
|
|
56136
56233
|
publisher.log(`note edited: ${note.id} \u2192 ${inboxes.length} inbox(es)`);
|
|
@@ -56356,7 +56453,8 @@ async function publishQuestion(publisher, content, {
|
|
|
56356
56453
|
expiresAt = null,
|
|
56357
56454
|
inReplyTo = void 0,
|
|
56358
56455
|
visibility = "public",
|
|
56359
|
-
spoilerText = null
|
|
56456
|
+
spoilerText = null,
|
|
56457
|
+
sensitive = false
|
|
56360
56458
|
} = {}) {
|
|
56361
56459
|
const { urls } = publisher;
|
|
56362
56460
|
const priv = visibility === "private" || visibility === "direct";
|
|
@@ -56383,6 +56481,7 @@ async function publishQuestion(publisher, content, {
|
|
|
56383
56481
|
const slug = published.slice(0, 10) + "-" + node_crypto_default.randomBytes(4).toString("hex");
|
|
56384
56482
|
const mentions = await publisher._mentionsFor(content, inReplyTo);
|
|
56385
56483
|
if (visibility === "direct") assertDirectAddressed(content, mentions);
|
|
56484
|
+
const reply = await replyTarget(publisher, inReplyTo, mentions, visibility);
|
|
56386
56485
|
const poll = {
|
|
56387
56486
|
multiple: !!multiple,
|
|
56388
56487
|
expiresAt: expiresAt || null,
|
|
@@ -56391,7 +56490,7 @@ async function publishQuestion(publisher, content, {
|
|
|
56391
56490
|
votersCount: 0,
|
|
56392
56491
|
// Resolved once, here: a tally rewrite must not cost a webfinger lookup
|
|
56393
56492
|
// per vote for people the poll named.
|
|
56394
|
-
mentionInboxes: [...new Set(mentions.map((m) => m.inbox).filter(Boolean))]
|
|
56493
|
+
mentionInboxes: [...new Set([...mentions.map((m) => m.inbox), reply?.inbox].filter(Boolean))]
|
|
56395
56494
|
};
|
|
56396
56495
|
const question = questionDoc({
|
|
56397
56496
|
urls,
|
|
@@ -56403,6 +56502,8 @@ async function publishQuestion(publisher, content, {
|
|
|
56403
56502
|
mentions,
|
|
56404
56503
|
visibility,
|
|
56405
56504
|
summary: spoilerText,
|
|
56505
|
+
sensitive,
|
|
56506
|
+
also: reply ? [reply.actor] : [],
|
|
56406
56507
|
container: priv ? urls.privateNotes : urls.notes,
|
|
56407
56508
|
options: poll.options,
|
|
56408
56509
|
multiple: poll.multiple,
|
|
@@ -56427,8 +56528,10 @@ async function publishQuestion(publisher, content, {
|
|
|
56427
56528
|
visibility,
|
|
56428
56529
|
poll,
|
|
56429
56530
|
inReplyTo,
|
|
56531
|
+
...reply ? { replyActor: reply.actor } : {},
|
|
56430
56532
|
...spoilerText ? { spoiler: spoilerText } : {},
|
|
56431
|
-
...question.
|
|
56533
|
+
...question.sensitive ? { sensitive: true } : {},
|
|
56534
|
+
...rowTags(question)
|
|
56432
56535
|
});
|
|
56433
56536
|
if (await publisher.store.commit?.() === false) publisher.log(`poll published but its timeline row was refused: ${question.id}`);
|
|
56434
56537
|
const create = publisher._pollActivity("Create", question, createActivityId(question.id));
|
|
@@ -56507,8 +56610,10 @@ async function republishPoll(publisher, questionId, { closing = null } = {}) {
|
|
|
56507
56610
|
inReplyTo: s.inReplyTo,
|
|
56508
56611
|
attachments: [],
|
|
56509
56612
|
mentions,
|
|
56613
|
+
also: s.replyActor ? [s.replyActor] : [],
|
|
56510
56614
|
visibility: s.visibility || "public",
|
|
56511
56615
|
summary: s.spoiler || null,
|
|
56616
|
+
sensitive: !!s.sensitive,
|
|
56512
56617
|
container,
|
|
56513
56618
|
options: poll.options,
|
|
56514
56619
|
multiple: !!poll.multiple,
|
|
@@ -56580,6 +56685,7 @@ var Publisher = class {
|
|
|
56580
56685
|
log: log2 = console.log,
|
|
56581
56686
|
probeFetch = null,
|
|
56582
56687
|
resolveMention = null,
|
|
56688
|
+
resolveActor = null,
|
|
56583
56689
|
clientOrigin = null
|
|
56584
56690
|
}) {
|
|
56585
56691
|
this.config = config;
|
|
@@ -56594,6 +56700,7 @@ var Publisher = class {
|
|
|
56594
56700
|
if (this.urls.toPod && this.remote?.setUrlMap) this.remote.setUrlMap(this.urls.toPod);
|
|
56595
56701
|
this.probeFetch = probeFetch || ((u, i) => this.remote.probe(u, i));
|
|
56596
56702
|
this.resolveMention = resolveMention;
|
|
56703
|
+
this.resolveActor = resolveActor;
|
|
56597
56704
|
this.pollTimers = /* @__PURE__ */ new Map();
|
|
56598
56705
|
this.log = log2;
|
|
56599
56706
|
}
|
|
@@ -56619,19 +56726,17 @@ var Publisher = class {
|
|
|
56619
56726
|
if (gw.frontActor) return String(gw.frontActor).replace(/ap\/actor\/?$/u, "ap/outbox");
|
|
56620
56727
|
return String(gw.url).replace(/ap\/inbox\/?$/u, "ap/outbox");
|
|
56621
56728
|
}
|
|
56622
|
-
|
|
56729
|
+
// Every actor document this identity publishes, from one place: the
|
|
56730
|
+
// profile publish and a Move both write it, and a Move that rebuilt it
|
|
56731
|
+
// by hand dropped the gateway inbox, the outbox door and the collections.
|
|
56732
|
+
actorDocFor({ priv = false, moderators = null, inbox = null, movedTo = this.config.movedTo || null } = {}) {
|
|
56623
56733
|
const { urls } = this;
|
|
56624
|
-
|
|
56625
|
-
const priv = await this.privateReady() === true;
|
|
56626
|
-
const moderators = (this.config.moderators || []).length ? urls.moderators : null;
|
|
56627
|
-
const gw = this.config.gateway;
|
|
56628
|
-
const gwActive = gw && gw.url && gw.mode && gw.mode !== "off";
|
|
56629
|
-
const actorDoc2 = actorDoc({
|
|
56734
|
+
return actorDoc({
|
|
56630
56735
|
urls,
|
|
56631
56736
|
handle: publicHandle(this.config),
|
|
56632
56737
|
name: this.config.name,
|
|
56633
56738
|
publicKeyPem: this.publicKeyPem,
|
|
56634
|
-
movedTo
|
|
56739
|
+
movedTo,
|
|
56635
56740
|
kind: this.config.kind,
|
|
56636
56741
|
approveJoins: followsNeedApproval(this.config),
|
|
56637
56742
|
assertionKey: this.assertionKey,
|
|
@@ -56645,7 +56750,7 @@ var Publisher = class {
|
|
|
56645
56750
|
pendingFollowers: priv ? urls.pendingFollowers : null,
|
|
56646
56751
|
pendingFollowing: priv ? urls.pendingFollowing : null,
|
|
56647
56752
|
blocked: priv ? urls.blocked : null,
|
|
56648
|
-
inbox
|
|
56753
|
+
inbox,
|
|
56649
56754
|
// The agent's own outbox endpoint, where it is reachable: a client
|
|
56650
56755
|
// following the actor must arrive somewhere that will take a write.
|
|
56651
56756
|
// Otherwise the Gateway's outbox door, when one is attached.
|
|
@@ -56655,6 +56760,15 @@ var Publisher = class {
|
|
|
56655
56760
|
oauthAuthorize: this.clientOrigin ? `${this.clientOrigin}oauth/authorize` : null,
|
|
56656
56761
|
oauthToken: this.clientOrigin ? `${this.clientOrigin}oauth/token` : null
|
|
56657
56762
|
});
|
|
56763
|
+
}
|
|
56764
|
+
async publishProfile({ force = false } = {}) {
|
|
56765
|
+
const { urls } = this;
|
|
56766
|
+
const host = new URL(urls.base).host;
|
|
56767
|
+
const priv = await this.privateReady() === true;
|
|
56768
|
+
const moderators = (this.config.moderators || []).length ? urls.moderators : null;
|
|
56769
|
+
const gw = this.config.gateway;
|
|
56770
|
+
const gwActive = gw && gw.url && gw.mode && gw.mode !== "off";
|
|
56771
|
+
const actorDoc2 = this.actorDocFor({ priv, moderators, inbox: gwActive ? gw.url : null });
|
|
56658
56772
|
const surface = node_crypto_default.createHash("sha256").update(JSON.stringify({
|
|
56659
56773
|
actor: actorDoc2,
|
|
56660
56774
|
handle: this.config.handle,
|
|
@@ -56865,21 +56979,15 @@ var Publisher = class {
|
|
|
56865
56979
|
await this.deliverer.deliverToAll(inboxes, moveActivity(urls, target, Date.parse(at)));
|
|
56866
56980
|
this.config.movedTo = target;
|
|
56867
56981
|
this.store.setConfig({ ...this.store.getConfig(), movedTo: target, movedAt: at });
|
|
56868
|
-
|
|
56869
|
-
|
|
56870
|
-
|
|
56871
|
-
|
|
56872
|
-
|
|
56873
|
-
|
|
56874
|
-
|
|
56875
|
-
|
|
56876
|
-
|
|
56877
|
-
summary: this.config.summary || null,
|
|
56878
|
-
icon: this.config.icon || null,
|
|
56879
|
-
image: this.config.image || null,
|
|
56880
|
-
fields: this.config.fields || [],
|
|
56881
|
-
webId: this.remote.webId || null,
|
|
56882
|
-
aliases: this.config.aliases || []
|
|
56982
|
+
const gw = this.config.gateway;
|
|
56983
|
+
const gwActive = gw && gw.url && gw.mode && gw.mode !== "off";
|
|
56984
|
+
const priv = await this.privateReady() === true;
|
|
56985
|
+
const moderators = (this.config.moderators || []).length ? urls.moderators : null;
|
|
56986
|
+
await writeMoved(this.remote, urls, this.actorDocFor({
|
|
56987
|
+
priv,
|
|
56988
|
+
moderators,
|
|
56989
|
+
inbox: gwActive ? gw.url : null,
|
|
56990
|
+
movedTo: target
|
|
56883
56991
|
}));
|
|
56884
56992
|
await this.store.flush();
|
|
56885
56993
|
this.log(`moved to ${target}: Move sent to ${inboxes.length} inbox(es), actor now advertises movedTo`);
|
|
@@ -57583,6 +57691,8 @@ async function onFollow(intake, activity, actor, { trusted = false } = {}) {
|
|
|
57583
57691
|
intake.log(`Accept sent \u2192 ${doc.inbox}`);
|
|
57584
57692
|
}
|
|
57585
57693
|
async function onUndo(intake, activity, actor, { trusted = false } = {}) {
|
|
57694
|
+
const inner = activity.object && typeof activity.object === "object" ? activity.object : null;
|
|
57695
|
+
if (inner?.type === "Like" || inner?.type === "Announce") return onUndoReaction(intake, inner, actor, { trusted });
|
|
57586
57696
|
if (typeof activity.object === "object" && activity.object?.type && activity.object.type !== "Follow") return;
|
|
57587
57697
|
const named = typeof activity.object === "string" ? activity.object : activity.object?.id;
|
|
57588
57698
|
if (!named && !trusted) return;
|
|
@@ -57670,6 +57780,47 @@ async function onAnnounce(intake, activity, actor, objectId) {
|
|
|
57670
57780
|
}
|
|
57671
57781
|
return intake.ingestNote(objectId, actor, { via: actor });
|
|
57672
57782
|
}
|
|
57783
|
+
async function onUndoReaction(intake, inner, actor, { trusted = false } = {}) {
|
|
57784
|
+
const innerActor = typeof inner.actor === "string" ? inner.actor : inner.actor?.id;
|
|
57785
|
+
if (innerActor && innerActor !== actor) return;
|
|
57786
|
+
const wrapped = inner.object;
|
|
57787
|
+
const target = wrapped && typeof wrapped === "object" && (wrapped.type === "Create" || wrapped.type === "Update") ? wrapped.object : wrapped;
|
|
57788
|
+
const objectId = typeof target === "string" ? target : target?.id;
|
|
57789
|
+
if (!objectId) return;
|
|
57790
|
+
if (objectId.startsWith(intake.urls.notes)) {
|
|
57791
|
+
const type = inner.type === "Like" ? "favourite" : "reblog";
|
|
57792
|
+
const gone = intake.store.removeNotifications((n) => n.type === type && n.actor === actor && n.noteId === objectId);
|
|
57793
|
+
if (gone) intake.log(`${inner.type} withdrawn by ${actor} on ${objectId}`);
|
|
57794
|
+
return;
|
|
57795
|
+
}
|
|
57796
|
+
if (inner.type !== "Announce") return;
|
|
57797
|
+
const followed = trusted || intake.store.getContacts().following.some((f) => f.actor === actor && f.accepted);
|
|
57798
|
+
if (!followed) return;
|
|
57799
|
+
const s = intake.store.getStatuses().find((x) => x.noteId === objectId);
|
|
57800
|
+
if (!s || s.kind === "post" || s.via !== actor) return;
|
|
57801
|
+
await intake.forget(s);
|
|
57802
|
+
intake.log(`boost withdrawn by ${actor}: ${objectId} left the timeline`);
|
|
57803
|
+
}
|
|
57804
|
+
async function onBlock(intake, activity, actor, { trusted = false } = {}) {
|
|
57805
|
+
const objectId = typeof activity.object === "string" ? activity.object : activity.object?.id;
|
|
57806
|
+
if (objectId !== intake.urls.actor) return;
|
|
57807
|
+
if (!trusted) {
|
|
57808
|
+
intake.log(`Block from ${actor} arrived unverified \u2014 ignored`);
|
|
57809
|
+
return;
|
|
57810
|
+
}
|
|
57811
|
+
const contacts = intake.store.getContacts();
|
|
57812
|
+
const wasFollower = contacts.followers.some((f) => f.actor === actor);
|
|
57813
|
+
const wasFollowing = contacts.following.some((f) => f.actor === actor);
|
|
57814
|
+
if (wasFollower) dropFollower(contacts, actor, "blocked-us");
|
|
57815
|
+
contacts.following = contacts.following.filter((f) => f.actor !== actor);
|
|
57816
|
+
intake.store.setContacts(contacts);
|
|
57817
|
+
const reqs = intake.store.getRequests();
|
|
57818
|
+
const left = reqs.filter((r) => r.actor !== actor);
|
|
57819
|
+
if (left.length !== reqs.length) intake.store.setRequests(left);
|
|
57820
|
+
await intake.republish({ followers: wasFollower, following: wasFollowing, pending: true });
|
|
57821
|
+
const what = [wasFollower && "their follow of us", wasFollowing && "our follow of them"].filter(Boolean).join(" and ");
|
|
57822
|
+
intake.log(`blocked by ${actor}: ${what || "no relationship"} dropped`);
|
|
57823
|
+
}
|
|
57673
57824
|
async function onDelete(intake, activity, actor) {
|
|
57674
57825
|
const objectId = typeof activity.object === "string" ? activity.object : activity.object?.id;
|
|
57675
57826
|
if (!objectId) return "Delete without object id";
|
|
@@ -57875,6 +58026,7 @@ async function ingestNote(intake, objectId, actor, { via, inline = null } = {})
|
|
|
57875
58026
|
const followed = via || known2 || !known2 && await intake.isCoMember(author);
|
|
57876
58027
|
const kind = followed ? "timeline" : "mention";
|
|
57877
58028
|
const mentions = [].concat(note.tag || []).filter((t) => t?.type === "Mention" && t.href && t.name).slice(0, MAX_MENTIONS).map((t) => ({ href: httpOnly(String(t.href).slice(0, MAX_URL_CHARS)), name: String(t.name).slice(0, 256) })).filter((m) => m.href);
|
|
58029
|
+
const hashtags = [].concat(note.tag || []).filter((t) => t?.type === "Hashtag" && t.name).slice(0, MAX_MENTIONS).map((t) => ({ name: String(t.name).replace(/^#/, "").slice(0, 128), url: httpOnly(String(t.href || "").slice(0, MAX_URL_CHARS)) })).filter((t) => t.name);
|
|
57878
58030
|
const emojis = emojisOf(note);
|
|
57879
58031
|
const poll = pollOf(note);
|
|
57880
58032
|
const audience = [].concat(note.to || [], note.cc || []).map(String);
|
|
@@ -57891,6 +58043,8 @@ async function ingestNote(intake, objectId, actor, { via, inline = null } = {})
|
|
|
57891
58043
|
...nonPublic ? { nonPublic: true } : {},
|
|
57892
58044
|
// The author's content warning, shown as one: plain text only.
|
|
57893
58045
|
...note.summary ? { spoiler: String(note.summary).replace(/<[^>]*>/g, "") } : {},
|
|
58046
|
+
...note.sensitive === true ? { sensitive: true } : {},
|
|
58047
|
+
...hashtags.length ? { tags: hashtags } : {},
|
|
57894
58048
|
...poll ? { poll } : {},
|
|
57895
58049
|
...emojis.length ? { emojis } : {},
|
|
57896
58050
|
...mentions.length ? { mentions } : {},
|
|
@@ -63413,6 +63567,8 @@ var Intake = class {
|
|
|
63413
63567
|
case "Add":
|
|
63414
63568
|
case "Remove":
|
|
63415
63569
|
return this.onAddRemove(activity, actor);
|
|
63570
|
+
case "Block":
|
|
63571
|
+
return this.onBlock(activity, actor, { trusted });
|
|
63416
63572
|
default:
|
|
63417
63573
|
this.log(`ignored ${activity.type} from ${actor}`);
|
|
63418
63574
|
}
|
|
@@ -63506,6 +63662,9 @@ var Intake = class {
|
|
|
63506
63662
|
onUndo(...a) {
|
|
63507
63663
|
return onUndo(this, ...a);
|
|
63508
63664
|
}
|
|
63665
|
+
onBlock(...a) {
|
|
63666
|
+
return onBlock(this, ...a);
|
|
63667
|
+
}
|
|
63509
63668
|
onCreate(...a) {
|
|
63510
63669
|
return onCreate(this, ...a);
|
|
63511
63670
|
}
|
|
@@ -64135,7 +64294,7 @@ var C2S = class {
|
|
|
64135
64294
|
return this.send(res, 405, { error: "deliveries go to this actor's inbox on the pod, which the actor document names; this address is the owner reading their own" });
|
|
64136
64295
|
}
|
|
64137
64296
|
const reader = await this.auth(req, pathname);
|
|
64138
|
-
if (!reader.ok) return this.send(res, reader.status, { error: reader.error });
|
|
64297
|
+
if (!reader.ok) return this.send(res, reader.status, { error: reader.error }, reader.headers || {});
|
|
64139
64298
|
return this.sendInbox(res, url);
|
|
64140
64299
|
}
|
|
64141
64300
|
if (req.method === "GET" || req.method === "HEAD") {
|
|
@@ -64148,7 +64307,7 @@ var C2S = class {
|
|
|
64148
64307
|
return this.send(res, 405, { error: "POST the outbox; GET redirects to the pod" });
|
|
64149
64308
|
}
|
|
64150
64309
|
const who = await this.auth(req, pathname);
|
|
64151
|
-
if (!who.ok) return this.send(res, who.status, { error: who.error });
|
|
64310
|
+
if (!who.ok) return this.send(res, who.status, { error: who.error }, who.headers || {});
|
|
64152
64311
|
if (this.agent.viewer) {
|
|
64153
64312
|
const took = await this.agent.requestTakeover?.();
|
|
64154
64313
|
if (!took) return this.send(res, 503, { error: "another agent is active for this pod \u2014 takeover failed, try again" });
|
|
@@ -64202,6 +64361,13 @@ var C2S = class {
|
|
|
64202
64361
|
return reply(422, { error: e.message || String(e) });
|
|
64203
64362
|
}
|
|
64204
64363
|
}
|
|
64364
|
+
// The actors a client addressed by id, beyond the audience words: the ones
|
|
64365
|
+
// in to/cc are listed and delivered to; the ones in bto/bcc are delivered
|
|
64366
|
+
// to and never listed (§6).
|
|
64367
|
+
addressedActors(activity, object) {
|
|
64368
|
+
const pick = (...fields) => [...new Set(fields.flatMap((f) => arr(activity[f] ?? object?.[f]).map(idOf)))].filter((a) => typeof a === "string" && /^https?:\/\//u.test(a) && a !== PUBLIC && a !== this.urls.followers && a !== this.urls.actor);
|
|
64369
|
+
return { also: pick("to", "cc"), deliverTo: pick("bto", "bcc") };
|
|
64370
|
+
}
|
|
64205
64371
|
async _dispatch(activity, { slug, raw, reply }) {
|
|
64206
64372
|
const agent2 = this.agent;
|
|
64207
64373
|
const object = typeof activity.object === "object" && activity.object !== null ? activity.object : null;
|
|
@@ -64211,9 +64377,10 @@ var C2S = class {
|
|
|
64211
64377
|
const makes = object?.type || "Note";
|
|
64212
64378
|
if (!object) return reply(422, { error: "a Create carries the object it creates" });
|
|
64213
64379
|
const visibility = this.visibilityOf(activity, object);
|
|
64380
|
+
const { also, deliverTo } = this.addressedActors(activity, object);
|
|
64214
64381
|
if (makes !== "Note" && makes !== "Question") {
|
|
64215
64382
|
const asSent = raw && typeof raw === "object" && !Array.isArray(raw) ? ACTIVITY_TYPES.has(raw.type) ? raw.object && typeof raw.object === "object" ? raw.object : null : raw : null;
|
|
64216
|
-
const made = await agent2.publisher.publishObject(asSent || object, { visibility, slug });
|
|
64383
|
+
const made = await agent2.publisher.publishObject(asSent || object, { visibility, slug, also, deliverTo });
|
|
64217
64384
|
return reply(201, { id: made.createId, object: made.id }, { location: made.createId });
|
|
64218
64385
|
}
|
|
64219
64386
|
const text = String(object.source?.content ?? object.content ?? "");
|
|
@@ -64229,7 +64396,8 @@ var C2S = class {
|
|
|
64229
64396
|
expiresAt: object.endTime || null,
|
|
64230
64397
|
inReplyTo: idOf(object.inReplyTo) || void 0,
|
|
64231
64398
|
visibility,
|
|
64232
|
-
spoilerText: object.summary || null
|
|
64399
|
+
spoilerText: object.summary || null,
|
|
64400
|
+
sensitive: object.sensitive === true
|
|
64233
64401
|
});
|
|
64234
64402
|
return reply(
|
|
64235
64403
|
201,
|
|
@@ -64250,7 +64418,10 @@ var C2S = class {
|
|
|
64250
64418
|
attachments,
|
|
64251
64419
|
visibility,
|
|
64252
64420
|
spoilerText: object.summary || null,
|
|
64253
|
-
|
|
64421
|
+
sensitive: object.sensitive === true,
|
|
64422
|
+
slug,
|
|
64423
|
+
also,
|
|
64424
|
+
deliverTo
|
|
64254
64425
|
});
|
|
64255
64426
|
return reply(
|
|
64256
64427
|
201,
|
|
@@ -64277,6 +64448,7 @@ var C2S = class {
|
|
|
64277
64448
|
await agent2.publisher.updateNote(s, {
|
|
64278
64449
|
content: text,
|
|
64279
64450
|
spoilerText: object?.summary || null,
|
|
64451
|
+
sensitive: object?.sensitive === void 0 ? null : object.sensitive === true,
|
|
64280
64452
|
attachments
|
|
64281
64453
|
});
|
|
64282
64454
|
return reply(200, { ok: true, object: s.noteId });
|
|
@@ -65482,7 +65654,7 @@ function page(api, items, url, { limit = 20, max = 40, idOf: idOf2 = (s) => api.
|
|
|
65482
65654
|
}
|
|
65483
65655
|
const pageItems2 = items.slice(0, n);
|
|
65484
65656
|
if (!pageItems2.length) return { items: pageItems2, headers: {} };
|
|
65485
|
-
const base =
|
|
65657
|
+
const base = url.origin + url.pathname;
|
|
65486
65658
|
const q = (extra) => {
|
|
65487
65659
|
const u = new URL(base);
|
|
65488
65660
|
for (const [k, v] of url.searchParams) if (!["max_id", "since_id", "min_id"].includes(k)) u.searchParams.set(k, v);
|
|
@@ -65608,7 +65780,7 @@ function status(api, s, { all } = {}) {
|
|
|
65608
65780
|
created_at: s.published || (/* @__PURE__ */ new Date()).toISOString(),
|
|
65609
65781
|
in_reply_to_id: s.inReplyTo ? api.store.idFor(s.inReplyTo) : null,
|
|
65610
65782
|
in_reply_to_account_id: null,
|
|
65611
|
-
sensitive: !!s.spoiler,
|
|
65783
|
+
sensitive: !!(s.spoiler || s.sensitive),
|
|
65612
65784
|
spoiler_text: s.spoiler || "",
|
|
65613
65785
|
visibility: s.visibility || "public",
|
|
65614
65786
|
language: null,
|
|
@@ -65648,7 +65820,7 @@ function status(api, s, { all } = {}) {
|
|
|
65648
65820
|
acct: bare.includes("@") ? bare : host ? `${user}@${host}` : bare
|
|
65649
65821
|
};
|
|
65650
65822
|
}),
|
|
65651
|
-
tags: [],
|
|
65823
|
+
tags: (s.tags || []).map((t) => ({ name: t.name, url: t.url })),
|
|
65652
65824
|
emojis: (s.emojis || []).map((e) => ({
|
|
65653
65825
|
shortcode: e.shortcode,
|
|
65654
65826
|
url: e.url,
|
|
@@ -66580,6 +66752,7 @@ async function handle6(api, ctx) {
|
|
|
66580
66752
|
if (ready !== true) return send(422, { error: ready });
|
|
66581
66753
|
}
|
|
66582
66754
|
const spoilerText = String(body.spoiler_text || "").trim() || null;
|
|
66755
|
+
const sensitive = body.sensitive === true || body.sensitive === "true" || body.sensitive === "1";
|
|
66583
66756
|
const mediaIds = [].concat(body.media_ids || body["media_ids[]"] || []).filter(Boolean);
|
|
66584
66757
|
const media = api.store.getMedia();
|
|
66585
66758
|
const attachments = mediaIds.map((id) => media[id] && { id, ...media[id] }).filter(Boolean);
|
|
@@ -66610,7 +66783,8 @@ async function handle6(api, ctx) {
|
|
|
66610
66783
|
expiresAt: new Date(Date.now() + seconds * 1e3).toISOString(),
|
|
66611
66784
|
inReplyTo,
|
|
66612
66785
|
visibility,
|
|
66613
|
-
spoilerText
|
|
66786
|
+
spoilerText,
|
|
66787
|
+
sensitive
|
|
66614
66788
|
});
|
|
66615
66789
|
return send(200, api.status(api.store.getStatuses().find((x) => x.noteId === q.id)));
|
|
66616
66790
|
} catch (e) {
|
|
@@ -66639,7 +66813,7 @@ async function handle6(api, ctx) {
|
|
|
66639
66813
|
try {
|
|
66640
66814
|
note = await api.agent.publisher.publishNote(
|
|
66641
66815
|
body.status,
|
|
66642
|
-
{ inReplyTo, attachments, visibility, spoilerText }
|
|
66816
|
+
{ inReplyTo, attachments, visibility, spoilerText, sensitive }
|
|
66643
66817
|
);
|
|
66644
66818
|
} catch (e) {
|
|
66645
66819
|
if (e.code === "unaddressed") return send(422, { error: e.message });
|
|
@@ -66663,7 +66837,7 @@ async function handle6(api, ctx) {
|
|
|
66663
66837
|
return send(200, [{
|
|
66664
66838
|
content: s.content || "",
|
|
66665
66839
|
spoiler_text: s.spoiler || "",
|
|
66666
|
-
sensitive: !!s.spoiler,
|
|
66840
|
+
sensitive: !!(s.spoiler || s.sensitive),
|
|
66667
66841
|
created_at: s.editedAt || s.published,
|
|
66668
66842
|
account: api.account(s.actor),
|
|
66669
66843
|
media_attachments: (s.attachments || []).map((a) => api.mediaJson(a)),
|
|
@@ -66688,9 +66862,10 @@ async function handle6(api, ctx) {
|
|
|
66688
66862
|
const media = api.store.getMedia();
|
|
66689
66863
|
const attachments = mediaIds.length ? mediaIds.map((id) => media[id] && { id, ...media[id] }).filter(Boolean) : null;
|
|
66690
66864
|
const spoilerText = String(body.spoiler_text || "").trim() || null;
|
|
66865
|
+
const sensitive = body.sensitive === void 0 ? null : body.sensitive === true || body.sensitive === "true" || body.sensitive === "1";
|
|
66691
66866
|
const patched = await api.agent.publisher.updateNote(
|
|
66692
66867
|
s,
|
|
66693
|
-
{ content: body.status, spoilerText, attachments }
|
|
66868
|
+
{ content: body.status, spoilerText, sensitive, attachments }
|
|
66694
66869
|
);
|
|
66695
66870
|
return send(200, api.status(patched || s));
|
|
66696
66871
|
}
|
|
@@ -66929,7 +67104,13 @@ var MastoApi = class _MastoApi {
|
|
|
66929
67104
|
res.end(body);
|
|
66930
67105
|
return true;
|
|
66931
67106
|
};
|
|
66932
|
-
const
|
|
67107
|
+
const scheme = String(this.scheme || (req.headers?.["x-forwarded-proto"] === "https" || req.socket?.encrypted ? "https" : "http")).replace(/:.*$/, "");
|
|
67108
|
+
let self2 = url;
|
|
67109
|
+
try {
|
|
67110
|
+
self2 = new URL(`${scheme}://${req.headers?.host || "localhost"}${this.mount || ""}${pathname}${url.search || ""}`);
|
|
67111
|
+
} catch {
|
|
67112
|
+
}
|
|
67113
|
+
const ctx = { req, res, pathname, url: self2, send };
|
|
66933
67114
|
if (await handle(this, ctx)) return true;
|
|
66934
67115
|
if (!pathname.startsWith("/api/")) return false;
|
|
66935
67116
|
if (await handle2(this, ctx)) return true;
|
|
@@ -67940,6 +68121,7 @@ var hostOf = (inbox) => {
|
|
|
67940
68121
|
}
|
|
67941
68122
|
};
|
|
67942
68123
|
var aboutTheHost = (e) => !e.status || e.status >= 500 || e.status === 429;
|
|
68124
|
+
var unsalvageable = (e) => e.status === 404 || e.status === 410;
|
|
67943
68125
|
var Deliverer = class {
|
|
67944
68126
|
// passive: signing-only (viewer-mode agents) — no queue drain timer, so a
|
|
67945
68127
|
// read-only agent never mutates shared delivery state. startQueue() flips
|
|
@@ -67952,9 +68134,11 @@ var Deliverer = class {
|
|
|
67952
68134
|
edPrivate = null,
|
|
67953
68135
|
proofKeyId = null,
|
|
67954
68136
|
log: log2 = console.log,
|
|
67955
|
-
passive = false
|
|
68137
|
+
passive = false,
|
|
68138
|
+
onGone = null
|
|
67956
68139
|
}) {
|
|
67957
68140
|
this.store = store;
|
|
68141
|
+
this.onGone = onGone;
|
|
67958
68142
|
this.keyId = keyId;
|
|
67959
68143
|
this.rsaPrivate = rsaPrivate;
|
|
67960
68144
|
this.actorId = actorId;
|
|
@@ -68050,6 +68234,10 @@ var Deliverer = class {
|
|
|
68050
68234
|
await this.deliverNow(inbox, signed);
|
|
68051
68235
|
this.log(`delivered ${signed.type} \u2192 ${inbox}`);
|
|
68052
68236
|
} catch (e) {
|
|
68237
|
+
if (unsalvageable(e)) {
|
|
68238
|
+
await this._unsalvageable(inbox, e);
|
|
68239
|
+
return;
|
|
68240
|
+
}
|
|
68053
68241
|
this.log(`delivery failed (${e.message}) \u2014 queued`);
|
|
68054
68242
|
const wait = e.retryAfterMs || 6e4;
|
|
68055
68243
|
if (aboutTheHost(e)) {
|
|
@@ -68059,6 +68247,26 @@ var Deliverer = class {
|
|
|
68059
68247
|
this._enqueue({ inbox, activity: signed, attempts: 1, nextAt: Date.now() + wait });
|
|
68060
68248
|
}
|
|
68061
68249
|
}
|
|
68250
|
+
// Not retried, and on a 410 the followers who received there are dropped,
|
|
68251
|
+
// so the next fan-out stops asking; the followers collection is republished
|
|
68252
|
+
// through onGone. A 404 drops only this delivery.
|
|
68253
|
+
async _unsalvageable(inbox, e) {
|
|
68254
|
+
if (e.status !== 410) {
|
|
68255
|
+
this.log(`delivery dropped (${e.message}) \u2014 nothing answers there`);
|
|
68256
|
+
return;
|
|
68257
|
+
}
|
|
68258
|
+
const contacts = this.store.getContacts?.();
|
|
68259
|
+
const left = (contacts?.followers || []).filter((f) => f.inbox === inbox || f.sharedInbox === inbox);
|
|
68260
|
+
this.log(`${inbox} is gone (410) \u2014 delivery dropped${left.length ? `, ${left.length} follower(s) there forgotten` : ""}`);
|
|
68261
|
+
if (!left.length) return;
|
|
68262
|
+
for (const f of left) dropFollower(contacts, f.actor, "inbox-gone");
|
|
68263
|
+
this.store.setContacts(contacts);
|
|
68264
|
+
try {
|
|
68265
|
+
await this.onGone?.();
|
|
68266
|
+
} catch (err) {
|
|
68267
|
+
this.log(`followers republish after 410: ${err.message}`);
|
|
68268
|
+
}
|
|
68269
|
+
}
|
|
68062
68270
|
// The queue has a ceiling: a fan-out into a sea of down servers must not grow
|
|
68063
68271
|
// queue.json without bound. Overflow keeps the evidence as a dead letter.
|
|
68064
68272
|
_enqueue(item) {
|
|
@@ -68143,6 +68351,11 @@ var Deliverer = class {
|
|
|
68143
68351
|
changed = true;
|
|
68144
68352
|
this.log(`retry ok: ${item.activity.type} \u2192 ${item.inbox}`);
|
|
68145
68353
|
} catch (e) {
|
|
68354
|
+
if (unsalvageable(e)) {
|
|
68355
|
+
changed = true;
|
|
68356
|
+
await this._unsalvageable(item.inbox, e);
|
|
68357
|
+
continue;
|
|
68358
|
+
}
|
|
68146
68359
|
item.attempts += 1;
|
|
68147
68360
|
changed = true;
|
|
68148
68361
|
const wait = e.retryAfterMs || ladderFor(item);
|
|
@@ -70436,7 +70649,8 @@ var BrowserAgent = class _BrowserAgent {
|
|
|
70436
70649
|
// The relay finds the account by the front's own key for it, which for a
|
|
70437
70650
|
// mail-door account is the full address, not the bare handle.
|
|
70438
70651
|
handle: doorKeyOf(config.gateway?.url) || config.handle,
|
|
70439
|
-
sessionFetch: session.fetch
|
|
70652
|
+
sessionFetch: session.fetch,
|
|
70653
|
+
onGone: () => this.publisher.publishCollections({ followers: true })
|
|
70440
70654
|
});
|
|
70441
70655
|
this.publisher = new Publisher({
|
|
70442
70656
|
config: this.store.getConfig(),
|
|
@@ -70449,7 +70663,8 @@ var BrowserAgent = class _BrowserAgent {
|
|
|
70449
70663
|
// Who a post names, resolved — the same lookup the DeviceAgent
|
|
70450
70664
|
// gives its publisher. Without it no mention from the browser ever
|
|
70451
70665
|
// resolved: a direct message went to nobody, a mention notified no one.
|
|
70452
|
-
resolveMention: (h) => resolveHandle(this, h)
|
|
70666
|
+
resolveMention: (h) => resolveHandle(this, h),
|
|
70667
|
+
resolveActor: (u) => this.intake.fetchAP(u)
|
|
70453
70668
|
});
|
|
70454
70669
|
this.atproto = new BrowserAtproto({ store: this.store, actorId: this.urls.actor, log: this.log });
|
|
70455
70670
|
this.publisher.atproto = this.atproto;
|