Fix worker removal and improve web UI defaults

- Parse optional http:// scheme prefix on worker specs so a scheme-typed
  IP is stored cleanly and its remove button works.
- Delete workers by their stored DB host/port instead of re-parsing the
  rendered URL, and surface add/remove errors in the UI.
- Default new builds to git URL instead of zip upload.
- Show worker hostname/IP from agent status in the address column.
- Always include DB host/port in worker API output.
This commit is contained in:
Brett Williams
2026-08-31 15:40:01 -05:00
parent f73a5bbb2f
commit 7a36ae083c
3 changed files with 44 additions and 19 deletions
+21 -9
View File
@@ -63,7 +63,8 @@ async function refreshWorkers() {
box.innerHTML = `<table>
<tr><th>Address</th><th>Status</th><th>OS / CPU</th><th></th></tr>` +
workers.map((w) => {
const addr = `${w.host || ""}:${w.port || ""}`;
const displayHost = w.online ? (w.hostname || w.ip || w.host) : w.host;
const addr = `${esc(displayHost)}:${esc(w.port || "")}`;
const status = w.online
? `<span class="badge done">ready · ${esc(w.status || "ok")}</span>`
: `<span class="badge failed">down</span>`;
@@ -72,7 +73,7 @@ async function refreshWorkers() {
<td><code>${esc(addr)}</code></td>
<td>${status}</td>
<td>${osCpu}</td>
<td><button class="remove-worker" data-url="${esc(w.url)}">Remove</button></td>
<td><button class="remove-worker" data-host="${esc(w.host)}" data-port="${esc(w.port)}">Remove</button></td>
</tr>`;
}).join("") + `</table>`;
} catch (_) {
@@ -85,6 +86,7 @@ $("#worker-form").addEventListener("submit", async (e) => {
const input = $("#worker-spec");
const spec = input.value.trim();
if (!spec) return;
const msg = $("#worker-msg");
try {
const resp = await fetch("/api/workers", {
method: "POST",
@@ -94,25 +96,35 @@ $("#worker-form").addEventListener("submit", async (e) => {
const data = await resp.json().catch(() => ({}));
if (!resp.ok) throw new Error(data.error || resp.statusText);
input.value = "";
msg.textContent = `Added worker ${spec}`;
msg.className = "msg";
refreshWorkers();
refreshCapabilities();
} catch (err) {
$("#worker-spec").value = "";
$("#worker-spec").placeholder = `Error: ${err.message}`;
input.value = "";
msg.textContent = `Error: ${err.message}`;
msg.className = "msg error";
}
});
$("#workers").addEventListener("click", async (e) => {
const btn = e.target.closest("button.remove-worker");
if (!btn) return;
const url = btn.dataset.url;
const host = url.replace(/^https?:\/\//, "").split(":")[0];
const port = url.replace(/^https?:\/\//, "").split(":")[1];
const msg = $("#worker-msg");
const host = btn.dataset.host;
const port = btn.dataset.port;
try {
await fetch(`/api/workers/${encodeURIComponent(port)}/${encodeURIComponent(host)}`, { method: "DELETE" });
const resp = await fetch(`/api/workers/${encodeURIComponent(port)}/${encodeURIComponent(host)}`, { method: "DELETE" });
const data = await resp.json().catch(() => ({}));
if (!resp.ok) throw new Error(data.error || resp.statusText);
msg.textContent = `Removed ${host}:${port}`;
msg.className = "msg";
refreshWorkers();
refreshCapabilities();
} catch (_) {}
} catch (err) {
msg.textContent = `Remove failed: ${err.message}`;
msg.className = "msg error";
}
});
async function refreshJobs() {
+7 -6
View File
@@ -19,6 +19,7 @@
<input type="text" id="worker-spec" placeholder="host:port (e.g. 192.168.1.40:9001)">
<button type="submit">Add worker</button>
</form>
<p id="worker-msg" class="msg"></p>
<div id="workers"></div>
</section>
@@ -28,18 +29,18 @@
<div class="field">
<label for="mode">Source</label>
<select id="mode">
<option value="upload">Upload zip</option>
<option value="git">Git repo</option>
<option value="upload">Upload zip</option>
</select>
</div>
<div class="field" id="upload-field">
<label for="file">Project zip</label>
<input type="file" id="file" accept=".zip">
</div>
<div class="field" id="url-field" hidden>
<div class="field" id="url-field">
<label for="url">Git URL</label>
<input type="text" id="url" placeholder="https://…">
</div>
<div class="field" id="upload-field" hidden>
<label for="file">Project zip</label>
<input type="file" id="file" accept=".zip">
</div>
<div class="fields-row">
<div class="field">
<label for="os">OS</label>