Merge pull request #1562 from guardicore/1545-telemetry-brief-loading
Bugfix: telemetry brief loading
This commit is contained in:
commit
35509b2671
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -5,6 +5,19 @@ file.
|
||||||
The format is based on [Keep a
|
The format is based on [Keep a
|
||||||
Changelog](https://keepachangelog.com/en/1.0.0/).
|
Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- A bug in network map page that caused delay of telemetry log loading. #1545
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
|
||||||
## [1.12.0] - 2021-10-27
|
## [1.12.0] - 2021-10-27
|
||||||
### Added
|
### Added
|
||||||
- A new exploiter that allows propagation via PowerShell Remoting. #1246
|
- A new exploiter that allows propagation via PowerShell Remoting. #1246
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"release:patch": "npm version patch && npm publish && git push --follow-tags",
|
"release:patch": "npm version patch && npm publish && git push --follow-tags",
|
||||||
"serve": "node server.js --env=dev",
|
"serve": "node server.js --env=dev",
|
||||||
"serve:dist": "node server.js --env=dist",
|
"serve:dist": "node server.js --env=dist",
|
||||||
"start": "webpack-dev-server --mode development --open --history-api-fallback --port 8000",
|
"start": "webpack-dev-server --mode development --open --history-api-fallback --port 8000 --host local-ip",
|
||||||
"snyk-protect": "snyk protect",
|
"snyk-protect": "snyk protect",
|
||||||
"prepare": "npm run snyk-protect"
|
"prepare": "npm run snyk-protect"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
import React, {useEffect, useRef, useState} from 'react';
|
||||||
|
import AuthComponent from '../AuthComponent';
|
||||||
|
import LoadingIcon from '../ui-components/LoadingIcon';
|
||||||
|
|
||||||
|
|
||||||
|
const authComponent = new AuthComponent({});
|
||||||
|
const telemetryUpdatePeriod = 5000; // UI will fetch telemetries every N milliseconds.
|
||||||
|
|
||||||
|
const TelemetryLog = (props: { onStatusChange: Function }) => {
|
||||||
|
|
||||||
|
let [telemetriesLoading, setTelemetriesLoading] = useState(true);
|
||||||
|
let [telemetryUpdateInProgress, setTelemetryUpdateInProgress] = useState(false);
|
||||||
|
let [telemetry, setTelemetry] = useState([]);
|
||||||
|
let [lastTelemetryTimestamp, setLastTelemetryTimestamp] = useState(null);
|
||||||
|
let [isScrolledUp, setIsScrolledUp] = useState(false);
|
||||||
|
let [telemetryLines, setTelemetryLines] = useState(0);
|
||||||
|
let [telemetryCurrentLine, setTelemetryCurrentLine] = useState(0);
|
||||||
|
|
||||||
|
let scrollTop = 0;
|
||||||
|
const telemetryConsole = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateTelemetryFromServer();
|
||||||
|
const interval = setInterval(updateTelemetryFromServer, telemetryUpdatePeriod);
|
||||||
|
return function cleanup() {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
function updateTelemetryFromServer() {
|
||||||
|
if (telemetryUpdateInProgress) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setTelemetryUpdateInProgress(true);
|
||||||
|
authComponent.authFetch('/api/telemetry-feed?timestamp=' + lastTelemetryTimestamp)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(res => {
|
||||||
|
if ('telemetries' in res) {
|
||||||
|
let newTelem = telemetry.concat(res['telemetries']);
|
||||||
|
setTelemetry(newTelem);
|
||||||
|
setLastTelemetryTimestamp(res['timestamp']);
|
||||||
|
setTelemetryUpdateInProgress(false);
|
||||||
|
setTelemetriesLoading(false);
|
||||||
|
props.onStatusChange();
|
||||||
|
|
||||||
|
let telemConsoleRef = telemetryConsole.current;
|
||||||
|
if (!isScrolledUp) {
|
||||||
|
telemConsoleRef.scrollTop = telemConsoleRef.scrollHeight - telemConsoleRef.clientHeight;
|
||||||
|
scrollTop = telemConsoleRef.scrollTop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleScroll(e) {
|
||||||
|
let element = e.target;
|
||||||
|
|
||||||
|
let telemetryStyle = window.getComputedStyle(element);
|
||||||
|
let telemetryLineHeight = parseInt((telemetryStyle.lineHeight).replace('px', ''));
|
||||||
|
|
||||||
|
setIsScrolledUp((element.scrollTop < scrollTop));
|
||||||
|
setTelemetryCurrentLine(Math.trunc(element.scrollTop / telemetryLineHeight) + 1);
|
||||||
|
setTelemetryLines(Math.trunc(element.scrollHeight / telemetryLineHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTelemetryConsole() {
|
||||||
|
return (
|
||||||
|
<div className="telemetry-console" onScroll={handleScroll} ref={telemetryConsole}>
|
||||||
|
{
|
||||||
|
telemetry.map(renderTelemetryEntry)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTelemetryEntry(telemetry) {
|
||||||
|
return (
|
||||||
|
<div key={telemetry.id}>
|
||||||
|
<span className="date">{telemetry.timestamp}</span>
|
||||||
|
<span className="source"> {telemetry.hostname}:</span>
|
||||||
|
<span className="event"> {telemetry.brief}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTelemetryLineCount() {
|
||||||
|
return (
|
||||||
|
<div className="telemetry-lines">
|
||||||
|
<b>[{telemetryCurrentLine}/{telemetryLines}]</b>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'telemetry-log-section'}>
|
||||||
|
{telemetriesLoading && <LoadingIcon/>}
|
||||||
|
{renderTelemetryLineCount()}
|
||||||
|
{renderTelemetryConsole()}
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TelemetryLog;
|
|
@ -10,6 +10,7 @@ import {getOptions, edgeGroupToColor} from 'components/map/MapOptions';
|
||||||
import AuthComponent from '../AuthComponent';
|
import AuthComponent from '../AuthComponent';
|
||||||
import '../../styles/components/Map.scss';
|
import '../../styles/components/Map.scss';
|
||||||
import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle';
|
import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle';
|
||||||
|
import TelemetryLog from '../map/TelemetryLog';
|
||||||
|
|
||||||
class MapPageComponent extends AuthComponent {
|
class MapPageComponent extends AuthComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -20,17 +21,8 @@ class MapPageComponent extends AuthComponent {
|
||||||
selected: null,
|
selected: null,
|
||||||
selectedType: null,
|
selectedType: null,
|
||||||
killPressed: false,
|
killPressed: false,
|
||||||
showKillDialog: false,
|
showKillDialog: false
|
||||||
telemetry: [],
|
|
||||||
telemetryLastTimestamp: null,
|
|
||||||
isScrolledUp: false,
|
|
||||||
telemetryLines: 0,
|
|
||||||
telemetryCurrentLine: 0,
|
|
||||||
telemetryUpdateInProgress: false
|
|
||||||
};
|
};
|
||||||
this.telemConsole = React.createRef();
|
|
||||||
this.handleScroll = this.handleScroll.bind(this);
|
|
||||||
this.scrollTop = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
events = {
|
events = {
|
||||||
|
@ -40,7 +32,7 @@ class MapPageComponent extends AuthComponent {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.getNodeStateListFromServer();
|
this.getNodeStateListFromServer();
|
||||||
this.updateMapFromServer();
|
this.updateMapFromServer();
|
||||||
this.interval = setInterval(this.timedEvents, 5000);
|
this.interval = setInterval(this.updateMapFromServer, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -55,11 +47,6 @@ class MapPageComponent extends AuthComponent {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
timedEvents = () => {
|
|
||||||
this.updateMapFromServer();
|
|
||||||
this.updateTelemetryFromServer();
|
|
||||||
};
|
|
||||||
|
|
||||||
updateMapFromServer = () => {
|
updateMapFromServer = () => {
|
||||||
this.authFetch('/api/netmap')
|
this.authFetch('/api/netmap')
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
|
@ -74,33 +61,6 @@ class MapPageComponent extends AuthComponent {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
updateTelemetryFromServer = () => {
|
|
||||||
if (this.state.telemetryUpdateInProgress) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.setState({telemetryUpdateInProgress: true});
|
|
||||||
this.authFetch('/api/telemetry-feed?timestamp=' + this.state.telemetryLastTimestamp)
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(res => {
|
|
||||||
if ('telemetries' in res) {
|
|
||||||
let newTelem = this.state.telemetry.concat(res['telemetries']);
|
|
||||||
this.setState(
|
|
||||||
{
|
|
||||||
telemetry: newTelem,
|
|
||||||
telemetryLastTimestamp: res['timestamp'],
|
|
||||||
telemetryUpdateInProgress: false
|
|
||||||
});
|
|
||||||
this.props.onStatusChange();
|
|
||||||
|
|
||||||
let telemConsoleRef = this.telemConsole.current;
|
|
||||||
if (!this.state.isScrolledUp) {
|
|
||||||
telemConsoleRef.scrollTop = telemConsoleRef.scrollHeight - telemConsoleRef.clientHeight;
|
|
||||||
this.scrollTop = telemConsoleRef.scrollTop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
selectionChanged(event) {
|
selectionChanged(event) {
|
||||||
if (event.nodes.length === 1) {
|
if (event.nodes.length === 1) {
|
||||||
this.authFetch('/api/netmap/node?id=' + event.nodes[0])
|
this.authFetch('/api/netmap/node?id=' + event.nodes[0])
|
||||||
|
@ -157,46 +117,6 @@ class MapPageComponent extends AuthComponent {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
renderTelemetryEntry(telemetry) {
|
|
||||||
return (
|
|
||||||
<div key={telemetry.id}>
|
|
||||||
<span className="date">{telemetry.timestamp}</span>
|
|
||||||
<span className="source"> {telemetry.hostname}:</span>
|
|
||||||
<span className="event"> {telemetry.brief}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleScroll(e) {
|
|
||||||
let element = e.target;
|
|
||||||
|
|
||||||
let telemetryStyle = window.getComputedStyle(element);
|
|
||||||
let telemetryLineHeight = parseInt((telemetryStyle.lineHeight).replace('px', ''));
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
isScrolledUp: (element.scrollTop < this.scrollTop),
|
|
||||||
telemetryCurrentLine: Math.trunc(element.scrollTop / telemetryLineHeight) + 1,
|
|
||||||
telemetryLines: Math.trunc(element.scrollHeight / telemetryLineHeight)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTelemetryConsole() {
|
|
||||||
return (
|
|
||||||
<div className="telemetry-console" onScroll={this.handleScroll} ref={this.telemConsole}>
|
|
||||||
{
|
|
||||||
this.state.telemetry.map(this.renderTelemetryEntry)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTelemetryLineCount() {
|
|
||||||
return (
|
|
||||||
<div className="telemetry-lines">
|
|
||||||
<b>[{this.state.telemetryCurrentLine}/{this.state.telemetryLines}]</b>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
@ -220,10 +140,9 @@ class MapPageComponent extends AuthComponent {
|
||||||
<span>Island Communication <FontAwesomeIcon icon={faMinus} size="lg" style={{color: '#a9aaa9'}}/></span>
|
<span>Island Communication <FontAwesomeIcon icon={faMinus} size="lg" style={{color: '#a9aaa9'}}/></span>
|
||||||
</div>
|
</div>
|
||||||
<div style={{height: '80vh'}} className={'map-window'}>
|
<div style={{height: '80vh'}} className={'map-window'}>
|
||||||
{this.renderTelemetryLineCount()}
|
|
||||||
{this.renderTelemetryConsole()}
|
|
||||||
<ReactiveGraph graph={this.state.graph} options={getOptions(this.state.nodeStateList)}
|
<ReactiveGraph graph={this.state.graph} options={getOptions(this.state.nodeStateList)}
|
||||||
events={this.events}/>
|
events={this.events}/>
|
||||||
|
<TelemetryLog onStatusChange={this.props.onStatusChange}/>
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={4}>
|
<Col xs={4}>
|
||||||
|
|
|
@ -46,3 +46,10 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.telemetry-log-section .loading-icon{
|
||||||
|
position: relative;
|
||||||
|
color: white;
|
||||||
|
z-index: 5;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue