warpgate/templates/web/share_detail.html
grabbit 466ea5cfa8 Add pre-mount remote path probe and per-share health status
Before mounting, probe each share's remote path with `rclone lsf`
(10s timeout, parallel execution). Failed shares are skipped — they
never get mounted or exposed to SMB/NFS/WebDAV — preventing the
silent hang that occurred when rclone mounted a nonexistent directory.

- ShareHealth enum: Pending → Probing → Healthy / Failed(reason)
- Supervisor: probe phase between preflight and mount, protocol
  configs generated after probe with only healthy shares
- Web UI: health-aware badges (OK/FAILED/PROBING/PENDING) with
  error messages on dashboard, status partial, and share detail
- JSON API: health + health_message fields on /api/status
- CLI: `warpgate status` queries daemon API first for tri-state
  display (OK/FAILED/DOWN), falls back to direct mount checks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:28:56 +08:00

77 lines
3.5 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Warpgate — {{ name }}</title>
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
<style>
:root {
--bg: #0f1117; --surface: #1a1d27; --border: #2a2d3a;
--text: #e1e4ed; --text-muted: #8b8fa3; --accent: #6c8aff;
--green: #4ade80; --red: #f87171; --yellow: #fbbf24;
--font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background: var(--bg); color: var(--text); font-family: var(--font); padding: 24px; max-width: 960px; margin: 0 auto; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
.breadcrumb { font-size: 0.85em; color: var(--text-muted); margin-bottom: 16px; }
h1 { font-size: 1.4em; margin-bottom: 16px; }
.badge { font-size: 0.75em; padding: 2px 8px; border-radius: 4px; font-weight: 600; vertical-align: middle; }
.badge-ok { background: rgba(74,222,128,0.15); color: var(--green); }
.badge-error { background: rgba(248,113,113,0.15); color: var(--red); }
.badge-ro { background: rgba(251,191,36,0.15); color: var(--yellow); }
.badge-warn { background: rgba(251,191,36,0.15); color: var(--yellow); }
.error-text { color: var(--red); }
.detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 24px; }
.detail-card { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 16px; }
.detail-card .label { font-size: 0.8em; color: var(--text-muted); margin-bottom: 4px; }
.detail-card .value { font-size: 1.2em; font-weight: 600; }
.info-table { width: 100%; border-collapse: collapse; }
.info-table td { padding: 8px 12px; border-bottom: 1px solid var(--border); }
.info-table td:first-child { color: var(--text-muted); width: 140px; }
</style>
</head>
<body>
<div class="breadcrumb"><a href="/">Dashboard</a> / {{ name }}</div>
<h1>
{{ name }}
{% if health == "OK" %}<span class="badge badge-ok">OK</span>{% elif health == "FAILED" %}<span class="badge badge-error">FAILED</span>{% elif health == "PROBING" %}<span class="badge badge-warn">PROBING</span>{% else %}<span class="badge badge-warn">PENDING</span>{% endif %}
{% if read_only %}<span class="badge badge-ro">Read-Only</span>{% endif %}
</h1>
<div class="detail-grid">
<div class="detail-card">
<div class="label">Cache Used</div>
<div class="value">{{ cache_display }}</div>
</div>
<div class="detail-card">
<div class="label">Dirty Files</div>
<div class="value">{{ dirty_count }}</div>
</div>
<div class="detail-card">
<div class="label">Transfer Speed</div>
<div class="value">{{ speed_display }}</div>
</div>
<div class="detail-card">
<div class="label">Active Transfers</div>
<div class="value">{{ transfers }}</div>
</div>
</div>
<table class="info-table">
<tr><td>Health</td><td>{{ health }}</td></tr>
{% if health == "FAILED" %}
<tr><td>Probe Error</td><td class="error-text">{{ health_message }}</td></tr>
{% endif %}
<tr><td>Mount Point</td><td>{{ mount_point }}</td></tr>
<tr><td>Remote Path</td><td>{{ remote_path }}</td></tr>
<tr><td>RC Port</td><td>{{ rc_port }}</td></tr>
<tr><td>Errored Files</td><td>{{ errored_files }}</td></tr>
<tr><td>Total Errors</td><td>{{ errors }}</td></tr>
</table>
</body>
</html>