mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-12-16 02:05:48 -07:00
Updates to Remote Shell and Device Details UI
This commit is contained in:
@@ -3,11 +3,11 @@
|
|||||||
import React, { useState, useEffect, useMemo, useCallback } from "react";
|
import React, { useState, useEffect, useMemo, useCallback } from "react";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
Stack,
|
||||||
Tabs,
|
Tabs,
|
||||||
Tab,
|
Tab,
|
||||||
Typography,
|
Typography,
|
||||||
Button,
|
Button,
|
||||||
IconButton,
|
|
||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
TextField,
|
TextField,
|
||||||
@@ -16,8 +16,13 @@ import {
|
|||||||
DialogContent,
|
DialogContent,
|
||||||
DialogActions
|
DialogActions
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
|
||||||
import StorageRoundedIcon from "@mui/icons-material/StorageRounded";
|
import StorageRoundedIcon from "@mui/icons-material/StorageRounded";
|
||||||
import MemoryRoundedIcon from "@mui/icons-material/MemoryRounded";
|
import MemoryRoundedIcon from "@mui/icons-material/MemoryRounded";
|
||||||
|
import LanRoundedIcon from "@mui/icons-material/LanRounded";
|
||||||
|
import AppsRoundedIcon from "@mui/icons-material/AppsRounded";
|
||||||
|
import ListAltRoundedIcon from "@mui/icons-material/ListAltRounded";
|
||||||
|
import TerminalRoundedIcon from "@mui/icons-material/TerminalRounded";
|
||||||
import SpeedRoundedIcon from "@mui/icons-material/SpeedRounded";
|
import SpeedRoundedIcon from "@mui/icons-material/SpeedRounded";
|
||||||
import DeveloperBoardRoundedIcon from "@mui/icons-material/DeveloperBoardRounded";
|
import DeveloperBoardRoundedIcon from "@mui/icons-material/DeveloperBoardRounded";
|
||||||
import MoreHorizIcon from "@mui/icons-material/MoreHoriz";
|
import MoreHorizIcon from "@mui/icons-material/MoreHoriz";
|
||||||
@@ -65,13 +70,13 @@ const SECTION_HEIGHTS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const TOP_TABS = [
|
const TOP_TABS = [
|
||||||
"Device Summary",
|
{ label: "Device Summary", icon: InfoOutlinedIcon },
|
||||||
"Storage",
|
{ label: "Storage", icon: StorageRoundedIcon },
|
||||||
"Memory",
|
{ label: "Memory", icon: MemoryRoundedIcon },
|
||||||
"Network",
|
{ label: "Network", icon: LanRoundedIcon },
|
||||||
"Installed Software",
|
{ label: "Installed Software", icon: AppsRoundedIcon },
|
||||||
"Activity History",
|
{ label: "Activity History", icon: ListAltRoundedIcon },
|
||||||
"Remote Shell",
|
{ label: "Remote Shell", icon: TerminalRoundedIcon },
|
||||||
];
|
];
|
||||||
|
|
||||||
const myTheme = themeQuartz.withParams({
|
const myTheme = themeQuartz.withParams({
|
||||||
@@ -377,8 +382,6 @@ export default function DeviceDetails({ device, onBack, onQuickJobLaunch, onPage
|
|||||||
return now - tsSec <= offlineAfter ? "Online" : "Offline";
|
return now - tsSec <= offlineAfter ? "Online" : "Offline";
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusColor = (s) => (s === "Online" ? "#00d18c" : "#ff4f4f");
|
|
||||||
|
|
||||||
const resolveAssemblyName = useCallback((scriptName, scriptPath) => {
|
const resolveAssemblyName = useCallback((scriptName, scriptPath) => {
|
||||||
const normalized = String(scriptPath || "").replace(/\\/g, "/").trim();
|
const normalized = String(scriptPath || "").replace(/\\/g, "/").trim();
|
||||||
const base = normalized ? normalized.split("/").pop() || "" : "";
|
const base = normalized ? normalized.split("/").pop() || "" : "";
|
||||||
@@ -771,6 +774,8 @@ export default function DeviceDetails({ device, onBack, onQuickJobLaunch, onPage
|
|||||||
label: "Hostname",
|
label: "Hostname",
|
||||||
value: meta.hostname || summary.hostname || agent.hostname || device?.hostname || "unknown",
|
value: meta.hostname || summary.hostname || agent.hostname || device?.hostname || "unknown",
|
||||||
},
|
},
|
||||||
|
{ label: "Agent ID", value: meta.agentId || summary.agent_id || "unknown" },
|
||||||
|
{ label: "Agent GUID", value: meta.agentGuid || summary.agent_guid || "unknown" },
|
||||||
{
|
{
|
||||||
label: "Last User",
|
label: "Last User",
|
||||||
value: meta.lastUser || summary.last_user || "unknown",
|
value: meta.lastUser || summary.last_user || "unknown",
|
||||||
@@ -801,8 +806,6 @@ export default function DeviceDetails({ device, onBack, onQuickJobLaunch, onPage
|
|||||||
value:
|
value:
|
||||||
meta.operatingSystem || summary.operating_system || agent.agent_operating_system || "unknown",
|
meta.operatingSystem || summary.operating_system || agent.agent_operating_system || "unknown",
|
||||||
},
|
},
|
||||||
{ label: "Agent ID", value: meta.agentId || summary.agent_id || "unknown" },
|
|
||||||
{ label: "Agent GUID", value: meta.agentGuid || summary.agent_guid || "unknown" },
|
|
||||||
{ label: "Agent Hash", value: meta.agentHash || summary.agent_hash || "unknown" },
|
{ label: "Agent Hash", value: meta.agentHash || summary.agent_hash || "unknown" },
|
||||||
],
|
],
|
||||||
[meta, summary, agent, device, formatDateTime, formatLastSeen]
|
[meta, summary, agent, device, formatDateTime, formatLastSeen]
|
||||||
@@ -940,7 +943,7 @@ export default function DeviceDetails({ device, onBack, onQuickJobLaunch, onPage
|
|||||||
width: 36,
|
width: 36,
|
||||||
height: 36,
|
height: 36,
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
background: "rgba(4,7,17,0.4)",
|
background: "transparent",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
@@ -1084,9 +1087,7 @@ export default function DeviceDetails({ device, onBack, onQuickJobLaunch, onPage
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
border: `1px solid ${MAGIC_UI.panelBorder}`,
|
background: "transparent",
|
||||||
background: 'transparent',
|
|
||||||
boxShadow: MAGIC_UI.glow,
|
|
||||||
p: { xs: 2, md: 3 },
|
p: { xs: 2, md: 3 },
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
@@ -1094,35 +1095,24 @@ export default function DeviceDetails({ device, onBack, onQuickJobLaunch, onPage
|
|||||||
minHeight: 0,
|
minHeight: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TextField
|
<Box
|
||||||
size="small"
|
|
||||||
label="Description"
|
|
||||||
value={description}
|
|
||||||
onChange={(e) => setDescription(e.target.value)}
|
|
||||||
onBlur={saveDescription}
|
|
||||||
placeholder="Add a friendly label"
|
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: 420,
|
display: "grid",
|
||||||
input: { color: "#fff" },
|
gridTemplateColumns: { xs: "1fr", xl: "1fr auto" },
|
||||||
"& .MuiOutlinedInput-root": {
|
alignItems: "flex-start",
|
||||||
backgroundColor: "rgba(4,7,17,0.65)",
|
gap: { xs: 2, md: 3 },
|
||||||
"& fieldset": { borderColor: "rgba(148,163,184,0.45)" },
|
|
||||||
"&:hover fieldset": { borderColor: MAGIC_UI.accentA },
|
|
||||||
},
|
|
||||||
label: { color: MAGIC_UI.textMuted },
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
{connectionType === "ssh" && (
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 1.5, minWidth: 0 }}>
|
||||||
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 1.5, alignItems: "center" }}>
|
|
||||||
<TextField
|
<TextField
|
||||||
size="small"
|
size="small"
|
||||||
label="SSH Endpoint"
|
label="Description"
|
||||||
value={connectionDraft}
|
value={description}
|
||||||
onChange={(e) => setConnectionDraft(e.target.value)}
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
placeholder="user@host or host"
|
onBlur={saveDescription}
|
||||||
|
placeholder="Add a friendly label"
|
||||||
sx={{
|
sx={{
|
||||||
minWidth: 260,
|
maxWidth: 420,
|
||||||
maxWidth: 360,
|
|
||||||
input: { color: "#fff" },
|
input: { color: "#fff" },
|
||||||
"& .MuiOutlinedInput-root": {
|
"& .MuiOutlinedInput-root": {
|
||||||
backgroundColor: "rgba(4,7,17,0.65)",
|
backgroundColor: "rgba(4,7,17,0.65)",
|
||||||
@@ -1132,36 +1122,112 @@ export default function DeviceDetails({ device, onBack, onQuickJobLaunch, onPage
|
|||||||
label: { color: MAGIC_UI.textMuted },
|
label: { color: MAGIC_UI.textMuted },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
{connectionType === "ssh" && (
|
||||||
size="small"
|
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 1.5, alignItems: "center" }}>
|
||||||
variant="outlined"
|
<TextField
|
||||||
onClick={saveConnectionEndpoint}
|
size="small"
|
||||||
disabled={connectionSaving || connectionDraft.trim() === connectionEndpoint.trim()}
|
label="SSH Endpoint"
|
||||||
sx={{
|
value={connectionDraft}
|
||||||
textTransform: "none",
|
onChange={(e) => setConnectionDraft(e.target.value)}
|
||||||
borderColor: MAGIC_UI.accentA,
|
placeholder="user@host or host"
|
||||||
color: MAGIC_UI.accentA,
|
sx={{
|
||||||
borderRadius: 999,
|
minWidth: 260,
|
||||||
px: 2,
|
maxWidth: 360,
|
||||||
}}
|
input: { color: "#fff" },
|
||||||
>
|
"& .MuiOutlinedInput-root": {
|
||||||
{connectionSaving ? "Saving..." : "Save"}
|
backgroundColor: "rgba(4,7,17,0.65)",
|
||||||
</Button>
|
"& fieldset": { borderColor: "rgba(148,163,184,0.45)" },
|
||||||
<Box sx={{ display: "flex", flexDirection: "column" }}>
|
"&:hover fieldset": { borderColor: MAGIC_UI.accentA },
|
||||||
{connectionMessage && (
|
},
|
||||||
<Typography variant="caption" sx={{ color: MAGIC_UI.accentA }}>
|
label: { color: MAGIC_UI.textMuted },
|
||||||
{connectionMessage}
|
}}
|
||||||
</Typography>
|
/>
|
||||||
)}
|
<Button
|
||||||
{connectionError && (
|
size="small"
|
||||||
<Typography variant="caption" sx={{ color: "#ff7b89" }}>
|
variant="outlined"
|
||||||
{connectionError}
|
onClick={saveConnectionEndpoint}
|
||||||
</Typography>
|
disabled={connectionSaving || connectionDraft.trim() === connectionEndpoint.trim()}
|
||||||
)}
|
sx={{
|
||||||
</Box>
|
textTransform: "none",
|
||||||
|
borderColor: MAGIC_UI.accentA,
|
||||||
|
color: MAGIC_UI.accentA,
|
||||||
|
borderRadius: 999,
|
||||||
|
px: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{connectionSaving ? "Saving..." : "Save"}
|
||||||
|
</Button>
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column" }}>
|
||||||
|
{connectionMessage && (
|
||||||
|
<Typography variant="caption" sx={{ color: MAGIC_UI.accentA }}>
|
||||||
|
{connectionMessage}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
{connectionError && (
|
||||||
|
<Typography variant="caption" sx={{ color: "#ff7b89" }}>
|
||||||
|
{connectionError}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
<Box
|
||||||
<GridShell sx={{ flexGrow: 1, minHeight: 0, height: SECTION_HEIGHTS.summary }}>
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
gap: 1.2,
|
||||||
|
justifyContent: { xs: "flex-start", xl: "flex-end" },
|
||||||
|
alignSelf: "flex-start",
|
||||||
|
mt: { xs: 0, md: -0.5 },
|
||||||
|
"& > *": {
|
||||||
|
background: "transparent !important",
|
||||||
|
border: "none !important",
|
||||||
|
boxShadow: "none !important",
|
||||||
|
borderRadius: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MetricCard
|
||||||
|
compact
|
||||||
|
icon={<DeveloperBoardRoundedIcon sx={{ fontSize: 24 }} />}
|
||||||
|
title="Processor"
|
||||||
|
main={deviceMetricData.cpuMain}
|
||||||
|
sub={deviceMetricData.cpuSub}
|
||||||
|
/>
|
||||||
|
<MetricCard
|
||||||
|
compact
|
||||||
|
icon={<MemoryRoundedIcon sx={{ fontSize: 24 }} />}
|
||||||
|
title="RAM"
|
||||||
|
main={deviceMetricData.memVal}
|
||||||
|
sub={deviceMetricData.memSpeed || " "}
|
||||||
|
/>
|
||||||
|
<MetricCard
|
||||||
|
compact
|
||||||
|
icon={<StorageRoundedIcon sx={{ fontSize: 24 }} />}
|
||||||
|
title="Storage"
|
||||||
|
main={deviceMetricData.storageMain}
|
||||||
|
sub={deviceMetricData.storageSub || " "}
|
||||||
|
/>
|
||||||
|
<MetricCard
|
||||||
|
compact
|
||||||
|
icon={<SpeedRoundedIcon sx={{ fontSize: 24 }} />}
|
||||||
|
title="Network"
|
||||||
|
main={deviceMetricData.netVal}
|
||||||
|
sub={deviceMetricData.nicLabel}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<GridShell
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
minHeight: 0,
|
||||||
|
height: SECTION_HEIGHTS.summary,
|
||||||
|
border: "none",
|
||||||
|
boxShadow: "none",
|
||||||
|
background: "transparent",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<AgGridReact
|
<AgGridReact
|
||||||
rowData={summaryGridRows}
|
rowData={summaryGridRows}
|
||||||
columnDefs={summaryGridColumns}
|
columnDefs={summaryGridColumns}
|
||||||
@@ -1534,15 +1600,7 @@ export default function DeviceDetails({ device, onBack, onQuickJobLaunch, onPage
|
|||||||
const status = lockedStatus || statusFromHeartbeat(agent.last_seen || device?.lastSeen);
|
const status = lockedStatus || statusFromHeartbeat(agent.last_seen || device?.lastSeen);
|
||||||
|
|
||||||
const displayHostname = meta.hostname || summary.hostname || agent.hostname || device?.hostname || "Device Details";
|
const displayHostname = meta.hostname || summary.hostname || agent.hostname || device?.hostname || "Device Details";
|
||||||
const guidForSubtitle =
|
const pageSubtitle = status ? `Status: ${status}` : "";
|
||||||
meta.agentGuid || summary.agent_guid || device?.agent_guid || device?.guid || device?.agentGuid || "";
|
|
||||||
const osLabel =
|
|
||||||
meta.operatingSystem || summary.operating_system || agent.agent_operating_system || agent.operating_system || "";
|
|
||||||
const subtitleParts = [];
|
|
||||||
if (status) subtitleParts.push(`Status: ${status}`);
|
|
||||||
if (osLabel) subtitleParts.push(osLabel);
|
|
||||||
if (guidForSubtitle) subtitleParts.push(`GUID ${guidForSubtitle}`);
|
|
||||||
const pageSubtitle = subtitleParts.join(" | ");
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onPageMetaChange?.({
|
onPageMetaChange?.({
|
||||||
@@ -1571,7 +1629,6 @@ export default function DeviceDetails({ device, onBack, onQuickJobLaunch, onPage
|
|||||||
p: { xs: 2, md: 3 },
|
p: { xs: 2, md: 3 },
|
||||||
borderRadius: 0,
|
borderRadius: 0,
|
||||||
background: "transparent",
|
background: "transparent",
|
||||||
border: `1px solid ${MAGIC_UI.panelBorder}`,
|
|
||||||
boxShadow: "none",
|
boxShadow: "none",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
@@ -1582,133 +1639,68 @@ export default function DeviceDetails({ device, onBack, onQuickJobLaunch, onPage
|
|||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
mb: 3,
|
position: "fixed",
|
||||||
display: "grid",
|
top: { xs: 72, md: 88 },
|
||||||
gridTemplateColumns: { xs: "1fr", lg: "1.5fr auto auto" },
|
right: { xs: 12, md: 20 },
|
||||||
alignItems: "center",
|
zIndex: 1400,
|
||||||
gap: 2,
|
pointerEvents: "none",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: 1.5, flexWrap: "wrap", minWidth: 0 }}>
|
<Stack direction="row" spacing={1.25} sx={{ pointerEvents: "auto" }}>
|
||||||
{onBack && (
|
<Button
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
onClick={onBack}
|
|
||||||
sx={{
|
|
||||||
textTransform: "none",
|
|
||||||
borderColor: "rgba(148,163,184,0.45)",
|
|
||||||
color: MAGIC_UI.textBright,
|
|
||||||
borderRadius: 999,
|
|
||||||
px: 2,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Back
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Box
|
|
||||||
component="span"
|
|
||||||
sx={{
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
borderRadius: 10,
|
|
||||||
backgroundColor: statusColor(status),
|
|
||||||
boxShadow: `0 0 12px ${statusColor(status)}`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Typography variant="body2" sx={{ color: MAGIC_UI.textMuted }}>
|
|
||||||
GUID: {meta.agentGuid || summary.agent_guid || "unknown"}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexWrap: "wrap",
|
|
||||||
gap: 1.2,
|
|
||||||
justifyContent: { xs: "flex-start", lg: "center" },
|
|
||||||
"& > *": { background: "transparent !important", border: "none !important", boxShadow: "none !important", borderRadius: 0 },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MetricCard
|
|
||||||
compact
|
|
||||||
icon={<DeveloperBoardRoundedIcon sx={{ fontSize: 24 }} />}
|
|
||||||
title="Processor"
|
|
||||||
main={deviceMetricData.cpuMain}
|
|
||||||
sub={deviceMetricData.cpuSub}
|
|
||||||
/>
|
|
||||||
<MetricCard
|
|
||||||
compact
|
|
||||||
icon={<MemoryRoundedIcon sx={{ fontSize: 24 }} />}
|
|
||||||
title="RAM"
|
|
||||||
main={deviceMetricData.memVal}
|
|
||||||
sub={deviceMetricData.memSpeed || " "}
|
|
||||||
/>
|
|
||||||
<MetricCard
|
|
||||||
compact
|
|
||||||
icon={<StorageRoundedIcon sx={{ fontSize: 24 }} />}
|
|
||||||
title="Storage"
|
|
||||||
main={deviceMetricData.storageMain}
|
|
||||||
sub={deviceMetricData.storageSub || " "}
|
|
||||||
/>
|
|
||||||
<MetricCard
|
|
||||||
compact
|
|
||||||
icon={<SpeedRoundedIcon sx={{ fontSize: 24 }} />}
|
|
||||||
title="Network"
|
|
||||||
main={deviceMetricData.netVal}
|
|
||||||
sub={deviceMetricData.nicLabel}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: 1.5, justifyContent: "flex-end" }}>
|
|
||||||
<IconButton
|
|
||||||
size="small"
|
size="small"
|
||||||
|
startIcon={<MoreHorizIcon />}
|
||||||
disabled={!(agent?.hostname || device?.hostname)}
|
disabled={!(agent?.hostname || device?.hostname)}
|
||||||
onClick={(e) => setMenuAnchor(e.currentTarget)}
|
onClick={(e) => setMenuAnchor(e.currentTarget)}
|
||||||
sx={{
|
sx={{
|
||||||
color: !(agent?.hostname || device?.hostname) ? MAGIC_UI.textMuted : MAGIC_UI.textBright,
|
backgroundImage: "linear-gradient(135deg,#7dd3fc,#c084fc)",
|
||||||
border: "1px solid rgba(148,163,184,0.45)",
|
color: "#0b1220",
|
||||||
borderRadius: 2,
|
borderRadius: 999,
|
||||||
width: 38,
|
textTransform: "none",
|
||||||
height: 38,
|
px: 2.2,
|
||||||
backgroundColor: "transparent",
|
minWidth: 120,
|
||||||
}}
|
boxShadow: "none",
|
||||||
>
|
"&:hover": {
|
||||||
<MoreHorizIcon fontSize="small" />
|
backgroundImage: "linear-gradient(135deg,#86e1ff,#d1a6ff)",
|
||||||
</IconButton>
|
boxShadow: "none",
|
||||||
<Menu
|
|
||||||
anchorEl={menuAnchor}
|
|
||||||
open={Boolean(menuAnchor)}
|
|
||||||
onClose={() => setMenuAnchor(null)}
|
|
||||||
PaperProps={{
|
|
||||||
sx: {
|
|
||||||
bgcolor: "rgba(8,12,24,0.96)",
|
|
||||||
color: "#fff",
|
|
||||||
border: `1px solid ${MAGIC_UI.panelBorder}`,
|
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MenuItem
|
Actions
|
||||||
disabled={!canLaunchQuickJob}
|
</Button>
|
||||||
onClick={() => {
|
</Stack>
|
||||||
setMenuAnchor(null);
|
|
||||||
if (!canLaunchQuickJob) return;
|
|
||||||
onQuickJobLaunch && onQuickJobLaunch(quickJobTargets);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Quick Job
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
onClick={() => {
|
|
||||||
setMenuAnchor(null);
|
|
||||||
setClearDialogOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Clear Device Activity
|
|
||||||
</MenuItem>
|
|
||||||
</Menu>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
<Menu
|
||||||
|
anchorEl={menuAnchor}
|
||||||
|
open={Boolean(menuAnchor)}
|
||||||
|
onClose={() => setMenuAnchor(null)}
|
||||||
|
PaperProps={{
|
||||||
|
sx: {
|
||||||
|
bgcolor: "rgba(8,12,24,0.96)",
|
||||||
|
color: "#fff",
|
||||||
|
border: `1px solid ${MAGIC_UI.panelBorder}`,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem
|
||||||
|
disabled={!canLaunchQuickJob}
|
||||||
|
onClick={() => {
|
||||||
|
setMenuAnchor(null);
|
||||||
|
if (!canLaunchQuickJob) return;
|
||||||
|
onQuickJobLaunch && onQuickJobLaunch(quickJobTargets);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Quick Job
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => {
|
||||||
|
setMenuAnchor(null);
|
||||||
|
setClearDialogOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Clear Device Activity
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
<Tabs
|
<Tabs
|
||||||
value={tab}
|
value={tab}
|
||||||
onChange={(e, v) => setTab(v)}
|
onChange={(e, v) => setTab(v)}
|
||||||
@@ -1748,8 +1740,13 @@ export default function DeviceDetails({ device, onBack, onQuickJobLaunch, onPage
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{TOP_TABS.map((label) => (
|
{TOP_TABS.map((tabDef) => (
|
||||||
<Tab key={label} label={label} />
|
<Tab
|
||||||
|
key={tabDef.label}
|
||||||
|
label={tabDef.label}
|
||||||
|
icon={<tabDef.icon sx={{ fontSize: 18 }} />}
|
||||||
|
iconPosition="start"
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<Box sx={{ mt: 1, flexGrow: 1, minHeight: 0, display: "flex", flexDirection: "column" }}>
|
<Box sx={{ mt: 1, flexGrow: 1, minHeight: 0, display: "flex", flexDirection: "column" }}>
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
LinearProgress,
|
LinearProgress,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import {
|
import {
|
||||||
TerminalRounded as TerminalIcon,
|
|
||||||
PlayArrowRounded as PlayIcon,
|
PlayArrowRounded as PlayIcon,
|
||||||
StopRounded as StopIcon,
|
StopRounded as StopIcon,
|
||||||
ContentCopy as CopyIcon,
|
ContentCopy as CopyIcon,
|
||||||
@@ -50,12 +49,10 @@ const gradientButtonSx = {
|
|||||||
color: "#0b1220",
|
color: "#0b1220",
|
||||||
borderRadius: 999,
|
borderRadius: 999,
|
||||||
textTransform: "none",
|
textTransform: "none",
|
||||||
boxShadow: "0 10px 26px rgba(124,58,237,0.28)",
|
|
||||||
px: 2.2,
|
px: 2.2,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
backgroundImage: "linear-gradient(135deg,#86e1ff,#d1a6ff)",
|
backgroundImage: "linear-gradient(135deg,#86e1ff,#d1a6ff)",
|
||||||
boxShadow: "0 12px 34px rgba(124,58,237,0.38)",
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -614,91 +611,43 @@ export default function ReverseTunnelPowershell({ device }) {
|
|||||||
<Box sx={{ display: "flex", flexDirection: "column", gap: 1.5, flexGrow: 1, minHeight: 0 }}>
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 1.5, flexGrow: 1, minHeight: 0 }}>
|
||||||
<Box>
|
<Box>
|
||||||
<Stack
|
<Stack
|
||||||
direction={{ xs: "column", md: "row" }}
|
direction={{ xs: "column", sm: "row" }}
|
||||||
spacing={1.5}
|
spacing={1.5}
|
||||||
alignItems={{ xs: "flex-start", md: "center" }}
|
alignItems={{ xs: "flex-start", sm: "center" }}
|
||||||
justifyContent="space-between"
|
justifyContent={{ xs: "flex-start", sm: "flex-end" }}
|
||||||
>
|
>
|
||||||
<Stack direction="row" spacing={1} alignItems="center" flexWrap="wrap">
|
<TextField
|
||||||
<TerminalIcon sx={{ fontSize: 22, color: MAGIC_UI.accentA }} />
|
select
|
||||||
<Typography variant="h6" sx={{ fontWeight: 700, letterSpacing: 0.3 }}>
|
label="Connection Type"
|
||||||
Remote Shell
|
size="small"
|
||||||
</Typography>
|
value={connectionType}
|
||||||
</Stack>
|
onChange={(e) => setConnectionType(e.target.value)}
|
||||||
|
|
||||||
<Stack direction={{ xs: "column", sm: "row" }} spacing={1} alignItems="center">
|
|
||||||
<TextField
|
|
||||||
select
|
|
||||||
label="Connection Type"
|
|
||||||
size="small"
|
|
||||||
value={connectionType}
|
|
||||||
onChange={(e) => setConnectionType(e.target.value)}
|
|
||||||
sx={{
|
|
||||||
minWidth: 180,
|
|
||||||
"& .MuiInputBase-root": {
|
|
||||||
backgroundColor: "rgba(12,18,35,0.85)",
|
|
||||||
color: MAGIC_UI.textBright,
|
|
||||||
borderRadius: 1.5,
|
|
||||||
},
|
|
||||||
"& fieldset": { borderColor: MAGIC_UI.panelBorder },
|
|
||||||
"&:hover fieldset": { borderColor: MAGIC_UI.accentA },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MenuItem value="ps">PowerShell</MenuItem>
|
|
||||||
</TextField>
|
|
||||||
<Tooltip title={isConnected ? "Disconnect session" : "Connect to agent"}>
|
|
||||||
<span>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
startIcon={isConnected ? <StopIcon /> : <PlayIcon />}
|
|
||||||
sx={gradientButtonSx}
|
|
||||||
disabled={!isConnected && !canStart}
|
|
||||||
onClick={isConnected ? handleDisconnect : requestTunnel}
|
|
||||||
>
|
|
||||||
{isConnected ? "Disconnect" : "Connect"}
|
|
||||||
</Button>
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Stack spacing={0.3} sx={{ mt: 1.25 }}>
|
|
||||||
<Typography
|
|
||||||
variant="body2"
|
|
||||||
sx={{
|
sx={{
|
||||||
color: milestones.tunnelReady ? MAGIC_UI.accentC : MAGIC_UI.textMuted,
|
minWidth: 180,
|
||||||
fontWeight: 700,
|
"& .MuiInputBase-root": {
|
||||||
|
backgroundColor: "rgba(12,18,35,0.85)",
|
||||||
|
color: MAGIC_UI.textBright,
|
||||||
|
borderRadius: 1.5,
|
||||||
|
},
|
||||||
|
"& fieldset": { borderColor: MAGIC_UI.panelBorder },
|
||||||
|
"&:hover fieldset": { borderColor: MAGIC_UI.accentA },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Tunnel:{" "}
|
<MenuItem value="ps">PowerShell</MenuItem>
|
||||||
<Typography component="span" variant="body2" sx={{ color: MAGIC_UI.textMuted, fontWeight: 500 }}>
|
</TextField>
|
||||||
{tunnelSteps.join(" > ")}
|
<Tooltip title={isConnected ? "Disconnect session" : "Connect to agent"}>
|
||||||
</Typography>
|
<span>
|
||||||
</Typography>
|
<Button
|
||||||
<Typography
|
size="small"
|
||||||
variant="body2"
|
startIcon={isConnected ? <StopIcon /> : <PlayIcon />}
|
||||||
sx={{
|
sx={gradientButtonSx}
|
||||||
color: milestones.operatorAttached ? MAGIC_UI.accentC : MAGIC_UI.textMuted,
|
disabled={!isConnected && !canStart}
|
||||||
fontWeight: 700,
|
onClick={isConnected ? handleDisconnect : requestTunnel}
|
||||||
}}
|
>
|
||||||
>
|
{isConnected ? "Disconnect" : "Connect"}
|
||||||
Websocket:{" "}
|
</Button>
|
||||||
<Typography component="span" variant="body2" sx={{ color: MAGIC_UI.textMuted, fontWeight: 500 }}>
|
</span>
|
||||||
{websocketSteps.join(" > ")}
|
</Tooltip>
|
||||||
</Typography>
|
|
||||||
</Typography>
|
|
||||||
<Typography
|
|
||||||
variant="body2"
|
|
||||||
sx={{
|
|
||||||
color: milestones.shellEstablished ? MAGIC_UI.accentC : MAGIC_UI.textMuted,
|
|
||||||
fontWeight: 700,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Remote Shell:{" "}
|
|
||||||
<Typography component="span" variant="body2" sx={{ color: MAGIC_UI.textMuted, fontWeight: 500 }}>
|
|
||||||
{shellSteps.join(" > ")}
|
|
||||||
</Typography>
|
|
||||||
</Typography>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -804,6 +753,44 @@ export default function ReverseTunnelPowershell({ device }) {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Stack spacing={0.3} sx={{ mt: 1.25 }}>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
sx={{
|
||||||
|
color: milestones.tunnelReady ? MAGIC_UI.accentC : MAGIC_UI.textMuted,
|
||||||
|
fontWeight: 700,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Tunnel:{" "}
|
||||||
|
<Typography component="span" variant="body2" sx={{ color: MAGIC_UI.textMuted, fontWeight: 500 }}>
|
||||||
|
{tunnelSteps.join(" > ")}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
sx={{
|
||||||
|
color: milestones.operatorAttached ? MAGIC_UI.accentC : MAGIC_UI.textMuted,
|
||||||
|
fontWeight: 700,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Websocket:{" "}
|
||||||
|
<Typography component="span" variant="body2" sx={{ color: MAGIC_UI.textMuted, fontWeight: 500 }}>
|
||||||
|
{websocketSteps.join(" > ")}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
sx={{
|
||||||
|
color: milestones.shellEstablished ? MAGIC_UI.accentC : MAGIC_UI.textMuted,
|
||||||
|
fontWeight: 700,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Remote Shell:{" "}
|
||||||
|
<Typography component="span" variant="body2" sx={{ color: MAGIC_UI.textMuted, fontWeight: 500 }}>
|
||||||
|
{shellSteps.join(" > ")}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user