import React, { useState, useEffect, useRef } from 'react';
import {
Activity,
Cpu,
HardDrive,
Terminal,
Settings,
RefreshCw,
CheckCircle,
XCircle,
Zap,
Flame,
Server,
AlertTriangle,
Play,
Search,
Code,
FileCode,
Clock,
ChevronDown,
ChevronUp,
Database,
ArrowRight,
Sliders
} from 'lucide-react';
const API_BASE = import.meta.env.VITE_API_BASE_URL ||
(window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'
? 'http://localhost:8000'
: (window.location.protocol === 'https:'
? 'https://rmm-backend.seekright.com'
: 'http://rmm-backend.seekright.com'));
// Secure API fetch wrapper
const apiFetch = (url, options = {}) => {
return fetch(url, options);
};
function App() {
const [activeTab, setActiveTab] = useState('dashboard'); // 'dashboard' | 'editor'
const [clients, setClients] = useState({});
const [telemetry, setTelemetry] = useState({});
const [logs, setLogs] = useState({});
const [whitelist, setWhitelist] = useState({});
// Dispatcher form state
const [selectedClients, setSelectedClients] = useState([]);
const [selectedCommand, setSelectedCommand] = useState('');
const [manualClient, setManualClient] = useState('');
const [manualCommand, setManualCommand] = useState('');
// Whitelist config editor state
const [editorText, setEditorText] = useState('{\n "nt": {},\n "posix": {}\n}');
const [jsonError, setJsonError] = useState(null); // Real-time JSON validation error
const [toast, setToast] = useState(null);
const [isSyncing, setIsSyncing] = useState(false);
const [filterClient, setFilterClient] = useState('all');
const [nodeFilter, setNodeFilter] = useState('all'); // 'all' | 'online' | 'offline'
const [searchQuery, setSearchQuery] = useState(''); // Live search filter for clients
const [expandedClients, setExpandedClients] = useState({}); // Expanded telemetry details for offline nodes
const terminalRef = useRef(null);
const showToast = (message, type = 'success') => {
setToast({ message, type });
setTimeout(() => setToast(null), 4000);
};
// 1. Initial configuration bootstrap
const fetchWhitelist = async () => {
try {
const response = await apiFetch(`${API_BASE}/api/get-raw-commands`);
if (response.ok) {
const data = await response.json();
setWhitelist(data);
setEditorText(JSON.stringify(data, null, 2));
}
} catch (err) {
console.error('Failed to load raw commands whitelist:', err);
}
};
// 2. Continuous telemetry & log sync polling
const syncFleetData = async () => {
setIsSyncing(true);
try {
// Sync active clients registry
const clientsRes = await apiFetch(`${API_BASE}/api/clients`);
if (clientsRes.ok) {
const clientsData = await clientsRes.json();
setClients(clientsData);
}
// Sync active telemetry details
const telRes = await apiFetch(`${API_BASE}/api/telemetry`);
if (telRes.ok) {
const telData = await telRes.json();
setTelemetry(telData);
}
// Sync circular history logs
const logsRes = await apiFetch(`${API_BASE}/api/logs`);
if (logsRes.ok) {
const logsData = await logsRes.json();
setLogs(logsData);
}
} catch (err) {
console.error('Network sync failure:', err);
} finally {
setIsSyncing(false);
}
};
useEffect(() => {
fetchWhitelist();
syncFleetData();
// Set up rapid 3-second fleet synchronization loop
const syncInterval = setInterval(syncFleetData, 3000);
return () => clearInterval(syncInterval);
}, []);
// Scroll terminal logs to bottom automatically when new actions populate,
// but only if the user is already looking at the latest logs (near the bottom)!
// If they scrolled up to inspect an error, we freeze the screen scroll.
useEffect(() => {
if (terminalRef.current) {
const { scrollTop, scrollHeight, clientHeight } = terminalRef.current;
// Define a 120px threshold close to the bottom
const isNearBottom = scrollHeight - scrollTop - clientHeight < 120;
const isFirstLoad = scrollTop === 0;
if (isNearBottom || isFirstLoad) {
terminalRef.current.scrollTop = scrollHeight;
}
}
}, [logs]);
// Real-time JSON validation
useEffect(() => {
try {
if (editorText.trim() === '') {
setJsonError('JSON configuration cannot be empty.');
return;
}
JSON.parse(editorText);
setJsonError(null);
} catch (err) {
setJsonError(err.message);
}
}, [editorText]);
// Dispatch command to client execution queue
const handleDispatch = async (e) => {
e.preventDefault();
// Resolve final list of targets
let finalClientsString = '';
if (manualClient.trim()) {
finalClientsString = manualClient.trim();
} else if (selectedClients.length > 0) {
finalClientsString = selectedClients.join(',');
}
const finalCommand = (manualCommand.trim() || selectedCommand).trim();
if (!finalClientsString) {
showToast('Please select target nodes or specify a manual name!', 'error');
return;
}
if (!finalCommand) {
showToast('Please specify a Whitelisted Operation Key!', 'error');
return;
}
try {
const response = await apiFetch(
`${API_BASE}/api/schedule-command?client_id=${encodeURIComponent(finalClientsString)}&command=${encodeURIComponent(finalCommand)}`,
{ method: 'POST' }
);
const data = await response.json();
if (response.ok) {
showToast(`Operation batch successfully dispatched to: ${finalClientsString}!`, 'success');
setManualCommand('');
setManualClient('');
setSelectedClients([]);
syncFleetData();
} else {
showToast(data.detail || 'Dispatch scheduling failed.', 'error');
}
} catch (err) {
showToast('Network error contacting central RMM server API.', 'error');
}
};
// Dynamic Whitelist Save Config
const handleSaveWhitelist = async () => {
if (jsonError) {
showToast('Cannot save configuration. Please resolve JSON errors first.', 'error');
return;
}
try {
const parsedConfig = JSON.parse(editorText);
const response = await apiFetch(`${API_BASE}/api/save-commands`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(parsedConfig)
});
if (response.ok) {
showToast('Dynamic Commands Whitelist successfully deployed to cluster!', 'success');
fetchWhitelist(); // Reload whitelists
} else {
const errorData = await response.json();
showToast(`Failed to deploy: ${errorData.detail}`, 'error');
}
} catch (err) {
showToast('Invalid JSON structure! Please audit your config syntax.', 'error');
}
};
const toggleClientDetails = (clientId) => {
setExpandedClients(prev => ({
...prev,
[clientId]: !prev[clientId]
}));
};
// Compile individual client arrays into a single global chronological timeline for terminal display
const getSortedTimeline = () => {
const timeline = [];
Object.entries(logs).forEach(([cid, entryList]) => {
// Apply system filter
if (filterClient !== 'all' && cid !== filterClient) {
return;
}
if (Array.isArray(entryList)) {
entryList.forEach((logItem) => {
timeline.push({
...logItem,
client_id: cid
});
});
}
});
// Sort ascending by ISO timestamp
return timeline.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
};
const sortedTimelineLogs = getSortedTimeline();
const activeClients = Object.values(clients).filter(c => c.active).length;
const totalClients = Object.keys(clients).length;
// Filter clients based on search query AND node status tab
const filteredClientEntries = Object.entries(clients).filter(([clientId, info]) => {
const matchesSearch = clientId.toLowerCase().includes(searchQuery.toLowerCase());
const matchesFilter =
nodeFilter === 'all' ||
(nodeFilter === 'online' && info.active) ||
(nodeFilter === 'offline' && !info.active);
return matchesSearch && matchesFilter;
});
return (
{/* Dynamic Toast Alerts */}
{toast && (
{toast.type === 'error' ? (
) : (
)}
{toast.message}
)}
{/* Modern Glass Header Section */}
{/* Main Container */}
{activeTab === 'dashboard' ? (
<>
{/* Dashboard Vitals: Grid of Cards (Left/Center Column) */}
{/* Central Metrics Board — clickable filter tabs */}
{/* ALL tab */}
{/* ONLINE tab */}
{/* OFFLINE tab */}
{/* Vitals Node Grid Cards */}
Remote Fleet Systems Grid ({filteredClientEntries.length})
{nodeFilter !== 'all' && (
{nodeFilter === 'online' ? '🟢 Online' : '🔴 Offline'}
)}
{/* Clear filter shortcut */}
{nodeFilter !== 'all' && (
)}
{/* Node Search Bar */}
setSearchQuery(e.target.value)}
className="w-full bg-slate-950/60 border border-white/5 rounded-xl pl-9 pr-4 py-1.5 text-xs text-slate-300 placeholder-slate-500 focus:outline-none focus:border-violet-500 focus:ring-1 focus:ring-violet-500/20 transition-all"
/>
{filteredClientEntries.length === 0 ? (
No systems matching your query.
Make sure the agent services are actively running
) : (
filteredClientEntries.map(([clientId, info]) => {
const clientTelemetry = telemetry[clientId];
const isOnline = info.active;
const lastSeenDate = info.last_seen ? new Date(info.last_seen).toLocaleString() : 'Never';
const isExpanded = expandedClients[clientId] || false;
{/* Render Compact Card for Offline System */}
if (!isOnline) {
return (
Last Contact
{lastSeenDate}
{/* Expanded Empty Telemetry Status */}
{isExpanded && (
Telemetry check-in pending. System is offline and not responding to pings.
)}
);
}
{/* Render Full Card for Active/Online System */}
return (
{/* Status top glow line */}
{/* Title block */}
LAST CHECK-IN
{lastSeenDate}
{/* Telemetry charts row */}
{clientTelemetry ? (
{/* CPU & RAM progress */}
{/* CPU Progress block */}
{(() => {
const cpu = clientTelemetry.cpu_percent || 0;
const isWarning = cpu > 80;
return (
CPU Load
{cpu}%
60
? 'bg-gradient-to-r from-amber-500 to-yellow-400'
: 'bg-gradient-to-r from-violet-500 to-indigo-500'
}`}
style={{ width: `${cpu}%` }}
/>
{isWarning && (
)}
);
})()}
{/* Memory Progress block */}
{(() => {
const total = clientTelemetry.memory_total_gb || 0;
const free = clientTelemetry.memory_free_gb || 0;
const used = (total - free).toFixed(2);
const ram = clientTelemetry.memory_percent || 0;
const isWarning = ram > 85;
return (
70
? 'bg-gradient-to-r from-amber-500 to-yellow-400'
: 'bg-gradient-to-r from-violet-500 to-indigo-500'
}`}
style={{ width: `${ram}%` }}
/>
Used: {used}G
Free: {free}G
Total: {total}G
);
})()}
{/* GPU and Partition details */}
{/* Partition Storage */}
Disk Drive Space
{clientTelemetry.disks && clientTelemetry.disks.length > 0 ? (
clientTelemetry.disks.map((d, index) => {
const diskTotal = d.total_gb || 0;
const diskFree = d.free_gb || 0;
const diskUsed = (diskTotal - diskFree).toFixed(2);
// Get friendly label
const diskLabel = d.label || (d.mount === '/' ? 'Computer' : (d.mount.split(/[/\\]/).pop() || 'Volume'));
return (
{/* Left side: Icon, Label, Device badge, Mount path */}
{diskLabel}
{d.device && (
{d.device}
)}
{d.device && on}
{d.mount}
{/* Right side: Free Space / Total */}
{diskFree} GB free
of {diskTotal} GB
{/* Progress bar */}
85
? 'bg-gradient-to-r from-rose-500 to-red-500'
: d.percent > 70
? 'bg-gradient-to-r from-amber-500 to-yellow-500'
: 'bg-gradient-to-r from-indigo-500 to-violet-500'
}`}
style={{ width: `${d.percent}%` }}
/>
{diskUsed} GB Used
85 ? "text-rose-400 font-bold" : ""}>
{d.percent}% Used
);
})
) : (
No storage drives reported.
)}
{/* Nvidia Core GPU Vitals */}
Nvidia GPU Accelerator
{clientTelemetry.gpus && clientTelemetry.gpus.length > 0 ? (
clientTelemetry.gpus.map((gpu, index) => (
🎮 {gpu.name}
Load: {gpu.utilization}
Temp: {gpu.temp}
Fans: {gpu.fan_speed}
VRAM:
{gpu.memory_used} / {gpu.memory_total || 'N/A'}
Power:
{gpu.power_draw} / {gpu.power_limit || 'N/A'}
))
) : (
No NVIDIA GPU hardware detected.
)}
) : (
Telemetry check-in pending... Waiting for dynamic vitals transmission
)}
);
})
)}
{/* Maintenance Command Dispatch deck (Right sidebar) */}
Maintenance Dispatcher
Send a whitelisted command down to a node
>
) : (
/* Visual Whitelist JSON Configuration Editor */
Visual Commands whitelist Editor
Permitted operations are checked against whitelist config and pushed to nodes without restarts.
{/* JSON Validation Status Badge */}
{jsonError ? (
Invalid JSON Config
) : (
Valid JSON Config
)}
{/* IDE Editor UI wrapper */}
{jsonError && (
JSON Parse Error: {jsonError}
)}
)}
{/* Scrolling DevOps CLI output shell terminal */}
);
}
// Simple custom Check icon component since lucide-react Check may sometimes fail or need explicit import mapping
function Check({ className, ...props }) {
return (
);
}
export default App;