diff --git a/src/components/settings/DHCPInfo.js b/src/components/settings/DHCPInfo.js index ddc9d4e..b178b36 100644 --- a/src/components/settings/DHCPInfo.js +++ b/src/components/settings/DHCPInfo.js @@ -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 ( -
-        {t("DHCP Active")}: {this.state.active.toString()}
-        
- {t("Start IP")}: {this.state.ip_start} -
- {t("End IP")}: {this.state.ip_end} -
- {t("Router IP")}: {this.state.router_ip} -
- {t("Lease Time")}: {this.state.lease_time} h
- {t("Domain")}: {this.state.domain} -
- {t("IPv6 Support")}: {this.state.ipv6_support.toString()} -
-
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + Hours + + + + + + + + + + + + + +
); } } diff --git a/src/components/settings/DNSInfo.js b/src/components/settings/DNSInfo.js index 8a6d9b3..5ecabf6 100644 --- a/src/components/settings/DNSInfo.js +++ b/src/components/settings/DNSInfo.js @@ -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() { diff --git a/src/components/settings/FTLInfo.js b/src/components/settings/FTLInfo.js index d87d00c..71f4bac 100644 --- a/src/components/settings/FTLInfo.js +++ b/src/components/settings/FTLInfo.js @@ -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() { diff --git a/src/components/settings/NetworkInfo.js b/src/components/settings/NetworkInfo.js index d4c7ce2..0cd94af 100644 --- a/src/components/settings/NetworkInfo.js +++ b/src/components/settings/NetworkInfo.js @@ -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 ( - -
- - - - - {this.state.interface} - - - - - - - - {this.state.ipv4_address} - - - - - - - - {this.state.ipv6_address} - - - - - - - - {this.state.hostname} - - - -
-
+
+ + + + + {this.state.interface} + + + + + + + + {this.state.ipv4_address} + + + + + + + + {this.state.ipv6_address} + + + + + + + + {this.state.hostname} + + + +
); } } diff --git a/src/components/settings/VersionInfo.js b/src/components/settings/VersionInfo.js index 5c6b59b..ced1ce5 100644 --- a/src/components/settings/VersionInfo.js +++ b/src/components/settings/VersionInfo.js @@ -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() { diff --git a/src/scss/_custom.scss b/src/scss/_custom.scss index a83320a..5548db6 100644 --- a/src/scss/_custom.scss +++ b/src/scss/_custom.scss @@ -178,3 +178,7 @@ width: 100%; text-align: left; } + +.form-check-label { + margin-bottom: 10px; +} diff --git a/src/validate.js b/src/validate.js index c802e9c..2bb61ce 100644 --- a/src/validate.js +++ b/src/validate.js @@ -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 + ); +} diff --git a/src/validate.test.js b/src/validate.test.js index da85b85..17eda8b 100644 --- a/src/validate.test.js +++ b/src/validate.test.js @@ -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); + }); + }); });