diff options
author | Emiliano Ciavatta | 2020-10-08 20:17:04 +0000 |
---|---|---|
committer | Emiliano Ciavatta | 2020-10-08 20:17:04 +0000 |
commit | d203f3c7e3bcaa20895c0f32f348cd1513ae9876 (patch) | |
tree | bc5beea659f6d1717a0e31b0ee10cde6699da2ad | |
parent | e1198433a63eec2c900ac8986dbf0ae7db16b777 (diff) |
Frontend folder structure refactor
-rw-r--r-- | connection_streams_controller.go | 44 | ||||
-rw-r--r-- | frontend/src/components/App.js (renamed from frontend/src/views/App.js) | 39 | ||||
-rw-r--r-- | frontend/src/components/Header.js (renamed from frontend/src/views/Header.js) | 12 | ||||
-rw-r--r-- | frontend/src/components/Header.scss (renamed from frontend/src/views/Header.scss) | 2 | ||||
-rw-r--r-- | frontend/src/components/Timeline.js (renamed from frontend/src/views/Timeline.js) | 2 | ||||
-rw-r--r-- | frontend/src/components/Timeline.scss (renamed from frontend/src/views/Timeline.scss) | 2 | ||||
-rw-r--r-- | frontend/src/components/dialogs/Filters.js (renamed from frontend/src/views/Filters.js) | 4 | ||||
-rw-r--r-- | frontend/src/components/objects/Connection.js (renamed from frontend/src/components/Connection.js) | 8 | ||||
-rw-r--r-- | frontend/src/components/objects/Connection.scss (renamed from frontend/src/components/Connection.scss) | 2 | ||||
-rw-r--r-- | frontend/src/components/objects/ConnectionMatchedRules.js (renamed from frontend/src/components/ConnectionMatchedRules.js) | 2 | ||||
-rw-r--r-- | frontend/src/components/objects/ConnectionMatchedRules.scss (renamed from frontend/src/components/ConnectionMatchedRules.scss) | 2 | ||||
-rw-r--r-- | frontend/src/components/objects/MessageAction.js (renamed from frontend/src/components/MessageAction.js) | 4 | ||||
-rw-r--r-- | frontend/src/components/objects/MessageAction.scss (renamed from frontend/src/components/MessageAction.scss) | 2 | ||||
-rw-r--r-- | frontend/src/components/pages/ConfigurationPage.js (renamed from frontend/src/components/panels/ConfigurationPane.js) | 10 | ||||
-rw-r--r-- | frontend/src/components/pages/ConfigurationPage.scss (renamed from frontend/src/components/panels/ConfigurationPane.scss) | 2 | ||||
-rw-r--r-- | frontend/src/components/pages/MainPage.js | 76 | ||||
-rw-r--r-- | frontend/src/components/pages/MainPage.scss | 24 | ||||
-rw-r--r-- | frontend/src/components/pages/ServiceUnavailablePage.js | 34 | ||||
-rw-r--r-- | frontend/src/components/pages/common.scss | 16 | ||||
-rw-r--r-- | frontend/src/components/panels/ConnectionsPane.js (renamed from frontend/src/views/Connections.js) | 59 | ||||
-rw-r--r-- | frontend/src/components/panels/ConnectionsPane.scss (renamed from frontend/src/views/Connections.scss) | 2 | ||||
-rw-r--r-- | frontend/src/components/panels/MainPane.js | 47 | ||||
-rw-r--r-- | frontend/src/components/panels/MainPane.scss | 17 | ||||
-rw-r--r-- | frontend/src/components/panels/PcapsPane.js (renamed from frontend/src/components/panels/PcapPane.js) | 6 | ||||
-rw-r--r-- | frontend/src/components/panels/PcapsPane.scss (renamed from frontend/src/components/panels/PcapPane.scss) | 0 | ||||
-rw-r--r-- | frontend/src/components/panels/RulesPane.js (renamed from frontend/src/components/panels/RulePane.js) | 6 | ||||
-rw-r--r-- | frontend/src/components/panels/RulesPane.scss (renamed from frontend/src/components/panels/RulePane.scss) | 0 | ||||
-rw-r--r-- | frontend/src/components/panels/ServicesPane.js (renamed from frontend/src/components/panels/ServicePane.js) | 6 | ||||
-rw-r--r-- | frontend/src/components/panels/ServicesPane.scss (renamed from frontend/src/components/panels/ServicePane.scss) | 0 | ||||
-rw-r--r-- | frontend/src/components/panels/StreamsPane.js (renamed from frontend/src/components/ConnectionContent.js) | 77 | ||||
-rw-r--r-- | frontend/src/components/panels/StreamsPane.scss (renamed from frontend/src/components/ConnectionContent.scss) | 2 | ||||
-rw-r--r-- | frontend/src/index.js | 2 | ||||
-rw-r--r-- | frontend/src/views/App.scss | 16 |
33 files changed, 293 insertions, 234 deletions
diff --git a/connection_streams_controller.go b/connection_streams_controller.go index 9251a3a..89e484d 100644 --- a/connection_streams_controller.go +++ b/connection_streams_controller.go @@ -28,8 +28,7 @@ import ( ) const ( - initialPayloadsSize = 1024 - defaultQueryFormatLimit = 8024 + initialMessagesSize = 1024 initialRegexSlicesCount = 8 pwntoolsMaxServerBytes = 20 ) @@ -66,8 +65,6 @@ type RegexSlice struct { type GetMessageFormat struct { Format string `form:"format"` - Skip uint64 `form:"skip"` - Limit uint64 `form:"limit"` } type DownloadMessageFormat struct { @@ -92,12 +89,8 @@ func (csc ConnectionStreamsController) GetConnectionMessages(c context.Context, return nil, false } - payloads := make([]*Message, 0, initialPayloadsSize) - var clientIndex, serverIndex, globalIndex uint64 - - if format.Limit <= 0 { - format.Limit = defaultQueryFormatLimit - } + messages := make([]*Message, 0, initialMessagesSize) + var clientIndex, serverIndex uint64 var clientBlocksIndex, serverBlocksIndex int var clientDocumentIndex, serverDocumentIndex int @@ -111,8 +104,8 @@ func (csc ConnectionStreamsController) GetConnectionMessages(c context.Context, return serverBlocksIndex < len(serverStream.BlocksIndexes) } - var payload *Message - payloadsBuffer := make([]*Message, 0, 16) + var message *Message + messagesBuffer := make([]*Message, 0, 16) contentChunkBuffer := new(bytes.Buffer) var lastContentSlice []byte var sideChanged, lastClient, lastServer bool @@ -129,7 +122,7 @@ func (csc ConnectionStreamsController) GetConnectionMessages(c context.Context, } size := uint64(end - start) - payload = &Message{ + message = &Message{ FromClient: true, Content: DecodeBytes(clientStream.Payload[start:end], format.Format), Index: start, @@ -138,7 +131,6 @@ func (csc ConnectionStreamsController) GetConnectionMessages(c context.Context, RegexMatches: findMatchesBetween(clientStream.PatternMatches, clientIndex, clientIndex+size), } clientIndex += size - globalIndex += size clientBlocksIndex++ lastContentSlice = clientStream.Payload[start:end] @@ -153,7 +145,7 @@ func (csc ConnectionStreamsController) GetConnectionMessages(c context.Context, } size := uint64(end - start) - payload = &Message{ + message = &Message{ FromClient: false, Content: DecodeBytes(serverStream.Payload[start:end], format.Format), Index: start, @@ -162,7 +154,6 @@ func (csc ConnectionStreamsController) GetConnectionMessages(c context.Context, RegexMatches: findMatchesBetween(serverStream.PatternMatches, serverIndex, serverIndex+size), } serverIndex += size - globalIndex += size serverBlocksIndex++ lastContentSlice = serverStream.Payload[start:end] @@ -172,49 +163,43 @@ func (csc ConnectionStreamsController) GetConnectionMessages(c context.Context, if !hasClientBlocks() { clientDocumentIndex++ clientBlocksIndex = 0 + clientIndex = 0 clientStream = csc.getConnectionStream(c, connectionID, true, clientDocumentIndex) } if !hasServerBlocks() { serverDocumentIndex++ serverBlocksIndex = 0 + serverIndex = 0 serverStream = csc.getConnectionStream(c, connectionID, false, serverDocumentIndex) } updateMetadata := func() { metadata := parsers.Parse(contentChunkBuffer.Bytes()) var isMetadataContinuation bool - for _, elem := range payloadsBuffer { + for _, elem := range messagesBuffer { elem.Metadata = metadata elem.IsMetadataContinuation = isMetadataContinuation isMetadataContinuation = true } - payloadsBuffer = payloadsBuffer[:0] + messagesBuffer = messagesBuffer[:0] contentChunkBuffer.Reset() } if sideChanged { updateMetadata() } - payloadsBuffer = append(payloadsBuffer, payload) + messagesBuffer = append(messagesBuffer, message) contentChunkBuffer.Write(lastContentSlice) if clientStream.ID.IsZero() && serverStream.ID.IsZero() { updateMetadata() } - if globalIndex > format.Skip { - // problem: waste of time if the payload is discarded - payloads = append(payloads, payload) - } - if globalIndex > format.Skip+format.Limit { - // problem: the last chunk is not parsed, but can be ok because it is not finished - updateMetadata() - return payloads, true - } + messages = append(messages, message) } - return payloads, true + return messages, true } func (csc ConnectionStreamsController) DownloadConnectionMessages(c context.Context, connectionID RowID, @@ -345,7 +330,6 @@ func findMatchesBetween(patternMatches map[uint][]PatternSlice, from, to uint64) continue } - log.Info(slice[0], slice[1], from, to) var start, end uint64 if from > slice[0] { start = 0 diff --git a/frontend/src/views/App.js b/frontend/src/components/App.js index 8105117..bf959c5 100644 --- a/frontend/src/views/App.js +++ b/frontend/src/components/App.js @@ -16,15 +16,11 @@ */ import React, {Component} from 'react'; -import './App.scss'; -import Header from "./Header"; -import MainPane from "../components/panels/MainPane"; -import Timeline from "./Timeline"; -import {BrowserRouter as Router} from "react-router-dom"; -import Filters from "./Filters"; -import ConfigurationPane from "../components/panels/ConfigurationPane"; -import Notifications from "../components/Notifications"; +import ConfigurationPage from "./pages/ConfigurationPage"; +import Notifications from "./Notifications"; import dispatcher from "../dispatcher"; +import MainPage from "./pages/MainPage"; +import ServiceUnavailablePage from "./pages/ServiceUnavailablePage"; class App extends Component { @@ -50,30 +46,15 @@ class App extends Component { } render() { - let modal; - if (this.state.filterWindowOpen && this.state.configured) { - modal = <Filters onHide={() => this.setState({filterWindowOpen: false})}/>; - } - return ( - <div className="main"> + <> <Notifications/> - {this.state.connected && - <Router> - <div className="main-header"> - <Header onOpenFilters={() => this.setState({filterWindowOpen: true})}/> - </div> - <div className="main-content"> - {this.state.configured ? <MainPane/> : - <ConfigurationPane onConfigured={() => this.setState({configured: true})}/>} - {modal} - </div> - <div className="main-footer"> - {this.state.configured && <Timeline/>} - </div> - </Router> + {this.state.connected ? + (this.state.configured ? <MainPage/> : + <ConfigurationPage onConfigured={() => this.setState({configured: true})}/>) : + <ServiceUnavailablePage/> } - </div> + </> ); } } diff --git a/frontend/src/views/Header.js b/frontend/src/components/Header.js index 2cfe9fb..4d29364 100644 --- a/frontend/src/views/Header.js +++ b/frontend/src/components/Header.js @@ -18,9 +18,9 @@ import React, {Component} from 'react'; import Typed from 'typed.js'; import './Header.scss'; -import {filtersDefinitions, filtersNames} from "../components/filters/FiltersDefinitions"; +import {filtersDefinitions, filtersNames} from "./filters/FiltersDefinitions"; import {Link, withRouter} from "react-router-dom"; -import ButtonField from "../components/fields/ButtonField"; +import ButtonField from "./fields/ButtonField"; class Header extends Component { @@ -71,9 +71,11 @@ class Header extends Component { <div className="row"> <div className="col-auto"> <h1 className="header-title type-wrap"> - <span style={{whiteSpace: 'pre'}} ref={(el) => { - this.el = el; - }}/> + <Link to="/"> + <span style={{whiteSpace: 'pre'}} ref={(el) => { + this.el = el; + }}/> + </Link> </h1> </div> diff --git a/frontend/src/views/Header.scss b/frontend/src/components/Header.scss index 0711159..e2e8e1c 100644 --- a/frontend/src/views/Header.scss +++ b/frontend/src/components/Header.scss @@ -1,4 +1,4 @@ -@import "../colors.scss"; +@import "../colors"; .header { height: 80px; diff --git a/frontend/src/views/Timeline.js b/frontend/src/components/Timeline.js index ebe3eb9..7be42e0 100644 --- a/frontend/src/views/Timeline.js +++ b/frontend/src/components/Timeline.js @@ -29,7 +29,7 @@ import { } from "react-timeseries-charts"; import {TimeRange, TimeSeries} from "pondjs"; import backend from "../backend"; -import ChoiceField from "../components/fields/ChoiceField"; +import ChoiceField from "./fields/ChoiceField"; import {withRouter} from "react-router-dom"; import log from "../log"; import dispatcher from "../dispatcher"; diff --git a/frontend/src/views/Timeline.scss b/frontend/src/components/Timeline.scss index 14360d4..eeb9d50 100644 --- a/frontend/src/views/Timeline.scss +++ b/frontend/src/components/Timeline.scss @@ -1,4 +1,4 @@ -@import "../colors.scss"; +@import "../colors"; .footer { padding: 15px; diff --git a/frontend/src/views/Filters.js b/frontend/src/components/dialogs/Filters.js index 3dd8280..35c11df 100644 --- a/frontend/src/views/Filters.js +++ b/frontend/src/components/dialogs/Filters.js @@ -17,8 +17,8 @@ import React, {Component} from 'react'; import {Col, Container, Modal, Row, Table} from "react-bootstrap"; -import {filtersDefinitions, filtersNames} from "../components/filters/FiltersDefinitions"; -import ButtonField from "../components/fields/ButtonField"; +import {filtersDefinitions, filtersNames} from "../filters/FiltersDefinitions"; +import ButtonField from "../fields/ButtonField"; class Filters extends Component { diff --git a/frontend/src/components/Connection.js b/frontend/src/components/objects/Connection.js index c7b0010..5e2beba 100644 --- a/frontend/src/components/Connection.js +++ b/frontend/src/components/objects/Connection.js @@ -18,10 +18,10 @@ import React, {Component} from 'react'; import './Connection.scss'; import {Form, OverlayTrigger, Popover} from "react-bootstrap"; -import backend from "../backend"; -import {dateTimeToTime, durationBetween, formatSize} from "../utils"; -import ButtonField from "./fields/ButtonField"; -import LinkPopover from "./objects/LinkPopover"; +import backend from "../../backend"; +import {dateTimeToTime, durationBetween, formatSize} from "../../utils"; +import ButtonField from "../fields/ButtonField"; +import LinkPopover from "./LinkPopover"; const classNames = require('classnames'); diff --git a/frontend/src/components/Connection.scss b/frontend/src/components/objects/Connection.scss index cc1ea96..3b9f479 100644 --- a/frontend/src/components/Connection.scss +++ b/frontend/src/components/objects/Connection.scss @@ -1,4 +1,4 @@ -@import "../colors.scss"; +@import "../../colors"; .connection { border-top: 3px solid $color-primary-3; diff --git a/frontend/src/components/ConnectionMatchedRules.js b/frontend/src/components/objects/ConnectionMatchedRules.js index 35643c5..73d5c5d 100644 --- a/frontend/src/components/ConnectionMatchedRules.js +++ b/frontend/src/components/objects/ConnectionMatchedRules.js @@ -17,7 +17,7 @@ import React, {Component} from 'react'; import './ConnectionMatchedRules.scss'; -import ButtonField from "./fields/ButtonField"; +import ButtonField from "../fields/ButtonField"; class ConnectionMatchedRules extends Component { diff --git a/frontend/src/components/ConnectionMatchedRules.scss b/frontend/src/components/objects/ConnectionMatchedRules.scss index 65d9ac8..f46a914 100644 --- a/frontend/src/components/ConnectionMatchedRules.scss +++ b/frontend/src/components/objects/ConnectionMatchedRules.scss @@ -1,4 +1,4 @@ -@import "../colors.scss"; +@import "../../colors"; .connection-matches { background-color: $color-primary-0; diff --git a/frontend/src/components/MessageAction.js b/frontend/src/components/objects/MessageAction.js index b94cbb9..9f199b7 100644 --- a/frontend/src/components/MessageAction.js +++ b/frontend/src/components/objects/MessageAction.js @@ -18,8 +18,8 @@ import React, {Component} from 'react'; import './MessageAction.scss'; import {Modal} from "react-bootstrap"; -import TextField from "./fields/TextField"; -import ButtonField from "./fields/ButtonField"; +import TextField from "../fields/TextField"; +import ButtonField from "../fields/ButtonField"; class MessageAction extends Component { diff --git a/frontend/src/components/MessageAction.scss b/frontend/src/components/objects/MessageAction.scss index faa23d3..996007b 100644 --- a/frontend/src/components/MessageAction.scss +++ b/frontend/src/components/objects/MessageAction.scss @@ -1,4 +1,4 @@ -@import "../colors.scss"; +@import "../../colors"; .message-action-value { font-size: 13px; diff --git a/frontend/src/components/panels/ConfigurationPane.js b/frontend/src/components/pages/ConfigurationPage.js index 9ae2cfb..6ab8ae3 100644 --- a/frontend/src/components/panels/ConfigurationPane.js +++ b/frontend/src/components/pages/ConfigurationPage.js @@ -16,8 +16,8 @@ */ import React, {Component} from 'react'; -import './common.scss'; -import './ConfigurationPane.scss'; +import '../panels/common.scss'; +import './ConfigurationPage.scss'; import LinkPopover from "../objects/LinkPopover"; import {Col, Container, Row} from "react-bootstrap"; import InputField from "../fields/InputField"; @@ -29,7 +29,7 @@ import Table from "react-bootstrap/Table"; import validation from "../../validation"; import backend from "../../backend"; -class ConfigurationPane extends Component { +class ConfigurationPage extends Component { constructor(props) { super(props); @@ -114,7 +114,7 @@ class ConfigurationPane extends Component { </tr>); return ( - <div className="configuration-pane"> + <div className="configuration-page"> <div className="pane"> <div className="pane-container"> <div className="pane-section"> @@ -176,4 +176,4 @@ class ConfigurationPane extends Component { } } -export default ConfigurationPane; +export default ConfigurationPage; diff --git a/frontend/src/components/panels/ConfigurationPane.scss b/frontend/src/components/pages/ConfigurationPage.scss index ef48b34..4509865 100644 --- a/frontend/src/components/panels/ConfigurationPane.scss +++ b/frontend/src/components/pages/ConfigurationPage.scss @@ -1,6 +1,6 @@ @import "../../colors"; -.configuration-pane { +.configuration-page { display: flex; align-items: center; justify-content: center; diff --git a/frontend/src/components/pages/MainPage.js b/frontend/src/components/pages/MainPage.js new file mode 100644 index 0000000..7376091 --- /dev/null +++ b/frontend/src/components/pages/MainPage.js @@ -0,0 +1,76 @@ +/* + * This file is part of caronte (https://github.com/eciavatta/caronte). + * Copyright (c) 2020 Emiliano Ciavatta. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import React, {Component} from 'react'; +import './MainPage.scss'; +import './common.scss'; +import Connections from "../panels/ConnectionsPane"; +import StreamsPane from "../panels/StreamsPane"; +import {BrowserRouter as Router, Route, Switch} from "react-router-dom"; +import Timeline from "../Timeline"; +import PcapsPane from "../panels/PcapsPane"; +import RulesPane from "../panels/RulesPane"; +import ServicesPane from "../panels/ServicesPane"; +import Header from "../Header"; +import Filters from "../dialogs/Filters"; +import MainPane from "../panels/MainPane"; + +class MainPage extends Component { + + state = {}; + + render() { + let modal; + if (this.state.filterWindowOpen) { + modal = <Filters onHide={() => this.setState({filterWindowOpen: false})}/>; + } + + return ( + <div className="page main-page"> + <Router> + <div className="page-header"> + <Header onOpenFilters={() => this.setState({filterWindowOpen: true})}/> + </div> + + <div className="page-content"> + <div className="pane connections-pane"> + <Connections onSelected={(c) => this.setState({selectedConnection: c})}/> + </div> + <div className="pane details-pane"> + <Switch> + <Route path="/pcaps" children={<PcapsPane/>}/> + <Route path="/rules" children={<RulesPane/>}/> + <Route path="/services" children={<ServicesPane/>}/> + <Route exact path="/connections/:id" + children={<StreamsPane connection={this.state.selectedConnection}/>}/> + <Route children={<MainPane/>}/> + </Switch> + </div> + + {modal} + </div> + + <div className="page-footer"> + <Timeline/> + </div> + </Router> + </div> + ); + } +} + +export default MainPage; diff --git a/frontend/src/components/pages/MainPage.scss b/frontend/src/components/pages/MainPage.scss new file mode 100644 index 0000000..3b1a689 --- /dev/null +++ b/frontend/src/components/pages/MainPage.scss @@ -0,0 +1,24 @@ +@import "../../colors"; + +.main-page { + .page-content { + display: flex; + flex: 1; + padding: 0 15px; + background-color: $color-primary-2; + + .connections-pane { + flex: 1 0; + margin-right: 7.5px; + } + + .details-pane { + flex: 1 1; + margin-left: 7.5px; + } + } + + .page-footer { + flex: 0; + } +} diff --git a/frontend/src/components/pages/ServiceUnavailablePage.js b/frontend/src/components/pages/ServiceUnavailablePage.js new file mode 100644 index 0000000..f27d84d --- /dev/null +++ b/frontend/src/components/pages/ServiceUnavailablePage.js @@ -0,0 +1,34 @@ +/* + * This file is part of caronte (https://github.com/eciavatta/caronte). + * Copyright (c) 2020 Emiliano Ciavatta. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import React, {Component} from 'react'; +import './MainPage.scss'; + +class ServiceUnavailablePage extends Component { + + state = {}; + + render() { + return ( + <div className="main-page"> + + </div> + ); + } +} + +export default ServiceUnavailablePage; diff --git a/frontend/src/components/pages/common.scss b/frontend/src/components/pages/common.scss new file mode 100644 index 0000000..fcf5c20 --- /dev/null +++ b/frontend/src/components/pages/common.scss @@ -0,0 +1,16 @@ +.page { + position: relative; + display: flex; + flex-direction: column; + height: 100vh; + + .page-header, + .page-footer { + flex: 0; + } + + .page-content { + overflow: hidden; + flex: 1; + } +} diff --git a/frontend/src/views/Connections.js b/frontend/src/components/panels/ConnectionsPane.js index b2edd3f..038ef8f 100644 --- a/frontend/src/views/Connections.js +++ b/frontend/src/components/panels/ConnectionsPane.js @@ -16,18 +16,18 @@ */ import React, {Component} from 'react'; -import './Connections.scss'; -import Connection from "../components/Connection"; +import './ConnectionsPane.scss'; +import Connection from "../objects/Connection"; import Table from 'react-bootstrap/Table'; import {Redirect} from 'react-router'; import {withRouter} from "react-router-dom"; -import backend from "../backend"; -import ConnectionMatchedRules from "../components/ConnectionMatchedRules"; -import log from "../log"; -import ButtonField from "../components/fields/ButtonField"; -import dispatcher from "../dispatcher"; +import backend from "../../backend"; +import ConnectionMatchedRules from "../objects/ConnectionMatchedRules"; +import log from "../../log"; +import ButtonField from "../fields/ButtonField"; +import dispatcher from "../../dispatcher"; -class Connections extends Component { +class ConnectionsPane extends Component { state = { loading: false, @@ -45,17 +45,22 @@ class Connections extends Component { this.queryLimit = 50; this.connectionsListRef = React.createRef(); this.lastScrollPosition = 0; - this.doQueryStringRedirect = false; - this.doSelectedConnectionRedirect = false; } componentDidMount() { - this.loadConnections({limit: this.queryLimit}) - .then(() => this.setState({loaded: true})); - if (this.props.initialConnection) { - this.setState({selected: this.props.initialConnection.id}); + const initialParams = {limit: this.queryLimit}; + + const match = this.props.location.pathname.match(/^\/connections\/([a-f0-9]{24})$/); + if (match != null) { + const id = match[1]; + initialParams.from = id; + backend.get(`/api/connections/${id}`) + .then(res => this.connectionSelected(res.json, false)) + .catch(error => log.error("Error loading initial connection", error)); } + this.loadConnections(initialParams, true).then(() => log.debug("Connections loaded")); + dispatcher.register("timeline_updates", payload => { this.connectionsListRef.current.scrollTop = 0; this.loadConnections({ @@ -78,16 +83,17 @@ class Connections extends Component { }); } - connectionSelected = (c) => { - this.doSelectedConnectionRedirect = true; + connectionSelected = (c, doRedirect = true) => { + this.doSelectedConnectionRedirect = doRedirect; this.setState({selected: c.id}); this.props.onSelected(c); + log.debug(`Connection ${c.id} selected`); }; componentDidUpdate(prevProps, prevState, snapshot) { - if (this.state.loaded && prevProps.location.search !== this.props.location.search) { + if (prevProps.location.search !== this.props.location.search) { this.loadConnections({limit: this.queryLimit}) - .then(() => log.info("Connections reloaded after query string update")); + .then(() => log.info("ConnectionsPane reloaded after query string update")); } } @@ -140,8 +146,7 @@ class Connections extends Component { } }; - async loadConnections(params) { - let url = "/api/connections"; + async loadConnections(params, isInitial = false) { const urlParams = new URLSearchParams(this.props.location.search); for (const [name, value] of Object.entries(params)) { urlParams.set(name, value); @@ -155,7 +160,7 @@ class Connections extends Component { await this.loadServices(); } - let res = (await backend.get(`${url}?${urlParams}`)).json; + let res = (await backend.get(`/api/connections?${urlParams}`)).json; let connections = this.state.connections; let firstConnection = this.state.firstConnection; @@ -163,8 +168,14 @@ class Connections extends Component { if (params !== undefined && params.from !== undefined && params.to === undefined) { if (res.length > 0) { - connections = this.state.connections.concat(res.slice(1)); + if (!isInitial) { + res = res.slice(1); + } + connections = this.state.connections.concat(res); lastConnection = connections[connections.length - 1]; + if (isInitial) { + firstConnection = connections[0]; + } if (connections.length > this.maxConnections) { connections = connections.slice(connections.length - this.maxConnections, connections.length - 1); @@ -228,7 +239,7 @@ class Connections extends Component { let loading = null; if (this.state.loading) { loading = <tr> - <td colSpan={9}>Loading...</td> + <td colSpan={10}>Loading...</td> </tr>; } @@ -290,4 +301,4 @@ class Connections extends Component { } -export default withRouter(Connections); +export default withRouter(ConnectionsPane); diff --git a/frontend/src/views/Connections.scss b/frontend/src/components/panels/ConnectionsPane.scss index de06096..06f5827 100644 --- a/frontend/src/views/Connections.scss +++ b/frontend/src/components/panels/ConnectionsPane.scss @@ -1,4 +1,4 @@ -@import "../colors.scss"; +@import "../../colors"; .connections-container { position: relative; diff --git a/frontend/src/components/panels/MainPane.js b/frontend/src/components/panels/MainPane.js index d34d58a..74c859c 100644 --- a/frontend/src/components/panels/MainPane.js +++ b/frontend/src/components/panels/MainPane.js @@ -17,57 +17,22 @@ import React, {Component} from 'react'; import './common.scss'; -import './MainPane.scss'; -import Connections from "../../views/Connections"; -import ConnectionContent from "../ConnectionContent"; -import {Route, Switch, withRouter} from "react-router-dom"; -import PcapPane from "./PcapPane"; -import backend from "../../backend"; -import RulePane from "./RulePane"; -import ServicePane from "./ServicePane"; -import log from "../../log"; +import './ServicesPane.scss'; class MainPane extends Component { state = {}; - componentDidMount() { - const match = this.props.location.pathname.match(/^\/connections\/([a-f0-9]{24})$/); - if (match != null) { - this.loading = true; - backend.get(`/api/connections/${match[1]}`) - .then(res => { - this.loading = false; - this.setState({selectedConnection: res.json}); - log.debug(`Initial connection ${match[1]} loaded`); - }) - .catch(error => log.error("Error loading initial connection", error)); - } - } - render() { return ( - <div className="main-pane"> - <div className="pane connections-pane"> - { - !this.loading && - <Connections onSelected={(c) => this.setState({selectedConnection: c})} - initialConnection={this.state.selectedConnection}/> - } - </div> - <div className="pane details-pane"> - <Switch> - <Route path="/pcaps" children={<PcapPane/>}/> - <Route path="/rules" children={<RulePane/>}/> - <Route path="/services" children={<ServicePane/>}/> - <Route exact path="/connections/:id" - children={<ConnectionContent connection={this.state.selectedConnection}/>}/> - <Route children={<ConnectionContent/>}/> - </Switch> + <div className="pane-container main-pane"> + <div className="pane-section"> + MainPane </div> </div> ); } + } -export default withRouter(MainPane); +export default MainPane; diff --git a/frontend/src/components/panels/MainPane.scss b/frontend/src/components/panels/MainPane.scss index 2973c00..c8460f2 100644 --- a/frontend/src/components/panels/MainPane.scss +++ b/frontend/src/components/panels/MainPane.scss @@ -1,22 +1,5 @@ @import "../../colors"; .main-pane { - display: flex; - height: 100%; - padding: 0 15px; - background-color: $color-primary-2; - .pane { - flex: 1; - } - - .connections-pane { - flex: 1 0; - margin-right: 7.5px; - } - - .details-pane { - flex: 1 1; - margin-left: 7.5px; - } } diff --git a/frontend/src/components/panels/PcapPane.js b/frontend/src/components/panels/PcapsPane.js index d5c2225..8722230 100644 --- a/frontend/src/components/panels/PcapPane.js +++ b/frontend/src/components/panels/PcapsPane.js @@ -16,7 +16,7 @@ */ import React, {Component} from 'react'; -import './PcapPane.scss'; +import './PcapsPane.scss'; import './common.scss'; import Table from "react-bootstrap/Table"; import backend from "../../backend"; @@ -28,7 +28,7 @@ import ButtonField from "../fields/ButtonField"; import LinkPopover from "../objects/LinkPopover"; import dispatcher from "../../dispatcher"; -class PcapPane extends Component { +class PcapsPane extends Component { state = { sessions: [], @@ -270,4 +270,4 @@ class PcapPane extends Component { } } -export default PcapPane; +export default PcapsPane; diff --git a/frontend/src/components/panels/PcapPane.scss b/frontend/src/components/panels/PcapsPane.scss index 4dbc2b2..4dbc2b2 100644 --- a/frontend/src/components/panels/PcapPane.scss +++ b/frontend/src/components/panels/PcapsPane.scss diff --git a/frontend/src/components/panels/RulePane.js b/frontend/src/components/panels/RulesPane.js index 9913962..a66cde7 100644 --- a/frontend/src/components/panels/RulePane.js +++ b/frontend/src/components/panels/RulesPane.js @@ -17,7 +17,7 @@ import React, {Component} from 'react'; import './common.scss'; -import './RulePane.scss'; +import './RulesPane.scss'; import Table from "react-bootstrap/Table"; import {Col, Container, Row} from "react-bootstrap"; import InputField from "../fields/InputField"; @@ -36,7 +36,7 @@ import dispatcher from "../../dispatcher"; const classNames = require('classnames'); const _ = require('lodash'); -class RulePane extends Component { +class RulesPane extends Component { emptyRule = { "name": "", @@ -435,4 +435,4 @@ class RulePane extends Component { } -export default RulePane; +export default RulesPane; diff --git a/frontend/src/components/panels/RulePane.scss b/frontend/src/components/panels/RulesPane.scss index 992445a..992445a 100644 --- a/frontend/src/components/panels/RulePane.scss +++ b/frontend/src/components/panels/RulesPane.scss diff --git a/frontend/src/components/panels/ServicePane.js b/frontend/src/components/panels/ServicesPane.js index fc7004b..bc82356 100644 --- a/frontend/src/components/panels/ServicePane.js +++ b/frontend/src/components/panels/ServicesPane.js @@ -17,7 +17,7 @@ import React, {Component} from 'react'; import './common.scss'; -import './ServicePane.scss'; +import './ServicesPane.scss'; import Table from "react-bootstrap/Table"; import {Col, Container, Row} from "react-bootstrap"; import InputField from "../fields/InputField"; @@ -34,7 +34,7 @@ import dispatcher from "../../dispatcher"; const classNames = require('classnames'); const _ = require('lodash'); -class ServicePane extends Component { +class ServicesPane extends Component { emptyService = { "port": 0, @@ -209,4 +209,4 @@ class ServicePane extends Component { } -export default ServicePane; +export default ServicesPane; diff --git a/frontend/src/components/panels/ServicePane.scss b/frontend/src/components/panels/ServicesPane.scss index daf7e79..daf7e79 100644 --- a/frontend/src/components/panels/ServicePane.scss +++ b/frontend/src/components/panels/ServicesPane.scss diff --git a/frontend/src/components/ConnectionContent.js b/frontend/src/components/panels/StreamsPane.js index b468277..c8bd121 100644 --- a/frontend/src/components/ConnectionContent.js +++ b/frontend/src/components/panels/StreamsPane.js @@ -16,47 +16,47 @@ */ import React, {Component} from 'react'; -import './ConnectionContent.scss'; +import './StreamsPane.scss'; import {Row} from 'react-bootstrap'; -import MessageAction from "./MessageAction"; -import backend from "../backend"; -import ButtonField from "./fields/ButtonField"; -import ChoiceField from "./fields/ChoiceField"; +import MessageAction from "../objects/MessageAction"; +import backend from "../../backend"; +import ButtonField from "../fields/ButtonField"; +import ChoiceField from "../fields/ChoiceField"; import DOMPurify from 'dompurify'; import ReactJson from 'react-json-view' -import {downloadBlob, getHeaderValue} from "../utils"; -import log from "../log"; +import {downloadBlob, getHeaderValue} from "../../utils"; +import log from "../../log"; const classNames = require('classnames'); -class ConnectionContent extends Component { +class StreamsPane extends Component { + + state = { + messages: [], + format: "default", + tryParse: true + }; constructor(props) { super(props); - this.state = { - loading: false, - connectionContent: null, - format: "default", - tryParse: true, - messageActionDialog: null - }; this.validFormats = ["default", "hex", "hexdump", "base32", "base64", "ascii", "binary", "decimal", "octal"]; } componentDidMount() { - if (this.props.connection != null) { - this.loadStream(); + if (this.props.connection && this.state.currentId !== this.props.connection.id) { + this.setState({currentId: this.props.connection.id}); + this.loadStream(this.props.connection.id); } document.title = "caronte:~/$"; } componentDidUpdate(prevProps, prevState, snapshot) { - if (this.props.connection != null && ( + if (this.props.connection && ( this.props.connection !== prevProps.connection || this.state.format !== prevState.format)) { this.closeRenderWindow(); - this.loadStream(); + this.loadStream(this.props.connection.id); } } @@ -64,15 +64,10 @@ class ConnectionContent extends Component { this.closeRenderWindow(); } - loadStream = () => { - this.setState({loading: true}); - // TODO: limit workaround. - backend.get(`/api/streams/${this.props.connection.id}?format=${this.state.format}&limit=999999`).then(res => { - this.setState({ - connectionContent: res.json, - loading: false - }); - }); + loadStream = (connectionId) => { + this.setState({messages: []}); + backend.get(`/api/streams/${connectionId}?format=${this.state.format}`) + .then(res => this.setState({messages: res.json})); }; setFormat = (format) => { @@ -128,7 +123,7 @@ class ConnectionContent extends Component { }; connectionsActions = (connectionMessage) => { - if (connectionMessage.metadata == null) { //} || !connectionMessage.metadata["reproducers"]) { + if (!connectionMessage.metadata) { return null; } @@ -169,9 +164,11 @@ class ConnectionContent extends Component { }; downloadStreamRaw = (value) => { - backend.download(`/api/streams/${this.props.connection.id}/download?format=${this.state.format}&type=${value}`) - .then(res => downloadBlob(res.blob, `${this.props.connection.id}-${value}-${this.state.format}.txt`)) - .catch(_ => log.error("Failed to download stream messages")); + if (this.state.currentId) { + backend.download(`/api/streams/${this.props.connection.id}/download?format=${this.state.format}&type=${value}`) + .then(res => downloadBlob(res.blob, `${this.state.currentId}-${value}-${this.state.format}.txt`)) + .catch(_ => log.error("Failed to download stream messages")); + } }; closeRenderWindow = () => { @@ -181,12 +178,14 @@ class ConnectionContent extends Component { }; render() { - const conn = this.props.connection; - const content = this.state.connectionContent; - - if (content == null) { - return <div>select a connection to view</div>; - } + const conn = this.props.connection || { + "ip_src": "0.0.0.0", + "ip_dst": "0.0.0.0", + "port_src": "0", + "port_dst": "0", + "started_at": new Date().toISOString(), + }; + const content = this.state.messages || []; let payload = content.map((c, i) => <div key={`content-${i}`} @@ -240,4 +239,4 @@ class ConnectionContent extends Component { } -export default ConnectionContent; +export default StreamsPane; diff --git a/frontend/src/components/ConnectionContent.scss b/frontend/src/components/panels/StreamsPane.scss index f4edec9..d5510cf 100644 --- a/frontend/src/components/ConnectionContent.scss +++ b/frontend/src/components/panels/StreamsPane.scss @@ -1,4 +1,4 @@ -@import "../colors.scss"; +@import "../../colors"; .connection-content { height: 100%; diff --git a/frontend/src/index.js b/frontend/src/index.js index e3e48de..ca1273a 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -19,7 +19,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import 'bootstrap/dist/css/bootstrap.css'; import './index.scss'; -import App from './views/App'; +import App from './components/App'; import * as serviceWorker from './serviceWorker'; import notifications from "./notifications"; diff --git a/frontend/src/views/App.scss b/frontend/src/views/App.scss deleted file mode 100644 index 87661c3..0000000 --- a/frontend/src/views/App.scss +++ /dev/null @@ -1,16 +0,0 @@ - -.main { - display: flex; - flex-direction: column; - height: 100vh; - - .main-content { - overflow: hidden; - flex: 1 1; - } - - .main-header, - .main-footer { - flex: 0 0; - } -} |