feat: pin a server from onboarding, derived from the address
This commit is contained in:
parent
3576e48b99
commit
1385ecb56c
3 changed files with 80 additions and 16 deletions
|
|
@ -167,23 +167,48 @@ $("restore-identity").onclick = async () => {
|
|||
return status(err.message, true);
|
||||
}
|
||||
showApp();
|
||||
// The address field is optional: restoring must succeed on its own even if
|
||||
// this fails (server not pinned yet, offline, typo) — recall can always be
|
||||
// retried from settings afterward.
|
||||
// The key/address fields are both optional, and neither can block the
|
||||
// identity restore that already succeeded above — a typo, an offline server
|
||||
// or an unregistered address just leaves that one step to retry from
|
||||
// settings afterward. No separate host field: it's already the part of the
|
||||
// address after the @, so asking for it twice would just be one more place
|
||||
// to enter it wrong (exactly how "pinned randogoth@smol.place" happened —
|
||||
// a whole address typed into what was meant to be a bare host). Pinning has
|
||||
// to happen before recall is attempted, since recall requires a pin.
|
||||
const keyText = $("restore-key").value.trim();
|
||||
const addressText = $("restore-address").value.trim();
|
||||
let message = "identity restored.", warn = false;
|
||||
const lines = ["identity restored."];
|
||||
let warn = false;
|
||||
|
||||
if (keyText) {
|
||||
if (!addressText) {
|
||||
lines.push("enter the address above too — its server is derived from the part after the @.");
|
||||
warn = true;
|
||||
} else {
|
||||
try {
|
||||
const host = parseAddress(addressText).host;
|
||||
pinServerFromText(host, keyText);
|
||||
lines.push(`pinned ${host}.`);
|
||||
} catch (err) {
|
||||
lines.push(err.message);
|
||||
warn = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (addressText) {
|
||||
try {
|
||||
const addr = await recallAccount(addressText);
|
||||
message += `\nrecalled ${addr.short}`;
|
||||
lines.push(`recalled ${addr.short}.`);
|
||||
renderAccount();
|
||||
renderSettings();
|
||||
} catch (err) {
|
||||
message += `\ncouldn't recall that address: ${err.message}`;
|
||||
lines.push(`couldn't recall that address: ${err.message}`);
|
||||
warn = true;
|
||||
}
|
||||
}
|
||||
status(message, warn);
|
||||
|
||||
status(lines.join("\n"), warn);
|
||||
};
|
||||
|
||||
// --- header ---------------------------------------------------------------------
|
||||
|
|
@ -852,14 +877,32 @@ function renderSettings() {
|
|||
].filter(node => node != null));
|
||||
}
|
||||
|
||||
// Shared by the settings pin form and the optional host/key fields on setup —
|
||||
// same validation, same unconditional pin. Both are direct, deliberate entry
|
||||
// of a specific key (unlike importData's conflict-averse merge), so overwrite
|
||||
// is the expected behavior here, not something to guard against.
|
||||
function pinServerFromText(hostText, keyText) {
|
||||
const host = hostText.trim().toLowerCase();
|
||||
// A pin is keyed by bare host — connect() strips the port before looking one
|
||||
// up (parseAddress splits user@host:port into three), so "user@host" or
|
||||
// "host:port" here would silently pin a string nothing ever looks up again,
|
||||
// exactly the mistake that produced "pinned <address>" followed by a
|
||||
// confusing "no pinned key" from recall right after.
|
||||
if (host.includes("@"))
|
||||
throw new SmolError(`"${host}" looks like an address, not a server — the part after the @ goes here`);
|
||||
if (host.includes(":"))
|
||||
throw new SmolError(`"${host}": a pin is by host only, no port — just the part before the colon`);
|
||||
const key = b32decode(keyText.trim());
|
||||
if (key.length !== 32) throw new SmolError(`server key is ${key.length} bytes, expected 32`);
|
||||
store.pinServer(host, key);
|
||||
return host;
|
||||
}
|
||||
|
||||
$("pin-form").onsubmit = (event) => {
|
||||
event.preventDefault();
|
||||
const form = event.target;
|
||||
try {
|
||||
const host = form.host.value.trim().toLowerCase();
|
||||
const key = b32decode(form.key.value.trim());
|
||||
if (key.length !== 32) throw new SmolError(`server key is ${key.length} bytes, expected 32`);
|
||||
store.pinServer(host, key);
|
||||
const host = pinServerFromText(form.host.value, form.key.value);
|
||||
status(`pinned ${host}`);
|
||||
renderSettings();
|
||||
form.reset();
|
||||
|
|
@ -1034,6 +1077,12 @@ function applyPresetServer() {
|
|||
} catch { /* malformed preset: leave unpinned rather than block boot */ }
|
||||
}
|
||||
|
||||
// A preset deployment already knows its own server, so asking again on the
|
||||
// setup screen would be redundant — and confusing in the other direction
|
||||
// ("why do I need to type this if it's supposed to be automatic?"). A generic
|
||||
// deployment has no preset, so this stays the only way to pin from onboarding.
|
||||
if (PRESET_SERVER) $("restore-key-label").hidden = true;
|
||||
|
||||
applyPresetServer();
|
||||
applyTheme();
|
||||
store.identity() ? showApp() : showSetup();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue