mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-09-01 18:47:39 +00:00
new ServiceMapModal component
This commit is contained in:
116
ui/src/components/ServiceMapModal/ServiceMapModal.tsx
Normal file
116
ui/src/components/ServiceMapModal/ServiceMapModal.tsx
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { Box, Fade, Modal, Backdrop } from "@material-ui/core";
|
||||||
|
import Api from "../../helpers/api";
|
||||||
|
import spinnerStyle from '../style/Spinner.module.sass';
|
||||||
|
import spinnerImg from '../assets/spinner.svg';
|
||||||
|
import ForceGraph2D, { GraphData } from 'react-force-graph-2d';
|
||||||
|
|
||||||
|
|
||||||
|
interface ServiceMapModalProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
api: Api
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ServiceMapNode {
|
||||||
|
name: string;
|
||||||
|
protocol: string;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ServiceMapEdge {
|
||||||
|
source: string;
|
||||||
|
destination: string;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ServiceMapGraph {
|
||||||
|
nodes: ServiceMapNode[];
|
||||||
|
edges: ServiceMapEdge[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClose, api }) => {
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||||
|
const [graphData, setGraphData] = useState<GraphData>({
|
||||||
|
nodes: [],
|
||||||
|
links: []
|
||||||
|
});
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '10%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%, 0%)',
|
||||||
|
width: '80vw',
|
||||||
|
bgcolor: 'background.paper',
|
||||||
|
borderRadius: '5px',
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 4,
|
||||||
|
color: '#000',
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const serviceMapData: ServiceMapGraph = await api.serviceMapData()
|
||||||
|
let data: GraphData = {
|
||||||
|
nodes: [],
|
||||||
|
links: []
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < serviceMapData.nodes.length; i++) {
|
||||||
|
data.nodes.push({
|
||||||
|
id: serviceMapData.nodes[i].name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < serviceMapData.edges.length; i++) {
|
||||||
|
data.links.push({
|
||||||
|
source: serviceMapData.edges[i].source,
|
||||||
|
target: serviceMapData.edges[i].destination
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setGraphData(data)
|
||||||
|
setIsLoading(false)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
}, [api]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
aria-labelledby="transition-modal-title"
|
||||||
|
aria-describedby="transition-modal-description"
|
||||||
|
open={isOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
closeAfterTransition
|
||||||
|
BackdropComponent={Backdrop}
|
||||||
|
BackdropProps={{
|
||||||
|
timeout: 500,
|
||||||
|
}}
|
||||||
|
style={{ overflow: 'auto' }}
|
||||||
|
>
|
||||||
|
<Fade in={isOpen}>
|
||||||
|
{/* <Box sx={style}> */}
|
||||||
|
<div>
|
||||||
|
{isLoading && <div className={spinnerStyle.spinnerContainer}>
|
||||||
|
<img alt="spinner" src={spinnerImg} style={{ height: 50 }} />
|
||||||
|
</div>}
|
||||||
|
{!isLoading && <ForceGraph2D
|
||||||
|
graphData={graphData}
|
||||||
|
linkDirectionalArrowLength={3.5}
|
||||||
|
linkDirectionalArrowRelPos={1}
|
||||||
|
linkCurvature={0.25}
|
||||||
|
/>}
|
||||||
|
</div>
|
||||||
|
{/* </Box> */}
|
||||||
|
</Fade>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
@@ -9,10 +9,11 @@ import playIcon from './assets/run.svg';
|
|||||||
import pauseIcon from './assets/pause.svg';
|
import pauseIcon from './assets/pause.svg';
|
||||||
import variables from '../variables.module.scss';
|
import variables from '../variables.module.scss';
|
||||||
import {StatusBar} from "./UI/StatusBar";
|
import {StatusBar} from "./UI/StatusBar";
|
||||||
import Api, {MizuApiURL, MizuWebsocketURL} from "../helpers/api";
|
import Api, {MizuWebsocketURL} from "../helpers/api";
|
||||||
import { ToastContainer, toast } from 'react-toastify';
|
import { ToastContainer, toast } from 'react-toastify';
|
||||||
import 'react-toastify/dist/ReactToastify.css';
|
import 'react-toastify/dist/ReactToastify.css';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
|
import { ServiceMapModal } from "./ServiceMapModal/ServiceMapModal";
|
||||||
|
|
||||||
const useLayoutStyles = makeStyles(() => ({
|
const useLayoutStyles = makeStyles(() => ({
|
||||||
details: {
|
details: {
|
||||||
@@ -60,6 +61,7 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
|||||||
const [tappingStatus, setTappingStatus] = useState(null);
|
const [tappingStatus, setTappingStatus] = useState(null);
|
||||||
|
|
||||||
const [serviceMapStatus, setServiceMapStatus] = useState(false);
|
const [serviceMapStatus, setServiceMapStatus] = useState(false);
|
||||||
|
const [serviceMapModalOpen, setServiceMapModalOpen] = useState(false);
|
||||||
|
|
||||||
const [isSnappedToBottom, setIsSnappedToBottom] = useState(true);
|
const [isSnappedToBottom, setIsSnappedToBottom] = useState(true);
|
||||||
|
|
||||||
@@ -274,22 +276,15 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
|||||||
const serviceMapStatusResponse = await api.serviceMapStatus();
|
const serviceMapStatusResponse = await api.serviceMapStatus();
|
||||||
if (serviceMapStatusResponse["status"] === "enabled") {
|
if (serviceMapStatusResponse["status"] === "enabled") {
|
||||||
setServiceMapStatus(true);
|
setServiceMapStatus(true);
|
||||||
} else {
|
}
|
||||||
setServiceMapStatus(false);
|
|
||||||
}
|
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
setServiceMapStatus(false);
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const openServiceMap = debounce(() => {
|
const openServiceMapModal = debounce(() => {
|
||||||
if (serviceMapStatus) {
|
setServiceMapModalOpen(true)
|
||||||
const url = `${MizuApiURL}servicemap/render`
|
|
||||||
window.open(url, "_blank")
|
|
||||||
}
|
|
||||||
// TODO: toast error message?
|
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
const resetServiceMap = debounce(async () => {
|
const resetServiceMap = debounce(async () => {
|
||||||
@@ -328,7 +323,7 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
|||||||
color: "#fff",
|
color: "#fff",
|
||||||
textTransform: "none",
|
textTransform: "none",
|
||||||
}}
|
}}
|
||||||
onClick={openServiceMap}
|
onClick={openServiceMapModal}
|
||||||
>
|
>
|
||||||
Service Map
|
Service Map
|
||||||
</Button>
|
</Button>
|
||||||
@@ -400,6 +395,7 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
|||||||
pauseOnFocusLoss
|
pauseOnFocusLoss
|
||||||
draggable
|
draggable
|
||||||
pauseOnHover />
|
pauseOnHover />
|
||||||
|
{serviceMapModalOpen && <ServiceMapModal isOpen={serviceMapModalOpen} onClose={() => setServiceMapModalOpen(false)} api={api} />}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user