diff --git a/src/App.jsx b/src/App.jsx
index 85b7763..5486734 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,15 +1,15 @@
import React, { useState, useEffect, useRef } from 'react';
-import {
- Activity,
- Cpu,
- HardDrive,
- Terminal,
- Settings,
- RefreshCw,
- CheckCircle,
- XCircle,
- Zap,
- Flame,
+import {
+ Activity,
+ Cpu,
+ HardDrive,
+ Terminal,
+ Settings,
+ RefreshCw,
+ CheckCircle,
+ XCircle,
+ Zap,
+ Flame,
Server,
AlertTriangle,
Play,
@@ -19,36 +19,12 @@ import {
Clock,
ChevronDown,
ChevronUp,
- Database,
ArrowRight,
Sliders
} from 'lucide-react';
import sideBarLogo from './assets/side-bar-logo.png';
-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 = {}) => {
- const token = localStorage.getItem('rmm_token');
- if (token) {
- options.headers = {
- ...options.headers,
- 'Authorization': `Bearer ${token}`
- };
- }
- return fetch(url, options).then(response => {
- if (response.status === 401) {
- localStorage.removeItem('rmm_token');
- window.dispatchEvent(new Event('rmm_unauthorized'));
- }
- return response;
- });
-};
+import { API_BASE, apiFetch } from './api';
function App() {
const [token, setToken] = useState(localStorage.getItem('rmm_token') || '');
@@ -61,17 +37,21 @@ function App() {
const [telemetry, setTelemetry] = useState({});
const [logs, setLogs] = useState({});
const [whitelist, setWhitelist] = useState({});
-
+
+ // Remote video fetch state
+ const [fileTransfers, setFileTransfers] = useState({});
+ const [videoNameInputs, setVideoNameInputs] = 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');
@@ -123,6 +103,12 @@ function App() {
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 {
@@ -172,6 +158,47 @@ function App() {
}
};
+ const handleRequestFile = async (clientId) => {
+ const filename = (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 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('');
@@ -327,14 +354,14 @@ function App() {
{/* Decorative background glows */}
-
+
-
@@ -387,11 +414,10 @@ function App() {
{/* Dynamic Toast Alerts */}
{toast && (
-
+ }`}>
{toast.type === 'error' ? (
) : (
@@ -405,35 +431,33 @@ function App() {
-
{/* Action Controls & Navigation tabs */}
-
-