Compare commits
2
Commits
b7894681e6
...
ce0b3fb74b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce0b3fb74b | ||
|
|
6aaf04e25b |
@@ -80,13 +80,9 @@ if (is_dir(LOG_DIR)) {
|
||||
$out['errors'] = array_slice($errors, 0, 20);
|
||||
|
||||
// ── Partner reachability ───────────────────────────────────────────────────
|
||||
$cacheFile = '/tmp/vv_partner_cache.json';
|
||||
$cacheTtl = 30;
|
||||
$partnerData = null;
|
||||
$partnerData = vv_cache_read('board_partner', 30);
|
||||
|
||||
if (file_exists($cacheFile) && (time() - (int)filemtime($cacheFile)) < $cacheTtl) {
|
||||
$partnerData = json_decode(file_get_contents($cacheFile), true);
|
||||
} else {
|
||||
if (!$partnerData) {
|
||||
// Discover partner hostname dynamically from master.conf (works for any number of hosts)
|
||||
$vars = vv_conf_vars();
|
||||
$mine = vv_get_hostname();
|
||||
@@ -113,7 +109,7 @@ if (file_exists($cacheFile) && (time() - (int)filemtime($cacheFile)) < $cacheTtl
|
||||
'reachable' => $reached,
|
||||
'latency' => $reached ? $elapsed : null,
|
||||
];
|
||||
@file_put_contents($cacheFile, json_encode($partnerData));
|
||||
vv_cache_write('board_partner', $partnerData);
|
||||
}
|
||||
}
|
||||
$out['partner'] = $partnerData;
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/scheduler.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['ok' => false, 'error' => 'POST only']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = trim($_POST['id'] ?? '');
|
||||
$enabled = ($_POST['enabled'] ?? '0') === '1';
|
||||
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/scheduler.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['ok' => false, 'error' => 'POST only']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
$enabled = ($_POST['enabled'] ?? '0') === '1';
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/config.php';
|
||||
require_once dirname(__DIR__) . '/include/common.php';
|
||||
|
||||
// Pull slow fields (docker/vm/top_procs/swap) from last full cache — stale is fine,
|
||||
// these don't change on a per-second basis. 600s window so the card stays populated
|
||||
// even if the full cache writer is temporarily behind.
|
||||
$_full = vv_cache_read('monitor', 600);
|
||||
$_dockerKb = $_full['mem']['docker_kb'] ?? 0;
|
||||
$_vmKb = $_full['mem']['vm_kb'] ?? 0;
|
||||
$_topProcs = $_full['mem']['top_procs'] ?? [];
|
||||
$_swapTotal = $_full['mem']['swap_total_kb'] ?? 0;
|
||||
$_swapUsed = $_full['mem']['swap_used_kb'] ?? 0;
|
||||
|
||||
// Fast memory: /proc/meminfo + ZFS ARC (no docker stats, no GQL)
|
||||
$_memRaw = [];
|
||||
foreach (file('/proc/meminfo') ?: [] as $_l) {
|
||||
if (preg_match('/^(\w+):\s+(\d+)/', $_l, $_m)) $_memRaw[$_m[1]] = (int)$_m[2];
|
||||
}
|
||||
$_totalKb = $_memRaw['MemTotal'] ?? 0;
|
||||
$_freeKb = $_memRaw['MemAvailable'] ?? 0;
|
||||
$_arcKb = 0;
|
||||
foreach (@file('/proc/spl/kstat/zfs/arcstats') ?: [] as $_l) {
|
||||
if (preg_match('/^size\s+\d+\s+(\d+)/', $_l, $_m)) { $_arcKb = (int)($_m[1] / 1024); break; }
|
||||
}
|
||||
if (!$_swapTotal) {
|
||||
$_swapTotal = $_memRaw['SwapTotal'] ?? 0;
|
||||
$_swapUsed = max(0, ($_memRaw['SwapTotal'] ?? 0) - ($_memRaw['SwapFree'] ?? 0));
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'cpu' => vv_cpu_per_core(),
|
||||
'mem' => [
|
||||
'total_kb' => $_totalKb,
|
||||
'free_kb' => $_freeKb,
|
||||
'used_kb' => max(0, $_totalKb - $_freeKb),
|
||||
'arc_kb' => $_arcKb,
|
||||
'docker_kb' => $_dockerKb,
|
||||
'vm_kb' => $_vmKb,
|
||||
'system_kb' => max(0, $_totalKb - $_freeKb - $_arcKb - $_dockerKb - $_vmKb),
|
||||
'swap_total_kb' => $_swapTotal,
|
||||
'swap_used_kb' => $_swapUsed,
|
||||
'top_procs' => $_topProcs,
|
||||
],
|
||||
'net' => vv_network_stats(),
|
||||
'ts' => time(),
|
||||
]);
|
||||
@@ -3,18 +3,11 @@ header('Content-Type: application/json');
|
||||
header('Cache-Control: no-cache, no-store');
|
||||
require_once dirname(__DIR__) . '/include/monitor.php';
|
||||
|
||||
$cacheFile = '/tmp/vv_cache_monitor_remote.json';
|
||||
$cacheTTL = 3600;
|
||||
|
||||
if (!isset($_GET['live']) && file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $cacheTTL) {
|
||||
echo file_get_contents($cacheFile);
|
||||
exit;
|
||||
if (!isset($_GET['live'])) {
|
||||
$cached = vv_cache_read('monitor_remote', 3600);
|
||||
if ($cached) { echo json_encode($cached); exit; }
|
||||
}
|
||||
|
||||
$out = json_encode([
|
||||
'remote_hosts' => vv_remote_hosts_stats(),
|
||||
'ts' => time(),
|
||||
]);
|
||||
|
||||
file_put_contents($cacheFile, $out);
|
||||
echo $out;
|
||||
$data = ['remote_hosts' => vv_remote_hosts_stats(), 'ts' => time()];
|
||||
vv_cache_write('monitor_remote', $data);
|
||||
echo json_encode($data);
|
||||
|
||||
@@ -70,9 +70,10 @@ if ($toArray) {
|
||||
$newLines = $resultLines;
|
||||
}
|
||||
|
||||
if (file_put_contents($confPath, implode('', $newLines)) === false) {
|
||||
if (!vv_write_conf_raw('master.conf', implode('', $newLines))) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Write failed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
vv_push_master_conf();
|
||||
echo json_encode(['ok' => true]);
|
||||
|
||||
@@ -101,9 +101,10 @@ $newBlockLines[] = $lines[$blockEnd];
|
||||
// Replace the original block in $lines
|
||||
array_splice($lines, $blockStart, $blockEnd - $blockStart + 1, $newBlockLines);
|
||||
|
||||
if (file_put_contents($confPath, implode('', $lines)) === false) {
|
||||
if (!vv_write_conf_raw('master.conf', implode('', $lines))) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Write failed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
vv_push_master_conf();
|
||||
echo json_encode(['ok' => true]);
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/scheduler.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['ok' => false, 'error' => 'POST only']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Batch save — all entries in one load/write/rebuild cycle
|
||||
if (!empty($_POST['batch'])) {
|
||||
$entries = json_decode($_POST['batch'], true) ?: [];
|
||||
|
||||
@@ -3,61 +3,36 @@ header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/monitor.php';
|
||||
require_once dirname(__DIR__) . '/include/media.php';
|
||||
|
||||
// CPU% — delta from own state file so it doesn't conflict with monitor.php
|
||||
$cpuPct = 0;
|
||||
$cpuLine = '';
|
||||
foreach (file('/proc/stat') ?: [] as $line) {
|
||||
if (strncmp($line, 'cpu ', 4) === 0) { $cpuLine = $line; break; }
|
||||
}
|
||||
if (preg_match('/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/', $cpuLine, $m)) {
|
||||
$c = [(int)$m[1],(int)$m[2],(int)$m[3],(int)$m[4],(int)$m[5],(int)$m[6],(int)$m[7]];
|
||||
$sf = '/tmp/vv_snap_cpu.json';
|
||||
$p = file_exists($sf) ? (json_decode(file_get_contents($sf), true) ?: null) : null;
|
||||
file_put_contents($sf, json_encode($c));
|
||||
if ($p && is_array($p)) {
|
||||
$dt = array_sum($c) - array_sum($p);
|
||||
$di = ($c[3] + $c[4]) - ($p[3] + $p[4]);
|
||||
$cpuPct = $dt > 0 ? max(0, min(100, (int)round((1 - $di / $dt) * 100))) : 0;
|
||||
}
|
||||
}
|
||||
// CPU% — shares /tmp/vv_cpu_stat.json with vv_cpu_per_core() so both read the same baseline
|
||||
$cpuPct = vv_cpu_per_core()['overall'] ?? 0;
|
||||
|
||||
// RAM%
|
||||
$mem = [];
|
||||
foreach (file('/proc/meminfo') ?: [] as $line) {
|
||||
if (preg_match('/^(MemTotal|MemAvailable):\s+(\d+)/', $line, $m)) $mem[$m[1]] = (int)$m[2];
|
||||
}
|
||||
$ramTotalMb = (int)(($mem['MemTotal'] ?? 0) / 1024);
|
||||
$ramUsedMb = (int)((($mem['MemTotal'] ?? 0) - ($mem['MemAvailable'] ?? 0)) / 1024);
|
||||
$res = vv_system_resources();
|
||||
$ramTotalMb = $res['ram_total_mb'];
|
||||
$ramUsedMb = $ramTotalMb - $res['ram_free_mb'];
|
||||
$ramPct = $ramTotalMb > 0 ? (int)round($ramUsedMb / $ramTotalMb * 100) : 0;
|
||||
|
||||
// Fallback state (fast file read, no exec)
|
||||
$fallbackState = 'UNKNOWN';
|
||||
foreach (@file('/tmp/fallback_state.db') ?: [] as $line) {
|
||||
if (preg_match('/^state=(.+)/', trim($line), $m)) { $fallbackState = trim($m[1]); break; }
|
||||
}
|
||||
$fbRaw = @file_get_contents('/tmp/fallback_state.db') ?: '';
|
||||
$fbData = vv_parse_kv_db($fbRaw);
|
||||
$fallbackState = $fbData['state'] ?? 'UNKNOWN';
|
||||
|
||||
// Partner
|
||||
$partner = vv_partner_state();
|
||||
$peers = array_values(array_filter($partner['hosts'], fn($h) => !$h['is_me']));
|
||||
|
||||
// Media sessions — cached 30s so the HTTP calls don't hold up every snapshot poll
|
||||
$streamCount = 0;
|
||||
$transcodeCount = 0;
|
||||
$mediaCacheFile = '/tmp/vv_snap_media.json';
|
||||
$cacheMaxAge = 30;
|
||||
$cacheValid = file_exists($mediaCacheFile) && (time() - filemtime($mediaCacheFile)) < $cacheMaxAge;
|
||||
if ($cacheValid) {
|
||||
$cached = json_decode(file_get_contents($mediaCacheFile), true) ?: [];
|
||||
} else {
|
||||
$media = vv_media_sessions();
|
||||
$cached = [
|
||||
$mediaCache = vv_cache_read('snap_media', 30);
|
||||
if (!$mediaCache) {
|
||||
$media = vv_media_sessions();
|
||||
$mediaCache = [
|
||||
'stream_count' => count($media['sessions']),
|
||||
'transcode_count' => count(array_filter($media['sessions'], fn($s) => !empty($s['is_tc']))),
|
||||
];
|
||||
file_put_contents($mediaCacheFile, json_encode($cached));
|
||||
vv_cache_write('snap_media', $mediaCache);
|
||||
}
|
||||
$streamCount = (int)($cached['stream_count'] ?? 0);
|
||||
$transcodeCount = (int)($cached['transcode_count'] ?? 0);
|
||||
$streamCount = (int)($mediaCache['stream_count'] ?? 0);
|
||||
$transcodeCount = (int)($mediaCache['transcode_count'] ?? 0);
|
||||
|
||||
echo json_encode([
|
||||
'cpu_pct' => $cpuPct,
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['ok' => false, 'error' => 'POST only']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$body = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
$action = $body['action'] ?? '';
|
||||
|
||||
@@ -6,23 +6,11 @@ require_once __DIR__ . '/config.php';
|
||||
// ── Conf helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
function vv_arr_scalar(string $raw, string $key): string {
|
||||
return preg_match('/^\s*' . preg_quote($key, '/') . '\s*=\s*"?([^"\n]*)"?/m', $raw, $m)
|
||||
? trim($m[1]) : '';
|
||||
return vv_parse_conf_scalar($raw, $key);
|
||||
}
|
||||
|
||||
// Returns all configured hosts found in master.conf as ['host1'=>'hostname', ...]
|
||||
// Returns ['host1' => 'hostname', 'host2' => 'hostname', ...] from master.conf.
|
||||
// Matches both HOST1="name" and HOST1_NAME="name" (either convention).
|
||||
function vv_arr_known_hosts(): array {
|
||||
$vars = vv_conf_vars(); // already parses HOST1="val" correctly
|
||||
$hosts = [];
|
||||
foreach ($vars as $k => $v) {
|
||||
if (preg_match('/^HOST(\d+)$/', $k, $m) && $v !== '') {
|
||||
$hosts['host' . $m[1]] = $v;
|
||||
}
|
||||
}
|
||||
ksort($hosts);
|
||||
return $hosts ?: ['host1' => 'HOST1'];
|
||||
return vv_known_hosts();
|
||||
}
|
||||
|
||||
function vv_arr_node_names(): array {
|
||||
|
||||
@@ -23,13 +23,10 @@ function vv_system_info(): array {
|
||||
$uptimeRaw = $os['uptime'] ?? '';
|
||||
if (is_numeric($uptimeRaw)) {
|
||||
$uptimeSec = (int)$uptimeRaw;
|
||||
$days = intdiv($uptimeSec, 86400);
|
||||
$hours = intdiv($uptimeSec % 86400, 3600);
|
||||
$mins = intdiv($uptimeSec % 3600, 60);
|
||||
$uptime = ($days ? "{$days}d " : '') . ($hours ? "{$hours}h " : '') . "{$mins}m";
|
||||
$uptime = vv_format_uptime($uptimeSec);
|
||||
} else {
|
||||
$uptimeSec = 0;
|
||||
$uptime = $uptimeRaw ?: '—';
|
||||
$uptime = $uptimeRaw ?: '—';
|
||||
}
|
||||
|
||||
$load = sys_getloadavg();
|
||||
@@ -57,10 +54,7 @@ function vv_system_info(): array {
|
||||
if (preg_match('/^model name\s*:\s*(.+)/', $line, $m)) { $cpuModel = trim($m[1]); break; }
|
||||
}
|
||||
$uptimeSec = (int)explode(' ', @file_get_contents('/proc/uptime') ?: '0')[0];
|
||||
$days = intdiv($uptimeSec, 86400);
|
||||
$hours = intdiv($uptimeSec % 86400, 3600);
|
||||
$mins = intdiv($uptimeSec % 3600, 60);
|
||||
$uptime = ($days ? "{$days}d " : '') . ($hours ? "{$hours}h " : '') . "{$mins}m";
|
||||
$uptime = vv_format_uptime($uptimeSec);
|
||||
|
||||
$load = sys_getloadavg();
|
||||
return [
|
||||
@@ -159,7 +153,10 @@ function vv_cpu_per_core(): array {
|
||||
|
||||
$stateFile = '/tmp/vv_cpu_stat.json';
|
||||
$prev = file_exists($stateFile) ? (json_decode(file_get_contents($stateFile), true) ?: []) : [];
|
||||
file_put_contents($stateFile, json_encode($raw));
|
||||
// Atomic write — concurrent fast/slow polls read a consistent snapshot
|
||||
$tmp = $stateFile . '.tmp';
|
||||
file_put_contents($tmp, json_encode($raw));
|
||||
rename($tmp, $stateFile);
|
||||
|
||||
$usage = function(array $c, ?array $p): int {
|
||||
if (!$p) return 0;
|
||||
@@ -298,7 +295,9 @@ function vv_network_stats(): array {
|
||||
$stateFile = '/tmp/vv_net_stat.json';
|
||||
$now = ['rx' => $rxBytes, 'tx' => $txBytes, 'ts' => microtime(true)];
|
||||
$prev = file_exists($stateFile) ? (json_decode(file_get_contents($stateFile), true) ?: []) : [];
|
||||
file_put_contents($stateFile, json_encode($now));
|
||||
$tmp = $stateFile . '.tmp';
|
||||
file_put_contents($tmp, json_encode($now));
|
||||
rename($tmp, $stateFile);
|
||||
|
||||
$rxRate = $txRate = 0;
|
||||
if (!empty($prev['ts']) && ($dt = $now['ts'] - $prev['ts']) > 0.1) {
|
||||
@@ -314,15 +313,15 @@ function vv_network_stats(): array {
|
||||
) ?: '');
|
||||
|
||||
// External IP — curl ifconfig.me, cached 5 min so we don't hammer it
|
||||
$extIpCache = '/tmp/vv_ext_ip.cache';
|
||||
$extIp = '';
|
||||
if (file_exists($extIpCache) && (time() - filemtime($extIpCache)) < 300) {
|
||||
$extIp = trim(file_get_contents($extIpCache) ?: '');
|
||||
$extIp = '';
|
||||
$extData = vv_cache_read('ext_ip', 300);
|
||||
if ($extData) {
|
||||
$extIp = $extData['ip'] ?? '';
|
||||
} else {
|
||||
$fetched = trim(shell_exec('curl -sf --max-time 4 https://ifconfig.me 2>/dev/null') ?: '');
|
||||
if (preg_match('/^\d+\.\d+\.\d+\.\d+$/', $fetched)) {
|
||||
$extIp = $fetched;
|
||||
file_put_contents($extIpCache, $extIp);
|
||||
vv_cache_write('ext_ip', ['ip' => $extIp]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -312,3 +312,57 @@ function vv_cache_write(string $key, array $data): void {
|
||||
file_put_contents($tmp, json_encode($data));
|
||||
rename($tmp, $f);
|
||||
}
|
||||
|
||||
// ── Shared utility functions (used across include/ and api/ files) ────────────
|
||||
|
||||
// Format seconds into "2d 3h 15m".
|
||||
function vv_format_uptime(int $seconds): string {
|
||||
$d = intdiv($seconds, 86400);
|
||||
$h = intdiv($seconds % 86400, 3600);
|
||||
$m = intdiv($seconds % 3600, 60);
|
||||
return ($d ? "{$d}d " : '') . ($h ? "{$h}h " : '') . "{$m}m";
|
||||
}
|
||||
|
||||
// Parse a scalar value from raw conf text. Matches KEY="value" or KEY=value.
|
||||
// Identical logic was previously duplicated as vv_arr_scalar / vv_wd_scalar /
|
||||
// vv_fb_scalar / vv_media_conf_scalar — all reduce to this one regex.
|
||||
function vv_parse_conf_scalar(string $raw, string $key): string {
|
||||
return preg_match('/^\s*' . preg_quote($key, '/') . '\s*=\s*"?([^"\n]*)"?/m', $raw, $m)
|
||||
? trim($m[1]) : '';
|
||||
}
|
||||
|
||||
// Parse a key=value state file (e.g. fallback_state.db, partnership_state.db).
|
||||
// Returns ['key' => 'value', ...]. Lines without '=' are ignored.
|
||||
function vv_parse_kv_db(string $text): array {
|
||||
$out = [];
|
||||
foreach (explode("\n", $text) as $line) {
|
||||
$line = trim($line);
|
||||
if ($line === '' || $line[0] === '#') continue;
|
||||
[$k, $v] = array_pad(explode('=', $line, 2), 2, '');
|
||||
if ($k !== '') $out[trim($k)] = trim($v);
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
// All configured hosts from master.conf as ['host1' => 'hostname', ...].
|
||||
// Canonical version — previously duplicated as vv_arr_known_hosts / vv_fb_known_hosts.
|
||||
function vv_known_hosts(): array {
|
||||
$vars = vv_conf_vars();
|
||||
$hosts = [];
|
||||
foreach ($vars as $k => $v) {
|
||||
if (preg_match('/^HOST(\d+)$/', $k, $m) && $v !== '') {
|
||||
$hosts['host' . $m[1]] = $v;
|
||||
}
|
||||
}
|
||||
ksort($hosts);
|
||||
return $hosts ?: ['host1' => 'HOST1'];
|
||||
}
|
||||
|
||||
// Local LAN IP via routing table — static-cached per request.
|
||||
// Previously duplicated in include/docker_folders.php and inline in include/docker.php.
|
||||
function vv_local_ip(): string {
|
||||
static $ip = null;
|
||||
if ($ip !== null) return $ip;
|
||||
$ip = trim(shell_exec("ip route get 8.8.8.8 2>/dev/null | awk '/src/{for(i=1;i<=NF;i++)if(\$i==\"src\")print \$(i+1)}'") ?? '');
|
||||
return $ip;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
<?php
|
||||
function vv_local_ip(): string {
|
||||
static $ip = null;
|
||||
if ($ip !== null) return $ip;
|
||||
$ip = trim(shell_exec("ip route get 8.8.8.8 2>/dev/null | awk '/src/{for(i=1;i<=NF;i++)if(\$i==\"src\")print \$(i+1)}'") ?? '');
|
||||
if (!$ip) $ip = gethostbyname(gethostname());
|
||||
return $ip;
|
||||
}
|
||||
require_once __DIR__ . '/config.php';
|
||||
|
||||
function vv_container_webui(string $name, array $portMap): string {
|
||||
$template = '/boot/config/plugins/dockerMan/templates-user/my-' . $name . '.xml';
|
||||
|
||||
@@ -7,15 +7,11 @@ require_once __DIR__ . '/partnership.php'; // vv_pt_ssh(), vv_pt_ts_peers()
|
||||
// ── Conf parsers ──────────────────────────────────────────────────────────────
|
||||
|
||||
function vv_fb_bash_array(string $raw, string $varname): array {
|
||||
if (!preg_match('/^\s*' . preg_quote($varname, '/') . '\s*=\s*\(\s*(.*?)\s*\)/ms', $raw, $m))
|
||||
return [];
|
||||
preg_match_all('/"([^"]*)"/', $m[1], $items);
|
||||
return array_values(array_filter($items[1]));
|
||||
return vv_parse_bash_array($raw, $varname);
|
||||
}
|
||||
|
||||
function vv_fb_scalar(string $raw, string $varname): string {
|
||||
return preg_match('/^\s*' . preg_quote($varname, '/') . '\s*=\s*"?([^"\n]*)"?/m', $raw, $m)
|
||||
? trim($m[1]) : '';
|
||||
return vv_parse_conf_scalar($raw, $varname);
|
||||
}
|
||||
|
||||
// ── State file ────────────────────────────────────────────────────────────────
|
||||
@@ -90,16 +86,7 @@ function vv_fb_covers(string $covering, string $remote, string $coveringRaw, str
|
||||
// ── Known hosts ───────────────────────────────────────────────────────────────
|
||||
|
||||
function vv_fb_known_hosts(): array {
|
||||
$master = vv_read_conf_raw('master.conf');
|
||||
preg_match_all('/^\s*(HOST(\d+))(?:_NAME)?\s*=\s*["\']?(\S+?)["\']?\s*$/m', $master, $m);
|
||||
$hosts = [];
|
||||
foreach ($m[1] as $i => $key) {
|
||||
$num = $m[2][$i];
|
||||
$name = trim($m[3][$i]);
|
||||
$hosts['host' . $num] = $name;
|
||||
}
|
||||
ksort($hosts);
|
||||
return $hosts ?: ['host1' => 'HOST1'];
|
||||
return vv_known_hosts();
|
||||
}
|
||||
|
||||
// ── Main data builder ─────────────────────────────────────────────────────────
|
||||
|
||||
@@ -6,8 +6,7 @@ require_once __DIR__ . '/config.php';
|
||||
// ── Conf reader ───────────────────────────────────────────────────────────────
|
||||
|
||||
function vv_media_conf_scalar(string $raw, string $key): string {
|
||||
return preg_match('/^\s*' . preg_quote($key, '/') . '\s*=\s*"?([^"\n]*)"?/m', $raw, $m)
|
||||
? trim($m[1]) : '';
|
||||
return vv_parse_conf_scalar($raw, $key);
|
||||
}
|
||||
|
||||
// ── Server list from host conf ────────────────────────────────────────────────
|
||||
|
||||
@@ -7,10 +7,7 @@ require_once __DIR__ . '/partnership.php';
|
||||
// ── Conf array parser (bash arrays) ──────────────────────────────────────────
|
||||
|
||||
function vv_wd_bash_array(string $raw, string $varname): array {
|
||||
if (!preg_match('/^\s*' . preg_quote($varname, '/') . '\s*=\s*\(\s*(.*?)\s*\)/ms', $raw, $m))
|
||||
return [];
|
||||
preg_match_all('/"([^"]*)"/', $m[1], $items);
|
||||
return array_values(array_filter($items[1]));
|
||||
return vv_parse_bash_array($raw, $varname);
|
||||
}
|
||||
|
||||
function vv_wd_bash_assoc(string $raw, string $varname): array {
|
||||
@@ -24,8 +21,7 @@ function vv_wd_bash_assoc(string $raw, string $varname): array {
|
||||
}
|
||||
|
||||
function vv_wd_scalar(string $raw, string $varname): string {
|
||||
return preg_match('/^\s*' . preg_quote($varname, '/') . '\s*=\s*"?([^"\n]*)"?/m', $raw, $m)
|
||||
? trim($m[1]) : '';
|
||||
return vv_parse_conf_scalar($raw, $varname);
|
||||
}
|
||||
|
||||
// ── State file parsers ────────────────────────────────────────────────────────
|
||||
|
||||
@@ -898,62 +898,7 @@ function vvPollMonitor() {
|
||||
}
|
||||
})();
|
||||
|
||||
// ── CPU ─────────────────────────────────────────────────────────────────
|
||||
const cpu = d.cpu ?? {};
|
||||
document.getElementById('vv-cpu-body').innerHTML = vvRenderCpu(cpu);
|
||||
|
||||
vvCpuHistory.push(cpu.overall ?? 0);
|
||||
if (vvCpuHistory.length > VV_HIST_MAX) vvCpuHistory.shift();
|
||||
vvDrawChart(document.getElementById('vv-cpu-canvas'), vvCpuHistory, '#4caf50', 'rgba(76,175,80,0.18)', true);
|
||||
|
||||
// ── Memory ──────────────────────────────────────────────────────────────
|
||||
const mem = d.mem ?? {};
|
||||
document.getElementById('vv-memory-body').innerHTML = vvRenderMemory(mem);
|
||||
|
||||
// ── Network ─────────────────────────────────────────────────────────────
|
||||
const net = d.net ?? {};
|
||||
if (net.available) {
|
||||
const rx = net.rx_bps ?? 0;
|
||||
const tx = net.tx_bps ?? 0;
|
||||
const linkMbps = net.speed_mbps ?? 0;
|
||||
const linkLabel = linkMbps >= 1000 ? (linkMbps / 1000) + ' Gb/s' : linkMbps ? linkMbps + ' Mb/s' : '—';
|
||||
const linkBps = linkMbps * 1e6;
|
||||
|
||||
vvNetRxHistory.push(rx);
|
||||
vvNetTxHistory.push(tx);
|
||||
if (vvNetRxHistory.length > VV_HIST_MAX) vvNetRxHistory.shift();
|
||||
if (vvNetTxHistory.length > VV_HIST_MAX) vvNetTxHistory.shift();
|
||||
|
||||
const maxSeen = Math.max(...vvNetRxHistory, ...vvNetTxHistory, 1);
|
||||
const maxBps = maxSeen * 1.25; // auto-scale with 25% headroom
|
||||
const peakRx = Math.max(...vvNetRxHistory, 0); // window peak (last 2 min)
|
||||
const peakTx = Math.max(...vvNetTxHistory, 0);
|
||||
|
||||
const ipRows = [
|
||||
net.local_ip ? `<div><span style="color:#555;font-size:9px;">LAN </span>${net.local_ip}</div>` : '',
|
||||
net.ext_ip ? `<div><span style="color:#555;font-size:9px;">EXT </span>${net.ext_ip}</div>` : '',
|
||||
net.ts_ip ? `<div><span style="color:#555;font-size:9px;">TS </span>${net.ts_ip}</div>` : '',
|
||||
].filter(Boolean).join('');
|
||||
|
||||
document.getElementById('vv-network-body').innerHTML =
|
||||
`<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px;">
|
||||
<div>
|
||||
<div style="font-size:12px;color:#888;margin-bottom:4px;">${net.iface} · ${linkLabel}</div>
|
||||
<div style="display:flex;gap:16px;font-size:13px;font-weight:600;">
|
||||
<span><span style="color:#4caf50;font-size:9px;margin-right:4px;">━ IN (RX)</span><span style="color:#4caf50;">${vvFmtBps(rx)}</span><span style="color:#444;font-size:9px;font-weight:400;margin-left:4px;">peak ${vvFmtBps(peakRx)}</span></span>
|
||||
<span><span style="color:#ff9800;font-size:9px;margin-right:4px;">━ OUT (TX)</span><span style="color:#ff9800;">${vvFmtBps(tx)}</span><span style="color:#444;font-size:9px;font-weight:400;margin-left:4px;">peak ${vvFmtBps(peakTx)}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align:right;font-size:11px;color:#aaa;line-height:1.6;">${ipRows}</div>
|
||||
</div>
|
||||
<canvas id="vv-net-canvas" style="width:100%;height:110px;display:block;"></canvas>`;
|
||||
|
||||
vvDrawNetChart(document.getElementById('vv-net-canvas'), vvNetRxHistory, vvNetTxHistory, maxBps);
|
||||
} else {
|
||||
document.getElementById('vv-network-body').innerHTML = '<p style="color:#555;font-style:italic">No network interface detected</p>';
|
||||
}
|
||||
|
||||
// ── CPU title (core count + temp to its right) ────────────────────────────
|
||||
// ── CPU title (core count + temp — watchdog data only comes from full poll) ──
|
||||
const _cpuTitleEl = document.getElementById('vv-cpu-title');
|
||||
if (_cpuTitleEl) {
|
||||
const _cpuTemp = d.watchdog?.stability?.cpu_temp ?? null;
|
||||
@@ -1634,6 +1579,69 @@ function vvPollMonitor() {
|
||||
vvPollMonitor();
|
||||
setInterval(vvPollMonitor, 2000);
|
||||
|
||||
// ── Fast poll: CPU, memory, network — 1-second live updates ──────────────────
|
||||
function vvPollFast() {
|
||||
fetch('/plugins/varaverk/api/monitor_fast.php')
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
// CPU
|
||||
const cpu = d.cpu ?? {};
|
||||
document.getElementById('vv-cpu-body').innerHTML = vvRenderCpu(cpu);
|
||||
vvCpuHistory.push(cpu.overall ?? 0);
|
||||
if (vvCpuHistory.length > VV_HIST_MAX) vvCpuHistory.shift();
|
||||
vvDrawChart(document.getElementById('vv-cpu-canvas'), vvCpuHistory, '#4caf50', 'rgba(76,175,80,0.18)', true);
|
||||
|
||||
// Memory
|
||||
document.getElementById('vv-memory-body').innerHTML = vvRenderMemory(d.mem ?? {});
|
||||
|
||||
// Network
|
||||
const net = d.net ?? {};
|
||||
if (net.available) {
|
||||
const rx = net.rx_bps ?? 0;
|
||||
const tx = net.tx_bps ?? 0;
|
||||
const linkMbps = net.speed_mbps ?? 0;
|
||||
const linkLabel = linkMbps >= 1000 ? (linkMbps / 1000) + ' Gb/s' : linkMbps ? linkMbps + ' Mb/s' : '—';
|
||||
|
||||
vvNetRxHistory.push(rx);
|
||||
vvNetTxHistory.push(tx);
|
||||
if (vvNetRxHistory.length > VV_HIST_MAX) vvNetRxHistory.shift();
|
||||
if (vvNetTxHistory.length > VV_HIST_MAX) vvNetTxHistory.shift();
|
||||
|
||||
const maxSeen = Math.max(...vvNetRxHistory, ...vvNetTxHistory, 1);
|
||||
const maxBps = maxSeen * 1.25;
|
||||
const peakRx = Math.max(...vvNetRxHistory, 0);
|
||||
const peakTx = Math.max(...vvNetTxHistory, 0);
|
||||
|
||||
const ipRows = [
|
||||
net.local_ip ? `<div><span style="color:#555;font-size:9px;">LAN </span>${net.local_ip}</div>` : '',
|
||||
net.ext_ip ? `<div><span style="color:#555;font-size:9px;">EXT </span>${net.ext_ip}</div>` : '',
|
||||
net.ts_ip ? `<div><span style="color:#555;font-size:9px;">TS </span>${net.ts_ip}</div>` : '',
|
||||
].filter(Boolean).join('');
|
||||
|
||||
document.getElementById('vv-network-body').innerHTML =
|
||||
`<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px;">
|
||||
<div>
|
||||
<div style="font-size:12px;color:#888;margin-bottom:4px;">${net.iface} · ${linkLabel}</div>
|
||||
<div style="display:flex;gap:16px;font-size:13px;font-weight:600;">
|
||||
<span><span style="color:#4caf50;font-size:9px;margin-right:4px;">━ IN (RX)</span><span style="color:#4caf50;">${vvFmtBps(rx)}</span><span style="color:#444;font-size:9px;font-weight:400;margin-left:4px;">peak ${vvFmtBps(peakRx)}</span></span>
|
||||
<span><span style="color:#ff9800;font-size:9px;margin-right:4px;">━ OUT (TX)</span><span style="color:#ff9800;">${vvFmtBps(tx)}</span><span style="color:#444;font-size:9px;font-weight:400;margin-left:4px;">peak ${vvFmtBps(peakTx)}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align:right;font-size:11px;color:#aaa;line-height:1.6;">${ipRows}</div>
|
||||
</div>
|
||||
<canvas id="vv-net-canvas" style="width:100%;height:110px;display:block;"></canvas>`;
|
||||
|
||||
vvDrawNetChart(document.getElementById('vv-net-canvas'), vvNetRxHistory, vvNetTxHistory, maxBps);
|
||||
} else {
|
||||
document.getElementById('vv-network-body').innerHTML = '<p style="color:#555;font-style:italic">No network interface detected</p>';
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
vvPollFast();
|
||||
setInterval(vvPollFast, 1000);
|
||||
|
||||
// Pin pools card width to CPU card width across rows
|
||||
function vvSyncCardWidths() {
|
||||
const cpu = document.getElementById('vv-cpu');
|
||||
|
||||
Reference in New Issue
Block a user