diff options
Diffstat (limited to 'frontend')
20 files changed, 340 insertions, 337 deletions
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/CheckField.js index 06a6da7..5cceac4 100644 --- a/frontend/src/components/fields/BooleanField.js +++ b/frontend/src/components/fields/CheckField.js @@ -1,10 +1,10 @@ import React, {Component} from 'react'; -import './BooleanField.scss'; +import './CheckField.scss'; import {randomClassName} from "../../utils"; const classNames = require('classnames'); -class BooleanField extends Component { +class CheckField extends Component { constructor(props) { super(props); @@ -13,7 +13,6 @@ class BooleanField extends Component { } render() { - const checked = this.props.checked || false; const small = this.props.small || false; const name = this.props.name || null; @@ -24,7 +23,7 @@ class BooleanField extends Component { }; return ( - <div className={classNames( "boolean-field", {"field-checked" : checked}, {"field-small": small})}> + <div className={classNames( "check-field", {"field-checked" : checked}, {"field-small": small})}> <div className="field-input"> <input type="checkbox" id={this.id} checked={checked} onChange={handler} /> <label htmlFor={this.id}>{(checked ? "✓ " : "✗ ") + name}</label> @@ -34,4 +33,4 @@ class BooleanField extends Component { } } -export default BooleanField; +export default CheckField; diff --git a/frontend/src/components/fields/BooleanField.scss b/frontend/src/components/fields/CheckField.scss index 6ec25f7..7b0ac5f 100644 --- a/frontend/src/components/fields/BooleanField.scss +++ b/frontend/src/components/fields/CheckField.scss @@ -1,7 +1,8 @@ @import '../../colors.scss'; -.boolean-field { +.check-field { font-size: 0.9em; + margin: 5px 0; .field-input { border-radius: 5px; diff --git a/frontend/src/components/fields/StringField.js b/frontend/src/components/fields/InputField.js index 7781b2d..af3b3df 100644 --- a/frontend/src/components/fields/StringField.js +++ b/frontend/src/components/fields/InputField.js @@ -1,10 +1,10 @@ import React, {Component} from 'react'; -import './StringField.scss'; +import './InputField.scss'; import {randomClassName} from "../../utils"; const classNames = require('classnames'); -class StringField extends Component { +class InputField extends Component { constructor(props) { super(props); @@ -13,7 +13,6 @@ class StringField extends Component { } render() { - const active = this.props.active || false; const invalid = this.props.invalid || false; const small = this.props.small || false; @@ -22,31 +21,41 @@ class StringField extends Component { 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 (e == null) { + 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 ( - <div className={classNames("string-field", {"field-active" : active}, {"field-invalid": invalid}, + <div className={classNames("input-field", {"field-active" : active}, {"field-invalid": invalid}, {"field-small": small}, {"field-inline": inline})}> <div className="field-wrapper"> { name && <div className="field-name"> - <label id={this.id}>{name}:</label> + <label>{name}:</label> </div> } <div className="field-input"> <div className="field-value"> - <input type={type} placeholder={this.props.defaultValue} aria-label={name} - aria-describedby={this.id} onChange={handler} value={value} /> + { type === "file" && <label for={this.id} className={"file-label"}> + {value.name || defaultValue}</label> } + <input type={type} placeholder={defaultValue} id={this.id} + aria-describedby={this.id} onChange={handler} {...inputProps} /> </div> - { value !== "" && + { type !== "file" && value !== "" && <div className="field-clear"> <span onClick={() => handler(null)}>del</span> </div> @@ -63,4 +72,4 @@ class StringField extends Component { } } -export default StringField; +export default InputField; diff --git a/frontend/src/components/fields/StringField.scss b/frontend/src/components/fields/InputField.scss index 2523c8d..cdb8c9f 100644 --- a/frontend/src/components/fields/StringField.scss +++ b/frontend/src/components/fields/InputField.scss @@ -1,7 +1,8 @@ @import '../../colors.scss'; -.string-field { +.input-field { font-size: 0.9em; + margin: 5px 0; .field-name { label { @@ -12,31 +13,52 @@ .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] { + .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; } - &[readonly]:focus { + .file-label:after { + content: "Browse"; + position: absolute; + right: 0; + top: 0; + padding: 7px 10px 7px 12px; background-color: $color-primary-1; - color: $color-primary-4; - box-shadow: none; + border-bottom-right-radius: 5px; + border-top-right-radius: 5px; } } } @@ -47,10 +69,14 @@ color: $color-primary-3 !important; } - .field-value input { + .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 { @@ -59,10 +85,14 @@ color: $color-primary-4 !important; } - .field-value input { + .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 { @@ -82,7 +112,7 @@ .field-input { width: 100%; - input { + input, .file-label { border-bottom-left-radius: 0; border-top-left-radius: 0; padding-left: 3px; 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 ( + <div className={classNames("text-field", {"field-active": this.props.active}, + {"field-invalid": this.props.invalid}, {"field-small": this.props.small})}> + {name && <label htmlFor={this.id}>{name}:</label>} + <textarea id={this.id} placeholder={this.props.defaultValue} onChange={handler} rows={rows} + readOnly={this.props.readonly} value={this.props.value} /> + {error && <div className="field-error">error: {error}</div>} + </div> + ); + } +} + +export default TextField; diff --git a/frontend/src/components/fields/TextField.scss b/frontend/src/components/fields/TextField.scss new file mode 100644 index 0000000..606f537 --- /dev/null +++ b/frontend/src/components/fields/TextField.scss @@ -0,0 +1,79 @@ +@import '../../colors.scss'; + +.text-field { + font-size: 0.9em; + margin: 5px 0; + + label { + display: block; + margin: 0; + } + + textarea { + background-color: $color-primary-2; + width: 100%; + border: none; + color: $color-primary-4; + border-radius: 5px; + padding: 7px 10px; + resize: none; + + &: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 { + textarea { + background-color: $color-primary-4 !important; + color: $color-primary-3 !important; + } + } + + &.field-invalid { + textarea { + background-color: $color-secondary-2 !important; + color: $color-primary-4 !important; + } + } + + &.field-small { + font-size: 0.8em; + } + + .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/filters/BooleanConnectionsFilter.js b/frontend/src/components/filters/BooleanConnectionsFilter.js index 490d185..4c5a78a 100644 --- a/frontend/src/components/filters/BooleanConnectionsFilter.js +++ b/frontend/src/components/filters/BooleanConnectionsFilter.js @@ -1,7 +1,7 @@ import React, {Component} from 'react'; import {withRouter} from "react-router-dom"; import {Redirect} from "react-router"; -import BooleanField from "../fields/BooleanField"; +import CheckField from "../fields/CheckField"; class BooleanConnectionsFilter extends Component { @@ -56,8 +56,8 @@ class BooleanConnectionsFilter extends Component { return ( <div className="filter" style={{"width": `${this.props.width}px`}}> - <BooleanField checked={this.toBoolean(this.state.filterActive)} name={this.props.filterName} - onChange={this.filterChanged} /> + <CheckField checked={this.toBoolean(this.state.filterActive)} name={this.props.filterName} + onChange={this.filterChanged} /> {redirect} </div> ); diff --git a/frontend/src/components/filters/StringConnectionsFilter.js b/frontend/src/components/filters/StringConnectionsFilter.js index 0d7f063..a304198 100644 --- a/frontend/src/components/filters/StringConnectionsFilter.js +++ b/frontend/src/components/filters/StringConnectionsFilter.js @@ -1,7 +1,7 @@ import React, {Component} from 'react'; import {withRouter} from "react-router-dom"; import {Redirect} from "react-router"; -import StringField from "../fields/StringField"; +import InputField from "../fields/InputField"; class StringConnectionsFilter extends Component { @@ -114,9 +114,9 @@ class StringConnectionsFilter extends Component { return ( <div className="filter" style={{"width": `${this.props.width}px`}}> - <StringField active={active} invalid={this.state.invalidValue} name={this.props.filterName} - defaultValue={this.props.defaultFilterValue} onChange={this.filterChanged} - value={this.state.fieldValue} inline={true} small={true} /> + <InputField active={active} invalid={this.state.invalidValue} name={this.props.filterName} + defaultValue={this.props.defaultFilterValue} onChange={this.filterChanged} + value={this.state.fieldValue} inline={true} small={true} /> {redirect} </div> ); diff --git a/frontend/src/components/panels/PcapPane.js b/frontend/src/components/panels/PcapPane.js index 9f3bc19..701edf2 100644 --- a/frontend/src/components/panels/PcapPane.js +++ b/frontend/src/components/panels/PcapPane.js @@ -2,10 +2,11 @@ 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"; -import BooleanField from "../fields/BooleanField"; +import {createCurlCommand, formatSize, timestampToTime2} from "../../utils"; +import {Button, Col, Container, Form, Row} from "react-bootstrap"; +import InputField from "../fields/InputField"; +import CheckField from "../fields/CheckField"; +import TextField from "../fields/TextField"; class PcapPane extends Component { @@ -14,10 +15,17 @@ class PcapPane extends Component { this.state = { sessions: [], - test: false + isFileValid: true, + isFileFocused: false, + selectedFile: null, + uploadFlushAll: false, + uploadStatusCode: null, + uploadOutput: null }; this.loadSessions = this.loadSessions.bind(this); + this.handleFileChange = this.handleFileChange.bind(this); + this.handleUploadPcap = this.handleUploadPcap.bind(this); } componentDidMount() { @@ -28,6 +36,34 @@ class PcapPane extends Component { backend.get("/api/pcap/sessions").then(res => this.setState({sessions: res})); } + handleFileChange(file) { + this.setState({ + isFileValid: file != null && file.type.endsWith("pcap"), + isFileFocused: false, + selectedFile: file + }); + } + + handleUploadPcap() { + if (this.state.selectedFile == null || !this.state.isFileValid) { + this.setState({isFileFocused: true}); + return; + } + + const formData = new FormData(); + formData.append( + "file", + this.state.selectedFile + ); + + backend.postFile("/api/pcap/upload", formData).then(response => + response.json().then(result => this.setState({ + uploadStatusCode: response.status + " " + response.statusText, + uploadOutput: JSON.stringify(result) + })) + ); + } + render() { let sessions = this.state.sessions.map(s => <tr className="table-row"> @@ -38,10 +74,20 @@ class PcapPane extends Component { <td>{s["processed_packets"]}</td> <td>{s["invalid_packets"]}</td> <td>undefined</td> - <td className="table-cell-action"><a target="_blank" href={"/api/pcap/sessions/" + s["id"] + "/download"}>download</a></td> + <td className="table-cell-action"><a target="_blank" + href={"/api/pcap/sessions/" + s["id"] + "/download"}>download</a> + </td> </tr> ); + const uploadOutput = this.state.uploadOutput != null ? this.state.uploadOutput : + createCurlCommand("pcap/upload", "POST", null, { + file: "@" + ((this.state.selectedFile != null && this.state.isFileValid) ? this.state.selectedFile.name : + "invalid.pcap"), + flush_all: this.state.uploadFlushAll + }) + ; + return ( <div className="pane-container"> <div className="pane-section"> @@ -77,19 +123,25 @@ class PcapPane extends Component { <Col> <div className="section-header"> <span className="api-request">POST /api/pcap/upload</span> - <span className="api-response"></span> + <span className="api-response">{this.state.uploadStatusCode}</span> </div> <div className="section-content"> - <Form.File className="custom-file" onChange={this.onFileChange} - label=".pcap/.pcapng" id="custom-file" - custom={true} - /> - - - <br/><br/><br/><br/> - <BooleanField small={true} name={"marked"} checked={this.state.test} onChange={(v) => this.setState({test: v})} /> - + <InputField type={"file"} name={"file"} invalid={!this.state.isFileValid} + active={this.state.isFileFocused} + onChange={this.handleFileChange} value={this.state.selectedFile} + defaultValue={"No .pcap[ng] selected"}/> + + <div className="upload-actions"> + <div className="upload-options"> + <span>options:</span> + <CheckField name="flush_all" checked={this.state.uploadFlushAll} + onChange={v => this.setState({uploadFlushAll: v})}/> + </div> + <Button variant="green" onClick={this.handleUploadPcap}>upload</Button> + </div> + + <TextField value={uploadOutput} rows={4} readonly small={true}/> </div> </Col> @@ -110,8 +162,6 @@ class PcapPane extends Component { </Container> - - </div> </div> diff --git a/frontend/src/components/panels/PcapPane.scss b/frontend/src/components/panels/PcapPane.scss index 3df87f5..ce28227 100644 --- a/frontend/src/components/panels/PcapPane.scss +++ b/frontend/src/components/panels/PcapPane.scss @@ -38,8 +38,6 @@ padding: 10px; } - - th { background-color: $color-primary-2; border-top: 3px solid $color-primary-3; @@ -50,6 +48,18 @@ padding: 5px; } + .upload-actions { + display: flex; + align-items: flex-end; + margin-bottom: 20px; + } + + .upload-options { + flex: 1; + span { + font-size: 0.9em; + } + } }
\ No newline at end of file diff --git a/frontend/src/utils.js b/frontend/src/utils.js index 46667d6..6a5411c 100644 --- a/frontend/src/utils.js +++ b/frontend/src/utils.js @@ -1,9 +1,19 @@ const timeRegex = /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/; -export function createCurlCommand(subCommand, data) { - let full = window.location.protocol + '//' + window.location.hostname + (window.location.port ? ':' + window.location.port : ''); - return `curl --request PUT \\\n --url ${full}/api${subCommand} \\\n ` + - `--header 'content-type: application/json' \\\n --data '${JSON.stringify(data)}'`; +export function createCurlCommand(subCommand, method = null, json = null, data = null) { + const full = window.location.protocol + '//' + window.location.hostname + (window.location.port ? ':' + window.location.port : ''); + + let contentType = null; + let content = null; + if (json != null) { + contentType = ' -H "Content-Type: application/json" \\\n'; + content = ` -d '${JSON.stringify(json)}'`; + } else if (data != null) { + contentType = ' -H "Content-Type: multipart/form-data" \\\n'; + content = " " + Object.entries(data).map(([key, value]) => `-F "${key}=${value}"`).join(" \\\n "); + } + + return `curl${method != null ? " -X " + method : ""} "${full}/api${subCommand}" \\\n` + contentType + "" + content; } export function validateIpAddress(ipAddress) { diff --git a/frontend/src/views/App.js b/frontend/src/views/App.js index ebead2f..ccfdb3a 100644 --- a/frontend/src/views/App.js +++ b/frontend/src/views/App.js @@ -2,12 +2,11 @@ import React, {Component} from 'react'; import Header from "./Header"; import MainPane from "./MainPane"; import Footer from "./Footer"; -import {BrowserRouter as Router, Route, Switch} from "react-router-dom"; +import {BrowserRouter as Router} from "react-router-dom"; import Services from "./Services"; import Filters from "./Filters"; import Rules from "./Rules"; import Config from "./Config"; -import Upload from "./Upload"; class App extends Component { @@ -18,7 +17,6 @@ class App extends Component { filterWindowOpen: false, rulesWindowOpen: false, configWindowOpen: false, - uploadWindowOpen: false, configDone: false }; @@ -49,9 +47,6 @@ class App extends Component { modal = <Config onHide={() => this.setState({configWindowOpen: false})} onDone={() => this.setState({configDone: true})}/>; } - if (this.state.uploadWindowOpen) { - modal = <Upload onHide={() => this.setState({uploadWindowOpen: false}) }/>; - } return ( <div className="app"> @@ -63,10 +58,7 @@ class App extends Component { onOpenUpload={() => this.setState({uploadWindowOpen: true})} onConfigDone={this.state.configDone} /> - <Switch> - <Route path="/connections/:id" children={<MainPane/>}/> - <Route path="/" children={<MainPane/>}/> - </Switch> + <MainPane /> {modal} <Footer/> </Router> diff --git a/frontend/src/views/Header.js b/frontend/src/views/Header.js index deb3ab7..e2d0e6a 100644 --- a/frontend/src/views/Header.js +++ b/frontend/src/views/Header.js @@ -3,6 +3,7 @@ import Typed from 'typed.js'; import './Header.scss'; import {Button} from "react-bootstrap"; import {filtersDefinitions, filtersNames} from "../components/filters/FiltersDefinitions"; +import {Link} from "react-router-dom"; class Header extends Component { @@ -68,7 +69,9 @@ class Header extends Component { <div className="col"> <div className="header-buttons"> <Button variant="pink" onClick={this.props.onOpenFilters}>filters</Button> - <Button variant="purple" onClick={this.props.onOpenUpload}>pcaps</Button> + <Link to="/pcaps"> + <Button variant="purple">pcaps</Button> + </Link> <Button variant="deep-purple" onClick={this.props.onOpenRules}>rules</Button> <Button variant="indigo" onClick={this.props.onOpenServices}>services</Button> <Button variant="blue" onClick={this.props.onOpenConfig} diff --git a/frontend/src/views/Header.scss b/frontend/src/views/Header.scss index f3bfec1..71d49e3 100644 --- a/frontend/src/views/Header.scss +++ b/frontend/src/views/Header.scss @@ -23,7 +23,7 @@ } .filters-bar { - padding: 8px 0; + padding: 3px 0; .filter { display: inline-block; diff --git a/frontend/src/views/MainPane.js b/frontend/src/views/MainPane.js index 88af4a7..ce755d1 100644 --- a/frontend/src/views/MainPane.js +++ b/frontend/src/views/MainPane.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import './MainPane.scss'; import Connections from "./Connections"; import ConnectionContent from "../components/ConnectionContent"; -import {withRouter} from "react-router-dom"; +import {Route, Switch, withRouter} from "react-router-dom"; import PcapPane from "../components/panels/PcapPane"; import backend from "../backend"; @@ -35,8 +35,10 @@ class MainPane extends Component { <Connections onSelected={(c) => this.setState({selectedConnection: c})} /> </div> <div className="col-md-6 pl-0 pane"> - <PcapPane /> - {/*<ConnectionContent connection={this.state.selectedConnection}/>*/} + <Switch> + <Route path="/pcaps" children={<PcapPane />} /> + <Route children={<ConnectionContent connection={this.state.selectedConnection} />} /> + </Switch> </div> </div> </div> diff --git a/frontend/src/views/Services.js b/frontend/src/views/Services.js index 22d61b3..97368dc 100644 --- a/frontend/src/views/Services.js +++ b/frontend/src/views/Services.js @@ -102,7 +102,7 @@ class Services extends Component { output += "assert(len(name) >= 3)\n"; } if (output === "") { - output = createCurlCommand("/services", { + output = createCurlCommand("/services", "PUT", { "port": this.state.port, "name": this.state.name, "color": this.state.color, diff --git a/frontend/src/views/Upload.js b/frontend/src/views/Upload.js deleted file mode 100644 index 29d514d..0000000 --- a/frontend/src/views/Upload.js +++ /dev/null @@ -1,218 +0,0 @@ -import React, {Component} from 'react'; -import './Upload.scss'; -import {Button, ButtonGroup, ToggleButton, Col, Container, Form, FormFile, InputGroup, Modal, Row, Table} from "react-bootstrap"; -import bsCustomFileInput from 'bs-custom-file-input' -import {createCurlCommand} from '../utils'; - -class Upload extends Component { - - constructor(props) { - super(props); - - this.state = { - selectedFile: null, - removeOriginal: false, - flushAll: false, - errors: "" - }; - - this.flushAllChanged = this.flushAllChanged.bind(this); - this.removeOriginalChanged = this.removeOriginalChanged.bind(this); - - - } - - flushAllChanged() { - this.setState({flushAll: !this.value}); - this.checked = !this.checked; - this.value = !this.value; - } - - removeOriginalChanged() { - this.setState({removeOriginal: !this.value}); - this.checked = !this.checked; - this.value = !this.value; - } - - onLocalFileChange = event => { - this.setState({ selectedFile: event.target.value }); - - }; - - onFileChange = event => { - this.setState({ selectedFile: event.target.files[0] }); - - }; - - componentDidMount() { - bsCustomFileInput.init() - } - - onFileProcess = () => { - const data = { - "file": this.state.selectedFile, - "flush_all": this.state.flushAll, - "delete_original_file": this.state.removeOriginal}; - - fetch('/api/pcap/file', { - method: 'POST', - body: JSON.stringify(data) - }) - .then(response => { - if (response.status === 202 ){ - this.props.onHide(); - } else { - response.json().then(data => { - this.setState( - {errors : data.error.toString()} - ); - }); - } - } - ); - } - - onFileUpload = () => { - const formData = new FormData(); - formData.append( - "file", - this.state.selectedFile, - this.state.selectedFile.name - ); - fetch('/api/pcap/upload', { - method: 'POST', - body: formData - }) - .then(response => { - if (response.status === 202 ){ - //this.setState({showConfig:false}); - this.props.onHide(); - //this.props.onDone(); - } else { - response.json().then(data => { - this.setState( - {errors : data.error.toString()} - ); - }); - } - } - ); - } - - - render() { - - return ( - <Modal - {...this.props} - show="true" - size="lg" - aria-labelledby="services-dialog" - centered - > - <Modal.Header> - <Modal.Title id="services-dialog"> - /usr/bin/load_pcap - </Modal.Title> - </Modal.Header> - <Modal.Body> - <Container> - <Row> - <Col> - --local - </Col> - <Col> - --upload - </Col> - </Row> - <Row> - <Col> - <Form.Control - type="text" - id="pcap-upload" - className="custom-file" - onChange={this.onLocalFileChange} - placeholder="local .pcap/.pcapng" - custom - /> - </Col> - <Col> - <Form.File - type="file" - className="custom-file" - onChange={this.onFileChange} - label=".pcap/.pcapng" - id="custom-file" - custom - /> - </Col> - </Row> - <br/> - <Row> - <Col> - <ButtonGroup toggle className="mb-2"> - <ToggleButton - type="checkbox" - variant="secondary" - checked={this.state.removeOriginal} - value={this.state.removeOriginal} - onChange={() => this.removeOriginalChanged()} - > - --remove-original-file - </ToggleButton> - </ButtonGroup> - </Col> - <Col> - </Col> - </Row> - <Row> - <Col> - <ButtonGroup toggle className="mb-2"> - <ToggleButton - type="checkbox" - variant="secondary" - checked={this.state.flushAll} - value={this.state.flushAll} - onChange={() => this.flushAllChanged()} - > - --flush-all - </ToggleButton> - </ButtonGroup> - </Col> - <Col> - </Col> - </Row> - <Row> - <Col> - <br/> - <Button variant="blue" onClick={this.onFileProcess}>process_local</Button> - </Col> - <Col> - <br/> - <Button variant="green" onClick={this.onFileUpload}>upload</Button> - </Col> - </Row> - <hr/> - <Row> - <div class="error"> - <b> - <br/> - {this.state.errors - .split('\n').map((item, key) => { - return <span key={key}>{item}<br/></span>}) - } - </b> - </div> - </Row> - </Container> - </Modal.Body> - <Modal.Footer className="dialog-footer"> - - <Button variant="red" onClick={this.props.onHide}>close</Button> - </Modal.Footer> - </Modal> - ); - } -} - -export default Upload; diff --git a/frontend/src/views/Upload.scss b/frontend/src/views/Upload.scss deleted file mode 100644 index e327b8c..0000000 --- a/frontend/src/views/Upload.scss +++ /dev/null @@ -1,21 +0,0 @@ -@import '../colors.scss'; - -.curl-output { - width: 100%; - font-size: 13px; -} - -#pcap-upload{ - align: center; - width: 100%; -} - -.btn-color { - border: 3px solid #fff; -} - -.dialog-footer { - .btn { - width: 80px; - } -} |