fedipod-server 0.19.0 → 0.21.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/dist/claims.js +3 -2
- package/lib/client/masto/render.mjs +1 -1
- package/lib/core/publisher/index.mjs +50 -11
- package/lib/core/social.mjs +3 -0
- package/lib/core/wire.mjs +44 -5
- package/lib/device/admin/surface.mjs +8 -0
- package/lib/device/cli/commands/setup.mjs +1 -0
- package/lib/device/setup.mjs +1 -0
- package/lib/gateway/front-core.mjs +25 -3
- package/lib/server/embed.mjs +1 -0
- package/package.json +1 -1
- package/run-agent.mjs +4 -0
- package/web/app/agent.mjs +1 -0
- package/web/app/dist/boot.js +1 -0
- package/web/app/dist/boot.js.map +2 -2
- package/web/app/dist/sw.js +91 -14
- package/web/app/dist/sw.js.map +3 -3
- package/web/app/signup.mjs +1 -0
- package/web/app/site/_redirects +1 -0
- package/web/app/site/boot.js +1 -0
- package/web/app/site/sw.js +375 -83
- package/web/front/admin.js +5 -1
package/web/app/dist/sw.js
CHANGED
|
@@ -9664,11 +9664,14 @@ function hostMeta(base) {
|
|
|
9664
9664
|
</XRD>
|
|
9665
9665
|
`;
|
|
9666
9666
|
}
|
|
9667
|
-
function jrd({ handle: handle7, host, actor, aliases = [] }) {
|
|
9667
|
+
function jrd({ handle: handle7, host, actor, aliases = [], page: page2 = null }) {
|
|
9668
9668
|
return {
|
|
9669
9669
|
subject: `acct:${handle7}@${host}`,
|
|
9670
9670
|
...aliases.length ? { aliases } : {},
|
|
9671
|
-
links: [
|
|
9671
|
+
links: [
|
|
9672
|
+
{ rel: "self", type: "application/activity+json", href: actor },
|
|
9673
|
+
...page2 ? [{ rel: "http://webfinger.net/rel/profile-page", type: "text/html", href: page2 }] : []
|
|
9674
|
+
]
|
|
9672
9675
|
};
|
|
9673
9676
|
}
|
|
9674
9677
|
function followsNeedApproval(config = {}) {
|
|
@@ -9794,9 +9797,30 @@ function actorDoc({
|
|
|
9794
9797
|
publicKey: { id: urls.actor + "#main-key", owner: urls.actor, publicKeyPem }
|
|
9795
9798
|
};
|
|
9796
9799
|
}
|
|
9797
|
-
function profilePageHtml({
|
|
9800
|
+
function profilePageHtml({
|
|
9801
|
+
name,
|
|
9802
|
+
address,
|
|
9803
|
+
summary = null,
|
|
9804
|
+
icon = null,
|
|
9805
|
+
image = null,
|
|
9806
|
+
fields = [],
|
|
9807
|
+
joined = null,
|
|
9808
|
+
pinned = [],
|
|
9809
|
+
kind = "person"
|
|
9810
|
+
}) {
|
|
9798
9811
|
const esc = (s) => String(s ?? "").replace(/[&<>"]/g, (c) => ({ ...HTML_ESCAPES, '"': """ })[c]);
|
|
9799
9812
|
const what = kind === "group" ? "a group" : "an account";
|
|
9813
|
+
const month = (iso) => {
|
|
9814
|
+
const d = new Date(iso);
|
|
9815
|
+
return Number.isNaN(d.getTime()) ? null : d.toLocaleDateString("en", { month: "long", year: "numeric", timeZone: "UTC" });
|
|
9816
|
+
};
|
|
9817
|
+
const day = (iso) => {
|
|
9818
|
+
const d = new Date(iso);
|
|
9819
|
+
return Number.isNaN(d.getTime()) ? "" : d.toISOString().slice(0, 10);
|
|
9820
|
+
};
|
|
9821
|
+
const fieldRows = fields.filter((f) => f?.name && f?.value !== void 0 && f?.value !== null && String(f.value) !== "");
|
|
9822
|
+
const joinedText = joined ? month(joined) : null;
|
|
9823
|
+
const pins = pinned.filter((x) => x && typeof x.content === "string");
|
|
9800
9824
|
return `<!doctype html>
|
|
9801
9825
|
<html lang="en">
|
|
9802
9826
|
<head>
|
|
@@ -9806,14 +9830,23 @@ function profilePageHtml({ name, address, summary = null, icon = null, kind = "p
|
|
|
9806
9830
|
<style>
|
|
9807
9831
|
:root { color-scheme: light dark; }
|
|
9808
9832
|
body { font: 112.5%/1.5 system-ui, sans-serif; max-width: 34rem; margin: 3rem auto; padding: 0 1rem; }
|
|
9809
|
-
img.
|
|
9833
|
+
img.header { width: 100%; max-height: 12rem; object-fit: cover; border-radius: 1rem; }
|
|
9834
|
+
img.avatar { width: 6rem; height: 6rem; border-radius: 1rem; object-fit: cover; margin-top: 1rem; }
|
|
9810
9835
|
h1 { margin: .5rem 0 0; }
|
|
9811
9836
|
.address { font-size: 1.1rem; user-select: all; }
|
|
9837
|
+
dl.fields { display: grid; grid-template-columns: max-content 1fr; gap: .4rem 1rem; margin: 1.5rem 0; }
|
|
9838
|
+
dl.fields dt { font-weight: 600; }
|
|
9839
|
+
dl.fields dd { margin: 0; overflow-wrap: anywhere; }
|
|
9840
|
+
.joined { margin: 1rem 0; }
|
|
9841
|
+
section.pinned h2 { font-size: 1.1rem; margin: 2rem 0 .5rem; }
|
|
9842
|
+
article.pin { border: 1px solid #8884; border-radius: 1rem; padding: 1rem 1.2rem; margin: 1rem 0; }
|
|
9843
|
+
article.pin p:last-child { margin-bottom: 0; }
|
|
9844
|
+
article.pin .when { font-size: 1rem; }
|
|
9812
9845
|
form { margin-top: 2rem; }
|
|
9813
9846
|
label { display: block; margin-bottom: .3rem; }
|
|
9814
9847
|
input { font: inherit; padding: .5rem; width: 14rem; max-width: 100%; }
|
|
9815
9848
|
button { font: inherit; padding: .5rem 1rem; }
|
|
9816
|
-
.hint { color: #
|
|
9849
|
+
.hint { color: #666; font-size: 1rem; }
|
|
9817
9850
|
.err { color: #b00020; }
|
|
9818
9851
|
.err:empty { display: none; }
|
|
9819
9852
|
@media (prefers-color-scheme: dark) {
|
|
@@ -9824,10 +9857,22 @@ button { font: inherit; padding: .5rem 1rem; }
|
|
|
9824
9857
|
</head>
|
|
9825
9858
|
<body>
|
|
9826
9859
|
<main>
|
|
9860
|
+
${image ? `<img class="header" src="${esc(image)}" alt="">` : ""}
|
|
9827
9861
|
${icon ? `<img class="avatar" src="${esc(icon)}" alt="">` : ""}
|
|
9828
9862
|
<h1>${esc(name)}</h1>
|
|
9829
9863
|
<p class="address">${esc(address)}</p>
|
|
9830
9864
|
${summary ? `<div>${summary}</div>` : ""}
|
|
9865
|
+
${fieldRows.length ? `<dl class="fields">
|
|
9866
|
+
${fieldRows.map((f) => `<dt>${esc(f.name)}</dt><dd>${esc(f.value)}</dd>`).join("\n")}
|
|
9867
|
+
</dl>` : ""}
|
|
9868
|
+
${joinedText ? `<p class="joined">Joined ${esc(joinedText)}</p>` : ""}
|
|
9869
|
+
${pins.length ? `<section class="pinned">
|
|
9870
|
+
<h2>Pinned</h2>
|
|
9871
|
+
${pins.map((x) => `<article class="pin">
|
|
9872
|
+
${x.content}
|
|
9873
|
+
<p class="when">${x.url ? `<a href="${esc(x.url)}">${esc(day(x.published))}</a>` : esc(day(x.published))}</p>
|
|
9874
|
+
</article>`).join("\n")}
|
|
9875
|
+
</section>` : ""}
|
|
9831
9876
|
<p>This is ${what} on the Fediverse. To follow it, paste the address above
|
|
9832
9877
|
into the search box of Mastodon or any Fediverse app \u2014 or use the form.</p>
|
|
9833
9878
|
<form id="follow">
|
|
@@ -56761,6 +56806,40 @@ var Publisher = class {
|
|
|
56761
56806
|
oauthToken: this.clientOrigin ? `${this.clientOrigin}oauth/token` : null
|
|
56762
56807
|
});
|
|
56763
56808
|
}
|
|
56809
|
+
// The human half: a page a browser can open and follow from. The actor
|
|
56810
|
+
// document is for servers; this is the address you hand to a person. It
|
|
56811
|
+
// shows who the account is, its fields, when it joined and what it pinned;
|
|
56812
|
+
// the counts and the posts live in the collections. Written only when its
|
|
56813
|
+
// content changed.
|
|
56814
|
+
async publishProfilePage({ force = false } = {}) {
|
|
56815
|
+
const { urls } = this;
|
|
56816
|
+
const host = new URL(urls.base).host;
|
|
56817
|
+
if (!this.config.createdAt) {
|
|
56818
|
+
const oldest = this.store.getStatuses().filter((s) => s.kind === "post" && s.published).map((s) => s.published).sort()[0];
|
|
56819
|
+
this.config.createdAt = oldest || (/* @__PURE__ */ new Date()).toISOString();
|
|
56820
|
+
this.store.setConfig?.({ ...this.store.getConfig?.(), createdAt: this.config.createdAt });
|
|
56821
|
+
}
|
|
56822
|
+
const pinned = this.store.getStatuses().filter((s) => s.kind === "post" && s.pinned && s.visibility !== "private" && s.visibility !== "direct").sort((a, b) => String(b.published).localeCompare(String(a.published))).slice(0, 5).map((s) => ({ content: s.content, published: s.published, url: s.noteId }));
|
|
56823
|
+
const frontActor = this.config.gateway?.frontActor;
|
|
56824
|
+
const address = frontActor ? `@${publicHandle(this.config)}@${new URL(frontActor).host}` : webfingerHost(urls.base) ? `@${this.config.handle}@${host}` : urls.actor;
|
|
56825
|
+
const html = profilePageHtml({
|
|
56826
|
+
name: this.config.name || this.config.handle,
|
|
56827
|
+
address,
|
|
56828
|
+
summary: this.config.summary ? contentHtml(this.config.summary) : null,
|
|
56829
|
+
icon: this.config.icon || null,
|
|
56830
|
+
image: this.config.image || null,
|
|
56831
|
+
fields: this.config.fields || [],
|
|
56832
|
+
joined: this.config.createdAt,
|
|
56833
|
+
pinned,
|
|
56834
|
+
kind: this.config.kind
|
|
56835
|
+
});
|
|
56836
|
+
const digest = node_crypto_default.createHash("sha256").update(html).digest("hex").slice(0, 32);
|
|
56837
|
+
const seen = this.store.read("published.json", {});
|
|
56838
|
+
if (!force && seen.pageDigest === digest) return false;
|
|
56839
|
+
await writeProfilePage(this.remote, urls, html);
|
|
56840
|
+
this.store.write("published.json", { ...this.store.read("published.json", {}), pageDigest: digest });
|
|
56841
|
+
return true;
|
|
56842
|
+
}
|
|
56764
56843
|
async publishProfile({ force = false } = {}) {
|
|
56765
56844
|
const { urls } = this;
|
|
56766
56845
|
const host = new URL(urls.base).host;
|
|
@@ -56777,6 +56856,7 @@ var Publisher = class {
|
|
|
56777
56856
|
version: AGENT_VERSION,
|
|
56778
56857
|
moderators: this.config.moderators || []
|
|
56779
56858
|
})).digest("hex").slice(0, 32);
|
|
56859
|
+
await this.publishProfilePage({ force });
|
|
56780
56860
|
if (!force && this.store.read("published.json", {}).surfaceDigest === surface) {
|
|
56781
56861
|
this.log("profile unchanged \u2014 nothing republished");
|
|
56782
56862
|
return { unreachable: [], updated: 0, skipped: true };
|
|
@@ -56784,7 +56864,7 @@ var Publisher = class {
|
|
|
56784
56864
|
await writeWebfinger(
|
|
56785
56865
|
this.remote,
|
|
56786
56866
|
urls,
|
|
56787
|
-
jrd({ handle: this.config.handle, host, actor: urls.actor })
|
|
56867
|
+
jrd({ handle: this.config.handle, host, actor: urls.actor, page: urls.profileHtml })
|
|
56788
56868
|
);
|
|
56789
56869
|
await writeHostMeta(this.remote, urls, hostMeta(urls.base));
|
|
56790
56870
|
const nodeinfoDocUrl = urls.home + "ap/nodeinfo-2.0";
|
|
@@ -56803,13 +56883,6 @@ var Publisher = class {
|
|
|
56803
56883
|
);
|
|
56804
56884
|
}
|
|
56805
56885
|
if (gwActive) await this.publishGatewayPolicy();
|
|
56806
|
-
await writeProfilePage(this.remote, urls, profilePageHtml({
|
|
56807
|
-
name: this.config.name || this.config.handle,
|
|
56808
|
-
address: webfingerHost(urls.base) ? `@${this.config.handle}@${host}` : urls.actor,
|
|
56809
|
-
summary: this.config.summary ? contentHtml(this.config.summary) : null,
|
|
56810
|
-
icon: this.config.icon || null,
|
|
56811
|
-
kind: this.config.kind
|
|
56812
|
-
}));
|
|
56813
56886
|
try {
|
|
56814
56887
|
const wrote = await linkInWebIdProfile(this.remote, {
|
|
56815
56888
|
actorUrl: urls.actor,
|
|
@@ -64145,6 +64218,9 @@ async function pinStatus(agent2, s, pinned) {
|
|
|
64145
64218
|
if (s.actor !== agent2.publisher.urls.actor) throw new Error("not your status");
|
|
64146
64219
|
const updated = agent2.store.updateStatus(s.noteId, { pinned: !!pinned });
|
|
64147
64220
|
await agent2.publisher.publishFeatured().catch((e) => agent2.log?.(`featured: ${e.message}`));
|
|
64221
|
+
if (agent2.publisher.publishProfilePage) {
|
|
64222
|
+
await agent2.publisher.publishProfilePage().catch((e) => agent2.log?.(`profile page: ${e.message}`));
|
|
64223
|
+
}
|
|
64148
64224
|
agent2.publisher.publishProfile?.().catch(() => {
|
|
64149
64225
|
});
|
|
64150
64226
|
return updated || s;
|
|
@@ -65613,7 +65689,7 @@ function account(api, actorUrl, { selfAcct } = {}) {
|
|
|
65613
65689
|
bot: false,
|
|
65614
65690
|
discoverable: true,
|
|
65615
65691
|
group: self2 ? api.store.getConfig()?.kind === "group" : cached.type === "Group",
|
|
65616
|
-
created_at: "2026-01-01T00:00:00.000Z",
|
|
65692
|
+
created_at: self2 && api.store.getConfig()?.createdAt || "2026-01-01T00:00:00.000Z",
|
|
65617
65693
|
note: (self2 ? api.store.getConfig()?.summary : cached.summary) || "",
|
|
65618
65694
|
url: actorUrl,
|
|
65619
65695
|
uri: actorUrl,
|
|
@@ -70538,6 +70614,7 @@ var BrowserAgent = class _BrowserAgent {
|
|
|
70538
70614
|
try {
|
|
70539
70615
|
await this.store.load({ force: true }).catch((e) => this.log(`re-reading state: ${e.message}`));
|
|
70540
70616
|
await this.publisher.healStatuses().catch((e) => this.log(`healing the timeline index: ${e.message}`));
|
|
70617
|
+
await this.publisher.publishProfilePage().catch((e) => this.log(`profile page: ${e.message}`));
|
|
70541
70618
|
this.deliverer?.startQueue?.();
|
|
70542
70619
|
await this.publisher.publishProfile();
|
|
70543
70620
|
await this.store.flush?.();
|