Files
restic-manager/web/templates/partials/fleet_update_inner.html
T
steve 6374201f6c
CI / Test (store) (pull_request) Successful in 5s
CI / Test (rest) (pull_request) Successful in 8s
CI / Build (windows/amd64) (pull_request) Successful in 7s
CI / Lint (pull_request) Successful in 11s
CI / Build (linux/amd64) (pull_request) Successful in 8s
CI / Build (linux/arm64) (pull_request) Successful in 7s
CI / Test (server-http) (pull_request) Successful in 1m35s
e2e / Playwright vs docker-compose (pull_request) Successful in 1m26s
Fix fleet update filters across refreshes
2026-08-22 12:04:26 +01:00

181 lines
8.5 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{/*
fleet_update_inner — inner panel for /settings/fleet-update.
Rendered both as part of the full page and as the htmx polling
fragment via /settings/fleet-update/partial.
Expects .Page to be a fleetUpdatePage struct (see fleet_update.go).
*/}}
{{define "fleet_update_inner"}}
{{$page := .Page}}
<div id="fleet-update-panel" class="mt-5"
hx-get="{{$page.PollURL}}"
hx-trigger="every 3s [document.visibilityState==='visible']"
hx-select="#fleet-update-panel"
hx-swap="outerHTML">
{{if and $page.Active (eq $page.Active.Status "running")}}
{{/* ---------- running state ---------- */}}
<div class="panel rounded-[7px] px-5 py-4">
<div class="flex items-baseline justify-between">
<div>
<span class="mono text-[12px] text-ink-fade">fleet update</span>
<span class="mono text-[12px] text-accent ml-2">running</span>
<span class="mono text-[11px] text-ink-fade ml-2">{{$page.Active.ID}}</span>
</div>
<form hx-post="/api/fleet-updates/{{$page.Active.ID}}/cancel" hx-swap="none">
<button class="btn btn-danger" type="submit"
onclick="return confirm('Cancel this fleet update? Hosts already updated stay updated; pending hosts will be skipped.');">
Cancel
</button>
</form>
</div>
<div class="text-[11.5px] text-ink-mute mt-1">
target <span class="mono text-ink-mid">{{$page.Active.TargetVersion}}</span>
{{if $page.Active.PauseAfterFirst}} · canary-first{{end}}
· started <span class="mono text-ink-mid">{{relTime $page.Active.StartedAt}}</span>
{{if $page.Active.CurrentHostID}}
· waiting on <span class="mono text-ink-mid">{{index $page.HostNames $page.Active.CurrentHostID}}</span>
{{end}}
</div>
</div>
{{template "fleet_update_rows" $page}}
{{else if $page.Active}}
{{/* ---------- terminal state (completed / halted / cancelled) ---------- */}}
<div class="panel rounded-[7px] px-5 py-4">
<div class="flex items-baseline justify-between">
<div>
<span class="mono text-[12px] text-ink-fade">last fleet update</span>
{{if eq $page.Active.Status "completed"}}
<span class="mono text-[12px] text-ok ml-2">completed</span>
{{else if eq $page.Active.Status "halted"}}
<span class="mono text-[12px] text-bad ml-2">halted</span>
{{else if eq $page.Active.Status "cancelled"}}
<span class="mono text-[12px] text-warn ml-2">cancelled</span>
{{else}}
<span class="mono text-[12px] text-ink-mid ml-2">{{$page.Active.Status}}</span>
{{end}}
<span class="mono text-[11px] text-ink-fade ml-2">{{$page.Active.ID}}</span>
</div>
</div>
<div class="text-[11.5px] text-ink-mute mt-1">
target <span class="mono text-ink-mid">{{$page.Active.TargetVersion}}</span>
· started <span class="mono text-ink-mid">{{relTime $page.Active.StartedAt}}</span>
{{if $page.Active.CompletedAt}} · finished <span class="mono text-ink-mid">{{relTime $page.Active.CompletedAt}}</span>{{end}}
</div>
{{if $page.Active.HaltedReason}}
<div class="text-[12px] text-bad mt-2">{{$page.Active.HaltedReason}}</div>
{{end}}
{{if eq $page.Active.Status "halted"}}
<div class="mt-3 flex gap-2 text-[12px]">
<a class="btn" href="#fleet-update-start-form">Resume remaining as new rollout</a>
{{range $page.ActiveRows}}{{if eq .Status "failed"}}<button class="btn" type="button" onclick="fleetSelectOnly('{{.HostID}}');document.getElementById('fleet-update-start-form').scrollIntoView()">Retry {{.HostName}}</button>{{end}}{{end}}
</div>
{{end}}
</div>
{{template "fleet_update_rows" $page}}
{{if gt (len $page.OutOfDateHosts) 0}}
<div class="mt-5">
{{template "fleet_update_idle_panel" $page}}
</div>
{{end}}
{{else}}
{{template "fleet_update_idle_panel" $page}}
{{end}}
</div>
{{end}}
{{define "fleet_update_rows"}}
{{$page := .}}
<div class="panel mt-3 rounded-[7px] overflow-hidden">
<div class="hairline grid items-baseline px-4 py-2.5 text-[11px] text-ink-fade uppercase tracking-[0.08em]"
style="grid-template-columns: 0.4fr 1.5fr 0.8fr 1.2fr 1.5fr; column-gap: 18px;">
<div>#</div>
<div>Host</div>
<div>Status</div>
<div>Job</div>
<div>Detail</div>
</div>
{{range $page.ActiveRows}}
<div class="grid items-center px-4 py-2.5 text-[12.5px] hairline"
style="grid-template-columns: 0.4fr 1.5fr 0.8fr 1.2fr 1.5fr; column-gap: 18px;">
<div class="mono text-ink-fade">{{.Position}}</div>
<div class="mono text-ink">{{if .HostName}}{{.HostName}}{{else}}{{.HostID}}{{end}}</div>
<div>
{{if eq .Status "pending"}}<span class="text-ink-fade">pending</span>
{{else if eq .Status "running"}}<span class="text-accent">running…</span>
{{else if eq .Status "succeeded"}}<span class="text-ok">succeeded</span>
{{else if eq .Status "failed"}}<span class="text-bad font-medium">failed</span>
{{else if eq .Status "skipped"}}<span class="text-ink-mute">skipped</span>
{{else}}<span class="text-ink-mute">{{.Status}}</span>{{end}}
</div>
<div>
{{if .JobID}}<a class="link mono text-[11.5px]" href="/jobs/{{.JobID}}">{{.JobID}}</a>{{else}}<span class="text-ink-fade"></span>{{end}}
</div>
<div class="mono text-[11.5px] text-ink-mute truncate" title="{{.FailedReason}}">{{.FailedReason}}</div>
</div>
{{end}}
</div>
{{end}}
{{define "fleet_update_idle_panel"}}
{{$page := .}}
<div class="panel rounded-[7px] px-5 py-4">
{{if eq (len $page.Candidates) 0}}
<div class="flex items-center gap-3">
<span class="dot dot-online"></span>
<div>
<div class="text-ink text-[14px] font-medium">No hosts enrolled.</div>
<div class="text-ink-mute text-[12px] mt-0.5">
Every online agent matches server version <span class="mono">{{$page.TargetVersion}}</span>.
</div>
</div>
</div>
{{else}}
<div class="flex items-baseline justify-between">
<h2 class="text-[14px] font-medium">{{len $page.OutOfDateHosts}} host{{if ne (len $page.OutOfDateHosts) 1}}s{{end}} out of date</h2>
<span class="mono text-[11px] text-ink-fade">target {{$page.TargetVersion}}</span>
</div>
<div class="mt-3 flex flex-wrap gap-2">
<input id="fleet-filter-name" class="field text-[12px]" placeholder="Filter name or tag" oninput="fleetFilter()">
<input id="fleet-filter-version" class="field text-[12px] mono" placeholder="Version" oninput="fleetFilter()">
<select id="fleet-filter-state" class="field text-[12px]" onchange="fleetFilter()">
<option value="all">All states</option><option value="eligible">Eligible</option><option value="excluded">Excluded</option>
</select>
<button type="button" class="btn" onclick="fleetSelectVisible(true)">Select visible eligible</button>
<button type="button" class="btn" onclick="fleetSelectVisible(false)">Clear selection</button>
</div>
<div class="panel mt-3 rounded-[7px] overflow-hidden">
{{range $page.Candidates}}
<label class="fleet-candidate grid items-center px-3 py-2 hairline text-[12px]"
data-name="{{.Host.Name}} {{range .Host.Tags}}{{.}} {{end}}" data-version="{{.Host.AgentVersion}}" data-state="{{if .Eligible}}eligible{{else}}excluded{{end}}"
style="grid-template-columns: 28px 1.4fr .8fr .8fr 1.2fr;gap:12px">
<input class="fleet-host" type="checkbox" value="{{.Host.ID}}" {{if .Eligible}}checked onchange="fleetReview()"{{else}}disabled{{end}}>
<span class="mono">{{.Host.Name}}</span>
<span class="mono text-ink-mute">{{if .Host.AgentVersion}}{{.Host.AgentVersion}}{{else}}unknown{{end}}</span>
<span class="mono text-ink-mute">{{$page.TargetVersion}}</span>
<span class="text-ink-mute">{{if .Eligible}}eligible{{else}}{{.Reason}}{{end}}</span>
</label>
{{end}}
</div>
<div class="mt-4 text-[12px] text-ink-mute">
<span id="fleet-selected-count">{{len $page.OutOfDateHosts}}</span> selected · sequential, halts on first failure · worst-case
<span id="fleet-timeout" class="mono">{{len $page.OutOfDateHosts}} × 95s</span>
</div>
<form class="mt-3 flex items-center gap-3" onsubmit="return fleetStart(event)">
<label class="text-[11.5px] text-ink-mute"><input id="fleet-canary" type="checkbox" checked> Pause after first host for canary review</label>
<button id="fleet-update-start-btn" class="btn btn-amber">Start agent update</button>
<span id="fleet-start-error" class="text-bad text-[12px]"></span>
</form>
{{end}}
</div>
{{end}}