mirror of
https://github.com/myronblair/jarvis
synced 2026-06-30 17:50:23 -05:00
Initial commit: JARVIS AI dashboard v2.3
- 4-tier chat: HA control → Ollama → Groq → Claude - Push-based agent system with heartbeat/metrics - Network monitoring, alerts, Proxmox, Home Assistant - Windows + Linux agent installers - Stats cache cron, facts collector, KB engine
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
// Network monitoring endpoint
|
||||
|
||||
function pingHost(string $ip): array {
|
||||
$cmd = 'ping -c 1 -W 1 ' . escapeshellarg($ip) . ' 2>/dev/null';
|
||||
$out = shell_exec($cmd);
|
||||
$alive = $out && strpos($out, '1 received') !== false;
|
||||
$latency = null;
|
||||
if ($alive && preg_match('/time=([\d.]+)/', $out, $m)) {
|
||||
$latency = (float)$m[1];
|
||||
}
|
||||
return ['alive' => $alive, 'latency_ms' => $latency];
|
||||
}
|
||||
|
||||
function scanSubnet(string $prefix, int $timeout = 10): array {
|
||||
$cmd = 'nmap -sn --host-timeout 1s ' . escapeshellarg($prefix . '.0/24') .
|
||||
' -oG - 2>/dev/null | grep "Up$" | awk \'{print $2}\'';
|
||||
$out = shell_exec($cmd) ?? '';
|
||||
$hosts = array_filter(explode("\n", trim($out)));
|
||||
return array_values($hosts);
|
||||
}
|
||||
|
||||
function getArpTable(): array {
|
||||
$out = shell_exec('arp -n 2>/dev/null') ?? '';
|
||||
$devices = [];
|
||||
foreach (explode("\n", trim($out)) as $line) {
|
||||
if (preg_match('/^([\d.]+)\s+\w+\s+([\w:]+)/', $line, $m)) {
|
||||
$devices[$m[1]] = strtolower($m[2]);
|
||||
}
|
||||
}
|
||||
return $devices;
|
||||
}
|
||||
|
||||
$action = $action ?? 'status';
|
||||
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
||||
|
||||
if ($action === 'scan') {
|
||||
$liveHosts = scanSubnet(LOCAL_SUBNET, 8);
|
||||
$arp = getArpTable();
|
||||
$known = JarvisDB::query('SELECT * FROM network_devices');
|
||||
$knownMap = [];
|
||||
foreach ($known as $d) $knownMap[$d['ip']] = $d;
|
||||
|
||||
$devices = [];
|
||||
foreach ($liveHosts as $ip) {
|
||||
$mac = $arp[$ip] ?? null;
|
||||
$known_dev = $knownMap[$ip] ?? null;
|
||||
$nsOut = shell_exec('timeout 1 nslookup ' . escapeshellarg($ip) . ' 2>/dev/null | grep "name ="');
|
||||
$hostname = null;
|
||||
if ($nsOut && preg_match('/name = (.+)\./', $nsOut, $nm)) {
|
||||
$hostname = rtrim($nm[1], '.');
|
||||
}
|
||||
$devices[] = [
|
||||
'ip' => $ip,
|
||||
'mac' => $mac,
|
||||
'hostname' => $hostname,
|
||||
'alias' => $known_dev['alias'] ?? null,
|
||||
'type' => $known_dev['device_type'] ?? 'unknown',
|
||||
'status' => 'online',
|
||||
];
|
||||
JarvisDB::execute(
|
||||
'INSERT INTO network_devices (ip, mac, hostname, status, last_seen) VALUES (?,?,?,\'online\',NOW())
|
||||
ON DUPLICATE KEY UPDATE mac=VALUES(mac), hostname=VALUES(hostname), status=\'online\', last_seen=NOW()',
|
||||
[$ip, $mac, $hostname]
|
||||
);
|
||||
}
|
||||
foreach ($knownMap as $ip => $dev) {
|
||||
if (!in_array($ip, $liveHosts)) {
|
||||
JarvisDB::execute('UPDATE network_devices SET status=\'offline\' WHERE ip=?', [$ip]);
|
||||
}
|
||||
}
|
||||
echo json_encode(['devices' => $devices, 'count' => count($devices), 'scanned_at' => date('c')]);
|
||||
|
||||
} elseif ($action === 'add' && $method === 'POST') {
|
||||
$ip = filter_var($data['ip'] ?? '', FILTER_VALIDATE_IP);
|
||||
$alias = substr(trim($data['alias'] ?? ''), 0, 100);
|
||||
$type = preg_replace('/[^a-z0-9_\-]/', '', strtolower($data['type'] ?? 'device'));
|
||||
if (!$ip) { echo json_encode(['error' => 'Invalid IP address']); exit; }
|
||||
if (!$alias) { echo json_encode(['error' => 'Name is required']); exit; }
|
||||
JarvisDB::execute(
|
||||
'INSERT INTO network_devices (ip, alias, device_type, status) VALUES (?,?,?,\'unknown\')
|
||||
ON DUPLICATE KEY UPDATE alias=VALUES(alias), device_type=VALUES(device_type)',
|
||||
[$ip, $alias, $type]
|
||||
);
|
||||
echo json_encode(['success' => true]);
|
||||
|
||||
} elseif ($action === 'delete' && $method === 'POST') {
|
||||
$ip = filter_var($data['ip'] ?? '', FILTER_VALIDATE_IP);
|
||||
if (!$ip) { echo json_encode(['error' => 'Invalid IP']); exit; }
|
||||
// Don't allow deleting agent-managed entries
|
||||
$isAgent = JarvisDB::query('SELECT id FROM registered_agents WHERE ip_address=? LIMIT 1', [$ip]);
|
||||
if (!empty($isAgent)) { echo json_encode(['error' => 'Cannot delete agent-managed device']); exit; }
|
||||
JarvisDB::execute('DELETE FROM network_devices WHERE ip=?', [$ip]);
|
||||
echo json_encode(['success' => true]);
|
||||
|
||||
} else {
|
||||
// Status: unified device list from agents + user-managed DB entries + external services
|
||||
$devices = [];
|
||||
|
||||
// Mark agents offline if not heard from in 2 minutes
|
||||
JarvisDB::execute(
|
||||
'UPDATE registered_agents SET status="offline" WHERE last_seen < DATE_SUB(NOW(), INTERVAL 2 MINUTE) AND status = "online"'
|
||||
);
|
||||
|
||||
// 1. Agent-based devices — status from heartbeat, no ping from DO needed
|
||||
$agents = JarvisDB::query(
|
||||
'SELECT agent_id, hostname, ip_address, status, last_seen, agent_type FROM registered_agents ORDER BY hostname'
|
||||
);
|
||||
$agentIPs = [];
|
||||
foreach ($agents as $ag) {
|
||||
$agentIPs[] = $ag['ip_address'];
|
||||
$devices[] = [
|
||||
'ip' => $ag['ip_address'],
|
||||
'name' => $ag['hostname'],
|
||||
'type' => 'agent',
|
||||
'agent_id' => $ag['agent_id'],
|
||||
'agent_type' => $ag['agent_type'],
|
||||
'alive' => $ag['status'] === 'online',
|
||||
'status' => $ag['status'],
|
||||
'last_seen' => $ag['last_seen'],
|
||||
'source' => 'agent',
|
||||
'deletable' => false,
|
||||
];
|
||||
}
|
||||
|
||||
// 2. User-managed devices from DB (named/aliased entries not covered by agents)
|
||||
$pinned = JarvisDB::query(
|
||||
'SELECT ip, alias, device_type, status, last_seen FROM network_devices
|
||||
WHERE alias IS NOT NULL AND alias != "" ORDER BY alias'
|
||||
);
|
||||
foreach ($pinned as $dev) {
|
||||
if (in_array($dev['ip'], $agentIPs)) continue; // agent already covers this IP
|
||||
$ping = pingHost($dev['ip']);
|
||||
$newStatus = $ping['alive'] ? 'online' : 'offline';
|
||||
JarvisDB::execute(
|
||||
'UPDATE network_devices SET status=?, last_seen=NOW() WHERE ip=?',
|
||||
[$newStatus, $dev['ip']]
|
||||
);
|
||||
$devices[] = [
|
||||
'ip' => $dev['ip'],
|
||||
'name' => $dev['alias'],
|
||||
'type' => $dev['device_type'] ?: 'device',
|
||||
'alive' => $ping['alive'],
|
||||
'latency_ms' => $ping['latency_ms'],
|
||||
'status' => $newStatus,
|
||||
'last_seen' => $dev['last_seen'],
|
||||
'source' => 'db',
|
||||
'deletable' => true,
|
||||
];
|
||||
}
|
||||
|
||||
// 3. External services we can actually ping from DO
|
||||
$external = [
|
||||
['ip' => '134.209.72.226', 'name' => 'FusionPBX DO', 'type' => 'server'],
|
||||
];
|
||||
foreach ($external as $host) {
|
||||
if (in_array($host['ip'], $agentIPs)) continue;
|
||||
$ping = pingHost($host['ip']);
|
||||
$devices[] = array_merge($host, [
|
||||
'alive' => $ping['alive'],
|
||||
'latency_ms' => $ping['latency_ms'],
|
||||
'status' => $ping['alive'] ? 'online' : 'offline',
|
||||
'source' => 'static',
|
||||
'deletable' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
echo json_encode(['devices' => $devices, 'timestamp' => date('c')]);
|
||||
}
|
||||
Reference in New Issue
Block a user