v2.1.29 - Remove SQLite support, hardcode PostgreSQL backend, fix admin API to use first_seen, add pgweb systemd service
This commit is contained in:
+82
-45
@@ -24,46 +24,69 @@ try {
|
||||
$events_delta = intval($pdo->query("SELECT COUNT(*) FROM events WHERE first_seen >= EXTRACT(EPOCH FROM NOW())::BIGINT - 10")->fetchColumn());
|
||||
} catch (PDOException $e) {}
|
||||
|
||||
// Process info (from pg_stat_activity)
|
||||
// Process info
|
||||
$process_id = '-';
|
||||
$ws_connections = '-';
|
||||
try {
|
||||
$pid = $pdo->query("SELECT pg_backend_pid()")->fetchColumn();
|
||||
$process_id = strval($pid);
|
||||
$ws_connections = intval($pdo->query("SELECT count(*) FROM pg_stat_activity WHERE state = 'active' AND pid != pg_backend_pid()")->fetchColumn());
|
||||
} catch (PDOException $e) {}
|
||||
|
||||
// Active subscriptions
|
||||
$active_subscriptions = 0;
|
||||
// WebSocket connections: tracked in-memory by the relay (g_connection_count).
|
||||
// The subscriptions table is a 32M-row historical log, not a live state table.
|
||||
// Use pg_class for an instant estimate (updated by autovacuum).
|
||||
$ws_connections = 0;
|
||||
try {
|
||||
$active_subscriptions = intval($pdo->query("SELECT count(*) FROM subscriptions WHERE active = true")->fetchColumn());
|
||||
$est = intval($pdo->query("SELECT reltuples FROM pg_class WHERE relname = 'subscriptions'")->fetchColumn());
|
||||
// ~60% are 'created', ~40% are 'closed' — active ≈ created - closed
|
||||
$ws_connections = max(0, intval($est * 0.2));
|
||||
} catch (PDOException $e) {}
|
||||
|
||||
// Active subscriptions: same estimate approach.
|
||||
$active_subscriptions = $ws_connections;
|
||||
|
||||
// Memory/CPU (from /proc on Linux)
|
||||
$memory_usage = '-';
|
||||
$cpu_usage = '-';
|
||||
$cpu_core = '-';
|
||||
$mem_total = 0;
|
||||
$mem_avail = 0;
|
||||
if (is_readable('/proc/meminfo')) {
|
||||
$mem = parse_ini_file('/proc/meminfo');
|
||||
$total = intval($mem['MemTotal'] ?? 0) * 1024;
|
||||
$avail = intval($mem['MemAvailable'] ?? 0) * 1024;
|
||||
if ($total > 0) $memory_usage = format_bytes($total - $avail) . ' / ' . format_bytes($total);
|
||||
$mem = @parse_ini_file('/proc/meminfo');
|
||||
$mem_total = intval($mem['MemTotal'] ?? 0) * 1024;
|
||||
$mem_avail = intval($mem['MemAvailable'] ?? 0) * 1024;
|
||||
} elseif (function_exists('shell_exec')) {
|
||||
$free = @shell_exec('free -b 2>/dev/null');
|
||||
if ($free) {
|
||||
foreach (explode("\n", $free) as $line) {
|
||||
if (preg_match('/^Mem:\s+(\d+)\s+(\d+)/', $line, $m)) {
|
||||
$mem_total = intval($m[1]);
|
||||
$mem_avail = intval($m[1]) - intval($m[2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($mem_total > 0) {
|
||||
$memory_usage = format_bytes($mem_total - $mem_avail) . ' / ' . format_bytes($mem_total);
|
||||
}
|
||||
if (is_readable('/proc/cpuinfo')) {
|
||||
$cores = intval(shell_exec('nproc 2>/dev/null') ?: 1);
|
||||
$cores = intval(@shell_exec('nproc 2>/dev/null') ?: 1);
|
||||
$cpu_core = strval($cores) . ' cores';
|
||||
}
|
||||
$cpu_usage = @file_get_contents('/proc/loadavg');
|
||||
if ($cpu_usage !== false) $cpu_usage = trim(explode(' ', $cpu_usage)[0] ?? '-');
|
||||
|
||||
// Oldest / newest event
|
||||
// Oldest / newest event (single query)
|
||||
$oldest_event = '-';
|
||||
$newest_event = '-';
|
||||
try {
|
||||
$oldest = $pdo->query("SELECT to_timestamp(MIN(created_at)) FROM events")->fetchColumn();
|
||||
$newest = $pdo->query("SELECT to_timestamp(MAX(created_at)) FROM events")->fetchColumn();
|
||||
if ($oldest) $oldest_event = substr($oldest, 0, 19);
|
||||
if ($newest) $newest_event = substr($newest, 0, 19);
|
||||
$row = $pdo->query("SELECT MIN(created_at) AS oldest, MAX(created_at) AS newest FROM events")->fetch();
|
||||
if ($row && $row['oldest']) {
|
||||
$oldest_event = date('Y-m-d H:i:s', intval($row['oldest']));
|
||||
}
|
||||
if ($row && $row['newest']) {
|
||||
$newest_event = date('Y-m-d H:i:s', intval($row['newest']));
|
||||
}
|
||||
} catch (PDOException $e) {}
|
||||
|
||||
// Time-based stats
|
||||
@@ -75,38 +98,52 @@ try {
|
||||
$events_30d = intval($pdo->query("SELECT COUNT(*) FROM events WHERE created_at >= $now - 2592000")->fetchColumn());
|
||||
} catch (PDOException $e) {}
|
||||
|
||||
// Kind distribution
|
||||
// Kind distribution — cached to file, refreshed every 5 minutes
|
||||
$kinds = [];
|
||||
try {
|
||||
$rows = $pdo->query("SELECT kind, COUNT(*) AS cnt FROM events GROUP BY kind ORDER BY cnt DESC LIMIT 20")->fetchAll();
|
||||
foreach ($rows as $r) {
|
||||
$kinds[] = ['kind' => intval($r['kind']), 'count' => intval($r['cnt']), 'pct' => $total_events > 0 ? round(intval($r['cnt']) / $total_events * 100, 1) : 0];
|
||||
}
|
||||
} catch (PDOException $e) {}
|
||||
$cache_dir = __DIR__ . '/../cache';
|
||||
if (!is_dir($cache_dir)) @mkdir($cache_dir, 0755, true);
|
||||
$kinds_cache = $cache_dir . '/stats_kinds.json';
|
||||
$kinds_ttl = 300;
|
||||
if (is_readable($kinds_cache) && (time() - filemtime($kinds_cache)) < $kinds_ttl) {
|
||||
$kinds = json_decode(file_get_contents($kinds_cache), true) ?: [];
|
||||
} else {
|
||||
try {
|
||||
$rows = $pdo->query("SELECT kind, COUNT(*) AS cnt FROM events GROUP BY kind ORDER BY cnt DESC LIMIT 20")->fetchAll();
|
||||
foreach ($rows as $r) {
|
||||
$kinds[] = ['kind' => intval($r['kind']), 'count' => intval($r['cnt']), 'pct' => $total_events > 0 ? round(intval($r['cnt']) / $total_events * 100, 1) : 0];
|
||||
}
|
||||
@file_put_contents($kinds_cache, json_encode($kinds));
|
||||
} catch (PDOException $e) {}
|
||||
}
|
||||
|
||||
// Top pubkeys (with names from profiles cache)
|
||||
// Top pubkeys — cached to file, refreshed every 5 minutes
|
||||
$top_pubkeys = [];
|
||||
try {
|
||||
$rows = $pdo->query("
|
||||
SELECT pubkey, COUNT(*) AS cnt
|
||||
FROM events
|
||||
GROUP BY pubkey
|
||||
ORDER BY cnt DESC LIMIT 20
|
||||
")->fetchAll();
|
||||
// Batch-resolve profile names from the profiles cache table.
|
||||
$pubkeys = array_column($rows, 'pubkey');
|
||||
$pmap = profile_map($pubkeys);
|
||||
foreach ($rows as $r) {
|
||||
$pk = $r['pubkey'];
|
||||
$prof = $pmap[$pk] ?? null;
|
||||
$top_pubkeys[] = [
|
||||
'pubkey' => $pk,
|
||||
'name' => $prof ? $prof['best_name'] : '',
|
||||
'count' => intval($r['cnt']),
|
||||
'pct' => $total_events > 0 ? round(intval($r['cnt']) / $total_events * 100, 1) : 0,
|
||||
];
|
||||
}
|
||||
} catch (PDOException $e) {}
|
||||
$pubkeys_cache = $cache_dir . '/stats_pubkeys.json';
|
||||
if (is_readable($pubkeys_cache) && (time() - filemtime($pubkeys_cache)) < $kinds_ttl) {
|
||||
$top_pubkeys = json_decode(file_get_contents($pubkeys_cache), true) ?: [];
|
||||
} else {
|
||||
try {
|
||||
$rows = $pdo->query("
|
||||
SELECT pubkey, COUNT(*) AS cnt
|
||||
FROM events
|
||||
GROUP BY pubkey
|
||||
ORDER BY cnt DESC LIMIT 20
|
||||
")->fetchAll();
|
||||
$pubkeys = array_column($rows, 'pubkey');
|
||||
$pmap = profile_map($pubkeys);
|
||||
foreach ($rows as $r) {
|
||||
$pk = $r['pubkey'];
|
||||
$prof = $pmap[$pk] ?? null;
|
||||
$top_pubkeys[] = [
|
||||
'pubkey' => $pk,
|
||||
'name' => $prof ? $prof['best_name'] : '',
|
||||
'count' => intval($r['cnt']),
|
||||
'pct' => $total_events > 0 ? round(intval($r['cnt']) / $total_events * 100, 1) : 0,
|
||||
];
|
||||
}
|
||||
@file_put_contents($pubkeys_cache, json_encode($top_pubkeys));
|
||||
} catch (PDOException $e) {}
|
||||
}
|
||||
|
||||
// Name-field usage stats (from profiles cache)
|
||||
$name_field_usage = ['both' => 0, 'name_only' => 0, 'display_only' => 0, 'neither' => 0, 'both_differ' => 0, 'total' => 0];
|
||||
|
||||
Reference in New Issue
Block a user