- {getDownloadLink(getHostName(), clientId, protocol, invalid)}
+ {getDownloadLink(getHostName(), clientId, protocol, !isValid)}
);
};
-
-export default reduxForm({ form: FORM_NAME.MOBILE_CONFIG })(MobileConfigForm);
diff --git a/client/src/components/ui/Guide/index.ts b/client/src/components/ui/Guide/index.ts
index ee660aebed6..f9411b5b10a 100644
--- a/client/src/components/ui/Guide/index.ts
+++ b/client/src/components/ui/Guide/index.ts
@@ -1 +1 @@
-export { default } from './Guide';
+export * from './Guide';
diff --git a/client/src/containers/Dashboard.ts b/client/src/containers/Dashboard.ts
index cc2c602caf4..c1902aed63c 100644
--- a/client/src/containers/Dashboard.ts
+++ b/client/src/containers/Dashboard.ts
@@ -1,7 +1,7 @@
import { connect } from 'react-redux';
import { toggleProtection, getClients } from '../actions';
-import { getStats, getStatsConfig, setStatsConfig } from '../actions/stats';
+import { getStats, getStatsConfig } from '../actions/stats';
import { getAccessList } from '../actions/access';
import Dashboard from '../components/Dashboard';
@@ -19,7 +19,7 @@ type DispatchProps = {
getStats: (...args: unknown[]) => unknown;
getStatsConfig: (...args: unknown[]) => unknown;
getAccessList: () => (dispatch: any) => void;
-}
+};
const mapDispatchToProps: DispatchProps = {
toggleProtection,
diff --git a/client/src/containers/Encryption.ts b/client/src/containers/Encryption.ts
index 48c798595b6..d64bd38c37a 100644
--- a/client/src/containers/Encryption.ts
+++ b/client/src/containers/Encryption.ts
@@ -1,7 +1,7 @@
import { connect } from 'react-redux';
import { getTlsStatus, setTlsConfig, validateTlsConfig } from '../actions/encryption';
-import Encryption from '../components/Settings/Encryption';
+import { Encryption } from '../components/Settings/Encryption';
const mapStateToProps = (state: any) => {
const { encryption } = state;
diff --git a/client/src/helpers/constants.ts b/client/src/helpers/constants.ts
index c94a6c003e3..7b54ae8d738 100644
--- a/client/src/helpers/constants.ts
+++ b/client/src/helpers/constants.ts
@@ -91,6 +91,7 @@ export const STANDARD_WEB_PORT = 80;
export const STANDARD_HTTPS_PORT = 443;
export const DNS_OVER_TLS_PORT = 853;
export const DNS_OVER_QUIC_PORT = 853;
+export const MIN_PORT = 1;
export const MAX_PORT = 65535;
export const EMPTY_DATE = '0001-01-01T00:00:00Z';
@@ -209,7 +210,7 @@ export const WHOIS_ICONS = {
export const DEFAULT_LOGS_FILTER = {
search: '',
- response_status: '',
+ response_status: 'all',
};
export const DEFAULT_LANGUAGE = 'en';
diff --git a/client/src/helpers/form.tsx b/client/src/helpers/form.tsx
index 62b9534b9d0..573a87c1434 100644
--- a/client/src/helpers/form.tsx
+++ b/client/src/helpers/form.tsx
@@ -1,304 +1,5 @@
-import React, { Fragment } from 'react';
-import { Trans } from 'react-i18next';
-import cn from 'classnames';
-
-import { createOnBlurHandler } from './helpers';
import { R_MAC_WITHOUT_COLON, R_UNIX_ABSOLUTE_PATH, R_WIN_ABSOLUTE_PATH } from './constants';
-interface renderFieldProps {
- id: string;
- input: object;
- className?: string;
- placeholder?: string;
- type?: string;
- disabled?: boolean;
- autoComplete?: string;
- normalizeOnBlur?: (...args: unknown[]) => unknown;
- min?: number;
- max?: number;
- step?: number;
- onScroll?: (...args: unknown[]) => unknown;
- meta: {
- touched?: boolean;
- error?: string;
- };
-}
-
-export const renderField = (props: renderFieldProps, elementType: any) => {
- const {
- input,
- id,
- className,
- placeholder,
- type,
- disabled,
- normalizeOnBlur,
- onScroll,
- autoComplete,
- meta: { touched, error },
- min,
- max,
- step,
- } = props;
-
- const onBlur = (event: any) => createOnBlurHandler(event, input, normalizeOnBlur);
-
- const element = React.createElement(elementType, {
- ...input,
- id,
- className,
- placeholder,
- autoComplete,
- disabled,
- type,
- min,
- max,
- step,
- onBlur,
- onScroll,
- });
-
- return (
- <>
- {element}
- {!disabled && touched && error && (
-
- {error}
-
- )}
- >
- );
-};
-
-export const renderTextareaField = (props: any) => renderField(props, 'textarea');
-
-export const renderInputField = (props: any) => renderField(props, 'input');
-
-interface renderGroupFieldProps {
- input: object;
- id?: string;
- className?: string;
- placeholder?: string;
- type?: string;
- disabled?: boolean;
- autoComplete?: string;
- isActionAvailable?: boolean;
- removeField?: (...args: unknown[]) => unknown;
- meta: {
- touched?: boolean;
- error?: string;
- };
- normalizeOnBlur?: (...args: unknown[]) => unknown;
-}
-
-export const renderGroupField = ({
- input,
- id,
- className,
- placeholder,
- type,
- disabled,
- autoComplete,
- isActionAvailable,
- removeField,
- meta: { touched, error },
- normalizeOnBlur,
-}: renderGroupFieldProps) => {
- const onBlur = (event: any) => createOnBlurHandler(event, input, normalizeOnBlur);
-
- return (
- <>
-
-
- {isActionAvailable && (
-
-
-
- )}
-
- {!disabled && touched && error && (
-
- {error}
-
- )}
- >
- );
-};
-
-interface renderRadioFieldProps {
- input: object;
- placeholder?: string;
- subtitle?: string;
- disabled?: boolean;
- meta: {
- touched?: boolean;
- error?: string;
- };
-}
-
-export const renderRadioField = ({
- input,
- placeholder,
- subtitle,
- disabled,
- meta: { touched, error },
-}: renderRadioFieldProps) => (
-
-
- {!disabled && touched && error && (
-
- {error}
-
- )}
-
-);
-
-interface CheckboxFieldProps {
- input: object;
- placeholder?: string;
- subtitle?: React.ReactNode;
- disabled?: boolean;
- onClick?: (...args: unknown[]) => unknown;
- modifier?: string;
- checked?: boolean;
- meta: {
- touched?: boolean;
- error?: string;
- };
-}
-
-export const CheckboxField = ({
- input,
- placeholder,
- subtitle,
- disabled,
- onClick,
- modifier = 'checkbox--form',
- meta: { touched, error },
-}: CheckboxFieldProps) => (
- <>
-
- {!disabled && touched && error && (
-
- {error}
-
- )}
- >
-);
-
-interface renderSelectFieldProps {
- input: object;
- disabled?: boolean;
- label?: string;
- children: unknown[] | React.ReactElement;
- meta: {
- touched?: boolean;
- error?: string;
- };
-}
-
-export const renderSelectField = ({ input, meta: { touched, error }, children, label }: renderSelectFieldProps) => {
- const showWarning = touched && error;
-
- return (
- <>
- {label && (
-
- )}
-
-
- {showWarning && (
-
- {error}
-
- )}
- >
- );
-};
-
-interface renderServiceFieldProps {
- input: object;
- placeholder?: string;
- disabled?: boolean;
- modifier?: string;
- icon?: string;
- meta: {
- touched?: boolean;
- error?: string;
- };
-}
-
-export const renderServiceField = ({
- input,
- placeholder,
- disabled,
- modifier,
- icon,
- meta: { touched, error },
-}: renderServiceFieldProps) => (
- <>
-
- {!disabled && touched && error && (
-
- {error}
-
- )}
- >
-);
-
/**
*
* @param {string} ip
diff --git a/client/src/helpers/helpers.tsx b/client/src/helpers/helpers.tsx
index 60851b36412..ba18b1c039c 100644
--- a/client/src/helpers/helpers.tsx
+++ b/client/src/helpers/helpers.tsx
@@ -28,7 +28,7 @@ import {
THEMES,
} from './constants';
import { LOCAL_STORAGE_KEYS, LocalStorageHelper } from './localStorageHelper';
-import { DhcpInterface } from '../initialState';
+import { DhcpInterface, InstallInterface } from '../initialState';
/**
* @param time {string} The time to format
@@ -217,9 +217,9 @@ export const getInterfaceIp = (option: any) => {
return interfaceIP;
};
-export const getIpList = (interfaces: DhcpInterface[]) =>
+export const getIpList = (interfaces: InstallInterface[]) =>
Object.values(interfaces)
- .reduce((acc: string[], curr: DhcpInterface) => acc.concat(curr.ip_addresses), [] as string[])
+ .reduce((acc: string[], curr: InstallInterface) => acc.concat(curr.ip_addresses), [] as string[])
.sort();
/**
@@ -468,8 +468,6 @@ export const getParamsForClientsSearch = (data: any, param: any, additionalParam
* @param {function} [normalizeOnBlur]
* @returns {function}
*/
-export const createOnBlurHandler = (event: any, input: any, normalizeOnBlur: any) =>
- normalizeOnBlur ? input.onBlur(normalizeOnBlur(event.target.value)) : input.onBlur();
export const checkFiltered = (reason: any) => reason.indexOf(FILTERED) === 0;
export const checkRewrite = (reason: any) => reason === FILTERED_STATUS.REWRITE;
diff --git a/client/src/helpers/validators.ts b/client/src/helpers/validators.ts
index a96199d7d71..4447497d6e1 100644
--- a/client/src/helpers/validators.ts
+++ b/client/src/helpers/validators.ts
@@ -24,7 +24,6 @@ import { ip4ToInt, isValidAbsolutePath } from './form';
import { isIpInCidr, parseSubnetMask } from './helpers';
// Validation functions
-// https://redux-form.com/8.3.0/examples/fieldlevelvalidation/
// If the value is valid, the validation function should return undefined.
/**
* @param value {string|number}
@@ -35,7 +34,7 @@ export const validateRequiredValue = (value: any) => {
if (formattedValue || formattedValue === 0 || (formattedValue && formattedValue.length !== 0)) {
return undefined;
}
- return 'form_error_required';
+ return i18next.t('form_error_required');
};
/**
@@ -51,7 +50,7 @@ export const validateIpv4RangeEnd = (_: any, allValues: any) => {
const { range_end, range_start } = allValues.v4;
if (ip4ToInt(range_end) <= ip4ToInt(range_start)) {
- return 'greater_range_start_error';
+ return i18next.t('greater_range_start_error');
}
return undefined;
@@ -63,7 +62,7 @@ export const validateIpv4RangeEnd = (_: any, allValues: any) => {
*/
export const validateIpv4 = (value: any) => {
if (value && !R_IPV4.test(value)) {
- return 'form_error_ip4_format';
+ return i18next.t('form_error_ip4_format');
}
return undefined;
};
@@ -108,16 +107,16 @@ export const validateNotInRange = (value: any, allValues: any) => {
*/
export const validateGatewaySubnetMask = (_: any, allValues: any) => {
if (!allValues || !allValues.v4 || !allValues.v4.subnet_mask || !allValues.v4.gateway_ip) {
- return 'gateway_or_subnet_invalid';
+ return i18next.t('gateway_or_subnet_invalid');
}
const { subnet_mask, gateway_ip } = allValues.v4;
if (validateIpv4(gateway_ip)) {
- return 'gateway_or_subnet_invalid';
+ return i18next.t('gateway_or_subnet_invalid');
}
- return parseSubnetMask(subnet_mask) ? undefined : 'gateway_or_subnet_invalid';
+ return parseSubnetMask(subnet_mask) ? undefined : i18next.t('gateway_or_subnet_invalid');
};
/**
@@ -126,7 +125,7 @@ export const validateGatewaySubnetMask = (_: any, allValues: any) => {
* @param allValues
*/
export const validateIpForGatewaySubnetMask = (value: any, allValues: any) => {
- if (!allValues || !allValues.v4 || !value) {
+ if (!allValues || !allValues.v4 || !value || !allValues.gateway_ip || !allValues.subnet_mask) {
return undefined;
}
@@ -139,7 +138,7 @@ export const validateIpForGatewaySubnetMask = (value: any, allValues: any) => {
const subnetPrefix = parseSubnetMask(subnet_mask);
if (!isIpInCidr(value, `${gateway_ip}/${subnetPrefix}`)) {
- return 'subnet_error';
+ return i18next.t('subnet_error');
}
return undefined;
@@ -149,7 +148,7 @@ export const validateIpForGatewaySubnetMask = (value: any, allValues: any) => {
* @param value {string}
* @returns {undefined|string}
*/
-export const validateClientId = (value: any) => {
+export const validateClientId = (value: string) => {
if (!value) {
return undefined;
}
@@ -165,7 +164,7 @@ export const validateClientId = (value: any) => {
R_CLIENT_ID.test(formattedValue)
)
) {
- return 'form_error_client_id_format';
+ return i18next.t('form_error_client_id_format');
}
return undefined;
};
@@ -180,7 +179,7 @@ export const validateConfigClientId = (value: any) => {
}
const formattedValue = value.trim();
if (formattedValue && !R_CLIENT_ID.test(formattedValue)) {
- return 'form_error_client_id_format';
+ return i18next.t('form_error_client_id_format');
}
return undefined;
};
@@ -195,7 +194,7 @@ export const validateServerName = (value: any) => {
}
const formattedValue = value ? value.trim() : value;
if (formattedValue && !R_DOMAIN.test(formattedValue)) {
- return 'form_error_server_name';
+ return i18next.t('form_error_server_name');
}
return undefined;
};
@@ -206,7 +205,7 @@ export const validateServerName = (value: any) => {
*/
export const validateIpv6 = (value: any) => {
if (value && !R_IPV6.test(value)) {
- return 'form_error_ip6_format';
+ return i18next.t('form_error_ip6_format');
}
return undefined;
};
@@ -217,7 +216,7 @@ export const validateIpv6 = (value: any) => {
*/
export const validateIp = (value: any) => {
if (value && !R_IPV4.test(value) && !R_IPV6.test(value)) {
- return 'form_error_ip_format';
+ return i18next.t('form_error_ip_format');
}
return undefined;
};
@@ -228,7 +227,7 @@ export const validateIp = (value: any) => {
*/
export const validateMac = (value: any) => {
if (value && !R_MAC.test(value)) {
- return 'form_error_mac_format';
+ return i18next.t('form_error_mac_format');
}
return undefined;
};
@@ -239,7 +238,7 @@ export const validateMac = (value: any) => {
*/
export const validatePort = (value: any) => {
if ((value || value === 0) && (value < STANDARD_WEB_PORT || value > MAX_PORT)) {
- return 'form_error_port_range';
+ return i18next.t('form_error_port_range');
}
return undefined;
};
@@ -250,7 +249,7 @@ export const validatePort = (value: any) => {
*/
export const validateInstallPort = (value: any) => {
if (value < 1 || value > MAX_PORT) {
- return 'form_error_port';
+ return i18next.t('form_error_port');
}
return undefined;
};
@@ -264,7 +263,7 @@ export const validatePortTLS = (value: any) => {
return undefined;
}
if (value && (value < STANDARD_WEB_PORT || value > MAX_PORT)) {
- return 'form_error_port_range';
+ return i18next.t('form_error_port_range');
}
return undefined;
};
@@ -281,7 +280,7 @@ export const validatePortQuic = validatePortTLS;
*/
export const validateIsSafePort = (value: any) => {
if (UNSAFE_PORTS.includes(value)) {
- return 'form_error_port_unsafe';
+ return i18next.t('form_error_port_unsafe');
}
return undefined;
};
@@ -292,7 +291,7 @@ export const validateIsSafePort = (value: any) => {
*/
export const validateDomain = (value: any) => {
if (value && !R_HOST.test(value)) {
- return 'form_error_domain_format';
+ return i18next.t('form_error_domain_format');
}
return undefined;
};
@@ -303,7 +302,7 @@ export const validateDomain = (value: any) => {
*/
export const validateAnswer = (value: any) => {
if (value && !R_IPV4.test(value) && !R_IPV6.test(value) && !R_HOST.test(value)) {
- return 'form_error_answer_format';
+ return i18next.t('form_error_answer_format');
}
return undefined;
};
@@ -314,7 +313,7 @@ export const validateAnswer = (value: any) => {
*/
export const validatePath = (value: any) => {
if (value && !isValidAbsolutePath(value) && !R_URL_REQUIRES_PROTOCOL.test(value)) {
- return 'form_error_url_or_path_format';
+ return i18next.t('form_error_url_or_path_format');
}
return undefined;
};
@@ -402,7 +401,7 @@ export const validatePlainDns = (value: any, allValues: any) => {
const { enabled } = allValues;
if (!enabled && !value) {
- return 'encryption_plain_dns_error';
+ return i18next.t('encryption_plain_dns_error');
}
return undefined;
diff --git a/client/src/initialState.ts b/client/src/initialState.ts
index 1e8dea2717a..dc2e22557a6 100644
--- a/client/src/initialState.ts
+++ b/client/src/initialState.ts
@@ -1,9 +1,7 @@
import {
- ALL_INTERFACES_IP,
BLOCKING_MODES,
DAY,
DEFAULT_LOGS_FILTER,
- INSTALL_FIRST_STEP,
STANDARD_DNS_PORT,
STANDARD_WEB_PORT,
TIME_UNITS,
@@ -11,6 +9,14 @@ import {
import { DEFAULT_BLOCKING_IPV4, DEFAULT_BLOCKING_IPV6 } from './reducers/dnsConfig';
import { Filter } from './helpers/helpers';
+export type InstallInterface = {
+ flags: string;
+ hardware_address: string;
+ ip_addresses: string[];
+ mtu: number;
+ name: string;
+};
+
export type InstallData = {
step: number;
processingDefault: boolean;
@@ -33,13 +39,7 @@ export type InstallData = {
ip: string;
error: string;
};
- interfaces: {
- flags: string;
- hardware_address: string;
- ip_addresses: string[];
- mtu: number;
- name: string;
- }[];
+ interfaces: InstallInterface[];
dnsVersion: string;
};
@@ -78,17 +78,17 @@ export type EncryptionData = {
};
export type Client = {
- blocked_services: string[],
+ blocked_services: string[];
blocked_services_schedule: {
- sun?: { start: number, end: number },
- mon?: { start: number, end: number },
- tue?: { start: number, end: number },
- wed?: { start: number, end: number },
- thu?: { start: number, end: number },
- fri?: { start: number, end: number },
- sat?: { start: number, end: number },
+ sun?: { start: number; end: number };
+ mon?: { start: number; end: number };
+ tue?: { start: number; end: number };
+ wed?: { start: number; end: number };
+ thu?: { start: number; end: number };
+ fri?: { start: number; end: number };
+ sat?: { start: number; end: number };
time_zone: string;
- },
+ };
filtering_enabled: boolean;
ids: string[];
ignore_querylog: boolean;
@@ -104,14 +104,14 @@ export type Client = {
upstreams_cache_size: number;
use_global_blocked_services: boolean;
use_global_settings: boolean;
-}
+};
export type AutoClient = {
ip: string;
name: string;
source: string;
whois_info: any;
-}
+};
export type DashboardData = {
processing: boolean;
@@ -151,13 +151,13 @@ export type SettingsData = {
order: number;
subtitle: string;
title: string;
- },
+ };
safebrowsing: {
enabled: boolean;
order: number;
subtitle: string;
title: string;
- },
+ };
safesearch: Record
;
};
};
@@ -182,7 +182,7 @@ export type RewritesData = {
export type NormalizedTopClients = {
auto: Record;
configured: Record;
-}
+};
export type StatsData = {
processingGetConfig: boolean;
@@ -256,13 +256,13 @@ export type DhcpData = {
interface_name: string;
check?: {
v4?: {
- other_server?: { found: string; error?: string },
- static_ip?: {static: string, ip: string},
- },
+ other_server?: { found: string; error?: string };
+ static_ip?: { static: string; ip: string };
+ };
v6?: {
- other_server?: { found: string; error?: string },
- static_ip?: {static: string, ip: string},
- },
+ other_server?: { found: string; error?: string };
+ static_ip?: { static: string; ip: string };
+ };
};
v4: {
gateway_ip: string;
@@ -321,7 +321,7 @@ export type DnsConfigData = {
ratelimit_subnet_len_ipv4?: number;
ratelimit_subnet_len_ipv6?: number;
edns_cs_use_custom?: boolean;
- edns_cs_custom_ip?: boolean;
+ edns_cs_custom_ip?: string;
cache_size?: number;
cache_ttl_max?: number;
cache_ttl_min?: number;
@@ -391,7 +391,6 @@ export type RootState = {
install?: InstallData;
toasts: { notices: any[] };
loadingBar: any;
- form: any;
};
export type InstallState = {
@@ -617,5 +616,4 @@ export const initialState: RootState = {
},
toasts: { notices: [] },
loadingBar: {},
- form: {},
};
diff --git a/client/src/install/Setup/AddressList.tsx b/client/src/install/Setup/AddressList.tsx
index b943d2a4198..4d9488faa1a 100644
--- a/client/src/install/Setup/AddressList.tsx
+++ b/client/src/install/Setup/AddressList.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import { getIpList, getDnsAddress, getWebAddress } from '../../helpers/helpers';
import { ALL_INTERFACES_IP } from '../../helpers/constants';
-import { DhcpInterface } from '../../initialState';
+import { InstallInterface } from '../../initialState';
interface renderItemProps {
ip: string;
@@ -28,7 +28,7 @@ const renderItem = ({ ip, port, isDns }: renderItemProps) => {
};
interface AddressListProps {
- interfaces: DhcpInterface[];
+ interfaces: InstallInterface[];
address: string;
port: number;
isDns?: boolean;
diff --git a/client/src/install/Setup/Auth.tsx b/client/src/install/Setup/Auth.tsx
index b9072b4e207..a0632383fc5 100644
--- a/client/src/install/Setup/Auth.tsx
+++ b/client/src/install/Setup/Auth.tsx
@@ -1,47 +1,47 @@
import React from 'react';
-
-import { Field, reduxForm } from 'redux-form';
-import { withTranslation, Trans } from 'react-i18next';
-import flow from 'lodash/flow';
-
-import i18n from '../../i18n';
-
+import { Controller, useForm } from 'react-hook-form';
+import { Trans, useTranslation } from 'react-i18next';
import Controls from './Controls';
+import { validatePasswordLength, validateRequiredValue } from '../../helpers/validators';
+import { Input } from '../../components/ui/Controls/Input';
-import { renderInputField } from '../../helpers/form';
-import { FORM_NAME } from '../../helpers/constants';
-import { validatePasswordLength } from '../../helpers/validators';
-
-const required = (value: any) => {
- if (value || value === 0) {
- return false;
- }
-
- return form_error_required;
+type AuthFormValues = {
+ username: string;
+ password: string;
+ confirm_password: string;
};
-const validate = (values: any) => {
- const errors: { confirm_password?: string } = {};
-
- if (values.confirm_password !== values.password) {
- errors.confirm_password = i18n.t('form_error_password');
- }
-
- return errors;
+type Props = {
+ onAuthSubmit: (values: AuthFormValues) => void;
};
-interface AuthProps {
- handleSubmit: (...args: unknown[]) => string;
- pristine: boolean;
- invalid: boolean;
- t: (...args: unknown[]) => string;
-}
-
-const Auth = (props: AuthProps) => {
- const { handleSubmit, pristine, invalid, t } = props;
+export const Auth = ({ onAuthSubmit }: Props) => {
+ const { t } = useTranslation();
+ const {
+ handleSubmit,
+ watch,
+ control,
+ formState: { isDirty, isValid },
+ } = useForm({
+ mode: 'onBlur',
+ defaultValues: {
+ username: '',
+ password: '',
+ confirm_password: '',
+ },
+ });
+
+ const password = watch('password');
+
+ const validateConfirmPassword = (value: string) => {
+ if (value !== password) {
+ return t('form_error_password');
+ }
+ return undefined;
+ };
return (
-