mirror of
https://github.com/gosticks/web.git
synced 2026-10-06 06:37:07 +00:00
Convert settings components to TypeScript
Signed-off-by: Mcat12 <newtoncat12@yahoo.com>
This commit is contained in:
Generated
+10
@@ -1505,6 +1505,16 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-bootstrap-typeahead": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-bootstrap-typeahead/-/react-bootstrap-typeahead-3.4.0.tgz",
|
||||
"integrity": "sha512-J2JFcXWoY4234vnBGGh41PdTLMLQT9tZzkn1iBzuf/lRJv+tnGX2BtmbFUmKhwFzGFbjHqKubUvtHnFsYNohRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*",
|
||||
"csstype": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"version": "16.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.0.11.tgz",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"@types/node": "^10.12.18",
|
||||
"@types/react": "^16.7.20",
|
||||
"@types/react-bootstrap-daterangepicker": "0.0.26",
|
||||
"@types/react-bootstrap-typeahead": "^3.4.0",
|
||||
"@types/react-dom": "^16.0.11",
|
||||
"@types/react-router-dom": "^4.3.1",
|
||||
"@types/react-table": "^6.7.21",
|
||||
|
||||
+16
-10
@@ -9,8 +9,22 @@
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React, { Fragment } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Col, FormGroup, Input, Label } from "reactstrap";
|
||||
import i18next from "i18next";
|
||||
|
||||
export interface ConditionalForwardingObject {
|
||||
enabled: boolean,
|
||||
routerIp: string,
|
||||
domain: string
|
||||
}
|
||||
|
||||
export interface ConditionalForwardingSettingsProps {
|
||||
settings: ConditionalForwardingObject,
|
||||
onUpdate: (settings: ConditionalForwardingObject) => void,
|
||||
isRouterIpValid: boolean;
|
||||
isDomainValid: boolean;
|
||||
t: i18next.TranslationFunction
|
||||
}
|
||||
|
||||
const ConditionalForwardingSettings = ({
|
||||
settings,
|
||||
@@ -18,7 +32,7 @@ const ConditionalForwardingSettings = ({
|
||||
isRouterIpValid,
|
||||
isDomainValid,
|
||||
t
|
||||
}) => (
|
||||
}: ConditionalForwardingSettingsProps) => (
|
||||
<Fragment>
|
||||
<FormGroup check>
|
||||
<Label check>
|
||||
@@ -61,12 +75,4 @@ const ConditionalForwardingSettings = ({
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
ConditionalForwardingSettings.propTypes = {
|
||||
settings: PropTypes.object.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
isRouterIpValid: PropTypes.bool.isRequired,
|
||||
isDomainValid: PropTypes.bool.isRequired,
|
||||
t: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ConditionalForwardingSettings;
|
||||
@@ -8,31 +8,26 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React, { Component } from "react";
|
||||
import { withNamespaces } from "react-i18next";
|
||||
import { ignoreCancel, makeCancelable } from "../../util";
|
||||
import api from "../../util/api";
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Form,
|
||||
FormGroup,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
Label
|
||||
} from "reactstrap";
|
||||
import {
|
||||
isPositiveNumber,
|
||||
isValidHostname,
|
||||
isValidIpv4
|
||||
} from "../../util/validate";
|
||||
import Alert from "../common/Alert";
|
||||
import React, { ChangeEvent, Component, FormEvent } from "react";
|
||||
import { WithNamespaces, withNamespaces } from "react-i18next";
|
||||
import { CancelablePromise, ignoreCancel, makeCancelable } from "../../util";
|
||||
import api, { ApiDhcpSettings, ApiResultResponse } from "../../util/api";
|
||||
import { Button, Col, Form, FormGroup, Input, InputGroup, InputGroupAddon, Label } from "reactstrap";
|
||||
import { isValidHostname, isValidIpv4 } from "../../util/validate";
|
||||
import Alert, { AlertType } from "../common/Alert";
|
||||
|
||||
class DHCPInfo extends Component {
|
||||
state = {
|
||||
export interface DHCPInfoState {
|
||||
alertMessage: string;
|
||||
alertType: AlertType;
|
||||
showAlert: boolean;
|
||||
processing: boolean;
|
||||
settings: ApiDhcpSettings;
|
||||
}
|
||||
|
||||
class DHCPInfo extends Component<WithNamespaces, DHCPInfoState> {
|
||||
state: DHCPInfoState = {
|
||||
alertMessage: "",
|
||||
alertType: "",
|
||||
alertType: "info",
|
||||
showAlert: false,
|
||||
processing: false,
|
||||
settings: {
|
||||
@@ -40,27 +35,20 @@ class DHCPInfo extends Component {
|
||||
ip_start: "",
|
||||
ip_end: "",
|
||||
router_ip: "",
|
||||
lease_time: "",
|
||||
lease_time: 0,
|
||||
domain: "",
|
||||
ipv6_support: false
|
||||
}
|
||||
};
|
||||
|
||||
private loadHandler: undefined | CancelablePromise<ApiDhcpSettings>;
|
||||
private updateHandler: undefined | CancelablePromise<ApiResultResponse>;
|
||||
|
||||
loadDHCPInfo = () => {
|
||||
this.loadHandler = makeCancelable(api.getDHCPInfo());
|
||||
this.loadHandler.promise
|
||||
.then(res => {
|
||||
this.setState({
|
||||
settings: {
|
||||
active: res.active,
|
||||
ip_start: res.ip_start,
|
||||
ip_end: res.ip_end,
|
||||
router_ip: res.router_ip,
|
||||
lease_time: res.lease_time,
|
||||
domain: res.domain,
|
||||
ipv6_support: res.ipv6_support
|
||||
}
|
||||
});
|
||||
this.setState({ settings: res });
|
||||
})
|
||||
.catch(ignoreCancel);
|
||||
};
|
||||
@@ -70,7 +58,9 @@ class DHCPInfo extends Component {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.loadHandler.cancel();
|
||||
if (this.loadHandler) {
|
||||
this.loadHandler.cancel();
|
||||
}
|
||||
|
||||
if (this.updateHandler) {
|
||||
this.updateHandler.cancel();
|
||||
@@ -85,9 +75,10 @@ class DHCPInfo extends Component {
|
||||
* @param attr {string} the event target attribute to use
|
||||
* @returns {function(Event)}
|
||||
*/
|
||||
onChange = (key, attr) => {
|
||||
return e => {
|
||||
const value = e.target[attr];
|
||||
onChange = (key: string, attr: string) => {
|
||||
return (e: ChangeEvent) => {
|
||||
// @ts-ignore
|
||||
const value: string = e.target[attr];
|
||||
|
||||
this.setState(oldState => ({
|
||||
settings: {
|
||||
@@ -103,7 +94,7 @@ class DHCPInfo extends Component {
|
||||
*
|
||||
* @param e the submit event
|
||||
*/
|
||||
saveSettings = e => {
|
||||
saveSettings = (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
const { t } = this.props;
|
||||
@@ -155,7 +146,7 @@ class DHCPInfo extends Component {
|
||||
* @param value the value to check
|
||||
* @param validator the validation function
|
||||
*/
|
||||
isSettingValid = (value, validator) => {
|
||||
isSettingValid = (value: string, validator: (value: string) => boolean) => {
|
||||
return (
|
||||
(!this.state.settings.active && value.length === 0) || validator(value)
|
||||
);
|
||||
@@ -180,10 +171,7 @@ class DHCPInfo extends Component {
|
||||
this.state.settings.router_ip,
|
||||
isValidIpv4
|
||||
);
|
||||
const isLeaseTimeValid = this.isSettingValid(
|
||||
this.state.settings.lease_time,
|
||||
isPositiveNumber
|
||||
);
|
||||
const isLeaseTimeValid = this.state.settings.lease_time >= 0;
|
||||
const isDomainValid = this.isSettingValid(
|
||||
this.state.settings.domain,
|
||||
isValidHostname
|
||||
@@ -262,7 +250,14 @@ class DHCPInfo extends Component {
|
||||
id="leaseTime"
|
||||
disabled={!this.state.settings.active}
|
||||
value={this.state.settings.lease_time}
|
||||
onChange={this.onChange("lease_time", "value")}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
this.setState(oldState => ({
|
||||
settings: {
|
||||
...oldState.settings,
|
||||
lease_time: parseInt(e.target.value)
|
||||
}
|
||||
}))
|
||||
}
|
||||
invalid={!isLeaseTimeValid}
|
||||
/>
|
||||
<InputGroupAddon addonType="append">Hours</InputGroupAddon>
|
||||
@@ -8,21 +8,31 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React, { Component } from "react";
|
||||
import { withNamespaces } from "react-i18next";
|
||||
import { ignoreCancel, makeCancelable } from "../../util";
|
||||
import api from "../../util/api";
|
||||
import React, { Component, FormEvent } from "react";
|
||||
import { WithNamespaces, withNamespaces } from "react-i18next";
|
||||
import { CancelablePromise, ignoreCancel, makeCancelable } from "../../util";
|
||||
import api, { ApiDnsSettings, ApiResultResponse } from "../../util/api";
|
||||
import DnsList from "./DnsList";
|
||||
import { Button, Col, Form, FormGroup } from "reactstrap";
|
||||
import ConditionalForwardingSettings from "./ConditionalForwardingSettings";
|
||||
import DnsOptionSettings from "./DnsOptionSettings";
|
||||
import Alert from "../common/Alert";
|
||||
import ConditionalForwardingSettings, { ConditionalForwardingObject } from "./ConditionalForwardingSettings";
|
||||
import DnsOptionSettings, { DnsOptionsObject } from "./DnsOptionSettings";
|
||||
import Alert, { AlertType } from "../common/Alert";
|
||||
import { isValidHostname, isValidIpv4 } from "../../util/validate";
|
||||
|
||||
class DNSInfo extends Component {
|
||||
state = {
|
||||
export interface DNSInfoState {
|
||||
alertMessage: string;
|
||||
alertType: AlertType;
|
||||
showAlert: boolean;
|
||||
processing: boolean;
|
||||
upstreamDns: Array<string>;
|
||||
conditionalForwarding: ConditionalForwardingObject;
|
||||
options: DnsOptionsObject;
|
||||
}
|
||||
|
||||
class DNSInfo extends Component<WithNamespaces, DNSInfoState> {
|
||||
state: DNSInfoState = {
|
||||
alertMessage: "",
|
||||
alertType: "",
|
||||
alertType: "info",
|
||||
showAlert: false,
|
||||
processing: false,
|
||||
upstreamDns: [],
|
||||
@@ -39,6 +49,9 @@ class DNSInfo extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
private loadHandler: undefined | CancelablePromise<ApiDnsSettings>;
|
||||
private updateHandler: undefined | CancelablePromise<ApiResultResponse>;
|
||||
|
||||
loadDNSInfo = () => {
|
||||
this.loadHandler = makeCancelable(api.getDNSInfo());
|
||||
this.loadHandler.promise
|
||||
@@ -70,26 +83,30 @@ class DNSInfo extends Component {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.loadHandler.cancel();
|
||||
if (this.loadHandler) {
|
||||
this.loadHandler.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
handleUpstreamAdd = upstream => {
|
||||
handleUpstreamAdd = (upstream: string) => {
|
||||
this.setState({
|
||||
upstreamDns: this.state.upstreamDns.concat(upstream)
|
||||
});
|
||||
};
|
||||
|
||||
handleUpstreamRemove = upstream => {
|
||||
handleUpstreamRemove = (upstream: string) => {
|
||||
this.setState({
|
||||
upstreamDns: this.state.upstreamDns.filter(item => item !== upstream)
|
||||
});
|
||||
};
|
||||
|
||||
handleConditionalForwardingUpdate = conditionalForwarding => {
|
||||
handleConditionalForwardingUpdate = (
|
||||
conditionalForwarding: ConditionalForwardingObject
|
||||
) => {
|
||||
this.setState({ conditionalForwarding });
|
||||
};
|
||||
|
||||
handleDnsOptionsUpdate = options => {
|
||||
handleDnsOptionsUpdate = (options: DnsOptionsObject) => {
|
||||
this.setState({ options });
|
||||
};
|
||||
|
||||
@@ -98,7 +115,7 @@ class DNSInfo extends Component {
|
||||
*
|
||||
* @param e the submit event
|
||||
*/
|
||||
saveSettings = e => {
|
||||
saveSettings = (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
const { t } = this.props;
|
||||
@@ -157,7 +174,7 @@ class DNSInfo extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
isCFSettingValid = (value, validator) => {
|
||||
isCFSettingValid = (value: string, validator: (value: string) => boolean) => {
|
||||
return (
|
||||
(!this.state.conditionalForwarding.enabled && value.length === 0) ||
|
||||
validator(value)
|
||||
@@ -9,12 +9,17 @@
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { ListGroup } from "reactstrap";
|
||||
import DnsListItem from "./DnsListItem";
|
||||
import DnsListNewItem from "./DnsListNewItem";
|
||||
import { isValidIpv4OptionalPort } from "../../util/validate";
|
||||
|
||||
export interface DnsListProps {
|
||||
upstreams: Array<string>;
|
||||
onAdd: (upstream: string) => void;
|
||||
onRemove: (upstream: string) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an upstream address is unique and valid
|
||||
*
|
||||
@@ -22,10 +27,10 @@ import { isValidIpv4OptionalPort } from "../../util/validate";
|
||||
* @param upstreams the list of current upstreams
|
||||
* @returns {boolean} if the upstream address is unique and valid
|
||||
*/
|
||||
export const isAddressValid = (address, upstreams) =>
|
||||
export const isAddressValid = (address: string, upstreams: Array<string>) =>
|
||||
!upstreams.includes(address) && isValidIpv4OptionalPort(address);
|
||||
|
||||
const DnsList = ({ upstreams, onAdd, onRemove }) => (
|
||||
const DnsList = ({ upstreams, onAdd, onRemove }: DnsListProps) => (
|
||||
<ListGroup style={{ overflowY: "scroll", maxHeight: "350px" }}>
|
||||
{upstreams.map(upstream => (
|
||||
<DnsListItem
|
||||
@@ -36,16 +41,10 @@ const DnsList = ({ upstreams, onAdd, onRemove }) => (
|
||||
))}
|
||||
<DnsListNewItem
|
||||
onAdd={onAdd}
|
||||
isValid={address => isAddressValid(address, upstreams)}
|
||||
isValid={(address: string) => isAddressValid(address, upstreams)}
|
||||
upstreams={upstreams}
|
||||
/>
|
||||
</ListGroup>
|
||||
);
|
||||
|
||||
DnsList.propTypes = {
|
||||
upstreams: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
onAdd: PropTypes.func.isRequired,
|
||||
onRemove: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default DnsList;
|
||||
@@ -9,10 +9,14 @@
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Button, ListGroupItem } from "reactstrap";
|
||||
|
||||
const DnsListItem = ({ address, onRemove }) => (
|
||||
export interface DnsListItemProps {
|
||||
address: string,
|
||||
onRemove: () => void;
|
||||
}
|
||||
|
||||
const DnsListItem = ({ address, onRemove }: DnsListItemProps) => (
|
||||
<ListGroupItem>
|
||||
{address}
|
||||
<Button color="danger" size="sm" className="pull-right" onClick={onRemove}>
|
||||
@@ -21,9 +25,4 @@ const DnsListItem = ({ address, onRemove }) => (
|
||||
</ListGroupItem>
|
||||
);
|
||||
|
||||
DnsListItem.propTypes = {
|
||||
address: PropTypes.string.isRequired,
|
||||
onRemove: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default DnsListItem;
|
||||
+22
-13
@@ -8,30 +8,39 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import React, { Component, RefObject } from "react";
|
||||
import { Button, InputGroup, InputGroupAddon, ListGroupItem } from "reactstrap";
|
||||
import { Typeahead } from "react-bootstrap-typeahead";
|
||||
import { withNamespaces } from "react-i18next";
|
||||
import { preconfiguredUpstreamOptions } from "./preconfiguredUpstreams";
|
||||
import { WithNamespaces, withNamespaces } from "react-i18next";
|
||||
import { PreconfiguredUpstreamOption, preconfiguredUpstreamOptions } from "./preconfiguredUpstreams";
|
||||
|
||||
export interface DnsListNewItemProps {
|
||||
onAdd: (address: string) => void;
|
||||
isValid: (address: string) => boolean;
|
||||
upstreams: Array<string>;
|
||||
}
|
||||
|
||||
export interface DnsListNewItemState {
|
||||
address: string;
|
||||
selected: Array<PreconfiguredUpstreamOption>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A component to add upstream DNS servers. The servers are either selected from
|
||||
* a list of preconfigured upstreams, or custom servers can be input.
|
||||
*/
|
||||
class DnsListNewItem extends Component {
|
||||
static propTypes = {
|
||||
onAdd: PropTypes.func.isRequired,
|
||||
isValid: PropTypes.func.isRequired,
|
||||
upstreams: PropTypes.arrayOf(PropTypes.string).isRequired
|
||||
};
|
||||
|
||||
state = {
|
||||
class DnsListNewItem extends Component<
|
||||
DnsListNewItemProps & WithNamespaces,
|
||||
DnsListNewItemState
|
||||
> {
|
||||
state: DnsListNewItemState = {
|
||||
address: "",
|
||||
selected: []
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
private readonly typeahead: RefObject<any>;
|
||||
|
||||
constructor(props: DnsListNewItemProps & WithNamespaces) {
|
||||
super(props);
|
||||
this.typeahead = React.createRef();
|
||||
}
|
||||
+19
-1
@@ -11,8 +11,26 @@
|
||||
import React, { Fragment } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Col, FormGroup, Input, Label } from "reactstrap";
|
||||
import i18next from "i18next";
|
||||
|
||||
const DnsOptionSettings = ({ settings, onUpdate, t }) => (
|
||||
export interface DnsOptionsObject {
|
||||
fqdnRequired: boolean;
|
||||
bogusPriv: boolean;
|
||||
dnssec: boolean;
|
||||
listeningType: string;
|
||||
}
|
||||
|
||||
export interface DnsOptionSettingsProps {
|
||||
settings: DnsOptionsObject;
|
||||
onUpdate: (settings: DnsOptionsObject) => void;
|
||||
t: i18next.TranslationFunction;
|
||||
}
|
||||
|
||||
const DnsOptionSettings = ({
|
||||
settings,
|
||||
onUpdate,
|
||||
t
|
||||
}: DnsOptionSettingsProps) => (
|
||||
<Fragment>
|
||||
<FormGroup row>
|
||||
<Label for="listeningBehavior" sm={5}>
|
||||
@@ -9,18 +9,17 @@
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { withNamespaces } from "react-i18next";
|
||||
import api from "../../util/api";
|
||||
import { WithNamespaces, withNamespaces } from "react-i18next";
|
||||
import api, { ApiFtlDbResponse } from "../../util/api";
|
||||
import { WithAPIData } from "../common/WithAPIData";
|
||||
|
||||
class FTLInfo extends Component {
|
||||
static propTypes = {
|
||||
fileSize: PropTypes.number.isRequired,
|
||||
queries: PropTypes.number.isRequired,
|
||||
sqliteVersion: PropTypes.string.isRequired
|
||||
};
|
||||
export interface FTLInfoProps {
|
||||
fileSize: number,
|
||||
queries: number,
|
||||
sqliteVersion: string
|
||||
}
|
||||
|
||||
class FTLInfo extends Component<FTLInfoProps & WithNamespaces, {}> {
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
@@ -36,7 +35,7 @@ class FTLInfo extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
export const transformData = data => ({
|
||||
export const transformData = (data: ApiFtlDbResponse): FTLInfoProps => ({
|
||||
fileSize: data.filesize,
|
||||
queries: data.queries,
|
||||
sqliteVersion: data.sqlite_version
|
||||
@@ -50,11 +49,12 @@ export const initialData = {
|
||||
|
||||
export const TranslatedFTLInfo = withNamespaces(["settings"])(FTLInfo);
|
||||
|
||||
export default props => (
|
||||
export default (props: any) => (
|
||||
<WithAPIData
|
||||
apiCall={api.getFTLdb}
|
||||
repeatOptions={{
|
||||
interval: 600000
|
||||
interval: 600000,
|
||||
ignoreCancel: true
|
||||
}}
|
||||
renderInitial={() => <TranslatedFTLInfo {...initialData} {...props} />}
|
||||
renderOk={data => <TranslatedFTLInfo {...transformData(data)} {...props} />}
|
||||
@@ -9,20 +9,19 @@
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { withNamespaces } from "react-i18next";
|
||||
import api from "../../util/api";
|
||||
import { Subtract, WithNamespaces, withNamespaces } from "react-i18next";
|
||||
import api, { ApiNetworkSettings } from "../../util/api";
|
||||
import { Col, Form, FormGroup, Input, Label } from "reactstrap";
|
||||
import { WithAPIData } from "../common/WithAPIData";
|
||||
|
||||
class NetworkInfo extends Component {
|
||||
static propTypes = {
|
||||
interface: PropTypes.string.isRequired,
|
||||
ipv4Address: PropTypes.string.isRequired,
|
||||
ipv6Address: PropTypes.string.isRequired,
|
||||
hostname: PropTypes.string.isRequired
|
||||
};
|
||||
export interface NetworkInfoProps extends WithNamespaces {
|
||||
interface: string;
|
||||
ipv4Address: string;
|
||||
ipv6Address: string;
|
||||
hostname: string;
|
||||
}
|
||||
|
||||
class NetworkInfo extends Component<NetworkInfoProps, {}> {
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
@@ -85,7 +84,9 @@ class NetworkInfo extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
export const transformData = data => ({
|
||||
export const transformData = (
|
||||
data: ApiNetworkSettings
|
||||
): Subtract<NetworkInfoProps, WithNamespaces> => ({
|
||||
interface: data.interface,
|
||||
ipv4Address: data.ipv4_address,
|
||||
ipv6Address: data.ipv6_address,
|
||||
@@ -103,7 +104,7 @@ export const TranslatedNetworkInfo = withNamespaces(["common", "settings"])(
|
||||
NetworkInfo
|
||||
);
|
||||
|
||||
export default props => (
|
||||
export default (props: any) => (
|
||||
<WithAPIData
|
||||
apiCall={api.getNetworkInfo}
|
||||
repeatOptions={{
|
||||
+36
-27
@@ -8,44 +8,50 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { withNamespaces } from "react-i18next";
|
||||
import { ignoreCancel, makeCancelable } from "../../util";
|
||||
import api from "../../util/api";
|
||||
import Alert from "../common/Alert";
|
||||
import React, { ChangeEvent, Component, FormEvent } from "react";
|
||||
import { WithNamespaces, withNamespaces } from "react-i18next";
|
||||
import { CancelablePromise, ignoreCancel, makeCancelable } from "../../util";
|
||||
import api, { ApiPreferences, ApiResultResponse } from "../../util/api";
|
||||
import Alert, { AlertType } from "../common/Alert";
|
||||
import { Button, Col, Form, FormGroup, Input, Label } from "reactstrap";
|
||||
import { PreferencesContext } from "../common/context";
|
||||
import languages from "../../languages";
|
||||
import languages from "../../languages.json";
|
||||
import i18n from "i18next";
|
||||
|
||||
class PreferenceSettings extends Component {
|
||||
static propTypes = {
|
||||
settings: PropTypes.shape({
|
||||
layout: PropTypes.string.isRequired
|
||||
}),
|
||||
refresh: PropTypes.func.isRequired
|
||||
};
|
||||
export interface PreferenceSettingsProps {
|
||||
settings: ApiPreferences;
|
||||
refresh: (preferences?: ApiPreferences) => void;
|
||||
}
|
||||
|
||||
state = {
|
||||
export interface PreferenceSettingsState {
|
||||
alertMessage: string;
|
||||
alertType: AlertType;
|
||||
showAlert: boolean;
|
||||
processing: boolean;
|
||||
settings: ApiPreferences;
|
||||
}
|
||||
|
||||
class PreferenceSettings extends Component<
|
||||
PreferenceSettingsProps & WithNamespaces,
|
||||
PreferenceSettingsState
|
||||
> {
|
||||
state: PreferenceSettingsState = {
|
||||
alertMessage: "",
|
||||
alertType: "",
|
||||
alertType: "info",
|
||||
showAlert: false,
|
||||
processing: false,
|
||||
// Initial value is the current settings
|
||||
settings: this.props.settings
|
||||
};
|
||||
|
||||
private loadHandler: undefined | CancelablePromise<ApiPreferences>;
|
||||
private updateHandler: undefined | CancelablePromise<ApiResultResponse>;
|
||||
|
||||
loadPreferences = () => {
|
||||
this.loadHandler = makeCancelable(api.getPreferences());
|
||||
this.loadHandler.promise
|
||||
.then(res => {
|
||||
this.setState({
|
||||
settings: {
|
||||
layout: res.layout,
|
||||
language: res.language
|
||||
}
|
||||
});
|
||||
this.setState({ settings: res });
|
||||
})
|
||||
.catch(ignoreCancel);
|
||||
};
|
||||
@@ -55,7 +61,9 @@ class PreferenceSettings extends Component {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.loadHandler.cancel();
|
||||
if (this.loadHandler) {
|
||||
this.loadHandler.cancel();
|
||||
}
|
||||
|
||||
if (this.updateHandler) {
|
||||
this.updateHandler.cancel();
|
||||
@@ -70,9 +78,10 @@ class PreferenceSettings extends Component {
|
||||
* @param attr {string} the event target attribute to use
|
||||
* @returns {function(Event)}
|
||||
*/
|
||||
onChange = (key, attr) => {
|
||||
return e => {
|
||||
const value = e.target[attr];
|
||||
onChange = (key: string, attr: string) => {
|
||||
return (e: ChangeEvent) => {
|
||||
// @ts-ignore
|
||||
const value: string = e.target[attr];
|
||||
|
||||
this.setState(oldState => ({
|
||||
settings: {
|
||||
@@ -88,7 +97,7 @@ class PreferenceSettings extends Component {
|
||||
*
|
||||
* @param e the submit event
|
||||
*/
|
||||
saveSettings = e => {
|
||||
saveSettings = (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
const { t } = this.props;
|
||||
@@ -9,19 +9,23 @@
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React, { Component } from "react";
|
||||
import { withNamespaces } from "react-i18next";
|
||||
import PropTypes from "prop-types";
|
||||
import { WithNamespaces, withNamespaces } from "react-i18next";
|
||||
import { Collapse } from "reactstrap";
|
||||
|
||||
class VersionCard extends Component {
|
||||
static propTypes = {
|
||||
branch: PropTypes.string.isRequired,
|
||||
hash: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
tag: PropTypes.string.isRequired
|
||||
};
|
||||
export interface VersionCardProps extends WithNamespaces {
|
||||
branch: string;
|
||||
hash: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
state = {
|
||||
export interface VersionCardState {
|
||||
collapsed: boolean
|
||||
}
|
||||
|
||||
class VersionCard extends Component<VersionCardProps, VersionCardState> {
|
||||
state: VersionCardState = {
|
||||
collapsed: true
|
||||
};
|
||||
|
||||
@@ -9,26 +9,12 @@
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { withNamespaces } from "react-i18next";
|
||||
import api from "../../util/api";
|
||||
import { WithNamespaces, withNamespaces } from "react-i18next";
|
||||
import api, { ApiVersions } from "../../util/api";
|
||||
import VersionCard from "./VersionCard";
|
||||
import { WithAPIData } from "../common/WithAPIData";
|
||||
|
||||
class VersionInfo extends Component {
|
||||
static versionPropType = PropTypes.shape({
|
||||
branch: PropTypes.string.isRequired,
|
||||
hash: PropTypes.string.isRequired,
|
||||
tag: PropTypes.string.isRequired
|
||||
}).isRequired;
|
||||
|
||||
static propTypes = {
|
||||
api: VersionInfo.versionPropType,
|
||||
core: VersionInfo.versionPropType,
|
||||
ftl: VersionInfo.versionPropType,
|
||||
web: VersionInfo.versionPropType
|
||||
};
|
||||
|
||||
class VersionInfo extends Component<ApiVersions & WithNamespaces, {}> {
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
@@ -75,7 +61,7 @@ class VersionInfo extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
export const initialData = {
|
||||
export const initialData: ApiVersions = {
|
||||
api: {
|
||||
branch: "unknown",
|
||||
hash: "unknown",
|
||||
@@ -100,10 +86,10 @@ export const initialData = {
|
||||
|
||||
export const TranslatedVersionInfo = withNamespaces(["common"])(VersionInfo);
|
||||
|
||||
export default props => (
|
||||
export default (props: any) => (
|
||||
<WithAPIData
|
||||
apiCall={api.getVersion}
|
||||
repeatOptions={{ interval: 600000 }}
|
||||
repeatOptions={{ interval: 600000, ignoreCancel: true }}
|
||||
renderInitial={() => <TranslatedVersionInfo {...initialData} {...props} />}
|
||||
renderOk={data => <TranslatedVersionInfo {...data} {...props} />}
|
||||
renderErr={() => <TranslatedVersionInfo {...initialData} {...props} />}
|
||||
+16
-7
@@ -9,9 +9,12 @@
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React from "react";
|
||||
import { shallow } from "enzyme";
|
||||
import DHCPInfo from "../DHCPInfo";
|
||||
import { shallow, ShallowWrapper } from "enzyme";
|
||||
import DHCPInfo, { DHCPInfoState } from "../DHCPInfo";
|
||||
import fetchMock from "fetch-mock";
|
||||
import { WithNamespaces } from "react-i18next";
|
||||
|
||||
const tick = global.tick;
|
||||
|
||||
const endpoint = "/admin/api/settings/dhcp";
|
||||
const fakeData = {
|
||||
@@ -51,7 +54,9 @@ it("disables the apply button if an input is invalid", async () => {
|
||||
it("disables inputs only when DHCP is not enabled", async () => {
|
||||
fetchMock.mock(endpoint, { body: fakeData });
|
||||
|
||||
const wrapper = shallow(<DHCPInfo />);
|
||||
const wrapper: ShallowWrapper<WithNamespaces, DHCPInfoState> = shallow(
|
||||
<DHCPInfo />
|
||||
);
|
||||
|
||||
await tick();
|
||||
wrapper.update();
|
||||
@@ -69,7 +74,9 @@ it("disables the apply button when processing setting update", async () => {
|
||||
fetchMock.get(endpoint, { body: fakeData });
|
||||
fetchMock.put(endpoint, { body: { status: "success" } });
|
||||
|
||||
const wrapper = shallow(<DHCPInfo />);
|
||||
const wrapper: ShallowWrapper<WithNamespaces, DHCPInfoState> = shallow(
|
||||
<DHCPInfo />
|
||||
);
|
||||
|
||||
await tick();
|
||||
|
||||
@@ -86,7 +93,9 @@ it("sends the correct data to the API when apply is clicked", async () => {
|
||||
fetchMock.get(endpoint, { body: fakeData });
|
||||
fetchMock.put(endpoint, { body: { status: "success" } });
|
||||
|
||||
const wrapper = shallow(<DHCPInfo />);
|
||||
const wrapper: ShallowWrapper<WithNamespaces, DHCPInfoState> = shallow(
|
||||
<DHCPInfo />
|
||||
);
|
||||
|
||||
await tick();
|
||||
|
||||
@@ -99,9 +108,9 @@ it("sends the correct data to the API when apply is clicked", async () => {
|
||||
expect(wrapper.find("Button")).toBeDisabled();
|
||||
|
||||
// The output of lastCall is [url, request]
|
||||
const updateRequest = fetchMock.lastCall(endpoint)[1];
|
||||
const updateRequest = fetchMock.lastCall(endpoint)![1];
|
||||
|
||||
expect(JSON.parse(updateRequest.body)).toEqual(fakeData);
|
||||
expect(JSON.parse(updateRequest!.body as string)).toEqual(fakeData);
|
||||
});
|
||||
|
||||
it("shows a success message after successfully saving settings", async () => {
|
||||
+16
-3
@@ -8,11 +8,19 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
export interface PreconfiguredUpstream {
|
||||
name: string;
|
||||
primaryIpv4: string;
|
||||
secondaryIpv4: string;
|
||||
primaryIpv6: string;
|
||||
secondaryIpv6: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of preconfigured upstream DNS servers. Each item has primary and
|
||||
* secondary IPv4 and IPv6 server entries. Some addresses may be empty.
|
||||
*/
|
||||
export const preconfiguredUpstreams = [
|
||||
export const preconfiguredUpstreams: Array<PreconfiguredUpstream> = [
|
||||
{
|
||||
name: "OpenDNS (ECS)",
|
||||
primaryIpv4: "208.67.222.222",
|
||||
@@ -78,6 +86,11 @@ export const preconfiguredUpstreams = [
|
||||
}
|
||||
];
|
||||
|
||||
export interface PreconfiguredUpstreamOption {
|
||||
label: string;
|
||||
address: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The preconfigured servers, but with an entry for each address. This is used
|
||||
* when displaying the servers in a list.
|
||||
@@ -85,12 +98,12 @@ export const preconfiguredUpstreams = [
|
||||
export const preconfiguredUpstreamOptions = preconfiguredUpstreams.flatMap(
|
||||
upstream => {
|
||||
// Parse the upstream address information into the new format
|
||||
const parseUpstream = (address, order, type) => ({
|
||||
const parseUpstream = (address: string, order: string, type: string) => ({
|
||||
label: upstream.name + " " + order + " " + type + " (" + address + ")",
|
||||
address
|
||||
});
|
||||
|
||||
let parsedUpstreams = [];
|
||||
let parsedUpstreams: Array<PreconfiguredUpstreamOption> = [];
|
||||
|
||||
if (upstream.primaryIpv4.length > 0) {
|
||||
parsedUpstreams.push(
|
||||
+66
-24
@@ -24,46 +24,88 @@ export interface ApiQuery {
|
||||
|
||||
export interface ApiHistoryResponse {
|
||||
cursor: null | string;
|
||||
history: Array<ApiQuery>
|
||||
history: Array<ApiQuery>;
|
||||
}
|
||||
|
||||
export interface ApiNetworkSettings {
|
||||
interface: string;
|
||||
ipv4_address: string;
|
||||
ipv6_address: string;
|
||||
hostname: string;
|
||||
}
|
||||
|
||||
export interface ApiVersion {
|
||||
branch: string;
|
||||
hash: string;
|
||||
tag: string;
|
||||
}
|
||||
|
||||
export interface ApiVersions {
|
||||
api: ApiVersion;
|
||||
core: ApiVersion;
|
||||
ftl: ApiVersion;
|
||||
web: ApiVersion;
|
||||
}
|
||||
|
||||
export interface ApiDnsSettings {
|
||||
upstream_dns: Array<string>,
|
||||
upstream_dns: Array<string>;
|
||||
options: {
|
||||
fqdn_required: boolean,
|
||||
bogus_priv: boolean,
|
||||
dnssec: boolean,
|
||||
listening_type: string
|
||||
},
|
||||
fqdn_required: boolean;
|
||||
bogus_priv: boolean;
|
||||
dnssec: boolean;
|
||||
listening_type: string;
|
||||
};
|
||||
conditional_forwarding: {
|
||||
enabled: boolean,
|
||||
router_ip: string,
|
||||
domain: string
|
||||
}
|
||||
enabled: boolean;
|
||||
router_ip: string;
|
||||
domain: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ApiDhcpSettings {
|
||||
active: boolean,
|
||||
ip_start: string,
|
||||
ip_end: string,
|
||||
router_ip: string,
|
||||
lease_time: number,
|
||||
domain: string,
|
||||
ipv6_support: boolean
|
||||
active: boolean;
|
||||
ip_start: string;
|
||||
ip_end: string;
|
||||
router_ip: string;
|
||||
lease_time: number;
|
||||
domain: string;
|
||||
ipv6_support: boolean;
|
||||
}
|
||||
|
||||
export interface ApiFtlDbResponse {
|
||||
filesize: number;
|
||||
queries: number;
|
||||
sqlite_version: string;
|
||||
}
|
||||
|
||||
export interface ApiSuccessResponse {
|
||||
status: "success";
|
||||
}
|
||||
|
||||
export interface ApiError {
|
||||
key: string;
|
||||
message: string;
|
||||
data: any;
|
||||
}
|
||||
|
||||
export interface ApiErrorResponse {
|
||||
error: ApiError;
|
||||
}
|
||||
|
||||
export type ApiResultResponse = ApiSuccessResponse | ApiErrorResponse;
|
||||
|
||||
export type WebLayout = "boxed" | "traditional";
|
||||
|
||||
export interface ApiPreferences {
|
||||
layout: WebLayout,
|
||||
language: string
|
||||
layout: WebLayout;
|
||||
language: string;
|
||||
}
|
||||
|
||||
export type Status = "enabled" | "disabled" | "unknown";
|
||||
export type StatusAction = "enable" | "disable";
|
||||
|
||||
export interface ApiStatus {
|
||||
status: Status
|
||||
status: Status;
|
||||
}
|
||||
|
||||
export default {
|
||||
@@ -142,13 +184,13 @@ export default {
|
||||
setStatus(action: StatusAction, time: number | null = null) {
|
||||
return http.post("dns/status", { action, time });
|
||||
},
|
||||
getNetworkInfo() {
|
||||
getNetworkInfo(): Promise<ApiNetworkSettings> {
|
||||
return http.get("settings/network");
|
||||
},
|
||||
getVersion() {
|
||||
getVersion(): Promise<ApiVersions> {
|
||||
return http.get("version");
|
||||
},
|
||||
getFTLdb() {
|
||||
getFTLdb(): Promise<ApiFtlDbResponse> {
|
||||
return http.get("settings/ftldb");
|
||||
},
|
||||
getDNSInfo(): Promise<ApiDnsSettings> {
|
||||
|
||||
Reference in New Issue
Block a user