From dcaab7124492f37c8aa6fb51c301a530a9ea2586 Mon Sep 17 00:00:00 2001 From: Bailey Taylor Date: Wed, 8 Oct 2025 09:46:24 +0800 Subject: [PATCH] Added new backfill button for CVE sync. --- src/components/admin/AdminControlsPanel.tsx | 797 +++++++++++--------- 1 file changed, 432 insertions(+), 365 deletions(-) diff --git a/src/components/admin/AdminControlsPanel.tsx b/src/components/admin/AdminControlsPanel.tsx index 8476652..06b7ab3 100644 --- a/src/components/admin/AdminControlsPanel.tsx +++ b/src/components/admin/AdminControlsPanel.tsx @@ -1,396 +1,463 @@ // src/components/admin/AdminControlsPanel.tsx -'use client'; + 'use client'; -import { useEffect, useState } from 'react'; -import { - Typography, - Box, - Button, - Alert, - CircularProgress, - Snackbar, - Dialog, - DialogTitle, - DialogContent, - DialogActions, - Select, - MenuItem, - FormControl, - InputLabel, -} from '@mui/material'; -import { SwitchTextTrack } from '@/components/SwitchTextTrack'; -import CVELogStream from '@/components/CVELogStream'; -import api from '@/lib/axios'; + import { useEffect, useState } from 'react'; + import { + Typography, + Box, + Button, + Alert, + CircularProgress, + Snackbar, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Select, + MenuItem, + FormControl, + InputLabel, + DialogContentText, + } from '@mui/material'; + import { SwitchTextTrack } from '@/components/SwitchTextTrack'; + import CVELogStream from '@/components/CVELogStream'; + import api from '@/lib/axios'; -export default function AdminControlsPanel() { - const [pingEnabled, setPingEnabled] = useState(null); - const [message, setMessage] = useState(''); - const [drawerOpen, setDrawerOpen] = useState(false); - const [logOutput, setLogOutput] = useState(''); - const [loading, setLoading] = useState(false); - const [toastOpen, setToastOpen] = useState(false); - const [toastMessage, setToastMessage] = useState(''); - const [clientList, setClientList] = useState>([]); - const [selectedClientId, setSelectedClientId] = useState(null); - const [dialogOpen, setDialogOpen] = useState(false); - const [cveVisible, setCveVisible] = useState(false); - const [kevVisible, setKevVisible] = useState(false); - const [msrcVisible, setMsrcVisible] = useState(false); + export default function AdminControlsPanel() { + const [pingEnabled, setPingEnabled] = useState(null); + const [message, setMessage] = useState(''); + const [drawerOpen, setDrawerOpen] = useState(false); + const [logOutput, setLogOutput] = useState(''); + const [loading, setLoading] = useState(false); + const [toastOpen, setToastOpen] = useState(false); + const [toastMessage, setToastMessage] = useState(''); + const [clientList, setClientList] = useState>([]); + const [selectedClientId, setSelectedClientId] = useState(null); + const [dialogOpen, setDialogOpen] = useState(false); + const [backfillDialogOpen, setBackfillDialogOpen] = useState(false); + const [cveVisible, setCveVisible] = useState(false); + const [kevVisible, setKevVisible] = useState(false); + const [msrcVisible, setMsrcVisible] = useState(false); - useEffect(() => { - const fetchInitialState = async () => { - try { - const res = await api.get("/system/ping-status"); - if (res?.data?.acceptPings !== undefined) { - setPingEnabled(res.data.acceptPings); + useEffect(() => { + const fetchInitialState = async () => { + try { + const res = await api.get("/system/ping-status"); + if (res?.data?.acceptPings !== undefined) { + setPingEnabled(res.data.acceptPings); + } + } catch (err) { + console.error("❌ Failed to fetch ping status", err); + setPingEnabled(false); } + }; + + fetchInitialState(); + }, []); + + const togglePing = async (enabled: boolean) => { + try { + const res = await api.post(`/admin/toggle-ping?enabled=${enabled}`); + setMessage(res.data); + setPingEnabled(enabled); } catch (err) { - console.error("❌ Failed to fetch ping status", err); - setPingEnabled(false); + console.error("Toggle failed:", err); + setMessage("Failed to update ping status"); } }; - fetchInitialState(); - }, []); - - const togglePing = async (enabled: boolean) => { - try { - const res = await api.post(`/admin/toggle-ping?enabled=${enabled}`); - setMessage(res.data); - setPingEnabled(enabled); - } catch (err) { - console.error("Toggle failed:", err); - setMessage("Failed to update ping status"); - } - }; - - - - useEffect(() => { - if (!drawerOpen) return; - - const interval = setInterval(async () => { - const res = await api.get('/admin/scripts/fetch-cve/logs'); - setLogOutput(res.data); - }, 3000); - - return () => clearInterval(interval); - }, [drawerOpen]); - - const fetchClients = async () => { - try { - const res = await api.get('/auth/clients'); // Adjust endpoint if needed - setClientList(res.data); // Expected: [{ clientId, name }] - } catch (err) { - console.error('Failed to fetch clients', err); - } - }; - - const runCveSync = async () => { - try { - setLoading(true); - await api.post('/admin/scripts/fetch-cve'); - setCveVisible(true); // 👈 Spawn CVE console - } catch (err: any) { - alert("❌ Failed to start CVE sync: " + (err?.response?.data || err.message)); - } finally { - setLoading(false); - } - }; - const runKevSync = async () => { - try { - setLoading(true); - await api.post('/admin/scripts/fetch-kev'); - setKevVisible(true); // 👈 Spawn KEV console - setToastMessage('✅ KEV sync started.'); - setToastOpen(true); - } catch (err: any) { - setToastMessage("❌ Failed to sync KEVs: " + (err?.response?.data || err.message)); - setToastOpen(true); - } finally { - setLoading(false); - } - }; - - const runMsrcSync = async () => { - try { - setLoading(true); - await api.post('/admin/scripts/fetch-msrc'); - setMsrcVisible(true); // 👈 Spawn MSRC console - setToastMessage('✅ MSRC sync started.'); - setToastOpen(true); - } catch (err: any) { - setToastMessage("❌ Failed to fetch Microsoft CVEs: " + (err?.response?.data || err.message)); - setToastOpen(true); - } finally { - setLoading(false); - } - }; - - const runVulnCacheRefresh = async () => { - try { - setLoading(true); - const res = await api.post('/admin/vulns/refresh-cache'); - setToastMessage(res.data); // Show backend result in toast - setToastOpen(true); - } catch (err: any) { - setToastMessage("❌ Failed to refresh vulnerability cache: " + (err?.response?.data || err.message)); - setToastOpen(true); - } finally { - setLoading(false); - } - }; - - const refreshSoftwareCache = async () => { - try { - setLoading(true); - const res = await api.post('/admin/software/refresh-cache'); - setToastMessage(res.data); - setToastOpen(true); - } catch (err: any) { - setToastMessage("❌ Failed to refresh installed software cache: " + (err?.response?.data || err.message)); - setToastOpen(true); - } finally { - setLoading(false); - } - }; - - const refreshStatistics = async () => { - try { - setLoading(true); - await api.post('/admin/statistics/refresh'); - setToastMessage('✅ CVE Statistics refreshed.'); - setToastOpen(true); - } catch (err: any) { - setToastMessage("❌ Failed to refresh statistics: " + (err?.response?.data || err.message)); - setToastOpen(true); - } finally { - setLoading(false); - } - }; - - const normalizeSoftware = async () => { - try { - setLoading(true); - const res = await api.post('/admin/software/normalize'); - setToastMessage(res.data); // ✅ show result - setToastOpen(true); - } catch (err: any) { - setToastMessage("❌ Failed to normalize software entries: " + (err?.response?.data || err.message)); - setToastOpen(true); - } finally { - setLoading(false); - } - }; - - const generateDemoDevice = async () => { - const clientId = prompt("Enter clientId for the demo device:"); - - if (!clientId) return; - - try { - setLoading(true); - const res = await api.post('/system/devices/demo', { - clientId: Number(clientId) - }); - setToastMessage(`✅ Demo device created: ${res.data.deviceId}`); - } catch (err: any) { - setToastMessage("❌ Failed to generate demo device: " + (err?.response?.data || err.message)); - } finally { - setLoading(false); - setToastOpen(true); - } - }; - - const openDialog = async () => { - await fetchClients(); - setDialogOpen(true); - }; - - return ( - - Admin Controls - - - Accept pings: - {pingEnabled === null ? ( - - ) : ( - togglePing(e.target.checked)} - /> - )} - - - {message && {message}} - - - - - - - - - - - - - - + useEffect(() => { + if (!drawerOpen) return; + const interval = setInterval(async () => { + const res = await api.get('/admin/scripts/fetch-cve/logs'); + setLogOutput(res.data); + }, 3000); - + return () => clearInterval(interval); + }, [drawerOpen]); + const fetchClients = async () => { + try { + const res = await api.get('/auth/clients'); + setClientList(res.data); + } catch (err) { + console.error('Failed to fetch clients', err); + } + }; + const runCveSync = async () => { + try { + setLoading(true); + await api.post('/admin/scripts/fetch-cve'); + setCveVisible(true); + } catch (err: any) { + alert("❌ Failed to start CVE sync: " + (err?.response?.data || err.message)); + } finally { + setLoading(false); + } + }; - + const runCveBackfill = async () => { + setBackfillDialogOpen(false); + try { + setLoading(true); + await api.post('/admin/scripts/fetch-cve-backfill'); + setCveVisible(true); + setToastMessage('⏳ CVE backfill started - this will take 20-30 hours to complete!'); + setToastOpen(true); + } catch (err: any) { + setToastMessage("❌ Failed to start CVE backfill: " + (err?.response?.data || err.message)); + setToastOpen(true); + } finally { + setLoading(false); + } + }; - + const runKevSync = async () => { + try { + setLoading(true); + await api.post('/admin/scripts/fetch-kev'); + setKevVisible(true); + setToastMessage('✅ KEV sync started.'); + setToastOpen(true); + } catch (err: any) { + setToastMessage("❌ Failed to sync KEVs: " + (err?.response?.data || err.message)); + setToastOpen(true); + } finally { + setLoading(false); + } + }; - + const runMsrcSync = async () => { + try { + setLoading(true); + await api.post('/admin/scripts/fetch-msrc'); + setMsrcVisible(true); + setToastMessage('✅ MSRC sync started.'); + setToastOpen(true); + } catch (err: any) { + setToastMessage("❌ Failed to fetch Microsoft CVEs: " + (err?.response?.data || err.message)); + setToastOpen(true); + } finally { + setLoading(false); + } + }; + const runVulnCacheRefresh = async () => { + try { + setLoading(true); + const res = await api.post('/admin/vulns/refresh-cache'); + setToastMessage(res.data); + setToastOpen(true); + } catch (err: any) { + setToastMessage("❌ Failed to refresh vulnerability cache: " + (err?.response?.data || err.message)); + setToastOpen(true); + } finally { + setLoading(false); + } + }; - setToastOpen(false)} - anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} -> - setToastOpen(false)} - severity="info" - sx={{ width: '100%' }} - > - {toastMessage} - - - setDialogOpen(false)} maxWidth="xs" fullWidth> - Select a Client - - - Client - - - - - + const refreshStatistics = async () => { + try { + setLoading(true); + await api.post('/admin/statistics/refresh'); + setToastMessage('✅ CVE Statistics refreshed.'); + setToastOpen(true); + } catch (err: any) { + setToastMessage("❌ Failed to refresh statistics: " + (err?.response?.data || err.message)); + setToastOpen(true); + } finally { + setLoading(false); + } + }; + + const normalizeSoftware = async () => { + try { + setLoading(true); + const res = await api.post('/admin/software/normalize'); + setToastMessage(res.data); + setToastOpen(true); + } catch (err: any) { + setToastMessage("❌ Failed to normalize software entries: " + (err?.response?.data || err.message)); + setToastOpen(true); + } finally { + setLoading(false); + } + }; + + const generateDemoDevice = async () => { + const clientId = prompt("Enter clientId for the demo device:"); + + if (!clientId) return; + + try { + setLoading(true); + const res = await api.post('/system/devices/demo', { + clientId: Number(clientId) + }); + setToastMessage(`✅ Demo device created: ${res.data.deviceId}`); + } catch (err: any) { + setToastMessage("❌ Failed to generate demo device: " + (err?.response?.data || err.message)); + } finally { + setLoading(false); + setToastOpen(true); + } + }; + + const openDialog = async () => { + await fetchClients(); + setDialogOpen(true); + }; + + return ( + + Admin Controls + + + Accept pings: + {pingEnabled === null ? ( + + ) : ( + togglePing(e.target.checked)} + /> + )} + + + {message && {message}} + + - - + + + + + + + + + + + + + - - ); -} + + + + + + + + + + + + + + setToastOpen(false)} + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + > + setToastOpen(false)} + severity="info" + sx={{ width: '100%' }} + > + {toastMessage} + + + + {/* Backfill Confirmation Dialog */} + setBackfillDialogOpen(false)} + maxWidth="sm" + fullWidth + > + ⚠️ Confirm CVE Backfill + + + This will download ALL CVEs from 2002 to present (~250,000 CVEs). +

+ Expected runtime: 20-30 hours +
+ API calls: ~8,000-10,000 requests +

+ The process is resumable - if it stops, you can restart it and it will continue from where it left off. +

+ Are you sure you want to proceed? +
+
+ + + + +
+ + {/* Demo Device Dialog */} + setDialogOpen(false)} maxWidth="xs" fullWidth> + Select a Client + + + Client + + + + + + + + + + + + ); + } \ No newline at end of file