varaverk: add Partner card — known hosts, owner tags, Tailscale online status

This commit is contained in:
Gmer4Lfe
2026-05-24 11:36:23 -04:00
parent cbac3949e0
commit c17be8d017
3 changed files with 73 additions and 0 deletions
@@ -4,6 +4,7 @@ require_once dirname(__DIR__) . '/include/monitor.php';
echo json_encode([
'fallback' => vv_fallback_state(),
'partner' => vv_partner_state(),
'resources' => vv_system_resources(),
'cpu' => vv_cpu_per_core(),
'mem' => vv_memory_breakdown(),
@@ -249,6 +249,45 @@ function vv_network_stats(): array {
];
}
function vv_partner_state(): array {
$vars = vv_conf_vars();
$myName = trim(shell_exec('hostname -s') ?: '');
// Parse Tailscale peer status once
$tsData = json_decode(shell_exec('tailscale status --json 2>/dev/null') ?: '{}', true) ?? [];
$tsPeers = [];
foreach ($tsData['Peer'] ?? [] as $peer) {
// DNSName is "hostname.tailnet.ts.net." — take the first label (full, not truncated)
$dns = $peer['DNSName'] ?? '';
$h = $dns ? strtolower(explode('.', $dns)[0]) : strtolower($peer['HostName'] ?? '');
if ($h) $tsPeers[$h] = (bool)($peer['Online'] ?? false);
}
$hosts = [];
foreach (['HOST1', 'HOST2', 'HOST3', 'HOST4'] as $id) {
$hostname = $vars[$id] ?? '';
if (!$hostname) continue;
$isMe = strcasecmp($hostname, $myName) === 0;
$isOwner = strcasecmp($id, $vars['PARTNERSHIP_OWNER_HOST'] ?? '') === 0;
$online = $isMe ? true : ($tsPeers[strtolower($hostname)] ?? null);
$hosts[] = [
'id' => $id,
'hostname' => $hostname,
'owner' => $vars[$id . '_OWNER'] ?? '',
'is_me' => $isMe,
'is_owner' => $isOwner,
'online' => $online,
];
}
return [
'enabled' => ($vars['PARTNERSHIP_ENABLED'] ?? 'false') === 'true',
'owner_host' => $vars['PARTNERSHIP_OWNER_HOST'] ?? '',
'sync_min' => (int)($vars['PARTNERSHIP_SYNC_INTERVAL'] ?? 15),
'hosts' => $hosts,
];
}
function vv_fallback_state(): array {
// State file written by fallback.sh
$stateFile = '/tmp/fallback_state.db';
@@ -9,6 +9,11 @@
<div id="vv-fallback-desc"></div>
</div>
<div class="vv-card" id="vv-partner" style="flex:2;min-width:220px;">
<h3>Partner</h3>
<div id="vv-partner-body">Loading...</div>
</div>
<div class="vv-card" id="vv-cpu" style="flex:2;min-width:240px;">
<h3>CPU</h3>
<div id="vv-cpu-body">Loading...</div>
@@ -318,6 +323,34 @@ function vvPollMonitor() {
}
document.getElementById('vv-fallback-desc').innerHTML = descHtml;
// ── Partner ─────────────────────────────────────────────────────────────
const pt = d.partner ?? {};
const ptHosts = pt.hosts ?? [];
const ptStatus = pt.enabled
? `<span style="color:#4caf50;">enabled</span> · sync every ${pt.sync_min}min`
: `<span style="color:#555;">disabled</span>`;
let ptHtml = `<div style="font-size:10px;color:#666;margin-bottom:10px;">${ptStatus}</div>`;
ptHosts.forEach(h => {
const dot = h.online === null ? '#555' : h.online ? '#4caf50' : '#f44336';
const label = h.online === null ? 'unknown' : h.online ? 'online' : 'offline';
const tags = [
h.is_me ? `<span style="background:#1a3a1a;color:#4caf50;font-size:8px;padding:1px 5px;border-radius:3px;margin-left:4px;">US</span>` : '',
h.is_owner ? `<span style="background:#1a2a3a;color:#4a9eff;font-size:8px;padding:1px 5px;border-radius:3px;margin-left:4px;">OWNER</span>` : '',
].join('');
ptHtml += `<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;">
<div>
<span style="font-size:10px;color:#555;margin-right:4px;">${h.id}</span>
<span style="font-size:12px;color:#ccc;font-weight:500;">${h.owner || h.hostname}</span>
${tags}
<div style="font-size:10px;color:#666;margin-top:1px;">${h.hostname}</div>
</div>
<div style="text-align:right;">
<span style="color:${dot};font-size:10px;">● ${label}</span>
</div>
</div>`;
});
document.getElementById('vv-partner-body').innerHTML = ptHtml;
// ── CPU ─────────────────────────────────────────────────────────────────
const cpu = d.cpu ?? {};
document.getElementById('vv-cpu-body').innerHTML = vvRenderCpu(cpu);