fedipod 1.30.0 → 1.32.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/README.md +1 -1
- package/gateway.md +37 -0
- package/gui.md +2 -2
- package/lib/gateway/front-core.mjs +34 -14
- package/lib/gateway/gateway-core.mjs +15 -6
- package/lib/gateway/notices.mjs +84 -0
- package/lib/gateway/quiet.mjs +184 -0
- package/package.json +1 -1
- package/scripts/stage-site.mjs +6 -3
- package/web/admin/bar.css +44 -10
- package/web/admin/client/index.html +29 -2
- package/web/admin/gateway.js +19 -0
- package/web/admin/index.html +46 -3
- package/web/admin/notices-bar.js +90 -0
- package/web/admin/record.js +1 -0
- package/web/admin/setup/index.html +29 -2
- package/web/admin/upkeep.js +5 -1
- package/web/app/admin-facade.mjs +24 -0
- package/web/app/agent.mjs +48 -0
- package/web/app/boot.mjs +2 -0
- package/web/app/dist/boot.js +3 -1
- package/web/app/dist/boot.js.map +2 -2
- package/web/app/dist/sw.js +86 -1
- package/web/app/dist/sw.js.map +3 -3
- package/web/front/admin.html +3 -1
- package/web/front/notices.html +69 -0
- package/web/front/notices.js +107 -0
package/web/app/dist/sw.js
CHANGED
|
@@ -69919,6 +69919,8 @@ var ADMIN_PATHS = /* @__PURE__ */ new Set([
|
|
|
69919
69919
|
"/park",
|
|
69920
69920
|
"/revive",
|
|
69921
69921
|
"/takeover",
|
|
69922
|
+
"/gateway/pause",
|
|
69923
|
+
"/gateway/close",
|
|
69922
69924
|
"/fediacct/connect",
|
|
69923
69925
|
"/fediacct/disconnect",
|
|
69924
69926
|
"/fediacct/callback"
|
|
@@ -70023,7 +70025,11 @@ var AdminFacade = class {
|
|
|
70023
70025
|
frontActor: g?.frontActor || null,
|
|
70024
70026
|
mode: g?.mode || "off",
|
|
70025
70027
|
hasSecret: !!g?.hmacSecret,
|
|
70026
|
-
stats: a.store.read("gateway-stats.json", { verified: 0, unverified: 0, lastAt: null })
|
|
70028
|
+
stats: a.store.read("gateway-stats.json", { verified: 0, unverified: 0, lastAt: null }),
|
|
70029
|
+
// The account's standing at the gateway — paused, closed — as the
|
|
70030
|
+
// gateway last said it (agent.mjs tellGateway). The record page
|
|
70031
|
+
// shows its pause and close controls only when this is here.
|
|
70032
|
+
standing: a.gatewayStanding || null
|
|
70027
70033
|
});
|
|
70028
70034
|
}
|
|
70029
70035
|
case "/deadletter":
|
|
@@ -70167,6 +70173,24 @@ var AdminFacade = class {
|
|
|
70167
70173
|
if (!await a.requestTakeover?.()) return json2(503, { error: "the lease could not be taken \u2014 the pod refused the write" });
|
|
70168
70174
|
return json2(200, { ok: true, mode: a.status().mode });
|
|
70169
70175
|
}
|
|
70176
|
+
// The account's standing at its gateway: paused by its owner, or closed
|
|
70177
|
+
// for good (front-core: accounts that go quiet). Both go to the gateway
|
|
70178
|
+
// with the pod session as proof, and its answer is the new standing.
|
|
70179
|
+
case "/gateway/pause": {
|
|
70180
|
+
if (typeof body.paused !== "boolean") return json2(400, { error: "paused must be true or false" });
|
|
70181
|
+
if (!a.pauseAtGateway || !a.gatewayApi) return json2(501, { error: "this account is not at a gateway" });
|
|
70182
|
+
const r = await a.pauseAtGateway(body.paused);
|
|
70183
|
+
if (!r) return json2(502, { error: "the gateway could not be reached" });
|
|
70184
|
+
return json2(r.status === 200 ? 200 : r.status, r);
|
|
70185
|
+
}
|
|
70186
|
+
case "/gateway/close": {
|
|
70187
|
+
const handle7 = String(a.store.getConfig()?.handle || "").toLowerCase();
|
|
70188
|
+
if (!handle7 || String(body.confirm || "").toLowerCase() !== handle7) return json2(400, { error: "type the handle to confirm" });
|
|
70189
|
+
if (!a.closeAtGateway || !a.gatewayApi) return json2(501, { error: "this account is not at a gateway" });
|
|
70190
|
+
const r = await a.closeAtGateway();
|
|
70191
|
+
if (!r) return json2(502, { error: "the gateway could not be reached" });
|
|
70192
|
+
return json2(r.status === 200 ? 200 : r.status, r);
|
|
70193
|
+
}
|
|
70170
70194
|
// Going quiet, and coming back. The record page's active/parked select.
|
|
70171
70195
|
case "/park": {
|
|
70172
70196
|
if (!await a.requestTakeover?.()) return json2(503, { error: "another device is active for this pod \u2014 park from there" });
|
|
@@ -71708,10 +71732,46 @@ var BrowserAgent = class _BrowserAgent {
|
|
|
71708
71732
|
this.log("took over from the other device (action here)");
|
|
71709
71733
|
return true;
|
|
71710
71734
|
}
|
|
71735
|
+
// One call to the gateway's owner API, proved with the pod session. What
|
|
71736
|
+
// comes back on 200 or 410 is the account's standing there, kept for the
|
|
71737
|
+
// manage page; anything else is logged and forgotten.
|
|
71738
|
+
async tellGateway(what, body) {
|
|
71739
|
+
if (!this.gatewayApi) return null;
|
|
71740
|
+
let res;
|
|
71741
|
+
try {
|
|
71742
|
+
res = await this.sessionFetch(`${this.gatewayApi}/${what}`, {
|
|
71743
|
+
method: "POST",
|
|
71744
|
+
headers: { "content-type": "application/json" },
|
|
71745
|
+
body: JSON.stringify(body)
|
|
71746
|
+
});
|
|
71747
|
+
} catch (e) {
|
|
71748
|
+
this.log(`gateway ${what}: ${e.message}`);
|
|
71749
|
+
return null;
|
|
71750
|
+
}
|
|
71751
|
+
const json2 = await res.json().catch(() => ({}));
|
|
71752
|
+
const out = { status: res.status, ...json2 };
|
|
71753
|
+
if (res.status === 200 || res.status === 410) this.gatewayStanding = out;
|
|
71754
|
+
else this.log(`gateway ${what}: ${res.status} ${json2.error || ""}`);
|
|
71755
|
+
return out;
|
|
71756
|
+
}
|
|
71757
|
+
openAtGateway() {
|
|
71758
|
+
return this.tellGateway("open", { handle: this.doorKey });
|
|
71759
|
+
}
|
|
71760
|
+
pauseAtGateway(paused) {
|
|
71761
|
+
return this.tellGateway("pause", { handle: this.doorKey, paused: !!paused });
|
|
71762
|
+
}
|
|
71763
|
+
closeAtGateway() {
|
|
71764
|
+
return this.tellGateway("close", { handle: this.doorKey, confirm: true });
|
|
71765
|
+
}
|
|
71766
|
+
static OPEN_EVERY_MS = 60 * 6e4;
|
|
71711
71767
|
// Become the active agent: renew the lease, then start what a viewer skips —
|
|
71712
71768
|
// publish the face, drain the inbox, run the mirrors.
|
|
71713
71769
|
async goActive() {
|
|
71714
71770
|
this.viewer = false;
|
|
71771
|
+
clearInterval(this._openTimer);
|
|
71772
|
+
this._openTimer = setInterval(() => {
|
|
71773
|
+
this.openAtGateway();
|
|
71774
|
+
}, _BrowserAgent.OPEN_EVERY_MS);
|
|
71715
71775
|
this.lease.onLost = () => this.demote();
|
|
71716
71776
|
this.lease.startRenewal();
|
|
71717
71777
|
try {
|
|
@@ -71738,6 +71798,8 @@ var BrowserAgent = class _BrowserAgent {
|
|
|
71738
71798
|
this.viewer = true;
|
|
71739
71799
|
this.log("another device took over \u2014 read-only here");
|
|
71740
71800
|
this.lease.stopRenewal();
|
|
71801
|
+
clearInterval(this._openTimer);
|
|
71802
|
+
this._openTimer = null;
|
|
71741
71803
|
this.intake?.stop?.();
|
|
71742
71804
|
this.deliverer?.stop?.();
|
|
71743
71805
|
this.importer?.stop?.();
|
|
@@ -71851,6 +71913,29 @@ var BrowserAgent = class _BrowserAgent {
|
|
|
71851
71913
|
sessionFetch: session.fetch,
|
|
71852
71914
|
onGone: () => this.publisher.publishCollections({ followers: true })
|
|
71853
71915
|
});
|
|
71916
|
+
this.gatewayApi = null;
|
|
71917
|
+
try {
|
|
71918
|
+
if (config.gateway?.url) this.gatewayApi = `${new URL(config.gateway.url).origin}/api`;
|
|
71919
|
+
} catch {
|
|
71920
|
+
this.gatewayApi = null;
|
|
71921
|
+
}
|
|
71922
|
+
this.doorKey = doorKeyOf(config.gateway?.url) || config.handle;
|
|
71923
|
+
this.gatewayStanding = null;
|
|
71924
|
+
const standing = await this.openAtGateway();
|
|
71925
|
+
if (standing?.status === 410) {
|
|
71926
|
+
const host = (() => {
|
|
71927
|
+
try {
|
|
71928
|
+
return new URL(this.gatewayApi).host;
|
|
71929
|
+
} catch {
|
|
71930
|
+
return "the gateway";
|
|
71931
|
+
}
|
|
71932
|
+
})();
|
|
71933
|
+
const address = this.doorKey.includes("@") ? `@${this.doorKey}` : `@${this.doorKey}@${host}`;
|
|
71934
|
+
const why = standing.closedBy === "owner" ? "you closed it" : "nothing opened it for six months";
|
|
71935
|
+
const e = new Error(`${address} is closed at ${host}: ${why}, and a closed address does not come back. Everything on your pod is untouched. The sign-in used here was ${webId}.`);
|
|
71936
|
+
e.code = "address-closed";
|
|
71937
|
+
throw e;
|
|
71938
|
+
}
|
|
71854
71939
|
this.publisher = new Publisher({
|
|
71855
71940
|
config: this.store.getConfig(),
|
|
71856
71941
|
remote: this.remote,
|