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,
ArrowRight,
Sliders
} from 'lucide-react';
import sideBarLogo from './assets/side-bar-logo.png';
import { API_BASE, apiFetch } from './api';
function App() {
const [token, setToken] = useState(localStorage.getItem('rmm_token') || '');
const [loginUsername, setLoginUsername] = useState('');
const [loginPassword, setLoginPassword] = useState('');
const [loginError, setLoginError] = useState('');
const [activeTab, setActiveTab] = useState('dashboard'); // 'dashboard' | 'editor'
const [clients, setClients] = useState({});
const [telemetry, setTelemetry] = useState({});
const [logs, setLogs] = useState({});
const [whitelist, setWhitelist] = useState({});
// Remote video fetch state
const [fileTransfers, setFileTransfers] = useState({});
const [videoNameInputs, setVideoNameInputs] = useState({});
const [shiftPathInputs, setShiftPathInputs] = useState({});
const [searchInputs, setSearchInputs] = 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);
}
// Sync remote video fetch transfer states
const ftRes = await apiFetch(`${API_BASE}/api/file-transfers`);
if (ftRes.ok) {
setFileTransfers(await ftRes.json());
}
} catch (err) {
console.error('Network sync failure:', err);
} finally {
setIsSyncing(false);
}
};
useEffect(() => {
const handleUnauthorized = () => {
setToken('');
};
window.addEventListener('rmm_unauthorized', handleUnauthorized);
return () => window.removeEventListener('rmm_unauthorized', handleUnauthorized);
}, []);
useEffect(() => {
if (!token) return;
fetchWhitelist();
syncFleetData();
// Set up rapid 3-second fleet synchronization loop
const syncInterval = setInterval(syncFleetData, 3000);
return () => clearInterval(syncInterval);
}, [token]);
const handleLogin = async (e) => {
e.preventDefault();
setLoginError('');
try {
const response = await fetch(`${API_BASE}/api/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: loginUsername, password: loginPassword })
});
const data = await response.json();
if (response.ok) {
localStorage.setItem('rmm_token', data.token);
setToken(data.token);
setLoginPassword('');
showToast('Login successful!', 'success');
} else {
setLoginError(data.detail || 'Login failed. Invalid username or password.');
}
} catch (err) {
setLoginError('Cannot connect to RMM backend server.');
}
};
const handleRequestFile = async (clientId, overrideName) => {
const filename = (overrideName || videoNameInputs[clientId] || '').trim();
if (!filename) {
showToast('Enter a video file name first.', 'error');
return;
}
try {
const response = await apiFetch(
`${API_BASE}/api/request-file?client_id=${encodeURIComponent(clientId)}&filename=${encodeURIComponent(filename)}`,
{ method: 'POST' }
);
if (response.ok) {
showToast(`Requested '${filename}' from ${clientId}`);
syncFleetData();
} else {
const data = await response.json().catch(() => ({}));
showToast(data.detail || 'Failed to request file.', 'error');
}
} catch (err) {
showToast('Cannot connect to RMM backend server.', 'error');
}
};
const handleSaveShiftPath = async (clientId) => {
const path = (shiftPathInputs[clientId] ?? clients[clientId]?.shift_path ?? '').trim();
try {
const response = await apiFetch(
`${API_BASE}/api/set-shift-path?client_id=${encodeURIComponent(clientId)}&shift_path=${encodeURIComponent(path)}`,
{ method: 'POST' }
);
if (response.ok) {
showToast(`SHIFT path saved for ${clientId}`);
syncFleetData();
} else {
const data = await response.json().catch(() => ({}));
showToast(data.detail || 'Failed to save SHIFT path.', 'error');
}
} catch (err) {
showToast('Cannot connect to RMM backend server.', 'error');
}
};
const handleSearchVideos = async (clientId) => {
const query = (searchInputs[clientId] || '').trim();
if (!query) {
showToast('Enter a search term first (e.g. a date like 20260716).', 'error');
return;
}
try {
const response = await apiFetch(
`${API_BASE}/api/request-search?client_id=${encodeURIComponent(clientId)}&query=${encodeURIComponent(query)}`,
{ method: 'POST' }
);
if (response.ok) {
showToast(`Searching '${query}' on ${clientId}...`);
syncFleetData();
} else {
const data = await response.json().catch(() => ({}));
showToast(data.detail || 'Failed to start search.', 'error');
}
} catch (err) {
showToast('Cannot connect to RMM backend server.', 'error');
}
};
const handleCancelUpload = async (clientId, filename) => {
try {
const response = await apiFetch(
`${API_BASE}/api/cancel-upload?client_id=${encodeURIComponent(clientId)}&filename=${encodeURIComponent(filename)}`,
{ method: 'POST' }
);
if (response.ok) {
showToast(`Cancelled upload for '${filename}'`);
syncFleetData();
} else {
const data = await response.json().catch(() => ({}));
showToast(data.detail || 'Failed to cancel upload.', 'error');
}
} catch (err) {
showToast('Cannot connect to server.', 'error');
}
};
const handleLogout = () => {
localStorage.removeItem('rmm_token');
setToken('');
setClients({});
setTelemetry({});
setLogs({});
showToast('Logged out successfully.', 'success');
};
// 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;
});
if (!token) {
return (
{/* Decorative background glows */}
{loginError && (
{loginError}
)}
);
}
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;
const ft = fileTransfers[clientId];
{/* Render Compact Card for Offline System */ }
if (!isOnline) {
return (
{clientId}
Offline
{info.agent_version ? `v${info.agent_version}` : 'legacy'}
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 */}
{clientId}
Active
{info.agent_version ? `v${info.agent_version}` : 'legacy'}
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}%
{clientTelemetry.cpu_temp !== undefined && clientTelemetry.cpu_temp !== null && (
@ {clientTelemetry.cpu_temp}°C
)}
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
)}
{/* Remote Video Fetch panel */}
Fetch Video From Node
{/* SHIFT folder location for this node */}
{/* Search videos inside the SHIFT folder */}
{parseFloat(info.agent_version) < 3.3 && (
Search needs agent v3.3-shift — run update_agent on this node first.
)}
{/* Search results */}
{info.file_search && (
Results for '{info.file_search.query}'
{info.file_search.searched_path && (
in {info.file_search.searched_path}
)}
{info.file_search.status === 'searching' && }
{info.file_search.status === 'done'
? `${(info.file_search.results || []).length} found`
: info.file_search.status}
{info.file_search.error && (
{info.file_search.error}
)}
{info.file_search.status === 'done' && (info.file_search.results || []).length > 0 && (
{info.file_search.results.map((r) => (
{r.name}
{r.size_mb !== undefined ? `${r.size_mb} MB` : ''}{r.modified ? ` · ${r.modified}` : ''}
))}
)}
)}
{ft && (
{ft.filename}
{(ft.status === 'requested' || ft.status === 'searching' || ft.status === 'uploading') && (
)}
{ft.status === 'ready' && }
{(ft.status === 'not_found' || ft.status === 'error') && }
{ft.status}
{ft.message && (
{ft.message}
)}
{ft.progress_percent !== undefined && ft.progress_percent !== null && (
)}
{(ft.status === 'requested' || ft.status === 'searching' || ft.status === 'uploading') && (
)}
{ft.status === 'ready' && (
)}
)}
);
})
)}
{/* Maintenance Command Dispatch deck (Right sidebar) */}
Maintenance Dispatcher
Send a whitelisted command down to a node
>
) : activeTab === 'editor' ? (
/* 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}
)}
) : null}
{/* 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;