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:
co-authored by
Claude Fable 5.1
parent
27f9665606
commit
f093b322ee
+12
-3
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user