0})}>
{conn.port_src} |
{conn.ip_dst} |
{conn.port_dst} |
+ {dateTimeToTime(conn.started_at)} |
@@ -107,7 +103,7 @@ class Connection extends Component {
|
{formatSize(conn.client_bytes)} |
{formatSize(conn.server_bytes)} |
-
+ |
{/*Hide this connection from the list)}>*/}
{/* obj == null ? null : Object.entries(obj).map(([key, value]) =>
- {key}: {value}
+ {key}: {value}
);
let m = connectionMessage.metadata;
diff --git a/frontend/src/components/ConnectionMatchedRules.js b/frontend/src/components/ConnectionMatchedRules.js
new file mode 100644
index 0000000..21f2a92
--- /dev/null
+++ b/frontend/src/components/ConnectionMatchedRules.js
@@ -0,0 +1,29 @@
+import React, {Component} from 'react';
+import './ConnectionMatchedRules.scss';
+import ButtonField from "./fields/ButtonField";
+
+class ConnectionMatchedRules extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ };
+ }
+
+ render() {
+ const matchedRules = this.props.matchedRules.map(mr => {
+ const rule = this.props.rules.find(r => r.id === mr);
+ return this.props.addMatchedRulesFilter(rule.id)} name={rule.name}
+ color={rule.color} small />;
+ });
+
+ return (
+
+ matched_rules: |
+ {matchedRules} |
+
+ );
+ }
+}
+
+export default ConnectionMatchedRules;
diff --git a/frontend/src/components/ConnectionMatchedRules.scss b/frontend/src/components/ConnectionMatchedRules.scss
new file mode 100644
index 0000000..ed18f3c
--- /dev/null
+++ b/frontend/src/components/ConnectionMatchedRules.scss
@@ -0,0 +1,23 @@
+@import '../colors.scss';
+
+.connection-matches {
+ background-color: $color-primary-0;
+
+ .rule-buttons {
+ padding: 0;
+ }
+
+ .button-field {
+ display: inline-block;
+ margin-right: 5px;
+
+ button {
+ font-size: 0.8em;
+ padding: 2px 10px;
+ }
+ }
+
+ .row-label {
+ font-size: 0.8em;
+ }
+}
diff --git a/frontend/src/components/panels/ConfigurationPane.js b/frontend/src/components/panels/ConfigurationPane.js
new file mode 100644
index 0000000..10309f6
--- /dev/null
+++ b/frontend/src/components/panels/ConfigurationPane.js
@@ -0,0 +1,162 @@
+import React, {Component} from 'react';
+import './common.scss';
+import './ConfigurationPane.scss';
+import LinkPopover from "../objects/LinkPopover";
+import {Col, Container, Row} from "react-bootstrap";
+import InputField from "../fields/InputField";
+import TextField from "../fields/TextField";
+import ButtonField from "../fields/ButtonField";
+import CheckField from "../fields/CheckField";
+import {createCurlCommand} from "../../utils";
+import Table from "react-bootstrap/Table";
+import validation from "../../validation";
+import backend from "../../backend";
+
+class ConfigurationPane extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ settings: {
+ "config": {
+ "server_address": "",
+ "flag_regex": "",
+ "auth_required": false
+ },
+ "accounts": {
+ }
+ },
+ newUsername: "",
+ newPassword: ""
+ };
+ }
+
+ saveSettings = () => {
+ if (this.validateSettings(this.state.settings)) {
+ backend.post("/setup", this.state.settings).then(_ => {
+ this.props.onConfigured();
+ }).catch(res => {
+ this.setState({setupStatusCode: res.status, setupResponse: JSON.stringify(res.json)});
+ });
+ }
+ };
+
+ validateSettings = (settings) => {
+ let valid = true;
+ if (!validation.isValidAddress(settings.config.server_address, true)) {
+ this.setState({serverAddressError: "invalid ip_address"});
+ valid = false;
+ }
+ if (settings.config.flag_regex.length < 8) {
+ this.setState({flagRegexError: "flag_regex.length < 8"});
+ valid = false;
+ }
+
+ return valid;
+ };
+
+ updateParam = (callback) => {
+ callback(this.state.settings);
+ this.setState({settings: this.state.settings});
+ };
+
+ addAccount = () => {
+ if (this.state.newUsername.length !== 0 && this.state.newPassword.length !== 0) {
+ const settings = this.state.settings;
+ settings.accounts[this.state.newUsername] = this.state.newPassword;
+
+ this.setState({
+ newUsername: "",
+ newPassword: "",
+ settings: settings
+ });
+ } else {
+ this.setState({
+ newUsernameActive: this.state.newUsername.length === 0,
+ newPasswordActive: this.state.newPassword.length === 0
+ });
+ }
+ };
+
+ render() {
+ const settings = this.state.settings;
+ const curlCommand = createCurlCommand("/setup", "POST", settings);
+
+ const accounts = Object.entries(settings.accounts).map(([username, password]) =>
+
+ {username} |
+ |
+ this.updateParam((s) => delete s.accounts[username]) }/> |
+ ).concat(
+ this.setState({newUsername: v})} /> |
+ this.setState({newPassword: v})} /> |
+ |
+ );
+
+ return (
+
+
+
+
+
+ POST /setup
+
+
+
+
+
+
+
+ this.updateParam((s) => s.config.server_address = v)} />
+ this.updateParam((s) => s.config.flag_regex = v)}
+ error={this.state.flagRegexError} />
+
+ this.updateParam((s) => s.config.auth_required = v)}/>
+
+
+
+
+
+ accounts:
+
+
+
+
+ username |
+ password |
+ actions |
+
+
+
+ {accounts}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default ConfigurationPane;
diff --git a/frontend/src/components/panels/ConfigurationPane.scss b/frontend/src/components/panels/ConfigurationPane.scss
new file mode 100644
index 0000000..955d2bc
--- /dev/null
+++ b/frontend/src/components/panels/ConfigurationPane.scss
@@ -0,0 +1,19 @@
+@import '../../colors';
+
+.configuration-pane {
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: $color-primary-0;
+
+ .pane {
+ flex-basis: 900px;
+ margin-bottom: 200px;
+ }
+
+ .pane-container {
+ padding-bottom: 1px;
+ }
+
+}
diff --git a/frontend/src/components/panels/MainPane.js b/frontend/src/components/panels/MainPane.js
new file mode 100644
index 0000000..3202d6d
--- /dev/null
+++ b/frontend/src/components/panels/MainPane.js
@@ -0,0 +1,56 @@
+import React, {Component} from 'react';
+import './common.scss';
+import './MainPane.scss';
+import Connections from "../../views/Connections";
+import ConnectionContent from "../ConnectionContent";
+import {Route, Switch, withRouter} from "react-router-dom";
+import PcapPane from "./PcapPane";
+import backend from "../../backend";
+import RulePane from "./RulePane";
+import ServicePane from "./ServicePane";
+
+class MainPane extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ selectedConnection: null,
+ loading: false
+ };
+ }
+
+ componentDidMount() {
+ const match = this.props.location.pathname.match(/^\/connections\/([a-f0-9]{24})$/);
+ if (match != null) {
+ this.setState({loading: true});
+ backend.get(`/api/connections/${match[1]}`)
+ .then(res => this.setState({selectedConnection: res.json, loading: false}))
+ .catch(error => console.log(error));
+ }
+ }
+
+ render() {
+ return (
+
+
+ {
+ !this.state.loading &&
+ this.setState({selectedConnection: c})}
+ initialConnection={this.state.selectedConnection} />
+ }
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+ );
+ }
+}
+
+export default withRouter(MainPane);
diff --git a/frontend/src/components/panels/MainPane.scss b/frontend/src/components/panels/MainPane.scss
new file mode 100644
index 0000000..04be347
--- /dev/null
+++ b/frontend/src/components/panels/MainPane.scss
@@ -0,0 +1,23 @@
+@import '../../colors';
+
+.main-pane {
+ height: 100%;
+ display: flex;
+ padding: 0 15px;
+ background-color: $color-primary-2;
+
+ .pane {
+ flex: 1;
+ }
+
+ .connections-pane {
+ flex: 1 0;
+ margin-right: 7.5px;
+ }
+
+ .details-pane {
+ flex: 1 1;
+ margin-left: 7.5px;
+ }
+
+}
diff --git a/frontend/src/components/panels/PcapPane.js b/frontend/src/components/panels/PcapPane.js
index e83e3da..7b3fde6 100644
--- a/frontend/src/components/panels/PcapPane.js
+++ b/frontend/src/components/panels/PcapPane.js
@@ -109,7 +109,7 @@ class PcapPane extends Component {
render() {
let sessions = this.state.sessions.map(s =>
-
+
{s["id"].substring(0, 8)} |
{dateTimeToTime(s["started_at"])} |
{durationBetween(s["started_at"], s["completed_at"])} |
@@ -119,8 +119,7 @@ class PcapPane extends Component {
|
- download
+ | download
|
);
diff --git a/frontend/src/components/panels/RulePane.js b/frontend/src/components/panels/RulePane.js
index 7cb849c..49364d2 100644
--- a/frontend/src/components/panels/RulePane.js
+++ b/frontend/src/components/panels/RulePane.js
@@ -13,6 +13,7 @@ import ChoiceField from "../fields/ChoiceField";
import ButtonField from "../fields/ButtonField";
import validation from "../../validation";
import LinkPopover from "../objects/LinkPopover";
+import {randomClassName} from "../../utils";
const classNames = require('classnames');
const _ = require('lodash');
@@ -220,7 +221,7 @@ class RulePane extends Component {
const pattern = this.state.selectedPattern || this.state.newPattern;
let rules = this.state.rules.map(r =>
- {
+ {
this.reset();
this.setState({selectedRule: _.cloneDeep(r)});
}} className={classNames("row-small", "row-clickable", {"row-selected": rule.id === r.id })}>
@@ -235,7 +236,7 @@ class RulePane extends Component {
rule.patterns.concat(this.state.newPattern) :
rule.patterns
).map(p => p === pattern ?
-
+
{
@@ -272,7 +273,7 @@ class RulePane extends Component {
|
:
-
+
{p.regex} |
{p.flags.caseless ? "yes": "no"} |
{p.flags.dot_all ? "yes": "no"} |
@@ -377,7 +378,7 @@ class RulePane extends Component {
regex |
- Aa |
+ !Aa |
.* |
\n+ |
UTF8 |
diff --git a/frontend/src/components/panels/ServicePane.js b/frontend/src/components/panels/ServicePane.js
index b21ad6c..eaefa64 100644
--- a/frontend/src/components/panels/ServicePane.js
+++ b/frontend/src/components/panels/ServicePane.js
@@ -100,7 +100,7 @@ class ServicePane extends Component {
const service = this.state.currentService;
let services = this.state.services.map(s =>
- {
+ {
this.reset();
this.setState({isUpdate: true, currentService: _.cloneDeep(s)});
}} className={classNames("row-small", "row-clickable", {"row-selected": service.port === s.port })}>
diff --git a/frontend/src/index.scss b/frontend/src/index.scss
index 9dcc692..9ba23de 100644
--- a/frontend/src/index.scss
+++ b/frontend/src/index.scss
@@ -11,8 +11,7 @@ body {
-moz-osx-font-smoothing: grayscale;
background-color: $color-primary-2;
color: $color-primary-4;
- height: 100%;
- max-height: 100%;
+ height: 100vh;
font-size: 100%;
}
@@ -67,10 +66,6 @@ a {
}
}
-textarea.form-control {
- resize: none;
-}
-
.table {
color: $color-primary-4;
}
diff --git a/frontend/src/views/App.js b/frontend/src/views/App.js
index 8bd5e46..fb4454c 100644
--- a/frontend/src/views/App.js
+++ b/frontend/src/views/App.js
@@ -1,62 +1,46 @@
import React, {Component} from 'react';
+import './App.scss';
import Header from "./Header";
-import MainPane from "./MainPane";
+import MainPane from "../components/panels/MainPane";
import Footer from "./Footer";
import {BrowserRouter as Router} from "react-router-dom";
-import Services from "./Services";
import Filters from "./Filters";
-import Config from "./Config";
+import backend from "../backend";
+import ConfigurationPane from "../components/panels/ConfigurationPane";
class App extends Component {
constructor(props) {
super(props);
- this.state = {
- servicesWindowOpen: false,
- filterWindowOpen: false,
- configWindowOpen: false,
- configDone: false
- };
-
- fetch('/api/services')
- .then(response => {
- if( response.status === 503){
- this.setState({configWindowOpen: true});
- } else if (response.status === 200){
- this.setState({configDone: true});
- }
- });
+ this.state = {};
+ }
+ componentDidMount() {
+ backend.get("/api/services").then(_ => this.setState({configured: true}));
}
render() {
let modal;
- if (this.state.servicesWindowOpen) {
- modal = this.setState({servicesWindowOpen: false})}/>;
- }
- if (this.state.filterWindowOpen) {
+ if (this.state.filterWindowOpen && this.state.configured) {
modal = this.setState({filterWindowOpen: false})}/>;
}
- if (this.state.configWindowOpen) {
- modal = this.setState({configWindowOpen: false})}
- onDone={() => this.setState({configDone: true})}/>;
- }
return (
-
+
- this.setState({servicesWindowOpen: true})}
- onOpenFilters={() => this.setState({filterWindowOpen: true})}
- onOpenConfig={() => this.setState({configWindowOpen: true})}
- onOpenUpload={() => this.setState({uploadWindowOpen: true})}
- onConfigDone={this.state.configDone}
- />
-
- {modal}
-
+
+ this.setState({filterWindowOpen: true})} />
+
+
+ {this.state.configured ? :
+ this.setState({configured: true})} />}
+ {modal}
+
+
+ {this.state.configured && }
+
-
);
}
diff --git a/frontend/src/views/App.scss b/frontend/src/views/App.scss
new file mode 100644
index 0000000..b25d4c9
--- /dev/null
+++ b/frontend/src/views/App.scss
@@ -0,0 +1,15 @@
+
+.main {
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+
+ .main-content {
+ flex: 1 1;
+ overflow: hidden;
+ }
+
+ .main-header, .main-footer {
+ flex: 0 0;
+ }
+}
diff --git a/frontend/src/views/Config.js b/frontend/src/views/Config.js
deleted file mode 100644
index b11f827..0000000
--- a/frontend/src/views/Config.js
+++ /dev/null
@@ -1,242 +0,0 @@
-import React, {Component} from 'react';
-import './Config.scss';
-import {Button, ButtonGroup, Col, Container, Form, Modal, Row, Table, ToggleButton} from "react-bootstrap";
-
-class Config extends Component {
-
- constructor(props) {
- super(props);
-
- this.state = {
- server_address: "",
- flag_regex: "",
- auth_required: false,
- accounts: {},
- showSignup: false,
- showConfig: true,
- tmpUser:"",
- tmpPass:"",
- tmpConf:"",
- errors:""
- };
-
- this.serverIpChanged = this.serverIpChanged.bind(this);
- this.flagRegexChanged = this.flagRegexChanged.bind(this);
- this.authRequiredChanged = this.authRequiredChanged.bind(this);
- this.userChanged = this.userChanged.bind(this);
- this.passwdChanged = this.passwdChanged.bind(this);
- this.confirmChanged = this.confirmChanged.bind(this);
- }
-
- serverIpChanged(event) {
- this.setState({server_address: event.target.value});
- }
-
- flagRegexChanged(event) {
- this.setState({flag_regex: event.target.value});
- }
-
- authRequiredChanged() {
- this.setState({auth_required: !this.value});
- this.checked = !this.checked;
- this.value = !this.value;
- }
-
- userChanged(event) {
- this.setState({tmpUser: event.target.value});
- }
-
- passwdChanged(event) {
- this.setState({tmpPass: event.target.value});
- }
-
- confirmChanged(event) {
- this.setState({tmpConf: event.target.value});
- }
-
- setup() {
- const requestOptions = {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- config: {
- server_address: this.state.server_address,
- flag_regex: this.state.flag_regex,
- auth_required: this.state.auth_required,
- },
- accounts: this.state.accounts
- })
- };
-
- fetch('/setup', requestOptions)
- .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()}
- );
- });
- }
- }
- );
-
- }
-
- signup(){
- if (this.state.tmpPass === this.state.tmpConf){
- const accounts = {...this.state.accounts};
- accounts[this.state.tmpUser] = this.state.tmpPass;
- this.setState({accounts});
- console.log(this.state);
- this.setState({showSignup:false,showConfig:true})
- }
- this.setState({tmpUser : ""});
- this.setState({tmpPass : ""});
- this.setState({tmpConf : ""});
- }
-
- render() {
- let rows = Object.keys(this.state.accounts).map(u =>
-
- {u} |
-
- );
-
-
-
- return (
- <>
-
-
-
- # passwd
-
-
-
-
-
-
- username:
-
-
-
-
- password:
-
-
-
-
- confirm password:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ~/.config
-
-
-
- Warning: once the configuration is completed, it cannot be changed unless you reset caronte :(
-
-
-
-
-
-
- this.authRequiredChanged()}
- >
- Authentication
-
-
-
-
-
-
-
- users |
-
-
-
- {rows}
-
-
- |
-
-
-
-
-
-
-
-
-
-
- server_address:
-
-
-
-
- flag_regex:
-
-
-
-
-
-
-
-
-
-
-
- {this.state.errors
- .split('\n').map((item, key) => {
- return {item} })
- }
-
-
-
-
-
-
-
-
-
-
-
- >
- );
- }
-}
-
-export default Config;
diff --git a/frontend/src/views/Config.scss b/frontend/src/views/Config.scss
deleted file mode 100644
index 331d7a7..0000000
--- a/frontend/src/views/Config.scss
+++ /dev/null
@@ -1,55 +0,0 @@
-@import '../colors.scss';
-
-.curl-output {
- width: 100%;
- font-size: 13px;
-}
-
-#passwd-form {
- margin:auto;
-}
-
-.users-list {
- .btn {
- width: 70px;
- }
-
- td {
- background-color: $color-primary-2;
- border-top: 2px solid $color-primary-0;
- vertical-align: middle;
- text-align: center;
- }
-
- th {
- background-color: $color-primary-1;
- text-align: center;
- }
-}
-
-.btn-color {
- border: 3px solid #fff;
-}
-
-.dialog-footer {
- .btn {
- width: 80px;
- }
-}
-
-.blink{
-
- span{
- animation: blink 1s linear infinite;
- }
- @keyframes blink{
- 0%{opacity: 0;}
- 50%{opacity: .5;}
- 100%{opacity: 1;}
- }
-
-}
-
-.error{
- color: red;
-}
diff --git a/frontend/src/views/Connections.js b/frontend/src/views/Connections.js
index 9dca7e9..73979c4 100644
--- a/frontend/src/views/Connections.js
+++ b/frontend/src/views/Connections.js
@@ -5,6 +5,7 @@ import Table from 'react-bootstrap/Table';
import {Redirect} from 'react-router';
import {withRouter} from "react-router-dom";
import backend from "../backend";
+import ConnectionMatchedRules from "../components/ConnectionMatchedRules";
class Connections extends Component {
@@ -62,11 +63,21 @@ class Connections extends Component {
};
addServicePortFilter = (port) => {
- let urlParams = new URLSearchParams(this.props.location.search);
+ const urlParams = new URLSearchParams(this.props.location.search);
urlParams.set("service_port", port);
this.setState({queryString: "?" + urlParams});
};
+ addMatchedRulesFilter = (matchedRule) => {
+ const urlParams = new URLSearchParams(this.props.location.search);
+ const oldMatchedRules = urlParams.getAll("matched_rules") || [];
+
+ if (!oldMatchedRules.includes(matchedRule)) {
+ urlParams.append("matched_rules", matchedRule);
+ this.setState({queryString: "?" + urlParams});
+ }
+ };
+
async loadConnections(params) {
let url = "/api/connections";
const urlParams = new URLSearchParams(this.props.location.search);
@@ -112,20 +123,15 @@ class Connections extends Component {
}
}
- let flagRule = this.state.flagRule;
let rules = this.state.rules;
- if (flagRule === null) {
+ if (rules === null) {
rules = (await backend.get("/api/rules")).json;
- flagRule = rules.filter(rule => {
- return rule.name === "flag";
- })[0];
}
this.setState({
loading: false,
connections: connections,
- rules: res,
- flagRule: flagRule,
+ rules: rules,
firstConnection: firstConnection,
lastConnection: lastConnection
});
@@ -158,6 +164,7 @@ class Connections extends Component {
srcport |
dstip |
dstport |
+ started_at |
duration |
up |
down |
@@ -166,13 +173,18 @@ class Connections extends Component {
{
- this.state.connections.map(c =>
- this.connectionSelected(c)}
- selected={this.state.selected === c.id} onMarked={marked => c.marked = marked}
- onEnabled={enabled => c.hidden = !enabled}
- containsFlag={this.state.flagRule && c.matched_rules.includes(this.state.flagRule.id)}
- addServicePortFilter={this.addServicePortFilter}/>
- )
+ this.state.connections.flatMap(c => {
+ return [ this.connectionSelected(c)}
+ selected={this.state.selected === c.id}
+ onMarked={marked => c.marked = marked}
+ onEnabled={enabled => c.hidden = !enabled}
+ addServicePortFilter={this.addServicePortFilter} />,
+ c.matched_rules.length > 0 &&
+
+ ];
+ })
}
{loading}
diff --git a/frontend/src/views/Filters.js b/frontend/src/views/Filters.js
index b62e7eb..ba7d467 100644
--- a/frontend/src/views/Filters.js
+++ b/frontend/src/views/Filters.js
@@ -1,5 +1,4 @@
import React, {Component} from 'react';
-import './Services.scss';
import {Col, Container, Modal, Row, Table} from "react-bootstrap";
import {filtersDefinitions, filtersNames} from "../components/filters/FiltersDefinitions";
import ButtonField from "../components/fields/ButtonField";
diff --git a/frontend/src/views/Header.js b/frontend/src/views/Header.js
index c38c22d..944f1d5 100644
--- a/frontend/src/views/Header.js
+++ b/frontend/src/views/Header.js
@@ -46,7 +46,7 @@ class Header extends Component {
render() {
let quickFilters = filtersNames.filter(name => this.state[`${name}_active`])
- .map(name => filtersDefinitions[name])
+ .map(name => {filtersDefinitions[name]})
.slice(0, 5);
return (
@@ -78,8 +78,9 @@ class Header extends Component {
-
+
+
+
diff --git a/frontend/src/views/MainPane.js b/frontend/src/views/MainPane.js
deleted file mode 100644
index d2950ab..0000000
--- a/frontend/src/views/MainPane.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import React, {Component} from 'react';
-import './MainPane.scss';
-import Connections from "./Connections";
-import ConnectionContent from "../components/ConnectionContent";
-import {Route, Switch, withRouter} from "react-router-dom";
-import PcapPane from "../components/panels/PcapPane";
-import backend from "../backend";
-import RulePane from "../components/panels/RulePane";
-import ServicePane from "../components/panels/ServicePane";
-
-class MainPane extends Component {
-
- constructor(props) {
- super(props);
- this.state = {
- selectedConnection: null,
- loading: false
- };
- }
-
- componentDidMount() {
- const match = this.props.location.pathname.match(/^\/connections\/([a-f0-9]{24})$/);
- if (match != null) {
- this.setState({loading: true});
- backend.get(`/api/connections/${match[1]}`)
- .then(res => this.setState({selectedConnection: res.json, loading: false}))
- .catch(error => console.log(error));
- }
- }
-
- render() {
- return (
-
-
-
-
- {
- !this.state.loading &&
- this.setState({selectedConnection: c})}
- initialConnection={this.state.selectedConnection} />
- }
-
-
-
- } />
- } />
- } />
- } />
- } />
-
-
-
-
-
- );
- }
-}
-
-export default withRouter(MainPane);
diff --git a/frontend/src/views/MainPane.scss b/frontend/src/views/MainPane.scss
deleted file mode 100644
index 20720ba..0000000
--- a/frontend/src/views/MainPane.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-.main-pane {
- .pane {
- height: calc(100vh - 210px);
- position: relative;
- }
-}
diff --git a/frontend/src/views/Services.js b/frontend/src/views/Services.js
deleted file mode 100644
index 97368dc..0000000
--- a/frontend/src/views/Services.js
+++ /dev/null
@@ -1,210 +0,0 @@
-import React, {Component} from 'react';
-import './Services.scss';
-import {Button, ButtonGroup, Col, Container, Form, FormControl, InputGroup, Modal, Row, Table} from "react-bootstrap";
-import {createCurlCommand} from '../utils';
-import backend from "../backend";
-
-class Services extends Component {
-
- constructor(props) {
- super(props);
- this.alphabet = 'abcdefghijklmnopqrstuvwxyz';
- this.colors = ["#E53935", "#D81B60", "#8E24AA", "#5E35B1", "#3949AB", "#1E88E5", "#039BE5", "#00ACC1",
- "#00897B", "#43A047", "#7CB342", "#9E9D24", "#F9A825", "#FB8C00", "#F4511E", "#6D4C41"];
-
- this.state = {
- services: {},
- port: 0,
- portValid: false,
- name: "",
- nameValid: false,
- color: this.colors[0],
- colorValid: false,
- notes: ""
- };
-
- this.portChanged = this.portChanged.bind(this);
- this.nameChanged = this.nameChanged.bind(this);
- this.notesChanged = this.notesChanged.bind(this);
- this.newService = this.newService.bind(this);
- this.editService = this.editService.bind(this);
- this.saveService = this.saveService.bind(this);
- this.loadServices = this.loadServices.bind(this);
- }
-
- componentDidMount() {
- this.loadServices();
- }
-
- portChanged(event) {
- let value = event.target.value.replace(/[^\d]/gi, '');
- let port = 0;
- if (value !== "") {
- port = parseInt(value);
- }
- this.setState({port: port});
- }
-
- nameChanged(event) {
- let value = event.target.value.replace(/[\s]/gi, '_').replace(/[^\w]/gi, '').toLowerCase();
- this.setState({name: value});
- }
-
- notesChanged(event) {
- this.setState({notes: event.target.value});
- }
-
- newService() {
- this.setState({name: "", port: 0, notes: ""});
- }
-
- editService(service) {
- this.setState({name: service.name, port: service.port, color: service.color, notes: service.notes});
- }
-
- saveService() {
- if (this.state.portValid && this.state.nameValid) {
- 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() {
- backend.get("/api/services").then(res => this.setState({services: res}));
- }
-
- componentDidUpdate(prevProps, prevState, snapshot) {
- if (this.state.name != null && prevState.name !== this.state.name) {
- this.setState({
- nameValid: this.state.name.length >= 3
- });
- }
- if (prevState.port !== this.state.port) {
- this.setState({
- portValid: this.state.port > 0 && this.state.port <= 65565
- });
- }
- }
-
- render() {
- let output = "";
- if (!this.state.portValid) {
- output += "assert(1 <= port <= 65565)\n";
- }
- if (!this.state.nameValid) {
- output += "assert(len(name) >= 3)\n";
- }
- if (output === "") {
- output = createCurlCommand("/services", "PUT", {
- "port": this.state.port,
- "name": this.state.name,
- "color": this.state.color,
- "notes": this.state.notes
- });
- }
- let rows = Object.values(this.state.services).map(s =>
-
- |
- {s.port} |
- {s.name} |
-
- );
-
- let colorButtons = this.colors.map((color, i) =>
- );
-
- return (
-
-
-
- ~/services
-
-
-
-
-
-
-
-
-
- |
- port |
- name |
-
-
-
- {rows}
-
-
-
-
-
- port:
-
-
-
-
- name:
-
-
-
-
- color:
-
- {colorButtons.slice(0, 8)}
-
-
- {colorButtons.slice(8, 18)}
-
-
-
-
- notes:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
-
-export default Services;
diff --git a/frontend/src/views/Services.scss b/frontend/src/views/Services.scss
deleted file mode 100644
index 2abb55e..0000000
--- a/frontend/src/views/Services.scss
+++ /dev/null
@@ -1,32 +0,0 @@
-@import '../colors.scss';
-
-.curl-output {
- width: 100%;
- font-size: 13px;
-}
-
-.services-list {
- .btn {
- width: 70px;
- }
-
- td {
- background-color: $color-primary-2;
- border-top: 2px solid $color-primary-0;
- vertical-align: middle;
- }
-
- th {
- background-color: $color-primary-1;
- }
-}
-
-.btn-color {
- border: 3px solid #fff;
-}
-
-.dialog-footer {
- .btn {
- width: 80px;
- }
-}
--
cgit v1.2.3-70-g09d2
|