Stage A: swap CSS tokens to the real Gitea dark palette (accent blue #4183c4); add Jinja formatting filters (humanbytes/duration/relative_time/localtime/shorten_path/thousands), a health status-header, and an inline SVG icon sprite; fix two UI bugs — unthemed action pages (htmx swap into #action-slot with a full-page HX-Request fallback) and polling wiping form input (hx-preserve). Stage B: four read-only panels that degrade to notices (never 500) — host resources (/proc, two-sample CPU%), VMware module health, template inventory (.vmem warn + last backup), and a path-contained file browser; backup card becomes a validated template dropdown; expensive dir-size walk is TTL-cached. Adds operations/inventory.py plus host_resources()/vmware_module_health() in operations/host.py. Stage C: parse_clone_name() adds a Job column (job_id + started) to the VM table; nav gains a Gitea link-out derived from the OIDC issuer origin (no fabricated run URLs); audit page gets action icons, relative time, and a client-side filter. Gates: 989 tests pass, coverage 94.12%, ruff + mypy --strict clean. Stage D (service management) intentionally deferred.
66 lines
2.5 KiB
HTML
66 lines
2.5 KiB
HTML
<div class="contents" id="host-partial" hx-get="/host" hx-trigger="every 5s" hx-swap="outerHTML">
|
|
<section class="card" id="disk-card">
|
|
<h2><svg class="icon" aria-hidden="true"><use href="#i-disk"></use></svg>Disk</h2>
|
|
{% if disk %}
|
|
<dl>
|
|
<dt>Path</dt><dd><code>{{ disk.path }}</code></dd>
|
|
<dt>Total</dt><dd>{{ "%.1f"|format(disk.total_gb) }} GB</dd>
|
|
<dt>Free</dt><dd>{{ "%.1f"|format(disk.free_gb) }} GB</dd>
|
|
<dt>Used</dt><dd>{{ "%.1f"|format(disk.used_pct) }}%</dd>
|
|
</dl>
|
|
{% else %}
|
|
<p class="notice notice-warn" role="alert">Disk status unavailable.</p>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<section class="card" id="runner-card">
|
|
<h2><svg class="icon" aria-hidden="true"><use href="#i-gear"></use></svg>Runners</h2>
|
|
{% if runners %}
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Service</th>
|
|
<th>Status</th>
|
|
<th>Sub-state</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for runner in runners %}
|
|
<tr class="{{ 'row-alert' if not runner.active else '' }}">
|
|
<td>{{ runner.name }}</td>
|
|
<td>
|
|
{% if runner.active %}
|
|
<span class="badge badge-success">active</span>
|
|
{% else %}
|
|
<span class="badge badge-danger">down</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ runner.sub_state }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<p class="notice notice-warn" role="alert">Runner status unavailable.</p>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<section class="card" id="backup-card">
|
|
<h2><svg class="icon" aria-hidden="true"><use href="#i-box"></use></svg>Template backup</h2>
|
|
<p class="field-hint">7z archive of the template dir under <root>/backups</p>
|
|
<form method="post" action="/host/backup" id="host-backup-form" aria-label="Backup template"
|
|
hx-post="/host/backup" hx-target="#action-slot" hx-swap="innerHTML">
|
|
<input type="hidden" name="csrf" value="{{ csrf_token }}">
|
|
<label for="host-backup-template">Template</label>
|
|
<select id="host-backup-template" name="template" required hx-preserve="true">
|
|
{% for name in template_names %}
|
|
<option value="{{ name }}">{{ name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<button type="submit" class="btn btn-danger" aria-label="Backup template">Backup template</button>
|
|
</form>
|
|
</section>
|
|
</div>
|