Update from hub: node self-update endpoint, hub push per machine / all, push-nodes CLI run by make deploy, outdated badges + Update buttons on Machines
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
e9be94e32b
commit
254e0cdd58
+18
-1
@@ -15,6 +15,8 @@ export default function Machines({ hub, onChanged, onOpen }: { hub: Me; onChange
|
||||
const [pw, setPw] = useState('')
|
||||
const [adding, setAdding] = useState(false)
|
||||
const [addErr, setAddErr] = useState('')
|
||||
const norm = (v?: string) => (v ?? '').replace(/^v/, '').replace(/-dirty$/, '')
|
||||
const outdated = (m: MachineStatus) => m.online && norm(m.version) !== norm(hub.version)
|
||||
|
||||
const load = useCallback(async () => {
|
||||
try { const r = await api.machines(); setList(r.nodes); setFp(r.fingerprint); setErr('') } catch (e) { setErr(e instanceof Error ? e.message : 'failed'); setList([]) }
|
||||
@@ -32,6 +34,19 @@ export default function Machines({ hub, onChanged, onOpen }: { hub: Me; onChange
|
||||
} catch (e) { setAddErr(e instanceof Error ? e.message : 'failed') }
|
||||
finally { setAdding(false) }
|
||||
}
|
||||
async function update(m: MachineStatus) {
|
||||
if (!window.confirm(`Update ${m.name} to ${norm(hub.version)}? Its Ham Lab restarts; open terminals there are lost.`)) return
|
||||
try { const r = await api.updateMachine(m.name); toast(`${m.name} updated to ${r.version}, restarting`); setTimeout(load, 4000) } catch (e) { toast(`${m.name}: ${e instanceof Error ? e.message : 'failed'}`, 'err') }
|
||||
}
|
||||
async function updateAll() {
|
||||
const n = list?.filter(outdated).length ?? 0
|
||||
if (!window.confirm(`Update ${n} machine${n === 1 ? '' : 's'} to ${norm(hub.version)}?`)) return
|
||||
try {
|
||||
const res = await api.updateAllMachines()
|
||||
for (const r of res) toast(r.ok ? `${r.name} → ${r.version}` : `${r.name}: ${r.error}`, r.ok ? 'ok' : 'err')
|
||||
setTimeout(load, 4000)
|
||||
} catch (e) { toast(e instanceof Error ? e.message : 'failed', 'err') }
|
||||
}
|
||||
async function remove(m: MachineStatus) {
|
||||
if (!window.confirm(`Remove ${m.name}? Its access token will be revoked. Nothing on ${m.name} itself is changed.`)) return
|
||||
try { await api.removeMachine(m.name); toast(`${m.name} removed`); await load(); onChanged() } catch (e) { toast(e instanceof Error ? e.message : 'failed', 'err') }
|
||||
@@ -46,7 +61,7 @@ export default function Machines({ hub, onChanged, onOpen }: { hub: Me; onChange
|
||||
<div className="muted small">{fp ? <>Certificate <span className="mono">{fp}</span></> : 'Serving plain HTTP (behind a reverse proxy). Set "tls": "auto" in config.json for a self-signed certificate if another hub should manage this host.'}</div>
|
||||
</section>
|
||||
|
||||
<div className="dash-bar"><h2 className="section">Managed machines · {list.length}</h2></div>
|
||||
<div className="dash-bar"><h2 className="section">Managed machines · {list.length}</h2><span className="spacer" />{list.some(outdated) && <Btn className="btn on" onClick={updateAll}>Update {list.filter(outdated).length} outdated</Btn>}</div>
|
||||
{err && <div className="error">{err}</div>}
|
||||
{list.length === 0 && <div className="notice">No machines yet. Install Ham Lab on another host, set its password, then add it below.</div>}
|
||||
<div className="cards">
|
||||
@@ -58,6 +73,7 @@ export default function Machines({ hub, onChanged, onOpen }: { hub: Me; onChange
|
||||
<span className="muted small mono">{m.url}</span>
|
||||
<span className="spacer" />
|
||||
{m.reboot_needed && <span className="badge" title="reboot required">⟳</span>}
|
||||
{outdated(m) && <span className="badge bad" title={`running ${norm(m.version)}, hub has ${norm(hub.version)}`}>outdated</span>}
|
||||
</div>
|
||||
<div className="props">
|
||||
<div><span className="k">Status</span><span className={m.online ? 'good' : 'warn-text'}>{m.online ? 'online' : m.error || 'offline'}</span></div>
|
||||
@@ -69,6 +85,7 @@ export default function Machines({ hub, onChanged, onOpen }: { hub: Me; onChange
|
||||
</div>
|
||||
<div className="svc-actions">
|
||||
<button className="btn on" disabled={!m.online} onClick={() => onOpen(m.name)}>Open</button>
|
||||
{outdated(m) && <Btn className="btn" onClick={() => update(m)}>Update</Btn>}
|
||||
<Btn className="btn danger" onClick={() => remove(m)}>Remove</Btn>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user