From d203f3c7e3bcaa20895c0f32f348cd1513ae9876 Mon Sep 17 00:00:00 2001 From: Emiliano Ciavatta Date: Thu, 8 Oct 2020 22:17:04 +0200 Subject: Frontend folder structure refactor --- frontend/src/components/panels/PcapsPane.js | 273 ++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 frontend/src/components/panels/PcapsPane.js (limited to 'frontend/src/components/panels/PcapsPane.js') diff --git a/frontend/src/components/panels/PcapsPane.js b/frontend/src/components/panels/PcapsPane.js new file mode 100644 index 0000000..8722230 --- /dev/null +++ b/frontend/src/components/panels/PcapsPane.js @@ -0,0 +1,273 @@ +/* + * 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 . + */ + +import React, {Component} from 'react'; +import './PcapsPane.scss'; +import './common.scss'; +import Table from "react-bootstrap/Table"; +import backend from "../../backend"; +import {createCurlCommand, dateTimeToTime, durationBetween, formatSize} from "../../utils"; +import InputField from "../fields/InputField"; +import CheckField from "../fields/CheckField"; +import TextField from "../fields/TextField"; +import ButtonField from "../fields/ButtonField"; +import LinkPopover from "../objects/LinkPopover"; +import dispatcher from "../../dispatcher"; + +class PcapsPane extends Component { + + state = { + sessions: [], + isUploadFileValid: true, + isUploadFileFocused: false, + uploadFlushAll: false, + isFileValid: true, + isFileFocused: false, + fileValue: "", + processFlushAll: false, + deleteOriginalFile: false + }; + + componentDidMount() { + this.loadSessions(); + + dispatcher.register("notifications", payload => { + if (payload.event === "pcap.upload" || payload.event === "pcap.file") { + this.loadSessions(); + } + }); + + document.title = "caronte:~/pcaps$"; + } + + loadSessions = () => { + backend.get("/api/pcap/sessions") + .then(res => this.setState({sessions: res.json, sessionsStatusCode: res.status})) + .catch(res => this.setState({ + sessions: res.json, sessionsStatusCode: res.status, + sessionsResponse: JSON.stringify(res.json) + })); + }; + + uploadPcap = () => { + if (this.state.uploadSelectedFile == null || !this.state.isUploadFileValid) { + this.setState({isUploadFileFocused: true}); + return; + } + + const formData = new FormData(); + formData.append("file", this.state.uploadSelectedFile); + formData.append("flush_all", this.state.uploadFlushAll); + backend.postFile("/api/pcap/upload", formData).then(res => { + this.setState({ + uploadStatusCode: res.status, + uploadResponse: JSON.stringify(res.json) + }); + this.resetUpload(); + this.loadSessions(); + }).catch(res => this.setState({ + uploadStatusCode: res.status, + uploadResponse: JSON.stringify(res.json) + }) + ); + }; + + processPcap = () => { + if (this.state.fileValue === "" || !this.state.isFileValid) { + this.setState({isFileFocused: true}); + return; + } + + backend.post("/api/pcap/file", { + file: this.state.fileValue, + flush_all: this.state.processFlushAll, + delete_original_file: this.state.deleteOriginalFile + }).then(res => { + this.setState({ + processStatusCode: res.status, + processResponse: JSON.stringify(res.json) + }); + this.resetProcess(); + this.loadSessions(); + }).catch(res => this.setState({ + processStatusCode: res.status, + processResponse: JSON.stringify(res.json) + }) + ); + }; + + resetUpload = () => { + this.setState({ + isUploadFileValid: true, + isUploadFileFocused: false, + uploadFlushAll: false, + uploadSelectedFile: null + }); + }; + + resetProcess = () => { + this.setState({ + isFileValid: true, + isFileFocused: false, + fileValue: "", + processFlushAll: false, + deleteOriginalFile: false, + }); + }; + + render() { + let sessions = this.state.sessions.map(s => + + {s["id"].substring(0, 8)} + {dateTimeToTime(s["started_at"])} + {durationBetween(s["started_at"], s["completed_at"])} + {formatSize(s["size"])} + {s["processed_packets"]} + {s["invalid_packets"]} + + download + + + ); + + const handleUploadFileChange = (file) => { + this.setState({ + isUploadFileValid: file == null || (file.type.endsWith("pcap") || file.type.endsWith("pcapng")), + isUploadFileFocused: false, + uploadSelectedFile: file, + uploadStatusCode: null, + uploadResponse: null + }); + }; + + const handleFileChange = (file) => { + this.setState({ + isFileValid: (file.endsWith("pcap") || file.endsWith("pcapng")), + isFileFocused: false, + fileValue: file, + processStatusCode: null, + processResponse: null + }); + }; + + const uploadCurlCommand = createCurlCommand("pcap/upload", "POST", null, { + file: "@" + ((this.state.uploadSelectedFile != null && this.state.isUploadFileValid) ? + this.state.uploadSelectedFile.name : "invalid.pcap"), + flush_all: this.state.uploadFlushAll + }); + + const fileCurlCommand = createCurlCommand("pcap/file", "POST", { + file: this.state.fileValue, + flush_all: this.state.processFlushAll, + delete_original_file: this.state.deleteOriginalFile + }); + + return ( +
+
+
+ GET /api/pcap/sessions + +
+ +
+
+ + + + + + + + + + + + + + + {sessions} + +
idstarted_atdurationsizeprocessed_packetsinvalid_packetspackets_per_serviceactions
+
+
+
+ +
+
+
+ POST /api/pcap/upload + +
+ +
+ +
+
+ options: + this.setState({uploadFlushAll: v})}/> +
+ +
+ + +
+
+ +
+
+ POST /api/pcap/file + +
+ +
+ + +
+
+ this.setState({processFlushAll: v})}/> + this.setState({deleteOriginalFile: v})}/> +
+ +
+ + +
+
+
+
+ ); + } +} + +export default PcapsPane; -- cgit v1.2.3-70-g09d2 From c745263e1b28e4cedffa88de764f11d6379d745d Mon Sep 17 00:00:00 2001 From: Emiliano Ciavatta Date: Thu, 15 Oct 2020 13:50:37 +0200 Subject: Update rules filters --- frontend/src/components/Timeline.js | 77 ++++++------- frontend/src/components/dialogs/Filters.js | 2 +- frontend/src/components/fields/TagField.js | 29 +++-- frontend/src/components/fields/TagField.scss | 99 +++++++++++------ frontend/src/components/fields/common.scss | 1 + .../components/filters/RulesConnectionsFilter.js | 23 +--- .../components/filters/RulesConnectionsFilter.scss | 121 --------------------- frontend/src/components/objects/MessageAction.js | 2 +- frontend/src/components/panels/PcapsPane.js | 6 +- frontend/src/components/panels/SearchPane.js | 14 +-- frontend/src/components/panels/StreamsPane.js | 2 +- statistics_controller.go | 5 +- 12 files changed, 139 insertions(+), 242 deletions(-) delete mode 100644 frontend/src/components/filters/RulesConnectionsFilter.scss (limited to 'frontend/src/components/panels/PcapsPane.js') diff --git a/frontend/src/components/Timeline.js b/frontend/src/components/Timeline.js index bc42a01..1d88bcb 100644 --- a/frontend/src/components/Timeline.js +++ b/frontend/src/components/Timeline.js @@ -35,7 +35,6 @@ import log from "../log"; import dispatcher from "../dispatcher"; const minutes = 60 * 1000; -const _ = require('lodash'); const classNames = require('classnames'); const leftSelectionPaddingMultiplier = 24; @@ -54,19 +53,26 @@ class Timeline extends Component { this.selectionTimeout = null; } - additionalFilters = () => { + componentDidMount() { const urlParams = new URLSearchParams(this.props.location.search); - if (this.state.metric === "matched_rules") { - return urlParams.getAll("matched_rules") || []; - } else { - return urlParams.get("service_port"); - } - }; + this.setState({ + servicePortFilter: urlParams.get("service_port") || null, + matchedRulesFilter: urlParams.getAll("matched_rules") || null + }); - componentDidMount() { - const additionalFilters = this.additionalFilters(); - this.setState({filters: additionalFilters}); - this.loadStatistics(this.state.metric, additionalFilters).then(() => log.debug("Statistics loaded after mount")); + this.loadStatistics(this.state.metric).then(() => log.debug("Statistics loaded after mount")); + + this.connectionsFiltersCallback = payload => { + if ("service_port" in payload && this.state.servicePortFilter !== payload["service_port"]) { + this.setState({servicePortFilter: payload["service_port"]}); + this.loadStatistics(this.state.metric).then(() => log.debug("Statistics reloaded after service port changed")); + } + if ("matched_rules" in payload && this.state.matchedRulesFilter !== payload["matched_rules"]) { + this.setState({matchedRulesFilter: payload["matched_rules"]}); + this.loadStatistics(this.state.metric).then(() => log.debug("Statistics reloaded after matched rules changed")); + } + }; + dispatcher.register("connections_filters", this.connectionsFiltersCallback); dispatcher.register("connection_updates", payload => { this.setState({ @@ -76,8 +82,10 @@ class Timeline extends Component { }); dispatcher.register("notifications", payload => { - if (payload.event === "services.edit") { - this.loadServices().then(() => this.adjustSelection()); + if (payload.event === "services.edit" && this.state.metric !== "matched_rules") { + this.loadServices().then(() => log.debug("Statistics reloaded after services updates")); + } else if (payload.event.startsWith("rules") && this.state.metric === "matched_rules") { + this.loadServices().then(() => log.debug("Statistics reloaded after rules updates")); } }); @@ -87,41 +95,29 @@ class Timeline extends Component { }); } - componentDidUpdate(prevProps, prevState, snapshot) { - const additionalFilters = this.additionalFilters(); - const updateStatistics = () => { - this.setState({filters: additionalFilters}); - this.loadStatistics(this.state.metric, additionalFilters).then(() => - log.debug("Statistics reloaded after filters changes")); - }; - - if (this.state.metric === "matched_rules") { - if (!Array.isArray(this.state.filters) || - !_.isEqual(_.sortBy(additionalFilters), _.sortBy(this.state.filters))) { - updateStatistics(); - } - } else { - if (this.state.filters !== additionalFilters) { - updateStatistics(); - } - } + componentWillUnmount() { + dispatcher.unregister(this.connectionsFiltersCallback); } - loadStatistics = async (metric, filters) => { + loadStatistics = async (metric) => { const urlParams = new URLSearchParams(); urlParams.set("metric", metric); let columns = []; if (metric === "matched_rules") { let rules = await this.loadRules(); - filters.forEach(id => { - urlParams.append("matched_rules", id); - }); - columns = rules.map(r => r.id); + if (this.state.matchedRulesFilter.length > 0) { + this.state.matchedRulesFilter.forEach(id => { + urlParams.append("rules_ids", id); + }); + columns = this.state.matchedRulesFilter; + } else { + columns = rules.map(r => r.id); + } } else { let services = await this.loadServices(); - const filteredPort = filters; - if (filteredPort && services[filters]) { + const filteredPort = this.state.servicePortFilter; + if (filteredPort && services[filteredPort]) { const service = services[filteredPort]; services = {}; services[filteredPort] = service; @@ -172,7 +168,6 @@ class Timeline extends Component { start, end }); - log.debug(`Loaded statistics for metric "${metric}"`); }; loadServices = async () => { @@ -279,7 +274,7 @@ class Timeline extends Component { "server_bytes_per_service", "duration_per_service", "matched_rules"]} values={["connections_per_service", "client_bytes_per_service", "server_bytes_per_service", "duration_per_service", "matched_rules"]} - onChange={(metric) => this.loadStatistics(metric, this.state.filters) + onChange={(metric) => this.loadStatistics(metric) .then(() => log.debug("Statistics loaded after metric changes"))} value={this.state.metric}/> diff --git a/frontend/src/components/dialogs/Filters.js b/frontend/src/components/dialogs/Filters.js index d2cce4f..a35ece2 100644 --- a/frontend/src/components/dialogs/Filters.js +++ b/frontend/src/components/dialogs/Filters.js @@ -16,7 +16,7 @@ */ import React, {Component} from 'react'; -import {Col, Container, Modal, Row} from "react-bootstrap"; +import {Modal} from "react-bootstrap"; import ButtonField from "../fields/ButtonField"; import './Filters.scss'; import {cleanNumber, validateIpAddress, validateMin, validatePort} from "../../utils"; diff --git a/frontend/src/components/fields/TagField.js b/frontend/src/components/fields/TagField.js index f1a48bd..89445b6 100644 --- a/frontend/src/components/fields/TagField.js +++ b/frontend/src/components/fields/TagField.js @@ -26,50 +26,47 @@ const _ = require('lodash'); class TagField extends Component { + state = {}; + constructor(props) { super(props); this.id = `field-${this.props.name || "noname"}-${randomClassName()}`; } - state = { - - }; - onAddition = (tag) => { if (typeof this.props.onChange === "function") { - const tags = [].concat(this.wrappedTags(), tag); - this.props.onChange(tags.map(t => t.name), true, tag); // true == addition + this.props.onChange([].concat(this.props.tags, tag), true, tag); // true == addition } }; onDelete = (i) => { if (typeof this.props.onChange === "function") { - const tags = this.wrappedTags(); + const tags = _.clone(this.props.tags); const tag = tags[i]; tags.splice(i, 1); - this.props.onChange(tags.map(t => t.name), true, tag); // false == delete + this.props.onChange(tags, true, tag); // false == delete } }; - wrappedTags = () => this.props.tags.map(t => new Object({"name": t})); render() { const small = this.props.small || false; const name = this.props.name || null; return ( -
- { name && +
+ {name &&
} - +
+ +
); } diff --git a/frontend/src/components/fields/TagField.scss b/frontend/src/components/fields/TagField.scss index e77db97..737f11f 100644 --- a/frontend/src/components/fields/TagField.scss +++ b/frontend/src/components/fields/TagField.scss @@ -1,31 +1,68 @@ @import "../../colors.scss"; .tag-field { + font-size: 0.9em; + margin: 5px 0; + + .field-name { + label { + margin: 0; + } + } + + &.field-small { + font-size: 0.8em; + } + + &.field-inline { + display: flex; + + .field-name { + padding: 6px 0 6px 7px; + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; + background-color: $color-primary-2; + } + + .field-input { + flex: 1; + + .react-tags { + padding-left: 3px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + } + + &:focus-within .field-name { + background-color: $color-primary-1; + } + } + .react-tags { - font-size: 12px; position: relative; - z-index: 10; - padding: 0 6px; - cursor: text; + display: flex; border-radius: 4px; background-color: $color-primary-2; - } - .react-tags.is-focused { - border-color: #b1b1b1; + &:focus-within, + &:focus-within .react-tags__search-input { + background-color: $color-primary-1; + } } .react-tags__selected { - display: inline; + display: inline-block; + flex: 0 1; + margin: 6px 0; + white-space: nowrap; } .react-tags__selected-tag { - font-size: 11px; - display: inline-block; - margin: 0 6px 6px 0; + font-size: 0.75em; + margin: 0 3px; padding: 2px 4px; color: $color-primary-3; - border: none; border-radius: 2px; background: $color-primary-4; } @@ -39,12 +76,15 @@ .react-tags__selected-tag:hover, .react-tags__selected-tag:focus { border-color: #b1b1b1; + background-color: $color-primary-0; + + &::after { + color: $color-primary-4; + } } .react-tags__search { - display: inline-block; - max-width: 100%; - padding: 7px 10px; + flex: 1 0; } @media screen and (min-width: 30em) { @@ -54,14 +94,7 @@ } .react-tags__search-input { - font-size: inherit; - line-height: inherit; - max-width: 100%; - margin: 0; - padding: 0; color: $color-primary-4; - border: 0; - outline: none; background-color: $color-primary-2; } @@ -71,6 +104,7 @@ .react-tags__suggestions { position: absolute; + z-index: 50; top: 100%; left: 0; width: 100%; @@ -87,30 +121,33 @@ margin: 4px -1px; padding: 0; list-style: none; - color: $color-primary-1; - border-radius: 2px; - background: $color-primary-4; + border-radius: 3px; + background: $color-primary-2; } .react-tags__suggestions li { - padding: 3px 5px; - border-bottom: 1px solid #ddd; + padding: 5px 10px; } .react-tags__suggestions li mark { font-weight: 600; - text-decoration: underline; + padding: 0; + color: $color-primary-4; background: none; } .react-tags__suggestions li:hover { cursor: pointer; - color: $color-primary-4; - background: $color-primary-0; + border-radius: 3px; + background: $color-primary-1; + + mark { + color: $color-primary-4; + } } .react-tags__suggestions li.is-active { - background: #b7cfe0; + background: $color-primary-3; } .react-tags__suggestions li.is-disabled { diff --git a/frontend/src/components/fields/common.scss b/frontend/src/components/fields/common.scss index 8fbef0d..e5dc65c 100644 --- a/frontend/src/components/fields/common.scss +++ b/frontend/src/components/fields/common.scss @@ -3,6 +3,7 @@ .field { input, textarea { + font-family: "Fira Code", monospace; width: 100%; padding: 7px 10px; color: $color-primary-4; diff --git a/frontend/src/components/filters/RulesConnectionsFilter.js b/frontend/src/components/filters/RulesConnectionsFilter.js index 4c993dc..8e40d30 100644 --- a/frontend/src/components/filters/RulesConnectionsFilter.js +++ b/frontend/src/components/filters/RulesConnectionsFilter.js @@ -17,10 +17,9 @@ import React, {Component} from 'react'; import {withRouter} from "react-router-dom"; -import './RulesConnectionsFilter.scss'; -import ReactTags from 'react-tag-autocomplete'; import backend from "../../backend"; import dispatcher from "../../dispatcher"; +import TagField from "../fields/TagField"; const classNames = require('classnames'); const _ = require('lodash'); @@ -59,16 +58,8 @@ class RulesConnectionsFilter extends Component { dispatcher.unregister(this.connectionsFiltersCallback); } - onDelete = (i) => { - const activeRules = _.clone(this.state.activeRules); - activeRules.splice(i, 1); - this.setState({activeRules}); - dispatcher.dispatch("connections_filters", {"matched_rules": activeRules.map(r => r.id)}); - }; - - onAddition = (rule) => { - if (!this.state.activeRules.includes(rule)) { - const activeRules = [].concat(this.state.activeRules, rule); + onChange = (activeRules) => { + if (!_.isEqual(activeRules.sort(), this.state.activeRules.sort())) { this.setState({activeRules}); dispatcher.dispatch("connections_filters", {"matched_rules": activeRules.map(r => r.id)}); } @@ -79,11 +70,9 @@ class RulesConnectionsFilter extends Component {
- - suggestion.name.startsWith(query) && !this.state.activeRules.includes(suggestion)}/> +
); diff --git a/frontend/src/components/filters/RulesConnectionsFilter.scss b/frontend/src/components/filters/RulesConnectionsFilter.scss deleted file mode 100644 index 0bb4952..0000000 --- a/frontend/src/components/filters/RulesConnectionsFilter.scss +++ /dev/null @@ -1,121 +0,0 @@ -@import "../../colors"; - -.filter-rules { - .react-tags { - font-size: 12px; - position: relative; - z-index: 10; - padding: 0 6px; - cursor: text; - border-radius: 4px; - background-color: $color-primary-2; - } - - .react-tags.is-focused { - border-color: #b1b1b1; - } - - .react-tags__selected { - display: inline; - } - - .react-tags__selected-tag { - font-size: 11px; - display: inline-block; - margin: 0 6px 6px 0; - padding: 2px 4px; - color: $color-primary-3; - border: none; - border-radius: 2px; - background: $color-primary-4; - } - - .react-tags__selected-tag::after { - margin-left: 8px; - content: "\2715"; - color: $color-primary-3; - } - - .react-tags__selected-tag:hover, - .react-tags__selected-tag:focus { - border-color: #b1b1b1; - } - - .react-tags__search { - display: inline-block; - max-width: 100%; - padding: 7px 10px; - } - - @media screen and (min-width: 30em) { - .react-tags__search { - position: relative; - } - } - - .react-tags__search-input { - font-size: inherit; - line-height: inherit; - max-width: 100%; - margin: 0; - padding: 0; - color: $color-primary-4; - border: 0; - outline: none; - background-color: $color-primary-2; - } - - .react-tags__search-input::-ms-clear { - display: none; - } - - .react-tags__suggestions { - position: absolute; - top: 100%; - left: 0; - width: 100%; - } - - @media screen and (min-width: 30em) { - .react-tags__suggestions { - width: 240px; - } - } - - .react-tags__suggestions ul { - font-size: 12px; - margin: 4px -1px; - padding: 0; - list-style: none; - color: $color-primary-1; - border-radius: 2px; - background: $color-primary-4; - } - - .react-tags__suggestions li { - padding: 3px 5px; - border-bottom: 1px solid #ddd; - } - - .react-tags__suggestions li mark { - font-weight: 600; - text-decoration: underline; - background: none; - } - - .react-tags__suggestions li:hover { - cursor: pointer; - color: $color-primary-4; - background: $color-primary-0; - } - - .react-tags__suggestions li.is-active { - background: #b7cfe0; - } - - .react-tags__suggestions li.is-disabled { - cursor: auto; - opacity: 0.5; - } - -} diff --git a/frontend/src/components/objects/MessageAction.js b/frontend/src/components/objects/MessageAction.js index 9f199b7..2b46320 100644 --- a/frontend/src/components/objects/MessageAction.js +++ b/frontend/src/components/objects/MessageAction.js @@ -43,7 +43,7 @@ class MessageAction extends Component { return ( - + {s["id"].substring(0, 8)} {dateTimeToTime(s["started_at"])} {durationBetween(s["started_at"], s["completed_at"])} @@ -166,13 +166,13 @@ class PcapsPane extends Component { }); }; - const uploadCurlCommand = createCurlCommand("pcap/upload", "POST", null, { + const uploadCurlCommand = createCurlCommand("/pcap/upload", "POST", null, { file: "@" + ((this.state.uploadSelectedFile != null && this.state.isUploadFileValid) ? this.state.uploadSelectedFile.name : "invalid.pcap"), flush_all: this.state.uploadFlushAll }); - const fileCurlCommand = createCurlCommand("pcap/file", "POST", { + const fileCurlCommand = createCurlCommand("/pcap/file", "POST", { file: this.state.fileValue, flush_all: this.state.processFlushAll, delete_original_file: this.state.deleteOriginalFile diff --git a/frontend/src/components/panels/SearchPane.js b/frontend/src/components/panels/SearchPane.js index 21ba139..1fb48ef 100644 --- a/frontend/src/components/panels/SearchPane.js +++ b/frontend/src/components/panels/SearchPane.js @@ -29,7 +29,6 @@ import dispatcher from "../../dispatcher"; import TagField from "../fields/TagField"; import CheckField from "../fields/CheckField"; -const classNames = require('classnames'); const _ = require('lodash'); class SearchPane extends Component { @@ -161,7 +160,7 @@ class SearchPane extends Component { const options = this.state.currentSearchOptions; let searches = this.state.searches.map(s => - + {s.id.substring(0, 8)} {this.extractPattern(s["search_options"])} {s["affected_connections_count"]} @@ -223,13 +222,14 @@ class SearchPane extends Component {
- {return {name: t};})} + name="terms" min={3} inline allowNew={true} readonly={regexOptionsModified || options.text_search.exact_phrase} - onChange={(tags) => this.updateParam(s => s.text_search.terms = tags)}/> - this.updateParam(s => s.text_search.terms = tags.map(t => t.name))}/> + {return {name: t};})} + name="excluded_terms" min={3} inline allowNew={true} readonly={regexOptionsModified || options.text_search.exact_phrase} - onChange={(tags) => this.updateParam(s => s.text_search.excluded_terms = tags)}/> + onChange={(tags) => this.updateParam(s => s.text_search.excluded_terms = tags.map(t => t.name))}/> or diff --git a/frontend/src/components/panels/StreamsPane.js b/frontend/src/components/panels/StreamsPane.js index 1aa5c53..be39777 100644 --- a/frontend/src/components/panels/StreamsPane.js +++ b/frontend/src/components/panels/StreamsPane.js @@ -65,7 +65,7 @@ class StreamsPane extends Component { } loadStream = (connectionId) => { - this.setState({messages: []}); + this.setState({messages: [], currentId: connectionId}); backend.get(`/api/streams/${connectionId}?format=${this.state.format}`) .then(res => this.setState({messages: res.json})); }; diff --git a/statistics_controller.go b/statistics_controller.go index fda7494..29f3fec 100644 --- a/statistics_controller.go +++ b/statistics_controller.go @@ -31,14 +31,14 @@ type StatisticRecord struct { ServerBytesPerService map[uint16]int `json:"server_bytes_per_service" bson:"server_bytes_per_service"` TotalBytesPerService map[uint16]int `json:"total_bytes_per_service" bson:"total_bytes_per_service"` DurationPerService map[uint16]int64 `json:"duration_per_service" bson:"duration_per_service"` - MatchedRules map[string]int64 `json:"matched_rules" bson:"matched_rules"` + MatchedRules map[string]int64 `json:"matched_rules" bson:"matched_rules"` } type StatisticsFilter struct { RangeFrom time.Time `form:"range_from"` RangeTo time.Time `form:"range_to"` Ports []uint16 `form:"ports"` - RulesIDs []string `form:"rules_ids"` + RulesIDs []string `form:"rules_ids"` Metric string `form:"metric"` } @@ -91,7 +91,6 @@ func (sc *StatisticsController) GetStatistics(context context.Context, filter St } } - log.Println(query) if err := query.All(&statisticRecords); err != nil { log.WithError(err).WithField("filter", filter).Error("failed to retrieve statistics") return []StatisticRecord{} -- cgit v1.2.3-70-g09d2 From 2fb8993008752063fa13f253784e9e92552e339d Mon Sep 17 00:00:00 2001 From: Emiliano Ciavatta Date: Fri, 16 Oct 2020 11:13:21 +0200 Subject: Refactor js files --- frontend/src/components/Header.js | 18 ++-- frontend/src/components/Notifications.js | 8 +- frontend/src/components/Timeline.js | 30 +++--- frontend/src/components/dialogs/Filters.js | 6 +- frontend/src/components/fields/ButtonField.js | 8 +- frontend/src/components/fields/CheckField.js | 12 +-- frontend/src/components/fields/InputField.js | 20 ++-- frontend/src/components/fields/TagField.js | 12 +-- frontend/src/components/fields/TextField.js | 10 +- .../src/components/fields/extensions/ColorField.js | 17 ++-- .../components/fields/extensions/NumericField.js | 6 +- frontend/src/components/filters/AdvancedFilters.js | 4 +- .../components/filters/BooleanConnectionsFilter.js | 6 +- .../src/components/filters/ExitSearchFilter.js | 4 +- .../components/filters/RulesConnectionsFilter.js | 6 +- .../components/filters/StringConnectionsFilter.js | 20 +--- frontend/src/components/objects/Connection.js | 12 +-- frontend/src/components/objects/LinkPopover.js | 15 +-- frontend/src/components/pages/ConfigurationPage.js | 43 ++++----- frontend/src/components/pages/MainPage.js | 20 ++-- .../src/components/pages/ServiceUnavailablePage.js | 4 +- frontend/src/components/panels/ConnectionsPane.js | 4 +- frontend/src/components/panels/MainPane.js | 12 +-- frontend/src/components/panels/PcapsPane.js | 34 +++---- frontend/src/components/panels/SearchPane.js | 106 +++++++++++---------- frontend/src/components/panels/StreamsPane.js | 18 ++-- frontend/src/dispatcher.js | 2 +- frontend/src/index.js | 20 ++-- frontend/src/notifications.js | 2 +- frontend/src/serviceWorker.js | 12 +-- frontend/src/setupProxy.js | 10 +- frontend/src/setupTests.js | 2 +- frontend/src/utils.js | 20 ++-- 33 files changed, 258 insertions(+), 265 deletions(-) (limited to 'frontend/src/components/panels/PcapsPane.js') diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index 119958b..ab16dd1 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -15,17 +15,17 @@ * along with this program. If not, see . */ -import React, {Component} from 'react'; -import Typed from 'typed.js'; -import './Header.scss'; +import React, {Component} from "react"; import {Link, withRouter} from "react-router-dom"; +import Typed from "typed.js"; +import {cleanNumber, validatePort} from "../utils"; import ButtonField from "./fields/ButtonField"; +import AdvancedFilters from "./filters/AdvancedFilters"; +import BooleanConnectionsFilter from "./filters/BooleanConnectionsFilter"; import ExitSearchFilter from "./filters/ExitSearchFilter"; -import {cleanNumber, validatePort} from "../utils"; -import StringConnectionsFilter from "./filters/StringConnectionsFilter"; import RulesConnectionsFilter from "./filters/RulesConnectionsFilter"; -import BooleanConnectionsFilter from "./filters/BooleanConnectionsFilter"; -import AdvancedFilters from "./filters/AdvancedFilters"; +import StringConnectionsFilter from "./filters/StringConnectionsFilter"; +import "./Header.scss"; class Header extends Component { @@ -49,7 +49,7 @@ class Header extends Component {

- { + { this.el = el; }}/> @@ -67,7 +67,7 @@ class Header extends Component { - +

diff --git a/frontend/src/components/Notifications.js b/frontend/src/components/Notifications.js index ad681a2..56a4508 100644 --- a/frontend/src/components/Notifications.js +++ b/frontend/src/components/Notifications.js @@ -15,12 +15,12 @@ * along with this program. If not, see . */ -import React, {Component} from 'react'; -import './Notifications.scss'; +import React, {Component} from "react"; import dispatcher from "../dispatcher"; +import "./Notifications.scss"; -const _ = require('lodash'); -const classNames = require('classnames'); +const _ = require("lodash"); +const classNames = require("classnames"); class Notifications extends Component { diff --git a/frontend/src/components/Timeline.js b/frontend/src/components/Timeline.js index 1d88bcb..8d1fd40 100644 --- a/frontend/src/components/Timeline.js +++ b/frontend/src/components/Timeline.js @@ -62,7 +62,7 @@ class Timeline extends Component { this.loadStatistics(this.state.metric).then(() => log.debug("Statistics loaded after mount")); - this.connectionsFiltersCallback = payload => { + this.connectionsFiltersCallback = (payload) => { if ("service_port" in payload && this.state.servicePortFilter !== payload["service_port"]) { this.setState({servicePortFilter: payload["service_port"]}); this.loadStatistics(this.state.metric).then(() => log.debug("Statistics reloaded after service port changed")); @@ -74,22 +74,22 @@ class Timeline extends Component { }; dispatcher.register("connections_filters", this.connectionsFiltersCallback); - dispatcher.register("connection_updates", payload => { + dispatcher.register("connection_updates", (payload) => { this.setState({ selection: new TimeRange(payload.from, payload.to), }); this.adjustSelection(); }); - dispatcher.register("notifications", payload => { + dispatcher.register("notifications", (payload) => { if (payload.event === "services.edit" && this.state.metric !== "matched_rules") { - this.loadServices().then(() => log.debug("Statistics reloaded after services updates")); + this.loadStatistics(this.state.metric).then(() => log.debug("Statistics reloaded after services updates")); } else if (payload.event.startsWith("rules") && this.state.metric === "matched_rules") { - this.loadServices().then(() => log.debug("Statistics reloaded after rules updates")); + this.loadStatistics(this.state.metric).then(() => log.debug("Statistics reloaded after rules updates")); } }); - dispatcher.register("pulse_timeline", payload => { + dispatcher.register("pulse_timeline", (payload) => { this.setState({pulseTimeline: true}); setTimeout(() => this.setState({pulseTimeline: false}), payload.duration); }); @@ -107,12 +107,12 @@ class Timeline extends Component { if (metric === "matched_rules") { let rules = await this.loadRules(); if (this.state.matchedRulesFilter.length > 0) { - this.state.matchedRulesFilter.forEach(id => { + this.state.matchedRulesFilter.forEach((id) => { urlParams.append("rules_ids", id); }); columns = this.state.matchedRulesFilter; } else { - columns = rules.map(r => r.id); + columns = rules.map((r) => r.id); } } else { let services = await this.loadServices(); @@ -124,7 +124,7 @@ class Timeline extends Component { } columns = Object.keys(services); - columns.forEach(port => urlParams.append("ports", port)); + columns.forEach((port) => urlParams.append("ports", port)); } const metrics = (await backend.get("/api/statistics?" + urlParams)).json; @@ -133,7 +133,7 @@ class Timeline extends Component { } const zeroFilledMetrics = []; - const toTime = m => new Date(m["range_start"]).getTime(); + const toTime = (m) => new Date(m["range_start"]).getTime(); let i = 0; for (let interval = toTime(metrics[0]) - minutes; interval <= toTime(metrics[metrics.length - 1]) + minutes; interval += minutes) { if (i < metrics.length && interval === toTime(metrics[i])) { @@ -144,7 +144,7 @@ class Timeline extends Component { const m = {}; m["range_start"] = new Date(interval); m[metric] = {}; - columns.forEach(c => m[metric][c] = 0); + columns.forEach((c) => m[metric][c] = 0); zeroFilledMetrics.push(m); } } @@ -152,7 +152,7 @@ class Timeline extends Component { const series = new TimeSeries({ name: "statistics", columns: ["time"].concat(columns), - points: zeroFilledMetrics.map(m => [m["range_start"]].concat(columns.map(c => + points: zeroFilledMetrics.map((m) => [m["range_start"]].concat(columns.map(c => ((metric in m) && (m[metric] != null)) ? (m[metric][c] || 0) : 0 ))) }); @@ -184,11 +184,11 @@ class Timeline extends Component { createStyler = () => { if (this.state.metric === "matched_rules") { - return styler(this.state.rules.map(rule => { + return styler(this.state.rules.map((rule) => { return {key: rule.id, color: rule.color, width: 2}; })); } else { - return styler(Object.keys(this.state.services).map(port => { + return styler(Object.keys(this.state.services).map((port) => { return {key: port, color: this.state.services[port].color, width: 2}; })); } @@ -227,7 +227,7 @@ class Timeline extends Component { }; aggregateSeries = (func) => { - const values = this.state.series.columns().map(c => this.state.series[func](c)); + const values = this.state.series.columns().map((c) => this.state.series[func](c)); return Math[func](...values); }; diff --git a/frontend/src/components/dialogs/Filters.js b/frontend/src/components/dialogs/Filters.js index a35ece2..a2407df 100644 --- a/frontend/src/components/dialogs/Filters.js +++ b/frontend/src/components/dialogs/Filters.js @@ -15,12 +15,12 @@ * along with this program. If not, see . */ -import React, {Component} from 'react'; +import React, {Component} from "react"; import {Modal} from "react-bootstrap"; -import ButtonField from "../fields/ButtonField"; -import './Filters.scss'; import {cleanNumber, validateIpAddress, validateMin, validatePort} from "../../utils"; +import ButtonField from "../fields/ButtonField"; import StringConnectionsFilter from "../filters/StringConnectionsFilter"; +import "./Filters.scss"; class Filters extends Component { diff --git a/frontend/src/components/fields/ButtonField.js b/frontend/src/components/fields/ButtonField.js index 850f837..de747a5 100644 --- a/frontend/src/components/fields/ButtonField.js +++ b/frontend/src/components/fields/ButtonField.js @@ -15,11 +15,11 @@ * along with this program. If not, see . */ -import React, {Component} from 'react'; -import './ButtonField.scss'; -import './common.scss'; +import React, {Component} from "react"; +import "./ButtonField.scss"; +import "./common.scss"; -const classNames = require('classnames'); +const classNames = require("classnames"); class ButtonField extends Component { diff --git a/frontend/src/components/fields/CheckField.js b/frontend/src/components/fields/CheckField.js index a0e2706..bfa1c9d 100644 --- a/frontend/src/components/fields/CheckField.js +++ b/frontend/src/components/fields/CheckField.js @@ -15,12 +15,12 @@ * along with this program. If not, see . */ -import React, {Component} from 'react'; -import './CheckField.scss'; -import './common.scss'; +import React, {Component} from "react"; import {randomClassName} from "../../utils"; +import "./CheckField.scss"; +import "./common.scss"; -const classNames = require('classnames'); +const classNames = require("classnames"); class CheckField extends Component { @@ -41,9 +41,9 @@ class CheckField extends Component { }; return ( -
+
- +
diff --git a/frontend/src/components/fields/InputField.js b/frontend/src/components/fields/InputField.js index 80cce3b..823989d 100644 --- a/frontend/src/components/fields/InputField.js +++ b/frontend/src/components/fields/InputField.js @@ -15,12 +15,12 @@ * along with this program. If not, see . */ -import React, {Component} from 'react'; -import './InputField.scss'; -import './common.scss'; +import React, {Component} from "react"; import {randomClassName} from "../../utils"; +import "./common.scss"; +import "./InputField.scss"; -const classNames = require('classnames'); +const classNames = require("classnames"); class InputField extends Component { @@ -59,23 +59,23 @@ class InputField extends Component { } return ( -
- { name && + {name &&
}
- { type === "file" && } + {type === "file" && } + readOnly={this.props.readonly}/>
- { type !== "file" && value !== "" && + {type !== "file" && value !== "" &&
handler(null)}>del
diff --git a/frontend/src/components/fields/TagField.js b/frontend/src/components/fields/TagField.js index 89445b6..9a36da4 100644 --- a/frontend/src/components/fields/TagField.js +++ b/frontend/src/components/fields/TagField.js @@ -15,14 +15,14 @@ * along with this program. If not, see . */ -import React, {Component} from 'react'; -import './TagField.scss'; -import './common.scss'; -import {randomClassName} from "../../utils"; +import React, {Component} from "react"; import ReactTags from "react-tag-autocomplete"; +import {randomClassName} from "../../utils"; +import "./common.scss"; +import "./TagField.scss"; -const classNames = require('classnames'); -const _ = require('lodash'); +const classNames = require("classnames"); +const _ = require("lodash"); class TagField extends Component { diff --git a/frontend/src/components/fields/TextField.js b/frontend/src/components/fields/TextField.js index 9237c0c..4dd77bd 100644 --- a/frontend/src/components/fields/TextField.js +++ b/frontend/src/components/fields/TextField.js @@ -15,12 +15,12 @@ * along with this program. If not, see . */ -import React, {Component} from 'react'; -import './TextField.scss'; -import './common.scss'; +import React, {Component} from "react"; import {randomClassName} from "../../utils"; +import "./common.scss"; +import "./TextField.scss"; -const classNames = require('classnames'); +const classNames = require("classnames"); class TextField extends Component { @@ -50,7 +50,7 @@ class TextField extends Component { {"field-invalid": this.props.invalid}, {"field-small": this.props.small})}> {name && }