From a30815021e61023f996b1450ddcd9164a6e18bef Mon Sep 17 00:00:00 2001 From: Emiliano Ciavatta Date: Thu, 8 Oct 2020 17:07:07 +0200 Subject: Add header license to all files --- frontend/src/components/fields/CheckField.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'frontend/src/components/fields/CheckField.js') diff --git a/frontend/src/components/fields/CheckField.js b/frontend/src/components/fields/CheckField.js index 33f4f83..dd44970 100644 --- a/frontend/src/components/fields/CheckField.js +++ b/frontend/src/components/fields/CheckField.js @@ -1,3 +1,20 @@ +/* + * 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 './CheckField.scss'; import './common.scss'; -- cgit v1.2.3-70-g09d2 From a44b70943ea4fce61261fc5fadf84a2a98fd2435 Mon Sep 17 00:00:00 2001 From: Emiliano Ciavatta Date: Mon, 12 Oct 2020 20:08:45 +0200 Subject: Add search pane on frontend --- connections_controller.go | 2 +- frontend/src/components/Header.js | 9 +- frontend/src/components/fields/CheckField.js | 2 +- frontend/src/components/fields/TagField.js | 78 ++++++ frontend/src/components/fields/TagField.scss | 120 +++++++++ .../src/components/filters/ExitSearchFilter.js | 52 ++++ .../src/components/filters/FiltersDispatcher.js | 57 ++++ .../components/filters/RulesConnectionsFilter.js | 2 +- .../components/filters/RulesConnectionsFilter.scss | 193 +++++++------- frontend/src/components/pages/MainPage.js | 5 + frontend/src/components/panels/SearchPane.js | 293 +++++++++++++++++++++ frontend/src/components/panels/SearchPane.scss | 51 ++++ 12 files changed, 764 insertions(+), 100 deletions(-) create mode 100644 frontend/src/components/fields/TagField.js create mode 100644 frontend/src/components/fields/TagField.scss create mode 100644 frontend/src/components/filters/ExitSearchFilter.js create mode 100644 frontend/src/components/filters/FiltersDispatcher.js create mode 100644 frontend/src/components/panels/SearchPane.js create mode 100644 frontend/src/components/panels/SearchPane.scss (limited to 'frontend/src/components/fields/CheckField.js') diff --git a/connections_controller.go b/connections_controller.go index a293a80..924cb53 100644 --- a/connections_controller.go +++ b/connections_controller.go @@ -151,7 +151,7 @@ func (cc ConnectionsController) GetConnections(c context.Context, filter Connect performedSearchID, _ := RowIDFromHex(filter.PerformedSearch) if !performedSearchID.IsZero() { performedSearch := cc.searchController.GetPerformedSearch(performedSearchID) - if !performedSearch.ID.IsZero() { + if !performedSearch.ID.IsZero() && len(performedSearch.AffectedConnections) > 0 { query = query.Filter(OrderedDocument{{"_id", UnorderedDocument{"$in": performedSearch.AffectedConnections}}}) } } diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index 4d29364..b72b532 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -21,6 +21,7 @@ import './Header.scss'; import {filtersDefinitions, filtersNames} from "./filters/FiltersDefinitions"; import {Link, withRouter} from "react-router-dom"; import ButtonField from "./fields/ButtonField"; +import ExitSearchFilter from "./filters/ExitSearchFilter"; class Header extends Component { @@ -50,7 +51,7 @@ class Header extends Component { componentWillUnmount() { this.typed.destroy(); - if (typeof window !== "undefined") { + if (typeof window) { window.removeEventListener("quick-filters", this.fetchStateFromLocalStorage); } } @@ -82,12 +83,16 @@ class Header extends Component {
{quickFilters} +
- + {/**/} + + + diff --git a/frontend/src/components/fields/CheckField.js b/frontend/src/components/fields/CheckField.js index dd44970..a0e2706 100644 --- a/frontend/src/components/fields/CheckField.js +++ b/frontend/src/components/fields/CheckField.js @@ -35,7 +35,7 @@ class CheckField extends Component { const small = this.props.small || false; const name = this.props.name || null; const handler = () => { - if (this.props.onChange) { + if (!this.props.readonly && this.props.onChange) { this.props.onChange(!checked); } }; diff --git a/frontend/src/components/fields/TagField.js b/frontend/src/components/fields/TagField.js new file mode 100644 index 0000000..f1a48bd --- /dev/null +++ b/frontend/src/components/fields/TagField.js @@ -0,0 +1,78 @@ +/* + * 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 './TagField.scss'; +import './common.scss'; +import {randomClassName} from "../../utils"; +import ReactTags from "react-tag-autocomplete"; + +const classNames = require('classnames'); +const _ = require('lodash'); + +class TagField extends Component { + + 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 + } + }; + + onDelete = (i) => { + if (typeof this.props.onChange === "function") { + const tags = this.wrappedTags(); + const tag = tags[i]; + tags.splice(i, 1); + this.props.onChange(tags.map(t => t.name), 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 && +
+ +
+ } + +
+ ); + } +} + +export default TagField; diff --git a/frontend/src/components/fields/TagField.scss b/frontend/src/components/fields/TagField.scss new file mode 100644 index 0000000..e77db97 --- /dev/null +++ b/frontend/src/components/fields/TagField.scss @@ -0,0 +1,120 @@ +@import "../../colors.scss"; + +.tag-field { + .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; + } +} \ No newline at end of file diff --git a/frontend/src/components/filters/ExitSearchFilter.js b/frontend/src/components/filters/ExitSearchFilter.js new file mode 100644 index 0000000..cfee298 --- /dev/null +++ b/frontend/src/components/filters/ExitSearchFilter.js @@ -0,0 +1,52 @@ +/* + * 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 {withRouter} from "react-router-dom"; +import CheckField from "../fields/CheckField"; +import dispatcher from "../../dispatcher"; + +class ExitSearchFilter extends Component { + + state = {}; + + componentDidMount() { + let params = new URLSearchParams(this.props.location.search); + this.setState({performedSearch: params.get("performed_search")}); + + dispatcher.register("connections_filters", payload => { + if (this.state.performedSearch !== payload["performed_search"]) { + this.setState({performedSearch: payload["performed_search"]}); + } + }); + } + + render() { + return ( + <> + {this.state.performedSearch && +
+ + dispatcher.dispatch("connections_filters", {"performed_search": null})} small/> +
} + + ); + } + +} + +export default withRouter(ExitSearchFilter); diff --git a/frontend/src/components/filters/FiltersDispatcher.js b/frontend/src/components/filters/FiltersDispatcher.js new file mode 100644 index 0000000..3769055 --- /dev/null +++ b/frontend/src/components/filters/FiltersDispatcher.js @@ -0,0 +1,57 @@ +/* + * 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 {withRouter} from "react-router-dom"; +import {Redirect} from "react-router"; +import dispatcher from "../../dispatcher"; + +class FiltersDispatcher extends Component { + + state = {}; + + componentDidMount() { + let params = new URLSearchParams(this.props.location.search); + this.setState({params}); + + dispatcher.register("connections_filters", payload => { + const params = this.state.params; + + Object.entries(payload).forEach(([key, value]) => { + if (value == null) { + params.delete(key); + } else { + params.set(key, value); + } + }); + + this.needRedirect = true; + this.setState({params}); + }); + } + + render() { + if (this.needRedirect) { + this.needRedirect = false; + return ; + } + + return null; + } +} + +export default withRouter(FiltersDispatcher); diff --git a/frontend/src/components/filters/RulesConnectionsFilter.js b/frontend/src/components/filters/RulesConnectionsFilter.js index 48affb0..fc0ad4d 100644 --- a/frontend/src/components/filters/RulesConnectionsFilter.js +++ b/frontend/src/components/filters/RulesConnectionsFilter.js @@ -89,7 +89,7 @@ class RulesConnectionsFilter extends Component { return (
-
+
+ }/> }/> }/> }/> @@ -67,6 +70,8 @@ class MainPage extends Component {
+ +
); diff --git a/frontend/src/components/panels/SearchPane.js b/frontend/src/components/panels/SearchPane.js new file mode 100644 index 0000000..21ba139 --- /dev/null +++ b/frontend/src/components/panels/SearchPane.js @@ -0,0 +1,293 @@ +/* + * 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 './common.scss'; +import './SearchPane.scss'; +import Table from "react-bootstrap/Table"; +import InputField from "../fields/InputField"; +import TextField from "../fields/TextField"; +import backend from "../../backend"; +import ButtonField from "../fields/ButtonField"; +import LinkPopover from "../objects/LinkPopover"; +import {createCurlCommand, dateTimeToTime, durationBetween} from "../../utils"; +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 { + + searchOptions = { + "text_search": { + "terms": null, + "excluded_terms": null, + "exact_phrase": "", + "case_sensitive": false + }, + "regex_search": { + "pattern": "", + "not_pattern": "", + "case_insensitive": false, + "multi_line": false, + "ignore_whitespaces": false, + "dot_character": false + }, + "timeout": 10 + }; + + state = { + searches: [], + currentSearchOptions: this.searchOptions, + }; + + componentDidMount() { + this.reset(); + this.loadSearches(); + + dispatcher.register("notifications", payload => { + if (payload.event === "searches.new") { + this.loadSearches(); + } + }); + + document.title = "caronte:~/searches$"; + } + + loadSearches = () => { + backend.get("/api/searches") + .then(res => this.setState({searches: res.json, searchesStatusCode: res.status})) + .catch(res => this.setState({searchesStatusCode: res.status, searchesResponse: JSON.stringify(res.json)})); + }; + + performSearch = () => { + const options = this.state.currentSearchOptions; + if (this.validateSearch(options)) { + backend.post("/api/searches/perform", options).then(res => { + this.reset(); + this.setState({searchStatusCode: res.status}); + this.loadSearches(); + this.viewSearch(res.json.id); + }).catch(res => { + this.setState({searchStatusCode: res.status, searchResponse: JSON.stringify(res.json)}); + }); + } + }; + + reset = () => { + this.setState({ + currentSearchOptions: _.cloneDeep(this.searchOptions), + exactPhraseError: null, + patternError: null, + notPatternError: null, + searchStatusCode: null, + searchesStatusCode: null, + searchResponse: null, + searchesResponse: null + }); + }; + + validateSearch = (options) => { + let valid = true; + if (options.text_search.exact_phrase && options.text_search.exact_phrase.length < 3) { + this.setState({exactPhraseError: "text_search.exact_phrase.length < 3"}); + valid = false; + } + if (options.regex_search.pattern && options.regex_search.pattern.length < 3) { + this.setState({patternError: "regex_search.pattern.length < 3"}); + valid = false; + } + if (options.regex_search.not_pattern && options.regex_search.not_pattern.length < 3) { + this.setState({exactPhraseError: "regex_search.not_pattern.length < 3"}); + valid = false; + } + + return valid; + }; + + updateParam = (callback) => { + callback(this.state.currentSearchOptions); + this.setState({currentSearchOptions: this.state.currentSearchOptions}); + }; + + extractPattern = (options) => { + let pattern = ""; + if (_.isEqual(options.regex_search, this.searchOptions.regex_search)) { // is text search + if (options.text_search.exact_phrase) { + pattern += `"${options.text_search.exact_phrase}"`; + } else { + pattern += options.text_search.terms.join(" "); + if (options.text_search.excluded_terms) { + pattern += " -" + options.text_search.excluded_terms.join(" -"); + } + } + options.text_search.case_sensitive && (pattern += "/s"); + } else { // is regex search + if (options.regex_search.pattern) { + pattern += "/" + options.regex_search.pattern + "/"; + } else { + pattern += "!/" + options.regex_search.not_pattern + "/"; + } + options.regex_search.case_insensitive && (pattern += "i"); + options.regex_search.multi_line && (pattern += "m"); + options.regex_search.ignore_whitespaces && (pattern += "x"); + options.regex_search.dot_character && (pattern += "s"); + } + + return pattern; + }; + + viewSearch = (searchId) => { + dispatcher.dispatch("connections_filters", {"performed_search": searchId}); + }; + + render() { + 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"]} + {dateTimeToTime(s["started_at"])} + {durationBetween(s["started_at"], s["finished_at"])} + this.viewSearch(s.id)}/> + + ); + + const textOptionsModified = !_.isEqual(this.searchOptions.text_search, options.text_search); + const regexOptionsModified = !_.isEqual(this.searchOptions.regex_search, options.regex_search); + + const curlCommand = createCurlCommand("/searches/perform", "POST", options); + + return ( +
+
+
+ GET /api/searches + {this.state.searchesStatusCode && + } +
+ +
+
+ + + + + + + + + + + + + {searches} + +
idpatternoccurrencesstarted_atdurationactions
+
+
+
+ +
+
+ POST /api/searches/perform + +
+ +
+ + NOTE: it is recommended to use the rules for recurring themes. Give preference to textual search over that with regex. + + +
+
+ this.updateParam(s => s.text_search.terms = tags)}/> + this.updateParam(s => s.text_search.excluded_terms = tags)}/> + + or + + this.updateParam(s => s.text_search.exact_phrase = v)} + readonly={regexOptionsModified || (Array.isArray(options.text_search.terms) && options.text_search.terms.length > 0)}/> + + this.updateParam(s => s.text_search.case_sensitive = v)}/> +
+ +
+ or +
+ +
+ this.updateParam(s => s.regex_search.pattern = v)}/> + or + this.updateParam(s => s.regex_search.not_pattern = v)}/> + +
+ this.updateParam(s => s.regex_search.case_insensitive = v)}/> + this.updateParam(s => s.regex_search.multi_line = v)}/> + this.updateParam(s => s.regex_search.ignore_whitespaces = v)}/> + this.updateParam(s => s.regex_search.dot_character = v)}/> +
+
+
+ + +
+ +
+ + +
+
+
+ ); + } + +} + +export default SearchPane; diff --git a/frontend/src/components/panels/SearchPane.scss b/frontend/src/components/panels/SearchPane.scss new file mode 100644 index 0000000..15fc7da --- /dev/null +++ b/frontend/src/components/panels/SearchPane.scss @@ -0,0 +1,51 @@ +.search-pane { + display: flex; + flex-direction: column; + + .searches-list { + overflow: hidden; + + .section-content { + height: 100%; + } + + .section-table { + height: calc(100% - 30px); + } + } + + .search-new { + .content-row { + display: flex; + + .text-search, + .regex-search { + flex: 1; + } + + .exclusive-separator { + font-size: 0.8em; + display: block; + text-align: center; + } + + .separator { + font-size: 0.9em; + flex: 0; + margin: auto 10px; + } + } + + .notes { + font-size: 0.8em; + } + + .checkbox-line { + .check-field { + display: inline-block; + margin-top: 0; + margin-right: 10px; + } + } + } +} -- 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/fields/CheckField.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 && }