265 lines
12 KiB
Plaintext
265 lines
12 KiB
Plaintext
{{define "updatesNodeRows"}}
|
|
{{range .}}
|
|
<tr>
|
|
<td>
|
|
<div class="fw-semibold text-body-emphasis">{{.Name}}</div>
|
|
<div class="small text-body-secondary">{{packageManagerLabel .PackageManager}}{{if .UpdatesLastError}} · {{.UpdatesLastError}}{{end}}</div>
|
|
</td>
|
|
<td class="text-nowrap">
|
|
<div class="d-flex align-items-center gap-2">
|
|
<span class="badge {{if gt .UpdatesAvailable 0}}text-bg-warning{{else}}text-bg-secondary{{end}}">{{.UpdatesAvailable}}</span>
|
|
{{if and (gt .UpdatesAvailable 0) .UpdatesDetails}}
|
|
<button
|
|
class="btn btn-link btn-sm updates-details-trigger"
|
|
type="button"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#updatePackagesModal"
|
|
data-node-name="{{.Name}}"
|
|
data-package-count="{{.UpdatesAvailable}}"
|
|
data-packages="{{.UpdatesDetails}}"
|
|
>
|
|
View packages
|
|
</button>
|
|
{{end}}
|
|
</div>
|
|
</td>
|
|
<td class="text-nowrap">{{if .UpdatesLastChecked}}{{.UpdatesLastChecked.Format "2006-01-02 15:04:05"}}{{else}}Never{{end}}</td>
|
|
<td>
|
|
<form id="node-policy-{{.ID}}" method="post" action="/updates/nodes/{{.ID}}/policy"></form>
|
|
<input class="form-check-input mt-0" type="checkbox" name="updates_scan_enabled" form="node-policy-{{.ID}}" {{if .UpdatesScanEnabled}}checked{{end}}>
|
|
</td>
|
|
<td>
|
|
<input class="form-check-input mt-0" type="checkbox" name="auto_updates_enabled" form="node-policy-{{.ID}}" {{if .AutoUpdatesEnabled}}checked{{end}}>
|
|
</td>
|
|
<td>
|
|
<div class="d-flex flex-wrap gap-2">
|
|
<button class="btn btn-outline-secondary btn-sm" type="submit" form="node-policy-{{.ID}}"><i class="ti ti-device-floppy"></i></button>
|
|
<form method="post" action="/updates/nodes/{{.ID}}/scan">
|
|
<button class="btn btn-outline-secondary btn-sm" type="submit"><i class="ti ti-refresh"></i></button>
|
|
</form>
|
|
<form method="post" action="/updates/nodes/{{.ID}}/apply">
|
|
<button class="btn btn-primary btn-sm" type="submit"><i class="ti ti-package-import"></i></button>
|
|
</form>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{{end}}
|
|
{{end}}
|
|
|
|
{{define "content"}}
|
|
{{$data := .Content}}
|
|
<section class="row g-3 mb-4">
|
|
<div class="col-12 col-md-6 col-xxl-3">
|
|
<article class="card border-0 shadow-sm uptime-summary-card h-100">
|
|
<div class="card-body">
|
|
<div class="uptime-summary-label"><i class="ti ti-package me-2"></i>Pending packages</div>
|
|
<div class="uptime-summary-value">{{$data.TotalUpdates}}</div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
<div class="col-12 col-md-6 col-xxl-3">
|
|
<article class="card border-0 shadow-sm uptime-summary-card h-100">
|
|
<div class="card-body">
|
|
<div class="uptime-summary-label"><i class="ti ti-server-2 me-2"></i>Nodes with updates</div>
|
|
<div class="uptime-summary-value">{{$data.NodesWithUpdates}}</div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
<div class="col-12 col-md-6 col-xxl-3">
|
|
<article class="card border-0 shadow-sm uptime-summary-card h-100">
|
|
<div class="card-body">
|
|
<div class="uptime-summary-label"><i class="ti ti-radar-2 me-2"></i>Scanned nodes</div>
|
|
<div class="uptime-summary-value">{{$data.ScannedNodes}}</div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
<div class="col-12 col-md-6 col-xxl-3">
|
|
<article class="card border-0 shadow-sm uptime-summary-card h-100">
|
|
<div class="card-body">
|
|
<div class="uptime-summary-label"><i class="ti ti-bolt me-2"></i>Auto update</div>
|
|
<div class="uptime-summary-value">{{$data.AutoUpdateNodes}}</div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="card border-0 shadow-sm mb-4">
|
|
<div class="card-body p-4 d-flex flex-column flex-xl-row align-items-xl-center justify-content-between gap-4">
|
|
<div class="min-w-0">
|
|
<div class="uptime-summary-label mb-2"><i class="ti ti-clock-cog me-2"></i>Global Auto Update Window</div>
|
|
<div class="h4 mb-1">{{updateWindowSummary $data.GlobalWindowStart $data.GlobalWindowEnd $data.GlobalUpdateDays}}</div>
|
|
<div class="text-body-secondary small">Automatic package upgrades respect this org-wide schedule so updates stay out of peak hours.</div>
|
|
</div>
|
|
<div class="d-flex flex-wrap gap-2">
|
|
<button class="btn btn-outline-secondary" type="button" data-bs-toggle="modal" data-bs-target="#globalUpdateWindowModal">
|
|
<i class="ti ti-settings me-2"></i>Edit Window
|
|
</button>
|
|
<form method="post" action="/updates/scan">
|
|
<button class="btn btn-outline-primary" type="submit"><i class="ti ti-refresh me-2"></i>Scan all nodes</button>
|
|
</form>
|
|
<form method="post" action="/updates/apply">
|
|
<button class="btn btn-primary" type="submit"><i class="ti ti-package-import me-2"></i>Update all</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{{range $data.Groups}}
|
|
<section class="card border-0 shadow-sm mb-4">
|
|
<div class="card-body p-4">
|
|
<div class="d-flex flex-column flex-xl-row align-items-xl-center justify-content-between gap-3 mb-4">
|
|
<div>
|
|
<h2 class="h4 mb-1">{{.Name}}</h2>
|
|
<div class="text-body-secondary small">{{.UpdatesAvailable}} updates on {{.NodesWithUpdates}} nodes</div>
|
|
</div>
|
|
<div class="d-flex flex-wrap gap-2">
|
|
<form method="post" action="/updates/groups/{{.ID}}/scan">
|
|
<button class="btn btn-outline-secondary btn-sm" type="submit"><i class="ti ti-refresh me-1"></i>Scan Group</button>
|
|
</form>
|
|
<form method="post" action="/updates/groups/{{.ID}}/apply">
|
|
<button class="btn btn-primary btn-sm" type="submit"><i class="ti ti-package-import me-1"></i>Update Group</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<form method="post" action="/updates/groups/{{.ID}}/policy" class="updates-policy-row mb-4">
|
|
<label class="option-check">
|
|
<input class="form-check-input" type="checkbox" name="updates_scan_enabled" {{if .ScanEnabled}}checked{{end}}>
|
|
<span>Scan this group</span>
|
|
</label>
|
|
<label class="option-check">
|
|
<input class="form-check-input" type="checkbox" name="auto_updates_enabled" {{if .AutoUpdate}}checked{{end}}>
|
|
<span>Auto update this group</span>
|
|
</label>
|
|
<button class="btn btn-outline-secondary btn-sm" type="submit"><i class="ti ti-device-floppy me-1"></i>Save</button>
|
|
</form>
|
|
<div class="table-responsive">
|
|
<table class="table align-middle mb-0 uptime-table uptime-table-nodes">
|
|
<thead>
|
|
<tr>
|
|
<th>Node</th>
|
|
<th>Updates</th>
|
|
<th>Last Scan</th>
|
|
<th>Scan</th>
|
|
<th>Auto</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{template "updatesNodeRows" .Nodes}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
{{end}}
|
|
|
|
{{if $data.Ungrouped}}
|
|
<section class="card border-0 shadow-sm">
|
|
<div class="card-body p-4">
|
|
<h2 class="h4 mb-4">Ungrouped</h2>
|
|
<div class="table-responsive">
|
|
<table class="table align-middle mb-0 uptime-table uptime-table-nodes">
|
|
<thead>
|
|
<tr>
|
|
<th>Node</th>
|
|
<th>Updates</th>
|
|
<th>Last Scan</th>
|
|
<th>Scan</th>
|
|
<th>Auto</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{template "updatesNodeRows" $data.Ungrouped}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
{{end}}
|
|
|
|
<div class="modal fade" id="globalUpdateWindowModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
<div class="modal-content border-0 shadow-lg">
|
|
<form method="post" action="/updates/settings/window">
|
|
<div class="modal-header">
|
|
<div class="min-w-0">
|
|
<h2 class="modal-title fs-5"><i class="ti ti-clock-cog me-2"></i>Global Auto Update Window</h2>
|
|
<div class="small text-body-secondary">Choose when automatic updates are allowed to run across the whole organization.</div>
|
|
</div>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body p-4 p-lg-5">
|
|
<div class="updates-window-editor">
|
|
<section class="add-vm-panel">
|
|
<div class="add-vm-panel-title"><i class="ti ti-calendar-time me-2"></i>Allowed Days</div>
|
|
<div class="updates-weekdays updates-weekdays-lg">
|
|
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="mon" {{if hasUpdateDay $data.GlobalUpdateDays "mon"}}checked{{end}}><span>Monday</span></label>
|
|
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="tue" {{if hasUpdateDay $data.GlobalUpdateDays "tue"}}checked{{end}}><span>Tuesday</span></label>
|
|
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="wed" {{if hasUpdateDay $data.GlobalUpdateDays "wed"}}checked{{end}}><span>Wednesday</span></label>
|
|
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="thu" {{if hasUpdateDay $data.GlobalUpdateDays "thu"}}checked{{end}}><span>Thursday</span></label>
|
|
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="fri" {{if hasUpdateDay $data.GlobalUpdateDays "fri"}}checked{{end}}><span>Friday</span></label>
|
|
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="sat" {{if hasUpdateDay $data.GlobalUpdateDays "sat"}}checked{{end}}><span>Saturday</span></label>
|
|
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="sun" {{if hasUpdateDay $data.GlobalUpdateDays "sun"}}checked{{end}}><span>Sunday</span></label>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="add-vm-panel">
|
|
<div class="add-vm-panel-title"><i class="ti ti-clock-hour-9 me-2"></i>Allowed Time Range</div>
|
|
<div class="row g-3">
|
|
<div class="col-12 col-md-6">
|
|
<label class="form-label">Start</label>
|
|
<input class="form-control" type="time" name="auto_update_window_start" value="{{$data.GlobalWindowStart}}">
|
|
</div>
|
|
<div class="col-12 col-md-6">
|
|
<label class="form-label">End</label>
|
|
<input class="form-control" type="time" name="auto_update_window_end" value="{{$data.GlobalWindowEnd}}">
|
|
</div>
|
|
</div>
|
|
<div class="small text-body-secondary mt-3">Leave start/end empty to allow automatic updates at any time on the selected days.</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal"><i class="ti ti-x me-1"></i>Cancel</button>
|
|
<button type="submit" class="btn btn-primary"><i class="ti ti-device-floppy me-1"></i>Save Window</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="updatePackagesModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
<div class="modal-content border-0 shadow-lg">
|
|
<div class="modal-header">
|
|
<div class="min-w-0">
|
|
<h2 class="modal-title fs-5"><i class="ti ti-package me-2"></i>Pending Packages</h2>
|
|
<div class="small text-body-secondary" data-update-packages-node></div>
|
|
</div>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table align-middle mb-0 updates-packages-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Package</th>
|
|
<th>Current</th>
|
|
<th>Available</th>
|
|
<th>Arch</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody data-update-packages-body>
|
|
<tr>
|
|
<td colspan="4" class="text-body-secondary text-center py-4">No pending packages.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|