fedipod-server 0.20.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/lib/client/masto/render.mjs +1 -1
- package/lib/core/publisher/index.mjs +49 -10
- package/lib/core/social.mjs +3 -0
- package/lib/core/wire.mjs +39 -3
- package/lib/device/cli/commands/setup.mjs +1 -0
- package/lib/device/setup.mjs +1 -0
- 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 +85 -11
- package/web/app/dist/sw.js.map +2 -2
- package/web/app/signup.mjs +1 -0
- package/web/app/site/boot.js +1 -0
- package/web/app/site/sw.js +85 -11
- package/web/front/admin.js +5 -1
package/web/app/dist/sw.js
CHANGED
|
@@ -9797,9 +9797,30 @@ function actorDoc({
|
|
|
9797
9797
|
publicKey: { id: urls.actor + "#main-key", owner: urls.actor, publicKeyPem }
|
|
9798
9798
|
};
|
|
9799
9799
|
}
|
|
9800
|
-
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
|
+
}) {
|
|
9801
9811
|
const esc = (s) => String(s ?? "").replace(/[&<>"]/g, (c) => ({ ...HTML_ESCAPES, '"': """ })[c]);
|
|
9802
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");
|
|
9803
9824
|
return `<!doctype html>
|
|
9804
9825
|
<html lang="en">
|
|
9805
9826
|
<head>
|
|
@@ -9809,14 +9830,23 @@ function profilePageHtml({ name, address, summary = null, icon = null, kind = "p
|
|
|
9809
9830
|
<style>
|
|
9810
9831
|
:root { color-scheme: light dark; }
|
|
9811
9832
|
body { font: 112.5%/1.5 system-ui, sans-serif; max-width: 34rem; margin: 3rem auto; padding: 0 1rem; }
|
|
9812
|
-
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; }
|
|
9813
9835
|
h1 { margin: .5rem 0 0; }
|
|
9814
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; }
|
|
9815
9845
|
form { margin-top: 2rem; }
|
|
9816
9846
|
label { display: block; margin-bottom: .3rem; }
|
|
9817
9847
|
input { font: inherit; padding: .5rem; width: 14rem; max-width: 100%; }
|
|
9818
9848
|
button { font: inherit; padding: .5rem 1rem; }
|
|
9819
|
-
.hint { color: #
|
|
9849
|
+
.hint { color: #666; font-size: 1rem; }
|
|
9820
9850
|
.err { color: #b00020; }
|
|
9821
9851
|
.err:empty { display: none; }
|
|
9822
9852
|
@media (prefers-color-scheme: dark) {
|
|
@@ -9827,10 +9857,22 @@ button { font: inherit; padding: .5rem 1rem; }
|
|
|
9827
9857
|
</head>
|
|
9828
9858
|
<body>
|
|
9829
9859
|
<main>
|
|
9860
|
+
${image ? `<img class="header" src="${esc(image)}" alt="">` : ""}
|
|
9830
9861
|
${icon ? `<img class="avatar" src="${esc(icon)}" alt="">` : ""}
|
|
9831
9862
|
<h1>${esc(name)}</h1>
|
|
9832
9863
|
<p class="address">${esc(address)}</p>
|
|
9833
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>` : ""}
|
|
9834
9876
|
<p>This is ${what} on the Fediverse. To follow it, paste the address above
|
|
9835
9877
|
into the search box of Mastodon or any Fediverse app \u2014 or use the form.</p>
|
|
9836
9878
|
<form id="follow">
|
|
@@ -56764,6 +56806,40 @@ var Publisher = class {
|
|
|
56764
56806
|
oauthToken: this.clientOrigin ? `${this.clientOrigin}oauth/token` : null
|
|
56765
56807
|
});
|
|
56766
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
|
+
}
|
|
56767
56843
|
async publishProfile({ force = false } = {}) {
|
|
56768
56844
|
const { urls } = this;
|
|
56769
56845
|
const host = new URL(urls.base).host;
|
|
@@ -56780,6 +56856,7 @@ var Publisher = class {
|
|
|
56780
56856
|
version: AGENT_VERSION,
|
|
56781
56857
|
moderators: this.config.moderators || []
|
|
56782
56858
|
})).digest("hex").slice(0, 32);
|
|
56859
|
+
await this.publishProfilePage({ force });
|
|
56783
56860
|
if (!force && this.store.read("published.json", {}).surfaceDigest === surface) {
|
|
56784
56861
|
this.log("profile unchanged \u2014 nothing republished");
|
|
56785
56862
|
return { unreachable: [], updated: 0, skipped: true };
|
|
@@ -56806,13 +56883,6 @@ var Publisher = class {
|
|
|
56806
56883
|
);
|
|
56807
56884
|
}
|
|
56808
56885
|
if (gwActive) await this.publishGatewayPolicy();
|
|
56809
|
-
await writeProfilePage(this.remote, urls, profilePageHtml({
|
|
56810
|
-
name: this.config.name || this.config.handle,
|
|
56811
|
-
address: webfingerHost(urls.base) ? `@${this.config.handle}@${host}` : urls.actor,
|
|
56812
|
-
summary: this.config.summary ? contentHtml(this.config.summary) : null,
|
|
56813
|
-
icon: this.config.icon || null,
|
|
56814
|
-
kind: this.config.kind
|
|
56815
|
-
}));
|
|
56816
56886
|
try {
|
|
56817
56887
|
const wrote = await linkInWebIdProfile(this.remote, {
|
|
56818
56888
|
actorUrl: urls.actor,
|
|
@@ -64148,6 +64218,9 @@ async function pinStatus(agent2, s, pinned) {
|
|
|
64148
64218
|
if (s.actor !== agent2.publisher.urls.actor) throw new Error("not your status");
|
|
64149
64219
|
const updated = agent2.store.updateStatus(s.noteId, { pinned: !!pinned });
|
|
64150
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
|
+
}
|
|
64151
64224
|
agent2.publisher.publishProfile?.().catch(() => {
|
|
64152
64225
|
});
|
|
64153
64226
|
return updated || s;
|
|
@@ -65616,7 +65689,7 @@ function account(api, actorUrl, { selfAcct } = {}) {
|
|
|
65616
65689
|
bot: false,
|
|
65617
65690
|
discoverable: true,
|
|
65618
65691
|
group: self2 ? api.store.getConfig()?.kind === "group" : cached.type === "Group",
|
|
65619
|
-
created_at: "2026-01-01T00:00:00.000Z",
|
|
65692
|
+
created_at: self2 && api.store.getConfig()?.createdAt || "2026-01-01T00:00:00.000Z",
|
|
65620
65693
|
note: (self2 ? api.store.getConfig()?.summary : cached.summary) || "",
|
|
65621
65694
|
url: actorUrl,
|
|
65622
65695
|
uri: actorUrl,
|
|
@@ -70541,6 +70614,7 @@ var BrowserAgent = class _BrowserAgent {
|
|
|
70541
70614
|
try {
|
|
70542
70615
|
await this.store.load({ force: true }).catch((e) => this.log(`re-reading state: ${e.message}`));
|
|
70543
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}`));
|
|
70544
70618
|
this.deliverer?.startQueue?.();
|
|
70545
70619
|
await this.publisher.publishProfile();
|
|
70546
70620
|
await this.store.flush?.();
|