Merge pull request #1562 from guardicore/1545-telemetry-brief-loading

Bugfix: telemetry brief loading
This commit is contained in:
VakarisZ 2021-10-28 17:58:38 +03:00 committed by GitHub
commit 35509b2671
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 127 additions and 86 deletions

View File

@ -5,6 +5,19 @@ file.
The format is based on [Keep a
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
### Added
- A new exploiter that allows propagation via PowerShell Remoting. #1246

View File

@ -15,7 +15,7 @@
"release:patch": "npm version patch && npm publish && git push --follow-tags",
"serve": "node server.js --env=dev",
"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",
"prepare": "npm run snyk-protect"
},

View File

@ -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;

View File

@ -10,6 +10,7 @@ import {getOptions, edgeGroupToColor} from 'components/map/MapOptions';
import AuthComponent from '../AuthComponent';
import '../../styles/components/Map.scss';
import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle';
import TelemetryLog from '../map/TelemetryLog';
class MapPageComponent extends AuthComponent {
constructor(props) {
@ -20,17 +21,8 @@ class MapPageComponent extends AuthComponent {
selected: null,
selectedType: null,
killPressed: false,
showKillDialog: false,
telemetry: [],
telemetryLastTimestamp: null,
isScrolledUp: false,
telemetryLines: 0,
telemetryCurrentLine: 0,
telemetryUpdateInProgress: false
showKillDialog: false
};
this.telemConsole = React.createRef();
this.handleScroll = this.handleScroll.bind(this);
this.scrollTop = 0;
}
events = {
@ -40,7 +32,7 @@ class MapPageComponent extends AuthComponent {
componentDidMount() {
this.getNodeStateListFromServer();
this.updateMapFromServer();
this.interval = setInterval(this.timedEvents, 5000);
this.interval = setInterval(this.updateMapFromServer, 5000);
}
componentWillUnmount() {
@ -55,11 +47,6 @@ class MapPageComponent extends AuthComponent {
});
};
timedEvents = () => {
this.updateMapFromServer();
this.updateTelemetryFromServer();
};
updateMapFromServer = () => {
this.authFetch('/api/netmap')
.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) {
if (event.nodes.length === 1) {
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() {
return (
@ -220,10 +140,9 @@ class MapPageComponent extends AuthComponent {
<span>Island Communication <FontAwesomeIcon icon={faMinus} size="lg" style={{color: '#a9aaa9'}}/></span>
</div>
<div style={{height: '80vh'}} className={'map-window'}>
{this.renderTelemetryLineCount()}
{this.renderTelemetryConsole()}
<ReactiveGraph graph={this.state.graph} options={getOptions(this.state.nodeStateList)}
events={this.events}/>
<TelemetryLog onStatusChange={this.props.onStatusChange}/>
</div>
</Col>
<Col xs={4}>

View File

@ -46,3 +46,10 @@
width: 100%;
padding-bottom: 10px;
}
.telemetry-log-section .loading-icon{
position: relative;
color: white;
z-index: 5;
margin-top: 30px;
}