diff options
| author | tv <tv@krebsco.de> | 2026-11-09 03:23:10 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-09 04:27:48 +0100 |
| commit | b52d4b6181e7fcf806a655d218e74de71b54cbf8 (patch) | |
| tree | 3cac1fb1adba43feb899159fdecb05e43cac3bff /src/api/dashboard | |
| parent | acafad962b5eea4772479d7f44912038d71e528e (diff) | |
dashboard: the replay window and archive retention in the remake dialog
The remake dialog set the kind, its input, the settle time and the switch delay, but not
the replay window or the archive retention. Now each has two fields, epochs and an age,
their defaults as placeholders: both empty sends nothing (the node's defaults), one
given sends that limit alone, as the API reads it; in the body, or for an import in the
query (`replayWindow`, `archiveRetention`). Epochs that aren't a whole number are
refused in the page.
Diffstat (limited to 'src/api/dashboard')
| -rw-r--r-- | src/api/dashboard/dashboard.html | 32 |
1 files changed, 32 insertions, 0 deletions
diff --git a/src/api/dashboard/dashboard.html b/src/api/dashboard/dashboard.html index d173418..dbf5480 100644 --- a/src/api/dashboard/dashboard.html +++ b/src/api/dashboard/dashboard.html @@ -418,6 +418,12 @@ title="How long nodes wait after the vote succeeded before switching, so the commit reaches every node first: seconds, or e.g. 500ms, 1m; empty: one keepalive interval"></label> <label title="Accept a switch delay below a keepalive interval (?force=switchDelay): slow nodes may miss the commit before the others switch, and have to rejoin"><input type="checkbox" id="remake-force-switch-delay"> even below a keepalive interval</label> + <label title="How far behind a returning node may be and still have its writes carried over automatically: at most this many remakes behind, or the first it missed proposed at most this long ago (seconds, or e.g. 12h, 7d). Both empty: 1 epoch or 7 days; one given: that limit alone">replay window + <input id="remake-replay-epochs" size="3" inputmode="numeric" placeholder="1"> epochs or + <input id="remake-replay-age" size="5" placeholder="7d"></label> + <label title="How long nodes keep the old document: this many epochs, or this long (seconds, or e.g. 12h, 30d). Both empty: 2 epochs or 30 days; one given: that limit alone">archive retention + <input id="remake-archive-epochs" size="3" inputmode="numeric" placeholder="2"> epochs or + <input id="remake-archive-age" size="5" placeholder="30d"></label> <p class="note">Writes made meanwhile that no rule covers are quarantined on the node that made them.</p> <div class="actions"><button type="submit" class="toggle">propose</button> @@ -4160,6 +4166,20 @@ document.getElementById("remake-json-file").addEventListener("change", event => file.text().then(text => { document.getElementById("remake-json").value = text; }) .catch(error => remakeResult("Reading " + file.name + ": " + error, true)); }); +// A retention from its two fields (`epochs` and `age`), or null for the node's default +// (both empty); throws where the epochs aren't a whole number. +function remakeRetention(name, label) { + const epochs = document.getElementById("remake-" + name + "-epochs").value.trim(); + const age = document.getElementById("remake-" + name + "-age").value.trim(); + if (!epochs && !age) return null; + const retention = {}; + if (epochs) { + if (!/^\d+$/.test(epochs)) throw new Error("The " + label + "'s epochs aren't a whole number."); + retention.epochs = Number(epochs); + } + if (age) retention.age = age; + return retention; +} remakeForm.addEventListener("submit", event => { event.preventDefault(); const kind = document.getElementById("remake-kind").value; @@ -4167,6 +4187,14 @@ remakeForm.addEventListener("submit", event => { const switchDelay = document.getElementById("remake-switch-delay").value.trim(); const force = document.getElementById("remake-force-switch-delay").checked ? "force=switchDelay" : ""; + let replayWindow, archiveRetention; + try { + replayWindow = remakeRetention("replay", "replay window"); + archiveRetention = remakeRetention("archive", "archive retention"); + } catch (error) { + remakeResult(error.message, true); + return; + } let request; if (kind === "import") { // The document as it is (octet-stream): its options in the query. @@ -4175,6 +4203,8 @@ remakeForm.addEventListener("submit", event => { const options = new URLSearchParams(force); if (settle) options.set("settle", settle); if (switchDelay) options.set("switchDelay", switchDelay); + if (replayWindow) options.set("replayWindow", JSON.stringify(replayWindow)); + if (archiveRetention) options.set("archiveRetention", JSON.stringify(archiveRetention)); const rules = remakeRules(); if (rules.length) options.set("rules", JSON.stringify(rules)); const query = options.size ? "?" + options : ""; @@ -4184,6 +4214,8 @@ remakeForm.addEventListener("submit", event => { const body = { kind }; if (settle) body.settle = settle; if (switchDelay) body.switchDelay = switchDelay; + if (replayWindow) body.replayWindow = replayWindow; + if (archiveRetention) body.archiveRetention = archiveRetention; if (kind === "replace") { let parsed; try { |
