From fdf53489eced715e1586f582882cb064721a965e Mon Sep 17 00:00:00 2001 From: therealbobo Date: Sun, 13 Sep 2020 12:07:11 +0200 Subject: service creation/edit fix --- frontend/src/views/Services.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'frontend/src/views/Services.js') diff --git a/frontend/src/views/Services.js b/frontend/src/views/Services.js index b95b01c..9f0fbd2 100644 --- a/frontend/src/views/Services.js +++ b/frontend/src/views/Services.js @@ -64,12 +64,33 @@ class Services extends Component { saveService() { if (this.state.portValid && this.state.nameValid) { + const requestOptions = { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + color: this.state.color, + name: this.state.name, + notes: this.state.notes, + port: this.state.port, + }) + }; + + + fetch('/api/services', requestOptions) + .then(function(response){ + console.log(response); + } + ); + + /* axios.put("/api/services", { name: this.state.name, port: this.state.port, color: this.state.color, notes: this.state.notes }); + */ + this.newService(); this.loadServices(); -- cgit v1.2.3-70-g09d2 From b5f3cd7accf3699fd7ed3053d671a4c254dfeaa3 Mon Sep 17 00:00:00 2001 From: therealbobo Date: Mon, 14 Sep 2020 09:45:18 +0200 Subject: auto startup configuration --- frontend/src/views/App.js | 9 +++++++++ frontend/src/views/Config.js | 9 +++++---- frontend/src/views/Services.js | 10 ---------- 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'frontend/src/views/Services.js') diff --git a/frontend/src/views/App.js b/frontend/src/views/App.js index 229eaed..5b49045 100644 --- a/frontend/src/views/App.js +++ b/frontend/src/views/App.js @@ -18,6 +18,15 @@ class App extends Component { rulesWindowOpen: false, configWindowOpen: false }; + + fetch('/api/services') + .then(response => { + if( response.status === 503){ + this.setState({configWindowOpen: true}); + } + }); + + } render() { diff --git a/frontend/src/views/Config.js b/frontend/src/views/Config.js index 438e2d6..9b220d9 100644 --- a/frontend/src/views/Config.js +++ b/frontend/src/views/Config.js @@ -4,7 +4,6 @@ import { import React, {Component, useState} from 'react'; import './Config.scss'; import {Button, ButtonGroup, ToggleButton, Col, Container, Form, FormControl, InputGroup, Modal, Row, Table} from "react-bootstrap"; -import axios from 'axios' import {createCurlCommand} from '../utils'; class Config extends Component { @@ -74,9 +73,11 @@ class Config extends Component { fetch('/setup', requestOptions) - .then(function(response){ - console.log(response.status); - console.log(response); + .then(response => { + if (response.status === 202 ){ + //this.setState({showConfig:false}); + this.props.onHide(); + } } ); diff --git a/frontend/src/views/Services.js b/frontend/src/views/Services.js index 9f0fbd2..0de021f 100644 --- a/frontend/src/views/Services.js +++ b/frontend/src/views/Services.js @@ -82,16 +82,6 @@ class Services extends Component { } ); - /* - axios.put("/api/services", { - name: this.state.name, - port: this.state.port, - color: this.state.color, - notes: this.state.notes - }); - */ - - this.newService(); this.loadServices(); } -- cgit v1.2.3-70-g09d2 From 8d07bfe5f17534b7301a064aeaf8ed8071f10a40 Mon Sep 17 00:00:00 2001 From: Emiliano Ciavatta Date: Wed, 23 Sep 2020 20:19:09 +0200 Subject: Frontend refactor: checkpoint --- .travis.yml | 1 - frontend/src/backend.js | 36 ++++++ frontend/src/colors.scss | 32 +++++- frontend/src/components/Connection.js | 27 ++--- frontend/src/components/ConnectionContent.js | 6 +- frontend/src/components/fields/StringField.js | 31 ++++++ frontend/src/components/fields/StringField.scss | 66 +++++++++++ .../src/components/filters/FiltersDefinitions.js | 33 ++++-- .../components/filters/RulesConnectionsFilter.js | 6 +- frontend/src/components/panels/PcapPane.js | 121 +++++++++++++++++++++ frontend/src/components/panels/PcapPane.scss | 55 ++++++++++ frontend/src/index.scss | 58 +++++++++- frontend/src/utils.js | 18 +++ frontend/src/views/Config.js | 8 +- frontend/src/views/Connections.js | 23 ++-- frontend/src/views/Footer.js | 4 +- frontend/src/views/Header.js | 12 +- frontend/src/views/Header.scss | 4 + frontend/src/views/MainPane.js | 8 +- frontend/src/views/Rules.js | 4 +- frontend/src/views/Services.js | 33 ++---- 21 files changed, 489 insertions(+), 97 deletions(-) create mode 100644 frontend/src/backend.js create mode 100644 frontend/src/components/fields/StringField.js create mode 100644 frontend/src/components/fields/StringField.scss create mode 100644 frontend/src/components/panels/PcapPane.js create mode 100644 frontend/src/components/panels/PcapPane.scss (limited to 'frontend/src/views/Services.js') diff --git a/.travis.yml b/.travis.yml index 1d66f6e..b59b12f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ services: - docker before_script: - - docker pull eciavatta/caronte-env - docker pull mongo:4 - docker-compose -f docker-compose.testing.yml up -d --build diff --git a/frontend/src/backend.js b/frontend/src/backend.js new file mode 100644 index 0000000..35ae6e3 --- /dev/null +++ b/frontend/src/backend.js @@ -0,0 +1,36 @@ + +async function request(method, url, data) { + const options = { + method: method, + mode: "cors", + cache: "no-cache", + credentials: "same-origin", + headers: { + "Content-Type": "application/json" + }, + redirect: "follow", + referrerPolicy: "no-referrer", + }; + if (data != null) { + options.body = JSON.stringify(data); + } + const result = await fetch(url, options); + return result.json(); +} + +const backend = { + get: (url = "") => { + return request("GET", url, null); + }, + post: (url = "", data = null) => { + return request("POST", url, data); + }, + put: (url = "", data = null) => { + return request("PUT", url, data); + }, + delete: (url = "", data = null) => { + return request("DELETE", url, data); + } +}; + +export default backend; diff --git a/frontend/src/colors.scss b/frontend/src/colors.scss index 064f9f5..d982f03 100644 --- a/frontend/src/colors.scss +++ b/frontend/src/colors.scss @@ -10,10 +10,30 @@ $color-secondary-2: #df3030; $color-secondary-3: #ff9d9d; $color-secondary-4: #ffdfdf; -$color-blue: #247085; -$color-blue-light: #a5b8be; -$color-blue-dark: #013b4c; +$color-red: #E53935; +$color-red-light: #FFEBEE; +$color-red-dark: #B71C1C; -$color-green: #25965d; -$color-green-light: #cde4d8; -$color-green-dark: #004321; +$color-pink: #D81B60; +$color-pink-light: #FCE4EC; +$color-pink-dark: #880E4F; + +$color-purple: #8E24AA; +$color-purple-light: #F3E5F5; +$color-purple-dark: #4A148C; + +$color-deep-purple: #5E35B1; +$color-deep-purple-light: #EDE7F6; +$color-deep-purple-dark: #311B92; + +$color-indigo: #3949AB; +$color-indigo-light: #E8EAF6; +$color-indigo-dark: #1A237E; + +$color-green: #43A047; +$color-green-light: #E8F5E9; +$color-green-dark: #1B5E20; + +$color-blue: #1E88E5; +$color-blue-light: #E3F2FD; +$color-blue-dark: #0D47A1; diff --git a/frontend/src/components/Connection.js b/frontend/src/components/Connection.js index 93c6438..1149584 100644 --- a/frontend/src/components/Connection.js +++ b/frontend/src/components/Connection.js @@ -1,7 +1,8 @@ import React, {Component} from 'react'; import './Connection.scss'; -import axios from 'axios' import {Button, Form, OverlayTrigger, Popover} from "react-bootstrap"; +import backend from "../backend"; +import {formatSize} from "../utils"; class Connection extends Component { @@ -19,22 +20,18 @@ class Connection extends Component { handleAction(name) { if (name === "hide") { const enabled = !this.props.data.hidden; - axios.post(`/api/connections/${this.props.data.id}/${enabled ? "hide" : "show"}`) - .then(res => { - if (res.status === 202) { - this.props.onEnabled(!enabled); - this.setState({update: true}); - } + backend.post(`/api/connections/${this.props.data.id}/${enabled ? "hide" : "show"}`) + .then(_ => { + this.props.onEnabled(!enabled); + this.setState({update: true}); }); } if (name === "mark") { const marked = this.props.data.marked; - axios.post(`/api/connections/${this.props.data.id}/${marked ? "unmark" : "mark"}`) - .then(res => { - if (res.status === 202) { - this.props.onMarked(!marked); - this.setState({update: true}); - } + backend.post(`/api/connections/${this.props.data.id}/${marked ? "unmark" : "mark"}`) + .then(_ => { + this.props.onMarked(!marked); + this.setState({update: true}); }); } if (name === "copy") { @@ -114,8 +111,8 @@ class Connection extends Component { {duration} - {conn.client_bytes} - {conn.server_bytes} + {formatSize(conn.client_bytes)} + {formatSize(conn.server_bytes)} {/*Hide this connection from the list)}>*/} diff --git a/frontend/src/components/ConnectionContent.js b/frontend/src/components/ConnectionContent.js index 20ec92b..0c00e8e 100644 --- a/frontend/src/components/ConnectionContent.js +++ b/frontend/src/components/ConnectionContent.js @@ -1,8 +1,8 @@ import React, {Component} from 'react'; import './ConnectionContent.scss'; import {Button, Dropdown, Row} from 'react-bootstrap'; -import axios from 'axios'; import MessageAction from "./MessageAction"; +import backend from "../backend"; const classNames = require('classnames'); @@ -27,9 +27,9 @@ class ConnectionContent extends Component { this.props.connection !== prevProps.connection || this.state.format !== prevState.format)) { this.setState({loading: true}); // TODO: limit workaround. - axios.get(`/api/streams/${this.props.connection.id}?format=${this.state.format}&limit=999999`).then(res => { + backend.get(`/api/streams/${this.props.connection.id}?format=${this.state.format}&limit=999999`).then(res => { this.setState({ - connectionContent: res.data, + connectionContent: res, loading: false }); }); diff --git a/frontend/src/components/fields/StringField.js b/frontend/src/components/fields/StringField.js new file mode 100644 index 0000000..09fe24d --- /dev/null +++ b/frontend/src/components/fields/StringField.js @@ -0,0 +1,31 @@ +import React, {Component} from 'react'; +import './StringField.scss'; + +const classNames = require('classnames'); + +class StringField extends Component { + + render() { + return ( +
+
+
+ {this.props.name}: +
+ +
+ + { this.props.active && +
+ this.props.onValueChanged("")}>del +
+ } +
+ ); + } +} + +export default StringField; diff --git a/frontend/src/components/fields/StringField.scss b/frontend/src/components/fields/StringField.scss new file mode 100644 index 0000000..7efac56 --- /dev/null +++ b/frontend/src/components/fields/StringField.scss @@ -0,0 +1,66 @@ +@import '../../colors.scss'; + +.field { + margin: 0 10px; + position: relative; + + .field-name-wrapper { + background-color: $color-primary-2; + padding: 3px 7px; + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; + } + + .field-name { + font-size: 13px; + } + + .field-value { + font-size: 13px; + padding-left: 0; + border-radius: 5px; + + &:focus { + background-color: $color-primary-2; + } + } + + &.field-active { + .field-name-wrapper { + background-color: $color-primary-4; + color: $color-primary-3; + } + + .field-value { + background-color: $color-primary-4; + color: $color-primary-3; + } + } + + &.field-invalid { + .field-name-wrapper { + background-color: $color-secondary-2; + color: $color-primary-4; + } + + .field-value { + background-color: $color-secondary-2; + color: $color-primary-4; + } + } + + .field-delete { + position: absolute; + right: 10px; + top: 10px; + z-index: 10; + font-size: 11px; + letter-spacing: -0.5px; + color: $color-primary-2; + cursor: pointer; + + .field-delete-icon { + font-weight: 800; + } + } +} diff --git a/frontend/src/components/filters/FiltersDefinitions.js b/frontend/src/components/filters/FiltersDefinitions.js index a582d02..d36792e 100644 --- a/frontend/src/components/filters/FiltersDefinitions.js +++ b/frontend/src/components/filters/FiltersDefinitions.js @@ -21,49 +21,60 @@ export const filtersDefinitions = { service_port: , + validateFunc={validatePort} + key="service_port_filter" />, matched_rules: , client_address: , + validateFunc={validateIpAddress} + key="client_address_filter" />, client_port: , + validateFunc={validatePort} + key="client_port_filter" />, min_duration: , + validateFunc={validateMin(0)} + key="min_duration_filter" />, max_duration: , + replaceFunc={cleanNumber} + key="max_duration_filter" />, min_bytes: , + validateFunc={validateMin(0)} + key="min_bytes_filter" />, max_bytes: , + replaceFunc={cleanNumber} + key="max_bytes_filter" />, started_after: , + decodeFunc={timestampToTime} + key="started_after_filter" />, started_before: , + decodeFunc={timestampToTime} + key="started_before_filter" />, closed_after: , + decodeFunc={timestampToTime} + key="closed_after_filter" />, closed_before: , + decodeFunc={timestampToTime} + key="closed_before_filter" />, marked: , hidden: }; diff --git a/frontend/src/components/filters/RulesConnectionsFilter.js b/frontend/src/components/filters/RulesConnectionsFilter.js index 358085f..621b6d6 100644 --- a/frontend/src/components/filters/RulesConnectionsFilter.js +++ b/frontend/src/components/filters/RulesConnectionsFilter.js @@ -3,7 +3,7 @@ import {withRouter} from "react-router-dom"; import {Redirect} from "react-router"; import './RulesConnectionsFilter.scss'; import ReactTags from 'react-tag-autocomplete'; -import axios from 'axios'; +import backend from "../../backend"; const classNames = require('classnames'); @@ -24,8 +24,8 @@ class RulesConnectionsFilter extends Component { let params = new URLSearchParams(this.props.location.search); let activeRules = params.getAll("matched_rules") || []; - axios.get("/api/rules").then(res => { - let rules = res.data.flatMap(rule => rule.enabled ? [{id: rule.id, name: rule.name}] : []); + backend.get("/api/rules").then(res => { + let rules = res.flatMap(rule => rule.enabled ? [{id: rule.id, name: rule.name}] : []); activeRules = rules.filter(rule => activeRules.some(id => rule.id === id)); this.setState({rules, activeRules, mounted: true}); }); diff --git a/frontend/src/components/panels/PcapPane.js b/frontend/src/components/panels/PcapPane.js new file mode 100644 index 0000000..817c7b5 --- /dev/null +++ b/frontend/src/components/panels/PcapPane.js @@ -0,0 +1,121 @@ +import React, {Component} from 'react'; +import './PcapPane.scss'; +import Table from "react-bootstrap/Table"; +import backend from "../../backend"; +import {formatSize, timestampToTime2} from "../../utils"; +import {Container, Row, Col, Form} from "react-bootstrap"; +import StringField from "../fields/StringField"; + +class PcapPane extends Component { + + constructor(props) { + super(props); + + this.state = { + sessions: [], + }; + + this.loadSessions = this.loadSessions.bind(this); + } + + componentDidMount() { + this.loadSessions(); + } + + loadSessions() { + backend.get("/api/pcap/sessions").then(res => this.setState({sessions: res})); + } + + render() { + let sessions = this.state.sessions.map(s => + + {s["id"].substring(0, 8)} + {timestampToTime2(s["started_at"])} + {((new Date(s["completed_at"]) - new Date(s["started_at"])) / 1000).toFixed(3)}s + {formatSize(s["size"])} + {s["processed_packets"]} + {s["invalid_packets"]} + undefined + download + + ); + + return ( +
+
+
+ GET /api/pcap/sessions + 200 OK +
+ +
+ + + + + + + + + + + + + + + {sessions} + +
idstarted_atdurationsizeprocessed_packetsinvalid_packetspackets_per_serviceactions
+
+
+ +
+ + + +
+ POST /api/pcap/upload + +
+ +
+ + + +



+ + +
+ + + +
+ POST /api/pcap/file + +
+ +
+ +
+ +
+
+ + + + +
+ +
+ + ); + } +} + +export default PcapPane; diff --git a/frontend/src/components/panels/PcapPane.scss b/frontend/src/components/panels/PcapPane.scss new file mode 100644 index 0000000..3df87f5 --- /dev/null +++ b/frontend/src/components/panels/PcapPane.scss @@ -0,0 +1,55 @@ +@import '../../colors.scss'; + +.pane-container { + background-color: $color-primary-3; + padding: 10px 10px 0; + height: 100%; + + .section-header { + background-color: $color-primary-2; + padding: 5px 10px; + height: 31px; + + font-weight: 500; + font-size: 14px; + + .api-response { + float: right; + } + } + + .section-table { + margin-top: 10px; + + .table-row { + background-color: $color-primary-0; + border-top: 3px solid $color-primary-3; + border-bottom: 3px solid $color-primary-3; + } + + .table-cell-action { + font-size: 13px; + font-weight: 600; + } + } + + .section-content { + background-color: $color-primary-0; + padding: 10px; + } + + + + th { + background-color: $color-primary-2; + border-top: 3px solid $color-primary-3; + border-bottom: 3px solid $color-primary-3; + font-size: 13.5px; + position: sticky; + top: 10px; + padding: 5px; + } + + + +} \ No newline at end of file diff --git a/frontend/src/index.scss b/frontend/src/index.scss index 53ce4dd..358fd70 100644 --- a/frontend/src/index.scss +++ b/frontend/src/index.scss @@ -49,14 +49,62 @@ pre { } .btn-red { - color: $color-secondary-4; - background-color: $color-secondary-2; - border-bottom: 5px solid $color-secondary-1; + color: $color-red-light; + background-color: $color-red; + border-bottom: 5px solid $color-red-dark; &:hover, &:active { - color: $color-secondary-4; - background-color: $color-secondary-1; + color: $color-red-light; + background-color: $color-red-dark; + } +} + +.btn-pink { + color: $color-pink-light; + background-color: $color-pink; + border-bottom: 5px solid $color-pink-dark; + + &:hover, + &:active { + color: $color-pink-light; + background-color: $color-pink-dark; + } +} + +.btn-purple { + color: $color-purple-light; + background-color: $color-purple; + border-bottom: 5px solid $color-purple-dark; + + &:hover, + &:active { + color: $color-purple-light; + background-color: $color-purple-dark; + } +} + +.btn-deep-purple { + color: $color-deep-purple-light; + background-color: $color-deep-purple; + border-bottom: 5px solid $color-deep-purple-dark; + + &:hover, + &:active { + color: $color-deep-purple-light; + background-color: $color-deep-purple-dark; + } +} + +.btn-indigo { + color: $color-indigo-light; + background-color: $color-indigo; + border-bottom: 5px solid $color-indigo-dark; + + &:hover, + &:active { + color: $color-indigo-light; + background-color: $color-indigo-dark; } } diff --git a/frontend/src/utils.js b/frontend/src/utils.js index 7381f69..4991755 100644 --- a/frontend/src/utils.js +++ b/frontend/src/utils.js @@ -64,3 +64,21 @@ export function timestampToDateTime(timestamp) { let d = new Date(timestamp); return d.toLocaleDateString() + " " + d.toLocaleTimeString(); } + +export function timestampToTime2(timestamp) { + let d = new Date(timestamp); + let hours = d.getHours(); + let minutes = "0" + d.getMinutes(); + let seconds = "0" + d.getSeconds(); + return hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2); +} + +export function formatSize(size) { + if (size < 1000) { + return `${size}`; + } else if (size < 1000000) { + return `${(size / 1000).toFixed(1)}K`; + } else { + return `${(size / 1000000).toFixed(1)}M`; + } +} diff --git a/frontend/src/views/Config.js b/frontend/src/views/Config.js index f5766eb..a770378 100644 --- a/frontend/src/views/Config.js +++ b/frontend/src/views/Config.js @@ -1,10 +1,6 @@ -import { - validateIpAddress, -} from "../utils"; -import React, {Component, useState} from 'react'; +import React, {Component} from 'react'; import './Config.scss'; -import {Button, ButtonGroup, ToggleButton, Col, Container, Form, FormControl, InputGroup, Modal, Row, Table} from "react-bootstrap"; -import {createCurlCommand} from '../utils'; +import {Button, ButtonGroup, Col, Container, Form, Modal, Row, Table, ToggleButton} from "react-bootstrap"; class Config extends Component { diff --git a/frontend/src/views/Connections.js b/frontend/src/views/Connections.js index 62733d7..da8958b 100644 --- a/frontend/src/views/Connections.js +++ b/frontend/src/views/Connections.js @@ -1,10 +1,10 @@ import React, {Component} from 'react'; import './Connections.scss'; -import axios from 'axios'; import Connection from "../components/Connection"; import Table from 'react-bootstrap/Table'; import {Redirect} from 'react-router'; import {withRouter} from "react-router-dom"; +import backend from "../backend"; class Connections extends Component { @@ -75,15 +75,15 @@ class Connections extends Component { } this.setState({loading: true, prevParams: params}); - let res = await axios.get(`${url}?${urlParams}`); + let res = await backend.get(`${url}?${urlParams}`); let connections = this.state.connections; let firstConnection = this.state.firstConnection; let lastConnection = this.state.lastConnection; if (params !== undefined && params.from !== undefined) { - if (res.data.length > 0) { - connections = this.state.connections.concat(res.data); + if (res.length > 0) { + connections = this.state.connections.concat(res); lastConnection = connections[connections.length - 1]; if (connections.length > this.maxConnections) { connections = connections.slice(connections.length - this.maxConnections, @@ -92,8 +92,8 @@ class Connections extends Component { } } } else if (params !== undefined && params.to !== undefined) { - if (res.data.length > 0) { - connections = res.data.concat(this.state.connections); + if (res.length > 0) { + connections = res.concat(this.state.connections); firstConnection = connections[0]; if (connections.length > this.maxConnections) { connections = connections.slice(0, this.maxConnections); @@ -101,8 +101,8 @@ class Connections extends Component { } } } else { - if (res.data.length > 0) { - connections = res.data; + if (res.length > 0) { + connections = res; firstConnection = connections[0]; lastConnection = connections[connections.length - 1]; } else { @@ -115,8 +115,7 @@ class Connections extends Component { let flagRule = this.state.flagRule; let rules = this.state.rules; if (flagRule === null) { - let res = await axios.get("/api/rules"); - rules = res.data; + rules = await backend.get("/api/rules"); flagRule = rules.filter(rule => { return rule.name === "flag"; })[0]; @@ -125,7 +124,7 @@ class Connections extends Component { this.setState({ loading: false, connections: connections, - rules: res.data, + rules: res, flagRule: flagRule, firstConnection: firstConnection, lastConnection: lastConnection @@ -134,7 +133,7 @@ class Connections extends Component { render() { let redirect; - let queryString = this.state.queryString !== null ? this.state.queryString : "" + let queryString = this.state.queryString !== null ? this.state.queryString : ""; if (this.state.selected) { let format = this.props.match.params.format; format = format !== undefined ? "/" + format : ""; diff --git a/frontend/src/views/Footer.js b/frontend/src/views/Footer.js index b6ffd9d..0a3c5a3 100644 --- a/frontend/src/views/Footer.js +++ b/frontend/src/views/Footer.js @@ -8,11 +8,11 @@ class Footer extends Component { - ) + ); } } diff --git a/frontend/src/views/Header.js b/frontend/src/views/Header.js index 0af7abf..5860d80 100644 --- a/frontend/src/views/Header.js +++ b/frontend/src/views/Header.js @@ -69,12 +69,12 @@ class Header extends Component {
- - - - - + + + + +
diff --git a/frontend/src/views/Header.scss b/frontend/src/views/Header.scss index e84e758..e36b2d6 100644 --- a/frontend/src/views/Header.scss +++ b/frontend/src/views/Header.scss @@ -16,6 +16,10 @@ .header-buttons { margin: 5px 0; text-align: right; + + button { + margin-left: 10px; + } } .filters-bar-wrapper { diff --git a/frontend/src/views/MainPane.js b/frontend/src/views/MainPane.js index 69de725..3c0d795 100644 --- a/frontend/src/views/MainPane.js +++ b/frontend/src/views/MainPane.js @@ -3,7 +3,8 @@ import './MainPane.scss'; import Connections from "./Connections"; import ConnectionContent from "../components/ConnectionContent"; import {withRouter} from "react-router-dom"; -import axios from 'axios'; +import PcapPane from "../components/panels/PcapPane"; +import backend from "../backend"; class MainPane extends Component { @@ -17,9 +18,9 @@ class MainPane extends Component { componentDidMount() { if ('id' in this.props.match.params) { const id = this.props.match.params.id; - axios.get(`/api/connections/${id}`).then(res => { + backend.get(`/api/connections/${id}`).then(res => { if (res.status === 200) { - this.setState({selectedConnection: res.data}); + this.setState({selectedConnection: res}); } }); } @@ -34,6 +35,7 @@ class MainPane extends Component { this.setState({selectedConnection: c})} />
+ {/**/}
diff --git a/frontend/src/views/Rules.js b/frontend/src/views/Rules.js index 3424410..bbc3bb6 100644 --- a/frontend/src/views/Rules.js +++ b/frontend/src/views/Rules.js @@ -1,7 +1,7 @@ import React, {Component} from 'react'; import './Services.scss'; import {Button, ButtonGroup, Col, Container, Form, FormControl, InputGroup, Modal, Row, Table} from "react-bootstrap"; -import axios from "axios"; +import backend from "../backend"; class Rules extends Component { @@ -18,7 +18,7 @@ class Rules extends Component { } loadRules() { - axios.get("/api/rules").then(res => this.setState({rules: res.data})); + backend.get("/api/rules").then(res => this.setState({rules: res.data})); } render() { diff --git a/frontend/src/views/Services.js b/frontend/src/views/Services.js index 0de021f..22d61b3 100644 --- a/frontend/src/views/Services.js +++ b/frontend/src/views/Services.js @@ -1,8 +1,8 @@ import React, {Component} from 'react'; import './Services.scss'; import {Button, ButtonGroup, Col, Container, Form, FormControl, InputGroup, Modal, Row, Table} from "react-bootstrap"; -import axios from 'axios' import {createCurlCommand} from '../utils'; +import backend from "../backend"; class Services extends Component { @@ -64,31 +64,20 @@ class Services extends Component { saveService() { if (this.state.portValid && this.state.nameValid) { - const requestOptions = { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - color: this.state.color, - name: this.state.name, - notes: this.state.notes, - port: this.state.port, - }) - }; - - - fetch('/api/services', requestOptions) - .then(function(response){ - console.log(response); - } - ); - - this.newService(); - this.loadServices(); + backend.put("/api/services", { + color: this.state.color, + name: this.state.name, + notes: this.state.notes, + port: this.state.port, + }).then(_ => { + this.newService(); + this.loadServices(); + }); } } loadServices() { - axios.get("/api/services").then(res => this.setState({services: res.data})); + backend.get("/api/services").then(res => this.setState({services: res})); } componentDidUpdate(prevProps, prevState, snapshot) { -- cgit v1.2.3-70-g09d2 From 44af615b32faf53c04cd38cb63782cf1b1332c94 Mon Sep 17 00:00:00 2001 From: Emiliano Ciavatta Date: Sat, 26 Sep 2020 12:05:27 +0200 Subject: General refactor --- frontend/package.json | 1 - frontend/src/backend.js | 38 ++-- frontend/src/components/fields/BooleanField.js | 37 ---- frontend/src/components/fields/BooleanField.scss | 34 ---- frontend/src/components/fields/CheckField.js | 36 ++++ frontend/src/components/fields/CheckField.scss | 35 ++++ frontend/src/components/fields/InputField.js | 75 +++++++ frontend/src/components/fields/InputField.scss | 147 ++++++++++++++ frontend/src/components/fields/StringField.js | 66 ------- frontend/src/components/fields/StringField.scss | 117 ----------- frontend/src/components/fields/TextField.js | 42 ++++ frontend/src/components/fields/TextField.scss | 79 ++++++++ .../components/filters/BooleanConnectionsFilter.js | 6 +- .../components/filters/StringConnectionsFilter.js | 8 +- frontend/src/components/panels/PcapPane.js | 86 ++++++-- frontend/src/components/panels/PcapPane.scss | 14 +- frontend/src/utils.js | 18 +- frontend/src/views/App.js | 12 +- frontend/src/views/Header.js | 5 +- frontend/src/views/Header.scss | 2 +- frontend/src/views/MainPane.js | 8 +- frontend/src/views/Services.js | 2 +- frontend/src/views/Upload.js | 218 --------------------- frontend/src/views/Upload.scss | 21 -- 24 files changed, 555 insertions(+), 552 deletions(-) delete mode 100644 frontend/src/components/fields/BooleanField.js delete mode 100644 frontend/src/components/fields/BooleanField.scss create mode 100644 frontend/src/components/fields/CheckField.js create mode 100644 frontend/src/components/fields/CheckField.scss create mode 100644 frontend/src/components/fields/InputField.js create mode 100644 frontend/src/components/fields/InputField.scss delete mode 100644 frontend/src/components/fields/StringField.js delete mode 100644 frontend/src/components/fields/StringField.scss create mode 100644 frontend/src/components/fields/TextField.js create mode 100644 frontend/src/components/fields/TextField.scss delete mode 100644 frontend/src/views/Upload.js delete mode 100644 frontend/src/views/Upload.scss (limited to 'frontend/src/views/Services.js') diff --git a/frontend/package.json b/frontend/package.json index bf995c5..3629e70 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,7 +9,6 @@ "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", - "axios": "^0.19.2", "bootstrap": "^4.4.1", "bs-custom-file-input": "^1.3.4", "classnames": "^2.2.6", diff --git a/frontend/src/backend.js b/frontend/src/backend.js index 35ae6e3..5eb0e40 100644 --- a/frontend/src/backend.js +++ b/frontend/src/backend.js @@ -1,11 +1,11 @@ -async function request(method, url, data) { +async function json(method, url, data, headers) { const options = { method: method, mode: "cors", cache: "no-cache", credentials: "same-origin", - headers: { + headers: headers || { "Content-Type": "application/json" }, redirect: "follow", @@ -18,19 +18,35 @@ async function request(method, url, data) { return result.json(); } +async function file(url, data, headers) { + const options = { + method: "POST", + mode: "cors", + cache: "no-cache", + credentials: "same-origin", + body: data, + redirect: "follow", + referrerPolicy: "no-referrer", + }; + return await fetch(url, options); +} + const backend = { - get: (url = "") => { - return request("GET", url, null); + get: (url = "", headers = null) => { + return json("GET", url, null, headers); }, - post: (url = "", data = null) => { - return request("POST", url, data); + post: (url = "", data = null, headers = null) => { + return json("POST", url, data, headers); }, - put: (url = "", data = null) => { - return request("PUT", url, data); + put: (url = "", data = null, headers = null) => { + return json("PUT", url, data, headers); + }, + delete: (url = "", data = null, headers = null) => { + return json("DELETE", url, data, headers); + }, + postFile: (url = "", data = null, headers = null) => { + return file(url, data, headers); }, - delete: (url = "", data = null) => { - return request("DELETE", url, data); - } }; export default backend; diff --git a/frontend/src/components/fields/BooleanField.js b/frontend/src/components/fields/BooleanField.js deleted file mode 100644 index 06a6da7..0000000 --- a/frontend/src/components/fields/BooleanField.js +++ /dev/null @@ -1,37 +0,0 @@ -import React, {Component} from 'react'; -import './BooleanField.scss'; -import {randomClassName} from "../../utils"; - -const classNames = require('classnames'); - -class BooleanField extends Component { - - constructor(props) { - super(props); - - this.id = `field-${this.props.name || "noname"}-${randomClassName()}`; - } - - render() { - - const checked = this.props.checked || false; - const small = this.props.small || false; - const name = this.props.name || null; - const handler = () => { - if (this.props.onChange) { - this.props.onChange(!checked); - } - }; - - return ( -
-
- - -
-
- ); - } -} - -export default BooleanField; diff --git a/frontend/src/components/fields/BooleanField.scss b/frontend/src/components/fields/BooleanField.scss deleted file mode 100644 index 6ec25f7..0000000 --- a/frontend/src/components/fields/BooleanField.scss +++ /dev/null @@ -1,34 +0,0 @@ -@import '../../colors.scss'; - -.boolean-field { - font-size: 0.9em; - - .field-input { - border-radius: 5px; - width: fit-content; - background-color: $color-primary-2; - - input { - display: none; - } - - label { - margin: 0; - padding: 6px 15px; - cursor: pointer; - } - - &:hover { - background-color: $color-primary-1; - } - } - - &.field-checked .field-input { - background-color: $color-primary-4 !important; - color: $color-primary-3; - } - - &.field-small { - font-size: 0.8em; - } -} diff --git a/frontend/src/components/fields/CheckField.js b/frontend/src/components/fields/CheckField.js new file mode 100644 index 0000000..5cceac4 --- /dev/null +++ b/frontend/src/components/fields/CheckField.js @@ -0,0 +1,36 @@ +import React, {Component} from 'react'; +import './CheckField.scss'; +import {randomClassName} from "../../utils"; + +const classNames = require('classnames'); + +class CheckField extends Component { + + constructor(props) { + super(props); + + this.id = `field-${this.props.name || "noname"}-${randomClassName()}`; + } + + render() { + const checked = this.props.checked || false; + const small = this.props.small || false; + const name = this.props.name || null; + const handler = () => { + if (this.props.onChange) { + this.props.onChange(!checked); + } + }; + + return ( +
+
+ + +
+
+ ); + } +} + +export default CheckField; diff --git a/frontend/src/components/fields/CheckField.scss b/frontend/src/components/fields/CheckField.scss new file mode 100644 index 0000000..7b0ac5f --- /dev/null +++ b/frontend/src/components/fields/CheckField.scss @@ -0,0 +1,35 @@ +@import '../../colors.scss'; + +.check-field { + font-size: 0.9em; + margin: 5px 0; + + .field-input { + border-radius: 5px; + width: fit-content; + background-color: $color-primary-2; + + input { + display: none; + } + + label { + margin: 0; + padding: 6px 15px; + cursor: pointer; + } + + &:hover { + background-color: $color-primary-1; + } + } + + &.field-checked .field-input { + background-color: $color-primary-4 !important; + color: $color-primary-3; + } + + &.field-small { + font-size: 0.8em; + } +} diff --git a/frontend/src/components/fields/InputField.js b/frontend/src/components/fields/InputField.js new file mode 100644 index 0000000..af3b3df --- /dev/null +++ b/frontend/src/components/fields/InputField.js @@ -0,0 +1,75 @@ +import React, {Component} from 'react'; +import './InputField.scss'; +import {randomClassName} from "../../utils"; + +const classNames = require('classnames'); + +class InputField extends Component { + + constructor(props) { + super(props); + + this.id = `field-${this.props.name || "noname"}-${randomClassName()}`; + } + + render() { + const active = this.props.active || false; + const invalid = this.props.invalid || false; + const small = this.props.small || false; + const inline = this.props.inline || false; + const name = this.props.name || null; + const value = this.props.value || ""; + const type = this.props.type || "text"; + const error = this.props.error || null; + const defaultValue = this.props.defaultValue || null; + const handler = (e) => { + if (this.props.onChange) { + if (type === "file") { + let file = e.target.files[0]; + this.props.onChange(file); + } else if (e == null) { + this.props.onChange(""); + } else { + this.props.onChange(e.target.value); + } + } + }; + let inputProps = {}; + if (type !== "file") { + inputProps["value"] = value; + } + + return ( +
+
+ { name && +
+ +
+ } +
+
+ { type === "file" && } + +
+ { type !== "file" && value !== "" && +
+ handler(null)}>del +
+ } +
+
+ {error && +
+ error: {error} +
+ } +
+ ); + } +} + +export default InputField; diff --git a/frontend/src/components/fields/InputField.scss b/frontend/src/components/fields/InputField.scss new file mode 100644 index 0000000..cdb8c9f --- /dev/null +++ b/frontend/src/components/fields/InputField.scss @@ -0,0 +1,147 @@ +@import '../../colors.scss'; + +.input-field { + font-size: 0.9em; + margin: 5px 0; + + .field-name { + label { + margin: 0; + } + } + + .field-input { + position: relative; + + .field-value { + input, .file-label { + background-color: $color-primary-2; + width: 100%; + border: none; + color: $color-primary-4; + border-radius: 5px; + padding: 7px 10px; + + &:focus { + background-color: $color-primary-1; + color: $color-primary-4; + box-shadow: none; + outline: none; + } + + &[readonly] { + background-color: $color-primary-2; + border: none; + color: $color-primary-4; + } + + &[readonly]:focus { + background-color: $color-primary-1; + color: $color-primary-4; + box-shadow: none; + } + } + + input[type="file"] { + display: none; + } + + .file-label { + margin: 0; + } + + .file-label:after { + content: "Browse"; + position: absolute; + right: 0; + top: 0; + padding: 7px 10px 7px 12px; + background-color: $color-primary-1; + border-bottom-right-radius: 5px; + border-top-right-radius: 5px; + } + } + } + + &.field-active { + &.field-inline .field-name { + background-color: $color-primary-4 !important; + color: $color-primary-3 !important; + } + + .field-value input, .field-value .file-label { + background-color: $color-primary-4 !important; + color: $color-primary-3 !important; + } + + .file-label:after { + background-color: $color-secondary-4 !important; + } + } + + &.field-invalid { + &.field-inline .field-name { + background-color: $color-secondary-2 !important; + color: $color-primary-4 !important; + } + + .field-value input, .field-value .file-label { + background-color: $color-secondary-2 !important; + color: $color-primary-4 !important; + } + + .file-label:after { + background-color: $color-secondary-1 !important; + } + } + + &.field-small { + font-size: 0.8em; + } + + &.field-inline .field-wrapper { + display: flex; + + .field-name { + background-color: $color-primary-2; + padding: 6px 7px; + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; + } + + .field-input { + width: 100%; + + input, .file-label { + border-bottom-left-radius: 0; + border-top-left-radius: 0; + padding-left: 3px; + } + } + + &:focus-within .field-name { + background-color: $color-primary-1; + } + } + + .field-clear { + position: absolute; + right: 8px; + top: 8px; + z-index: 10; + font-size: 0.9em; + font-weight: 600; + letter-spacing: -0.5px; + cursor: pointer; + } + + &.field-active .field-clear { + color: $color-primary-2; + } + + .field-error { + padding: 5px 10px; + font-size: 0.9em; + color: $color-secondary-0; + } +} diff --git a/frontend/src/components/fields/StringField.js b/frontend/src/components/fields/StringField.js deleted file mode 100644 index 7781b2d..0000000 --- a/frontend/src/components/fields/StringField.js +++ /dev/null @@ -1,66 +0,0 @@ -import React, {Component} from 'react'; -import './StringField.scss'; -import {randomClassName} from "../../utils"; - -const classNames = require('classnames'); - -class StringField extends Component { - - constructor(props) { - super(props); - - this.id = `field-${this.props.name || "noname"}-${randomClassName()}`; - } - - render() { - - const active = this.props.active || false; - const invalid = this.props.invalid || false; - const small = this.props.small || false; - const inline = this.props.inline || false; - const name = this.props.name || null; - const value = this.props.value || ""; - const type = this.props.type || "text"; - const error = this.props.error || null; - const handler = (e) => { - if (this.props.onChange) { - if (e == null) { - this.props.onChange(""); - } else { - this.props.onChange(e.target.value); - } - } - }; - - return ( -
-
- { name && -
- -
- } -
-
- -
- { value !== "" && -
- handler(null)}>del -
- } -
-
- {error && -
- error: {error} -
- } -
- ); - } -} - -export default StringField; diff --git a/frontend/src/components/fields/StringField.scss b/frontend/src/components/fields/StringField.scss deleted file mode 100644 index 2523c8d..0000000 --- a/frontend/src/components/fields/StringField.scss +++ /dev/null @@ -1,117 +0,0 @@ -@import '../../colors.scss'; - -.string-field { - font-size: 0.9em; - - .field-name { - label { - margin: 0; - } - } - - .field-input { - position: relative; - - .field-value input { - background-color: $color-primary-2; - width: 100%; - border: none; - color: $color-primary-4; - border-radius: 5px; - padding: 7px 10px; - - &:focus { - background-color: $color-primary-1; - color: $color-primary-4; - box-shadow: none; - outline: none; - } - - &[readonly] { - background-color: $color-primary-2; - border: none; - color: $color-primary-4; - } - - &[readonly]:focus { - background-color: $color-primary-1; - color: $color-primary-4; - box-shadow: none; - } - } - } - - &.field-active { - &.field-inline .field-name { - background-color: $color-primary-4 !important; - color: $color-primary-3 !important; - } - - .field-value input { - background-color: $color-primary-4 !important; - color: $color-primary-3 !important; - } - } - - &.field-invalid { - &.field-inline .field-name { - background-color: $color-secondary-2 !important; - color: $color-primary-4 !important; - } - - .field-value input { - background-color: $color-secondary-2 !important; - color: $color-primary-4 !important; - } - } - - &.field-small { - font-size: 0.8em; - } - - &.field-inline .field-wrapper { - display: flex; - - .field-name { - background-color: $color-primary-2; - padding: 6px 7px; - border-top-left-radius: 5px; - border-bottom-left-radius: 5px; - } - - .field-input { - width: 100%; - - input { - border-bottom-left-radius: 0; - border-top-left-radius: 0; - padding-left: 3px; - } - } - - &:focus-within .field-name { - background-color: $color-primary-1; - } - } - - .field-clear { - position: absolute; - right: 8px; - top: 8px; - z-index: 10; - font-size: 0.9em; - font-weight: 600; - letter-spacing: -0.5px; - cursor: pointer; - } - - &.field-active .field-clear { - color: $color-primary-2; - } - - .field-error { - padding: 5px 10px; - font-size: 0.9em; - color: $color-secondary-0; - } -} diff --git a/frontend/src/components/fields/TextField.js b/frontend/src/components/fields/TextField.js new file mode 100644 index 0000000..86b98ed --- /dev/null +++ b/frontend/src/components/fields/TextField.js @@ -0,0 +1,42 @@ +import React, {Component} from 'react'; +import './TextField.scss'; +import {randomClassName} from "../../utils"; + +const classNames = require('classnames'); + +class TextField extends Component { + + constructor(props) { + super(props); + + this.id = `field-${this.props.name || "noname"}-${randomClassName()}`; + } + + render() { + const name = this.props.name || null; + const error = this.props.error || null; + const rows = this.props.rows || 3; + + const handler = (e) => { + if (this.props.onChange) { + if (e == null) { + this.props.onChange(""); + } else { + this.props.onChange(e.target.value); + } + } + }; + + return ( +
+ {name && } +