Merge branch 'development' into footer-dev

This commit is contained in:
Mark Drobnak
2018-11-05 13:07:44 -05:00
committed by GitHub
8 changed files with 316 additions and 173 deletions
+160 -34
View File
@@ -11,16 +11,27 @@
import React, { Component } from "react";
import { translate } from "react-i18next";
import { api, ignoreCancel, makeCancelable } from "../../utils";
import {
Button,
Col,
Form,
FormGroup,
Input,
InputGroup,
InputGroupAddon,
Label
} from "reactstrap";
import { isPositiveNumber, isValidHostname, isValidIpv4 } from "../../validate";
class DHCPInfo extends Component {
state = {
active: "---",
ip_start: "---",
ip_end: "---",
router_ip: "---",
lease_time: "---",
domain: "---",
ipv6_support: "---"
active: false,
ip_start: "",
ip_end: "",
router_ip: "",
lease_time: 24,
domain: "",
ipv6_support: false
};
constructor(props) {
@@ -45,18 +56,7 @@ class DHCPInfo extends Component {
ipv6_support: res.ipv6_support
});
})
.catch(ignoreCancel)
.catch(() => {
this.setState({
active: "-!-",
ip_start: "-!-",
ip_end: "-!-",
router_ip: "-!-",
lease_time: "-!-",
domain: "-!-",
ipv6_support: "-!-"
});
});
.catch(ignoreCancel);
}
componentDidMount() {
@@ -67,25 +67,151 @@ class DHCPInfo extends Component {
this.updateHandler.cancel();
}
/**
* Create a function which will update the key in the state with the value
* of the event attribute.
*
* @param key {string} the state to update
* @param attr {string} the event target attribute to use
* @returns {function(Event)}
*/
onChange = (key, attr) => {
return e =>
this.setState({
[key]: e.target[attr]
});
};
/**
* Save changes to DHCP settings
*
* @param e the submit event
*/
saveSettings = e => {
e.preventDefault();
// TODO: send settings to API
};
render() {
const { t } = this.props;
const isIpStartValid = isValidIpv4(this.state.ip_start);
const isIpEndValid = isValidIpv4(this.state.ip_end);
const isRouterIpValid = isValidIpv4(this.state.router_ip);
const isLeaseTimeValid = isPositiveNumber(this.state.lease_time);
const isDomainValid = isValidHostname(this.state.domain);
return (
<pre>
{t("DHCP Active")}: {this.state.active.toString()}
<br />
{t("Start IP")}: {this.state.ip_start}
<br />
{t("End IP")}: {this.state.ip_end}
<br />
{t("Router IP")}: {this.state.router_ip}
<br />
{t("Lease Time")}: {this.state.lease_time} h<br />
{t("Domain")}: {this.state.domain}
<br />
{t("IPv6 Support")}: {this.state.ipv6_support.toString()}
<br />
</pre>
<Form onSubmit={this.saveSettings}>
<FormGroup check>
<Label check>
<Input
type="checkbox"
checked={this.state.active}
onChange={this.onChange("active", "checked")}
/>
{t("Enabled")}
</Label>
</FormGroup>
<FormGroup row>
<Label for="startIP" sm={2}>
{t("Start IP")}
</Label>
<Col sm={10}>
<Input
id="startIP"
disabled={!this.state.active}
value={this.state.ip_start}
onChange={this.onChange("ip_start", "value")}
invalid={this.state.active && !isIpStartValid}
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="endIP" sm={2}>
{t("End IP")}
</Label>
<Col sm={10}>
<Input
id="endIP"
disabled={!this.state.active}
value={this.state.ip_end}
onChange={this.onChange("ip_end", "value")}
invalid={this.state.active && !isIpEndValid}
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="routerIP" sm={2}>
{t("Router IP")}
</Label>
<Col sm={10}>
<Input
id="routerIP"
disabled={!this.state.active}
value={this.state.router_ip}
onChange={this.onChange("router_ip", "value")}
invalid={this.state.active && !isRouterIpValid}
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="leaseTime" sm={2}>
{t("Lease Time")}
</Label>
<Col sm={10}>
<InputGroup>
<Input
id="leaseTime"
disabled={!this.state.active}
value={this.state.lease_time}
onChange={this.onChange("lease_time", "value")}
invalid={this.state.active && !isLeaseTimeValid}
/>
<InputGroupAddon addonType={"append"}>Hours</InputGroupAddon>
</InputGroup>
</Col>
</FormGroup>
<FormGroup row>
<Label for="domain" sm={2}>
{t("Domain")}
</Label>
<Col sm={10}>
<Input
id="domain"
disabled={!this.state.active}
value={this.state.domain}
onChange={this.onChange("domain", "value")}
invalid={this.state.active && !isDomainValid}
/>
</Col>
</FormGroup>
<FormGroup check>
<Label check>
<Input
type="checkbox"
disabled={!this.state.active}
checked={this.state.ipv6_support}
onChange={this.onChange("ipv6_support", "checked")}
/>
{t("IPv6 Support")}
</Label>
</FormGroup>
<Button
type="submit"
disabled={
this.state.active &&
(!isIpStartValid ||
!isIpEndValid ||
!isRouterIpValid ||
!isLeaseTimeValid ||
!isDomainValid)
}
>
{t("Apply")}
</Button>
</Form>
);
}
}
+9 -25
View File
@@ -14,17 +14,17 @@ import { api, ignoreCancel, makeCancelable } from "../../utils";
class DNSInfo extends Component {
state = {
upstream_dns: ["---"],
upstream_dns: [],
conditional_forwarding: {
enabled: "---",
router_ip: "---",
domain: "---"
enabled: false,
router_ip: "",
domain: ""
},
options: {
fqdn_required: "---",
bogus_priv: "---",
dnssec: "---",
listening_type: "---"
fqdn_required: false,
bogus_priv: false,
dnssec: false,
listening_type: "single"
}
};
@@ -42,23 +42,7 @@ class DNSInfo extends Component {
.then(res => {
this.setState(res);
})
.catch(ignoreCancel)
.catch(() => {
this.setState({
upstream_dns: ["-!-"],
options: {
fqdn_required: "-!-",
bogus_priv: "-!-",
dnssec: "-!-",
listening_type: "-!-"
},
conditional_forwarding: {
enabled: "-!-",
router_ip: "-!-",
domain: "-!-"
}
});
});
.catch(ignoreCancel);
}
componentDidMount() {
+4 -11
View File
@@ -14,9 +14,9 @@ import { api, ignoreCancel, makeCancelable } from "../../utils";
class FTLInfo extends Component {
state = {
filesize: "---",
queries: "---",
sqlite_version: "---"
filesize: 0,
queries: 0,
sqlite_version: ""
};
constructor(props) {
@@ -37,14 +37,7 @@ class FTLInfo extends Component {
sqlite_version: res.sqlite_version
});
})
.catch(ignoreCancel)
.catch(() => {
this.setState({
queries: "-!-",
filesize: "-!-",
sqlite_version: "-!-"
});
});
.catch(ignoreCancel);
}
componentDidMount() {
+48 -58
View File
@@ -8,17 +8,17 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
import React, { Component, Fragment } from "react";
import React, { Component } from "react";
import { translate } from "react-i18next";
import { api, ignoreCancel, makeCancelable } from "../../utils";
import { Col, Form, FormGroup, Input, Label } from "reactstrap";
class NetworkInfo extends Component {
state = {
interface: "---",
ipv4_address: "---",
ipv6_address: "---",
hostname: "---"
interface: "",
ipv4_address: "",
ipv6_address: "",
hostname: ""
};
constructor(props) {
@@ -40,15 +40,7 @@ class NetworkInfo extends Component {
hostname: res.hostname
});
})
.catch(ignoreCancel)
.catch(() => {
this.setState({
interface: "-!-",
ipv4_address: "-!-",
ipv6_address: "-!-",
hostname: "-!-"
});
});
.catch(ignoreCancel);
}
componentDidMount() {
@@ -63,50 +55,48 @@ class NetworkInfo extends Component {
const { t } = this.props;
return (
<Fragment>
<Form>
<FormGroup row>
<Label className="bold" for="interface" sm={4}>
{t("Interface")}
</Label>
<Col sm={8}>
<Input plaintext id="interface">
{this.state.interface}
</Input>
</Col>
</FormGroup>
<FormGroup row>
<Label className="bold" for="ipv4_address" sm={4}>
{t("IPv4 address")}
</Label>
<Col sm={8}>
<Input plaintext id="ipv4_address">
{this.state.ipv4_address}
</Input>
</Col>
</FormGroup>
<FormGroup row>
<Label className="bold" for="ipv6_address" sm={4}>
{t("IPv6 address")}
</Label>
<Col sm={8}>
<Input plaintext id="ipv6_address">
{this.state.ipv6_address}
</Input>
</Col>
</FormGroup>
<FormGroup row>
<Label className="bold" for="hostname" sm={4}>
{t("Hostname")}
</Label>
<Col sm={8}>
<Input plaintext id="hostname">
{this.state.hostname}
</Input>
</Col>
</FormGroup>
</Form>
</Fragment>
<Form>
<FormGroup row>
<Label className="bold" for="interface" sm={4}>
{t("Interface")}
</Label>
<Col sm={8}>
<Input plaintext id="interface">
{this.state.interface}
</Input>
</Col>
</FormGroup>
<FormGroup row>
<Label className="bold" for="ipv4_address" sm={4}>
{t("IPv4 address")}
</Label>
<Col sm={8}>
<Input plaintext id="ipv4_address">
{this.state.ipv4_address}
</Input>
</Col>
</FormGroup>
<FormGroup row>
<Label className="bold" for="ipv6_address" sm={4}>
{t("IPv6 address")}
</Label>
<Col sm={8}>
<Input plaintext id="ipv6_address">
{this.state.ipv6_address}
</Input>
</Col>
</FormGroup>
<FormGroup row>
<Label className="bold" for="hostname" sm={4}>
{t("Hostname")}
</Label>
<Col sm={8}>
<Input plaintext id="hostname">
{this.state.hostname}
</Input>
</Col>
</FormGroup>
</Form>
);
}
}
+13 -37
View File
@@ -16,24 +16,24 @@ import VersionCard from "./VersionCard";
class VersionInfo extends Component {
state = {
api: {
branch: "---",
hash: "---",
tag: "---"
branch: "unknown",
hash: "unknown",
tag: "unknown"
},
core: {
branch: "---",
hash: "---",
tag: "---"
branch: "unknown",
hash: "unknown",
tag: "unknown"
},
ftl: {
branch: "---",
hash: "---",
tag: "---"
branch: "unknown",
hash: "unknown",
tag: "unknown"
},
web: {
branch: "---",
hash: "---",
tag: "---"
branch: "unknown",
hash: "unknown",
tag: "unknown"
}
};
@@ -51,31 +51,7 @@ class VersionInfo extends Component {
.then(res => {
this.setState(res);
})
.catch(ignoreCancel)
.catch(() => {
this.setState({
api: {
branch: "-!-",
hash: "-!-",
tag: "-!-"
},
core: {
branch: "-!-",
hash: "-!-",
tag: "-!-"
},
ftl: {
branch: "-!-",
hash: "-!-",
tag: "-!-"
},
web: {
branch: "-!-",
hash: "-!-",
tag: "-!-"
}
});
});
.catch(ignoreCancel);
}
componentDidMount() {
+4
View File
@@ -178,3 +178,7 @@
width: 100%;
text-align: left;
}
.form-check-label {
margin-bottom: 10px;
}
+25 -3
View File
@@ -21,7 +21,7 @@ export function isValidHostname(hostname) {
// Must not be all numbers and periods
const joined = segments.join("");
// If the hostname without periods make a number, deny
if (isStrictNumeric(joined)) return false;
if (isPositiveNumber(joined)) return false;
return /^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)+(\.([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*))*$/.test(
hostname
@@ -36,10 +36,10 @@ export function isValidDomain(domain) {
return isValidHostname(domain);
}
export function isStrictNumeric(input) {
export function isPositiveNumber(input) {
// Because parseInt has limitations, e.g. parseInt("15ex") is parsed to 15
// Caution, does not work with negative numbers, replace with /^(\-|\+)?([0-9])$/ if needed
return /^[0-9]*$/.test(input);
return /^[0-9]+$/.test(input);
}
export function isValidRegex(regex) {
@@ -50,3 +50,25 @@ export function isValidRegex(regex) {
}
return true;
}
/**
* Check if the string is a valid IPv4 address
*
* @param address {string} the address to check
* @returns {boolean} if the address is a valid IPv4 address
*/
export function isValidIpv4(address) {
const segments = address.split(".");
// Must have 4 segments
if (segments.length !== 4) {
return false;
}
// No segment can be longer than 3 characters or be empty
// All segments must be numbers (positive)
return segments.every(
segment => isPositiveNumber(segment) && parseInt(segment) < 256
);
}
+53 -5
View File
@@ -8,7 +8,12 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
import { isStrictNumeric, isValidDomain, isValidRegex } from "./validate";
import {
isPositiveNumber,
isValidDomain,
isValidIpv4,
isValidRegex
} from "./validate";
describe("Testing the validation functions", () => {
describe("isValidDomain", () => {
@@ -102,19 +107,24 @@ describe("Testing the validation functions", () => {
});
});
describe("isStrictNumeric", () => {
describe("isPositiveNumber", () => {
it("passes 1234567890", () => {
const result = isStrictNumeric("1234567890");
const result = isPositiveNumber("1234567890");
expect(result).toBe(true);
});
it("fails 1234a567890", () => {
const result = isStrictNumeric("1234a567890");
const result = isPositiveNumber("1234a567890");
expect(result).toBe(false);
});
it("fails 1100101O1", () => {
const result = isStrictNumeric("1100101O1");
const result = isPositiveNumber("1100101O1");
expect(result).toBe(false);
});
it("fails empty string", () => {
const result = isPositiveNumber("");
expect(result).toBe(false);
});
});
@@ -140,4 +150,42 @@ describe("Testing the validation functions", () => {
expect(isValidRegex("[0-")).toBe(false);
});
});
describe("isValidIpv4", () => {
it("passes 127.0.0.1", () => {
expect(isValidIpv4("127.0.0.1")).toBe(true);
});
it("passes 1.1.1.1", () => {
expect(isValidIpv4("1.1.1.1")).toBe(true);
});
it("passes 111.111.111.111", () => {
expect(isValidIpv4("111.111.111.111")).toBe(true);
});
it("fails 1111.1.1.1", () => {
expect(isValidIpv4("1111.1.1.1")).toBe(false);
});
it("fails 1.1.1.", () => {
expect(isValidIpv4("1.1.1.")).toBe(false);
});
it("fails empty string", () => {
expect(isValidIpv4("")).toBe(false);
});
it("fails 8.8", () => {
expect(isValidIpv4("8.8")).toBe(false);
});
it("fails 10. 10.1.1", () => {
expect(isValidIpv4("10. 10.1.1")).toBe(false);
});
it("fails 555.666.777.888", () => {
expect(isValidIpv4("555.666.777.888")).toBe(false);
});
});
});