Bug bash fixes: hash routing + back button, hub-only pages reset on machine switch, exited terminals reap after 10 min, self-update refuses while busy unless forced, touch-sized controls, power confirm by machine name, down interfaces collapsed and empty modules hidden, load label, offline backoff, cron log newest first, procs empty message, timer timestamp parsing

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-18 17:46:16 -07:00
co-authored by Claude Fable 5.1
parent 27f9665606
commit f093b322ee
14 changed files with 126 additions and 36 deletions
+12 -3
View File
@@ -7,6 +7,11 @@ let base = ''
export function setMachine(name: string | null) { base = name ? `/api/nodes/${name}` : '' }
export const apiPath = (p: string) => (base && p.startsWith('/api/') ? base + p.slice(4) : p)
// A machine that just failed to answer is left alone for a few seconds instead of
// being hit again on every tick of every page.
const downUntil: Record<string, number> = {}
function machineOf(p: string): string | null { const m = p.match(/^\/api\/nodes\/([a-z0-9-]+)\//); return m ? m[1] : null }
let inflight = 0
function track(delta: number) {
inflight += delta
@@ -14,10 +19,13 @@ function track(delta: number) {
}
async function req<T>(method: string, path: string, body?: unknown, local = false): Promise<T> {
const target = local ? path : apiPath(path)
const mach = machineOf(target)
if (mach && downUntil[mach] > Date.now()) throw new Error(`${mach} unreachable (retrying shortly)`)
track(1)
let r: Response
try {
r = await fetch(local ? path : apiPath(path), {
r = await fetch(target, {
method,
credentials: 'same-origin',
headers: body !== undefined ? { 'Content-Type': 'application/json' } : undefined,
@@ -28,6 +36,7 @@ async function req<T>(method: string, path: string, body?: unknown, local = fals
throw new Error('network error')
}
track(-1)
if (r.status === 502 && mach) downUntil[mach] = Date.now() + 5000
if (r.status === 401) {
let msg = 'unauthenticated'
try { msg = (await r.json()).error ?? msg } catch { /* ignore */ }
@@ -167,8 +176,8 @@ export const api = {
me: () => req<Me>('GET', '/api/me'),
machines: () => req<{ nodes: MachineStatus[]; host: string; fingerprint?: string }>('GET', '/api/nodes', undefined, true),
addMachine: (name: string, url: string, password: string) => req<MachineStatus>('POST', '/api/nodes', { name, url, password }, true),
updateMachine: (name: string) => req<PushResult>('POST', `/api/machines/${encodeURIComponent(name)}/update`, undefined, true),
updateAllMachines: () => req<PushResult[]>('POST', '/api/machines-update-all', undefined, true),
updateMachine: (name: string, force = false) => req<PushResult>('POST', `/api/machines/${encodeURIComponent(name)}/update${force ? '?force=1' : ''}`, undefined, true),
updateAllMachines: (force = false) => req<PushResult[]>('POST', `/api/machines-update-all${force ? '?force=1' : ''}`, undefined, true),
changePassword: (current: string, password: string) => req<void>('POST', '/api/password', { current, password }, true),
tokens: () => req<{ name: string; created: string }[]>('GET', '/api/tokens', undefined, true),
revokeToken: (name: string) => req<void>('DELETE', `/api/tokens/${encodeURIComponent(name)}`, undefined, true),