225 lines
12 KiB
Plaintext
225 lines
12 KiB
Plaintext
{{define "content"}}
|
|
{{$data := .Content}}
|
|
<section class="d-flex flex-column flex-xl-row justify-content-between align-items-xl-end gap-3 mb-4">
|
|
<div>
|
|
<h1 class="display-6 fw-bold mb-0">{{$data.Node.Name}}</h1>
|
|
</div>
|
|
<div class="d-flex flex-wrap gap-2">
|
|
{{if and $.User (ne $.User.Role "viewer")}}
|
|
<button class="btn btn-outline-secondary" type="button" data-bs-toggle="modal" data-bs-target="#editNodeModal">
|
|
<i class="ti ti-pencil me-2"></i>Edit
|
|
</button>
|
|
{{end}}
|
|
<a class="btn btn-primary" href="/nodes/{{$data.Node.ID}}/console">
|
|
<i class="ti ti-terminal-2 me-2"></i>Open Console
|
|
</a>
|
|
<button class="btn btn-outline-secondary" type="button" data-copy-value="{{$data.Node.IPAddress}}" data-bs-toggle-tooltip="tooltip" data-bs-title="Copy IP">
|
|
<i class="ti ti-copy me-2"></i>Copy IP
|
|
</button>
|
|
<button class="btn btn-outline-secondary" type="button" data-copy-value="ssh -p {{$data.Node.SSHPort}} {{$data.Node.SSHUsername}}@{{$data.Node.IPAddress}}" data-bs-toggle-tooltip="tooltip" data-bs-title="Copy SSH command">
|
|
<i class="ti ti-terminal-2 me-2"></i>Copy SSH
|
|
</button>
|
|
<form method="post" action="/nodes/{{$data.Node.ID}}/actions/refresh"><button class="btn btn-outline-primary"><i class="ti ti-refresh me-2"></i>Refresh Stats</button></form>
|
|
<form method="post" action="/nodes/{{$data.Node.ID}}/actions/wake"><button class="btn btn-outline-secondary"><i class="ti ti-bolt me-2"></i>Wake</button></form>
|
|
<form method="post" action="/nodes/{{$data.Node.ID}}/actions/restart"><button class="btn btn-outline-secondary"><i class="ti ti-rotate-clockwise-2 me-2"></i>Restart</button></form>
|
|
<form method="post" action="/nodes/{{$data.Node.ID}}/actions/shutdown"><button class="btn btn-danger"><i class="ti ti-power me-2"></i>Shutdown</button></form>
|
|
<form method="post" action="/nodes/{{$data.Node.ID}}/delete" onsubmit="return confirm('Delete this VM? This removes its jobs and command history.')"><button class="btn btn-outline-danger"><i class="ti ti-trash me-2"></i>Delete VM</button></form>
|
|
</div>
|
|
</section>
|
|
|
|
{{if and $.User (ne $.User.Role "viewer")}}
|
|
<div class="modal fade" id="editNodeModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-xl">
|
|
<div class="modal-content border-0 shadow-lg">
|
|
<form method="post" action="/nodes/{{$data.Node.ID}}">
|
|
<div class="modal-header">
|
|
<h2 class="modal-title fs-5"><i class="ti ti-pencil me-2"></i>Edit VM</h2>
|
|
<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="add-vm-form">
|
|
<section class="add-vm-panel">
|
|
<div class="add-vm-panel-title"><i class="ti ti-server-2 me-2"></i>Node</div>
|
|
<div class="row g-3">
|
|
<div class="col-12 col-lg-6">
|
|
<label class="form-label">IP address</label>
|
|
<input type="text" class="form-control" name="ip_address" value="{{$data.Node.IPAddress}}" required>
|
|
</div>
|
|
<div class="col-12 col-lg-6">
|
|
<label class="form-label">Name</label>
|
|
<input type="text" class="form-control" name="name" value="{{$data.Node.Name}}">
|
|
</div>
|
|
<div class="col-12 col-lg-6">
|
|
<label class="form-label">Group</label>
|
|
<select class="form-select" name="group_id">
|
|
<option value="">None</option>
|
|
{{range $.AvailableGroups}}<option value="{{.ID}}" {{if eq .ID $data.SelectedGroupID}}selected{{end}}>{{.Name}}</option>{{end}}
|
|
</select>
|
|
</div>
|
|
<div class="col-12 col-lg-6">
|
|
<label class="form-label">Tag</label>
|
|
<input type="text" class="form-control" name="tag" list="edit-vm-tag-options" value="{{$data.Node.Tag}}" placeholder="prod, edge, lab">
|
|
<datalist id="edit-vm-tag-options">
|
|
{{range $.AvailableTags}}<option value="{{.}}"></option>{{end}}
|
|
</datalist>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="add-vm-panel">
|
|
<div class="add-vm-panel-title"><i class="ti ti-key me-2"></i>Access</div>
|
|
<div class="row g-3">
|
|
<div class="col-12 col-lg-6">
|
|
<label class="form-label">Username</label>
|
|
<input type="text" class="form-control" name="ssh_username" value="{{$data.Node.SSHUsername}}" required>
|
|
</div>
|
|
<div class="col-12 col-lg-6">
|
|
<label class="form-label">Password</label>
|
|
<input type="password" class="form-control" name="ssh_password" placeholder="Leave blank to keep current password">
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="add-vm-panel add-vm-panel-wide">
|
|
<div class="add-vm-panel-title"><i class="ti ti-adjustments me-2"></i>Options</div>
|
|
<div class="row g-3 align-items-start">
|
|
<div class="col-12 col-lg-8">
|
|
<label class="form-label">Notes</label>
|
|
<textarea class="form-control" name="notes" rows="4">{{$data.Node.Notes}}</textarea>
|
|
</div>
|
|
<div class="col-12 col-lg-4">
|
|
<label class="form-label">Behavior</label>
|
|
<div class="vstack gap-2">
|
|
<label class="option-check">
|
|
<input class="form-check-input" type="checkbox" name="updates_scan_enabled" {{if $data.Node.UpdatesScanEnabled}}checked{{end}}>
|
|
<span>Scan for updates</span>
|
|
</label>
|
|
<label class="option-check">
|
|
<input class="form-check-input" type="checkbox" name="auto_updates_enabled" {{if $data.Node.AutoUpdatesEnabled}}checked{{end}}>
|
|
<span>Enable auto updates</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</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</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
<section class="row g-3 mb-4 node-live" data-node-id="{{$data.Node.ID}}">
|
|
<div class="col-12 col-md-6 col-xxl-3">
|
|
<div class="card border-0 shadow-sm stat-card kpi-card kpi-card-featured h-100" data-kpi="cpu">
|
|
<div class="kpi-graph"></div>
|
|
<div class="card-body position-relative">
|
|
<div class="kpi-card-label mb-3">CPU usage</div>
|
|
<div class="kpi-card-featured-value" data-kpi-value="cpu">{{printf "%.1f" $data.Node.CPUUsage}}%</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-6 col-xxl-3">
|
|
<div class="card border-0 shadow-sm stat-card kpi-card h-100" data-kpi="ram">
|
|
<div class="kpi-graph"></div>
|
|
<div class="card-body position-relative">
|
|
<div class="small text-body-secondary mb-2">RAM</div>
|
|
<div class="display-6 fw-bold" data-kpi-value="ram">{{printf "%.1f" $data.Node.RAMUsage}}%</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-6 col-xxl-3">
|
|
<div class="card border-0 shadow-sm stat-card kpi-card h-100" data-kpi="disk">
|
|
<div class="kpi-graph"></div>
|
|
<div class="card-body position-relative">
|
|
<div class="small text-body-secondary mb-2">Disk</div>
|
|
<div class="display-6 fw-bold" data-kpi-value="disk">{{printf "%.1f" $data.Node.DiskUsage}}%</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-6 col-xxl-3">
|
|
<div class="card border-0 shadow-sm stat-card kpi-card h-100" data-kpi="uptime">
|
|
<div class="kpi-graph"></div>
|
|
<div class="card-body position-relative">
|
|
<div class="small text-body-secondary mb-2">Uptime</div>
|
|
<div class="fs-3 fw-bold" data-kpi-value="uptime">{{uptime $data.Node.UptimeSeconds}}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="row g-4">
|
|
<div class="col-12 col-xl-4">
|
|
<article class="card border-0 shadow-sm h-100">
|
|
<div class="card-body p-4">
|
|
<div class="system-summary vstack gap-3">
|
|
<div class="system-summary-item">
|
|
<div class="chip-icon chip-icon-plain system-summary-icon system-summary-icon-plain">
|
|
<i class="{{nodeIconClass $data.Node.Distro $data.Node.PackageManager}} distro-icon distro-icon-lg"></i>
|
|
</div>
|
|
<div class="min-w-0">
|
|
<div class="system-summary-label">Distribution</div>
|
|
<div class="system-summary-value text-truncate">{{$data.Node.Distro}}</div>
|
|
</div>
|
|
</div>
|
|
<div class="system-summary-item">
|
|
<div class="chip-icon chip-icon-plain system-summary-icon system-summary-icon-plain">
|
|
<i class="{{packageManagerIconClass $data.Node.PackageManager}}"></i>
|
|
</div>
|
|
<div class="min-w-0">
|
|
<div class="system-summary-label">Package Manager</div>
|
|
<div class="system-summary-value text-truncate">{{packageManagerLabel $data.Node.PackageManager}}</div>
|
|
</div>
|
|
</div>
|
|
<div class="system-summary-stats">
|
|
<button class="system-summary-stat system-summary-copy" type="button" data-copy-value="{{$data.Node.IPAddress}}" data-bs-toggle-tooltip="tooltip" data-bs-title="Copy IP">
|
|
<span class="text-body-secondary">IP</span>
|
|
<span class="text-end">{{$data.Node.IPAddress}}</span>
|
|
</button>
|
|
<button class="system-summary-stat system-summary-copy" type="button" data-copy-value="{{$data.Node.MACAddress}}" data-bs-toggle-tooltip="tooltip" data-bs-title="Copy MAC">
|
|
<span class="text-body-secondary">MAC</span>
|
|
<span class="text-end">{{if $data.Node.MACAddress}}{{$data.Node.MACAddress}}{{else}}-{{end}}</span>
|
|
</button>
|
|
<button class="system-summary-stat system-summary-copy" type="button" data-copy-value="{{$data.Node.SSHUsername}}" data-bs-toggle-tooltip="tooltip" data-bs-title="Copy username">
|
|
<span class="text-body-secondary">Username</span>
|
|
<span class="text-end">{{if $data.Node.SSHUsername}}{{$data.Node.SSHUsername}}{{else}}-{{end}}</span>
|
|
</button>
|
|
<button class="system-summary-stat system-summary-copy" type="button" data-copy-value="{{$data.Node.Architecture}}" data-bs-toggle-tooltip="tooltip" data-bs-title="Copy architecture">
|
|
<span class="text-body-secondary">Architecture</span>
|
|
<span class="text-end">{{$data.Node.Architecture}}</span>
|
|
</button>
|
|
<button class="system-summary-stat system-summary-copy" type="button" data-copy-value="{{$data.Node.KernelVersion}}" data-bs-toggle-tooltip="tooltip" data-bs-title="Copy kernel">
|
|
<span class="text-body-secondary">Kernel</span>
|
|
<span class="text-end">{{$data.Node.KernelVersion}}</span>
|
|
</button>
|
|
<button class="system-summary-stat system-summary-copy" type="button" data-copy-value="{{if $data.Node.MemoryTotalMB}}{{$data.Node.MemoryTotalMB}} MB{{end}}" data-bs-toggle-tooltip="tooltip" data-bs-title="Copy memory">
|
|
<span class="text-body-secondary">Memory</span>
|
|
<span class="text-end">{{if $data.Node.MemoryTotalMB}}{{$data.Node.MemoryTotalMB}} MB{{else}}-{{end}}</span>
|
|
</button>
|
|
<button class="system-summary-stat system-summary-copy" type="button" data-copy-value="{{if $data.Node.DiskTotalGB}}{{$data.Node.DiskTotalGB}} GB{{end}}" data-bs-toggle-tooltip="tooltip" data-bs-title="Copy disk">
|
|
<span class="text-body-secondary">Disk</span>
|
|
<span class="text-end">{{if $data.Node.DiskTotalGB}}{{$data.Node.DiskTotalGB}} GB{{else}}-{{end}}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
|
|
<div class="col-12 col-xl-8">
|
|
<article class="card border-0 shadow-sm h-100">
|
|
<div class="card-body p-0">
|
|
<div class="console-shell">
|
|
<div id="console" class="console-output console-terminal" data-ws="/nodes/{{$data.Node.ID}}/console/ws" data-xterm="true"></div>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
</section>
|
|
{{end}}
|