aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--frontend/src/colors.scss8
-rw-r--r--frontend/src/components/Connection.js78
-rw-r--r--frontend/src/components/Connection.scss49
-rw-r--r--frontend/src/components/ConnectionContent.js2
-rw-r--r--frontend/src/index.scss114
-rw-r--r--frontend/src/utils.js5
-rw-r--r--frontend/src/views/App.js19
-rw-r--r--frontend/src/views/Connections.js61
-rw-r--r--frontend/src/views/Connections.scss32
-rw-r--r--frontend/src/views/Header.js22
-rw-r--r--frontend/src/views/MainPane.js10
-rw-r--r--frontend/src/views/Services.js142
-rw-r--r--frontend/src/views/Services.scss16
13 files changed, 400 insertions, 158 deletions
diff --git a/frontend/src/colors.scss b/frontend/src/colors.scss
index 290a0cd..75f1828 100644
--- a/frontend/src/colors.scss
+++ b/frontend/src/colors.scss
@@ -9,3 +9,11 @@ $color-secondary-1: #A20A0A;
$color-secondary-2: #DF3030;
$color-secondary-3: #FF9D9D;
$color-secondary-4: #FFDFDF;
+
+$color-blue: #247085;
+$color-blue-light: #A5B8BE;
+$color-blue-dark: #013B4C;
+
+$color-green: #25965D;
+$color-green-light: #CDE4D8;
+$color-green-dark: #004321; \ No newline at end of file
diff --git a/frontend/src/components/Connection.js b/frontend/src/components/Connection.js
index 4bdc8cd..ce2b173 100644
--- a/frontend/src/components/Connection.js
+++ b/frontend/src/components/Connection.js
@@ -1,24 +1,13 @@
import React, {Component} from 'react';
import './Connection.scss';
-import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
-import {
- faCloudDownloadAlt,
- faCloudUploadAlt,
- faComment,
- faEyeSlash,
- faHourglassHalf,
- faLaptop,
- faLink,
- faServer,
- faThumbtack,
-} from '@fortawesome/free-solid-svg-icons'
+import {Button, OverlayTrigger, Tooltip} from "react-bootstrap";
class Connection extends Component {
render() {
let conn = this.props.data
- let serviceName = "assign"
- let serviceColor = "#fff"
- if (conn.service != null) {
+ let serviceName = "/dev/null"
+ let serviceColor = "#0F192E"
+ if (conn.service.port !== 0) {
serviceName = conn.service.name
serviceColor = conn.service.color
}
@@ -27,41 +16,40 @@ class Connection extends Component {
let duration = ((closedAt - startedAt) / 1000).toFixed(3)
let timeInfo = `Started at ${startedAt}\nClosed at ${closedAt}\nProcessed at ${new Date(conn.processed_at)}`
+ let classes = "connection"
+ if (this.props.selected) {
+ classes += " connection-selected"
+ }
+ if (conn.marked){
+ classes += " connection-marked"
+ }
return (
- <tr className={conn.marked ? "connection connection-marked" : "connection"}>
- <div className="connection-header">
+ <tr className={classes}>
+ <td>
<span className="connection-service">
- <button className="btn" style={{
+ <Button size="sm" style={{
"backgroundColor": serviceColor
- }}>{serviceName}</button>
- </span>
- <span className="connection-src">
- <FontAwesomeIcon icon={faLaptop}/>
- <span className="connection-ip-port">{conn.ip_src}:{conn.port_src}</span>
- </span>
- <span className="connection-separator">{"->"}</span>
- <span className="connection-dst">
- <FontAwesomeIcon icon={faServer}/>
- <span className="connection-ip-port">{conn.ip_dst}:{conn.port_dst}</span>
+ }}>{serviceName}</Button>
</span>
-
- <span className="connection-duration" data-toggle="tooltip" data-placement="top" title={timeInfo}>
- <FontAwesomeIcon icon={faHourglassHalf}/>
- <span className="connection-seconds">{duration}s</span>
- </span>
- <span className="connection-bytes">
- <FontAwesomeIcon icon={faCloudDownloadAlt}/>
- <span className="connection-bytes-count">{conn.client_bytes}</span>
- <FontAwesomeIcon icon={faCloudUploadAlt}/>
- <span className="connection-bytes-count">{conn.server_bytes}</span>
- </span>
- <span className="connection-hide"><FontAwesomeIcon icon={faEyeSlash}/></span>
- <span className="connection-mark"><FontAwesomeIcon icon={faThumbtack}/></span>
- <span className="connection-comment"><FontAwesomeIcon icon={faComment}/></span>
- <span className="connection-link"><FontAwesomeIcon icon={faLink}/></span>
- </div>
-
+ </td>
+ <td className="clickable" onClick={() => this.props.onSelected()}>{conn.ip_src}</td>
+ <td className="clickable" onClick={() => this.props.onSelected()}>{conn.port_src}</td>
+ <td className="clickable" onClick={() => this.props.onSelected()}>{conn.ip_dst}</td>
+ <td className="clickable" onClick={() => this.props.onSelected()}>{conn.port_dst}</td>
+ <td className="clickable" onClick={() => this.props.onSelected()}>
+ {/*<OverlayTrigger placement="top" overlay={<Tooltip id={`tooltip-${conn.id}`}>{timeInfo}</Tooltip>}>*/}
+ <span className="test-tooltip">{duration}s</span>
+ {/*</OverlayTrigger>*/}
+ </td>
+ <td className="clickable" onClick={() => this.props.onSelected()}>{conn.client_bytes}</td>
+ <td className="clickable" onClick={() => this.props.onSelected()}>{conn.server_bytes}</td>
+ <td>
+ <span className="connection-icon connection-hide">%</span>
+ <span className="connection-icon connection-mark">!!</span>
+ <span className="connection-icon connection-comment">@</span>
+ <span className="connection-icon connection-link">#</span>
+ </td>
</tr>
);
diff --git a/frontend/src/components/Connection.scss b/frontend/src/components/Connection.scss
index 8910cb4..d27ebc8 100644
--- a/frontend/src/components/Connection.scss
+++ b/frontend/src/components/Connection.scss
@@ -2,52 +2,41 @@
.connection {
background-color: $color-primary-0;
- margin-bottom: 5px;
+ border-top: 3px solid $color-primary-3;
+ border-bottom: 3px solid $color-primary-3;
+ td {
+ vertical-align: middle;
+ }
&.connection-marked {
border-right: 5px solid $color-secondary-2;
}
.connection-service {
- padding: 0 12px 0 0;
-
.btn {
- font-size: 12px;
- width: 120px;
+ width: 100%;
}
- }
-
- .connection-separator {
- margin: 0 10px;
- }
-
- .connection-duration {
- margin-left: 15px;
- //.connection-seconds {
- // width: 75px;
- // display: inline-block;
- // text-align: right;
- //}
- }
-
- .connection-bytes {
- margin-left: 10px;
- margin-right: 10px;
-
- .connection-bytes-count {
- margin-right: 5px;
+ .btn:hover {
+ background-color: $color-primary-1 !important;
+ color: $color-primary-4;
}
}
- .connection-hide, .connection-mark, .connection-comment, .connection-link {
- margin-left: 5px;
+ .connection-icon {
+ font-weight: 600;
+ font-size: 18px;
+ margin-right: 6px;
+ cursor: pointer;
}
+ &:hover {
+ background-color: $color-primary-2;
+ }
- svg {
- margin-right: 6px;
+ &.connection-selected {
+ background-color: $color-primary-2;
}
}
diff --git a/frontend/src/components/ConnectionContent.js b/frontend/src/components/ConnectionContent.js
index 061282c..bd35b5c 100644
--- a/frontend/src/components/ConnectionContent.js
+++ b/frontend/src/components/ConnectionContent.js
@@ -11,8 +11,6 @@ class ConnectionContent extends Component {
render() {
let content = this.props.connectionPayload
- console.log(content)
-
if (content === undefined) {
return <div>nope</div>
}
diff --git a/frontend/src/index.scss b/frontend/src/index.scss
index 5c18095..def3fdf 100644
--- a/frontend/src/index.scss
+++ b/frontend/src/index.scss
@@ -21,41 +21,64 @@ pre {
font-size: 14px;
}
-button {
- :focus {
- outline: none;
- box-shadow: none;
- }
-}
.btn {
border-radius: 0;
+ background-color: $color-primary-2;
+ border: none;
+ border-bottom: 5px solid $color-primary-1;
color: $color-primary-4;
+ outline: none;
+ padding: 5px 12px;
+ font-weight: 500;
+
+ &:hover, &:active {
+ background-color: $color-primary-1;
+ color: $color-primary-4;
+ }
+
+ &:focus, &:active {
+ outline: none !important;
+ box-shadow: none !important;
+ }
}
-.btn-primary {
- margin-left: 20px;
- padding: 5px 12px;
+.btn-sm {
+ border: none;
+ font-size: 12px;
+}
+
+.btn-red {
+ color: $color-secondary-4;
background-color: $color-secondary-2;
- font-weight: 500;
- border: 0;
border-bottom: 5px solid $color-secondary-1;
- color: $color-secondary-4;
- outline:none;
+ &:hover, &:active {
+ color: $color-secondary-4;
+ background-color: $color-secondary-1;
+ }
+}
+.btn-blue {
+ color: $color-blue-light;
+ background-color: $color-blue;
+ border-bottom: 5px solid $color-blue-dark;
- :focus, :active {
- outline: none;
- box-shadow: none;
+ &:hover, &:active {
+ color: $color-blue-light;
+ background-color: $color-blue-dark;
}
-
}
-.btn-primary:hover {
- background-color: $color-secondary-1;
- border-color: $color-secondary-1;
- outline: none;
+.btn-green {
+ color: $color-green-light;
+ background-color: $color-green;
+ border-bottom: 5px solid $color-green-dark;
+
+ &:hover, &:active {
+ color: $color-green-light;
+ background-color: $color-green-dark;
+ }
}
a {
@@ -85,4 +108,53 @@ a {
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: $color-secondary-2;
+}
+
+.clickable {
+ cursor: pointer;
+}
+
+.modal-content {
+ background-color: $color-primary-0;
+
+ .modal-header {
+ background-color: $color-primary-2;
+ border: none;
+ }
+
+ .modal-footer {
+ border: none;
+ }
+}
+
+input.form-control, textarea.form-control {
+ background-color: $color-primary-2;
+ border: none;
+ color: $color-primary-4;
+
+ &:focus {
+ background-color: $color-primary-1;
+ color: $color-primary-4;
+ box-shadow: 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;
+ }
+}
+
+textarea.form-control {
+ resize: none;
+}
+
+.table {
+ color: $color-primary-4;
} \ No newline at end of file
diff --git a/frontend/src/utils.js b/frontend/src/utils.js
new file mode 100644
index 0000000..2487f09
--- /dev/null
+++ b/frontend/src/utils.js
@@ -0,0 +1,5 @@
+
+export function createCurlCommand(subCommand, data) {
+ return `curl --request PUT \\\n --url ${window.location.hostname}/api${subCommand} \\\n ` +
+ `--header 'content-type: application/json' \\\n --data '${JSON.stringify(data)}'`
+}
diff --git a/frontend/src/views/App.js b/frontend/src/views/App.js
index 4d80da5..2b444a2 100644
--- a/frontend/src/views/App.js
+++ b/frontend/src/views/App.js
@@ -4,20 +4,35 @@ import './App.scss';
import MainPane from "./MainPane";
import Footer from "./Footer";
import {Route, BrowserRouter as Router, Switch} from "react-router-dom";
+import Services from "./Services";
class App extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ servicesShow: false
+ };
+ }
+
render() {
+ let modal = ""
+ if (this.state.servicesShow) {
+ modal = <Services onHide={() => this.setState({servicesShow: false})} />
+ }
+
return (
<div className="app">
<Router>
- <Header/>
+ <Header onOpenServices={() => this.setState({servicesShow: true})}/>
<Switch>
+ <Route path="/connections/:id" children={<MainPane/>} />
<Route path="/" children={<MainPane/>} />
- <Route path="/connection/:id" children={<MainPane/>} />
</Switch>
+ {modal}
<Footer/>
</Router>
+
</div>
);
}
diff --git a/frontend/src/views/Connections.js b/frontend/src/views/Connections.js
index 5876a40..fa7798e 100644
--- a/frontend/src/views/Connections.js
+++ b/frontend/src/views/Connections.js
@@ -2,8 +2,8 @@ import React, {Component} from 'react';
import './Connections.scss';
import axios from 'axios'
import Connection from "../components/Connection";
-import {Link} from "react-router-dom";
import Table from 'react-bootstrap/Table';
+import {Redirect} from 'react-router';
class Connections extends Component {
constructor(props) {
@@ -19,34 +19,16 @@ class Connections extends Component {
}
render() {
- let connection = {
- "id": "5dd95ff0fe7ae01ae7f419c2",
- "ip_src": "10.62.82.1",
- "ip_dst": "10.62.82.2",
- "port_src": 59113,
- "port_dst": 23179,
- "started_at": "2019-11-23T16:36:00.1Z",
- "closed_at": "2019-11-23T16:36:00.971Z",
- "client_bytes": 331,
- "server_bytes": 85,
- "client_documents": 1,
- "server_documents": 1,
- "processed_at": "2020-04-21T17:10:29.532Z",
- "matched_rules": [],
- "hidden": false,
- "marked": true,
- "comment": "",
- "service": {
- "port": 23179,
- "name": "kaboom",
- "color": "#3C6D3C",
- "notes": "wdddoddddddw"
- }
+ let redirect = ""
+ if (this.state.selected) {
+ redirect = <Redirect push to={"/connections/" + this.state.selected} />;
}
return (
+
<div className="connections">
- <Table striped hover>
+ <div className="connections-header-padding"/>
+ <Table borderless size="sm">
<thead>
<tr>
<th>service</th>
@@ -61,31 +43,16 @@ class Connections extends Component {
</tr>
</thead>
<tbody>
- <tr>
- <td>1</td>
- <td>Mark</td>
- <td>Otto</td>
- <td>@mdo</td>
- </tr>
- <tr>
- <td>2</td>
- <td>Jacob</td>
- <td>Thornton</td>
- <td>@fat</td>
- </tr>
- <tr>
- <td>3</td>
- <td colSpan="2">Larry the Bird</td>
- <td>@twitter</td>
- </tr>
+ {
+ this.state.connections.map(c =>
+ <Connection key={c.id} data={c} onSelected={() => this.setState({selected: c.id})}
+ selected={this.state.selected === c.id}/>
+ )
+ }
</tbody>
</Table>
- {
- this.state.connections.map(c =>
- <Link to={"/connection/" + c.id}><Connection data={c} /></Link>
- )
- }
+ {redirect}
</div>
);
}
diff --git a/frontend/src/views/Connections.scss b/frontend/src/views/Connections.scss
index da74e11..6d2de3f 100644
--- a/frontend/src/views/Connections.scss
+++ b/frontend/src/views/Connections.scss
@@ -2,12 +2,40 @@
.connections {
background-color: $color-primary-3;
- padding: 10px 10px 10px 10px;
+ padding: 0 10px;
+ position: relative;
height: 100%;
overflow: auto;
.table {
- color: $color-primary-4;
+ margin-top: 10px;
}
+
+ .connections-header-padding {
+ position: sticky;
+ height: 10px;
+ background-color: $color-primary-3;
+ top: 0;
+ left: 0;
+ right: 0;
+ margin-bottom: -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;
+ }
+
+ &:hover::-webkit-scrollbar-thumb {
+ background: $color-secondary-2;
+ }
+
+
} \ No newline at end of file
diff --git a/frontend/src/views/Header.js b/frontend/src/views/Header.js
index b3f919b..ac272e6 100644
--- a/frontend/src/views/Header.js
+++ b/frontend/src/views/Header.js
@@ -1,9 +1,17 @@
import React, {Component} from 'react';
import Typed from 'typed.js';
import './Header.scss';
+import {Button} from "react-bootstrap";
class Header extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ servicesShow: false
+ };
+ }
+
componentDidMount() {
const options = {
strings: ["caronte$ "],
@@ -28,15 +36,11 @@ class Header extends Component {
</div>
<div className="col">
<div className="header-buttons">
- <button className="btn-primary">
- ➕ pcaps
- </button>
- <button className="btn-primary">
- ➕ rules
- </button>
- <button className="btn-primary">
- ➕ services
- </button>
+ <Button variant="yellow" size="sm">pcaps</Button>
+ <Button variant="blue">rules</Button>
+ <Button variant="red" onClick={this.props.onOpenServices}>
+ services
+ </Button>
</div>
</div>
</div>
diff --git a/frontend/src/views/MainPane.js b/frontend/src/views/MainPane.js
index 88b5376..0fc083e 100644
--- a/frontend/src/views/MainPane.js
+++ b/frontend/src/views/MainPane.js
@@ -26,6 +26,16 @@ class MainPane extends Component {
}
componentDidMount() {
+ if (this.props.match.params.id !== this.state.id) {
+ const id = this.props.match.params.id;
+ this.setState({id: id});
+
+ axios.get(`/api/streams/${id}`).then(res => this.setState({connectionContent: res.data}))
+
+
+ }
+
+
}
render() {
diff --git a/frontend/src/views/Services.js b/frontend/src/views/Services.js
new file mode 100644
index 0000000..1b3789d
--- /dev/null
+++ b/frontend/src/views/Services.js
@@ -0,0 +1,142 @@
+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';
+
+class Services extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ services: {},
+ port: "",
+ portValid: false
+ }
+
+ this.portChanged = this.portChanged.bind(this);
+ }
+
+ componentDidMount() {
+ axios.get("/api/services").then(res => this.setState({services: res.data}))
+ }
+
+ portChanged(event) {
+ let value = event.target.value.replace(/[^\d]/gi, '')
+ let intValue = parseInt(value)
+ this.setState({port: value, portValid: intValue > 0 && intValue <= 65565})
+
+
+ }
+
+
+ render() {
+ let curl = createCurlCommand("/services", {
+ "port": this.state.port,
+ "name": "aaaaa",
+ "color": "#fff",
+ "notes": "aaa"
+ })
+
+ let rows = Object.values(this.state.services).map(s =>
+ <tr>
+ <td><Button size="sm" style={{
+ "backgroundColor": s.color
+ }}>edit</Button></td>
+ <td>{s.port}</td>
+ <td>{s.name}</td>
+ </tr>
+ )
+
+
+
+
+ return (
+ <Modal
+ {...this.props}
+ show="true"
+ size="lg"
+ aria-labelledby="contained-modal-title-vcenter"
+ centered
+ >
+ <Modal.Header>
+ <Modal.Title id="contained-modal-title-vcenter">
+ ~/services
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Container>
+ <Row>
+ <Col md={7}>
+ <Table borderless size="sm" className="services-list">
+ <thead>
+ <tr>
+ <th><Button size="sm">new</Button></th>
+ <th>name</th>
+ <th>port</th>
+ </tr>
+ </thead>
+ <tbody>
+ {rows}
+ </tbody>
+ </Table>
+ </Col>
+ <Col md={5}>
+ <Form>
+ <Form.Group controlId="servicePort">
+ <Form.Label>port:</Form.Label>
+ <Form.Control type="text" onChange={this.portChanged} value={this.state.port} />
+ <Form.Text className="text-muted">
+ {!this.state.portValid ? "assert(1 <= port <= 65565)" : ""}
+ </Form.Text>
+ </Form.Group>
+
+ <Form.Group controlId="serviceName">
+ <Form.Label>name:</Form.Label>
+ <Form.Control type="text" required min={3} max={16} />
+ <Form.Text className="text-muted">
+ {"assert(len(name) >= 3)"}
+ </Form.Text>
+ </Form.Group>
+
+ <Form.Group controlId="serviceColor">
+ <Form.Label>color:</Form.Label>
+ <ButtonGroup aria-label="Basic example">
+ <Button variant="secondary">Left</Button>
+ <Button variant="secondary">Middle</Button>
+ <Button variant="secondary">Right</Button>
+ </ButtonGroup>
+ </Form.Group>
+
+ <Form.Group controlId="serviceNotes">
+ <Form.Label>notes:</Form.Label>
+ <Form.Control type="textarea" />
+ </Form.Group>
+ </Form>
+
+
+ </Col>
+
+ </Row>
+
+ <Row>
+ <Col md={12}>
+ <InputGroup>
+ <FormControl as="textarea" rows={9} className="curl-output" readOnly={true}>{curl}
+ </FormControl>
+ </InputGroup>
+
+ </Col>
+ </Row>
+ </Container>
+ </Modal.Body>
+ <Modal.Footer className="dialog-footer">
+ <Button variant="green" onClick={this.props.onHide}>save</Button>
+ <Button variant="red" onClick={this.props.onHide}>close</Button>
+ </Modal.Footer>
+ </Modal>
+ );
+ }
+}
+
+export default Services;
diff --git a/frontend/src/views/Services.scss b/frontend/src/views/Services.scss
new file mode 100644
index 0000000..fd65beb
--- /dev/null
+++ b/frontend/src/views/Services.scss
@@ -0,0 +1,16 @@
+.curl-output {
+ width: 100%;
+ font-size: 13px;
+}
+
+.services-list {
+ .btn {
+ width: 150px;
+ }
+}
+
+.dialog-footer {
+ .btn {
+ width: 80px;
+ }
+} \ No newline at end of file