diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index a30e5d0b..71d9daaf 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -1,7 +1,8 @@ { "client_settings": "Client settings", - "example_upstream_reserved": "you can specify DNS upstream <0>for a specific domain(s)", - "upstream_parallel": "Use parallel queries to speed up resolving by simultaneously querying all upstream servers", + "example_upstream_reserved": "You can specify DNS upstream <0>for the specific domain(s)", + "upstream_parallel": "Use parallel requests to speed up resolving by simultaneously querying all upstream servers", + "parallel_requests": "Parallel requests", "bootstrap_dns": "Bootstrap DNS servers", "bootstrap_dns_desc": "Bootstrap DNS servers are used to resolve IP addresses of the DoH/DoT resolvers you specify as upstreams.", "check_dhcp_servers": "Check for DHCP servers", @@ -453,6 +454,8 @@ "example_rewrite_wildcard": "rewrite responses for all <0>example.org subdomains.", "disable_ipv6": "Disable IPv6", "disable_ipv6_desc": "If this feature is enabled, all DNS queries for IPv6 addresses (type AAAA) will be dropped.", + "fastest_addr": "Fastest IP address", + "fastest_addr_desc": "Query all DNS servers and return the fastest IP address among all responses", "autofix_warning_text": "If you click \"Fix\", AdGuard Home will configure your system to use AdGuard Home DNS server.", "autofix_warning_list": "It will perform these tasks: <0>Deactivate system DNSStubListener <0>Set DNS server address to 127.0.0.1 <0>Replace symbolic link target of /etc/resolv.conf with /run/systemd/resolve/resolv.conf <0>Stop DNSStubListener (reload systemd-resolved service)", "autofix_warning_result": "As a result all DNS requests from your system will be processed by AdGuard Home by default.", diff --git a/client/src/actions/dnsConfig.js b/client/src/actions/dnsConfig.js index 1976613e..c7a4dc2f 100644 --- a/client/src/actions/dnsConfig.js +++ b/client/src/actions/dnsConfig.js @@ -2,6 +2,7 @@ import { createAction } from 'redux-actions'; import apiClient from '../api/Api'; import { addErrorToast, addSuccessToast } from './index'; +import { normalizeTextarea } from '../helpers/helpers'; export const getDnsConfigRequest = createAction('GET_DNS_CONFIG_REQUEST'); export const getDnsConfigFailure = createAction('GET_DNS_CONFIG_FAILURE'); @@ -25,8 +26,26 @@ export const setDnsConfigSuccess = createAction('SET_DNS_CONFIG_SUCCESS'); export const setDnsConfig = config => async (dispatch) => { dispatch(setDnsConfigRequest()); try { - await apiClient.setDnsConfig(config); - dispatch(addSuccessToast('config_successfully_saved')); + const data = { ...config }; + + let hasDnsSettings = false; + if (Object.prototype.hasOwnProperty.call(data, 'bootstrap_dns')) { + data.bootstrap_dns = normalizeTextarea(config.bootstrap_dns); + hasDnsSettings = true; + } + if (Object.prototype.hasOwnProperty.call(data, 'upstream_dns')) { + data.upstream_dns = normalizeTextarea(config.upstream_dns); + hasDnsSettings = true; + } + + await apiClient.setDnsConfig(data); + + if (hasDnsSettings) { + dispatch(addSuccessToast('updated_upstream_dns_toast')); + } else { + dispatch(addSuccessToast('config_successfully_saved')); + } + dispatch(setDnsConfigSuccess(config)); } catch (error) { dispatch(addErrorToast({ error })); diff --git a/client/src/actions/index.js b/client/src/actions/index.js index 0d212b1b..0cff2116 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -244,6 +244,7 @@ export const getDnsStatus = () => async (dispatch) => { dispatch(dnsStatusFailure()); window.location.reload(true); }; + const handleRequestSuccess = (response) => { const dnsStatus = response.data; const { running } = dnsStatus; @@ -265,42 +266,6 @@ export const getDnsStatus = () => async (dispatch) => { } }; -export const getDnsSettingsRequest = createAction('GET_DNS_SETTINGS_REQUEST'); -export const getDnsSettingsFailure = createAction('GET_DNS_SETTINGS_FAILURE'); -export const getDnsSettingsSuccess = createAction('GET_DNS_SETTINGS_SUCCESS'); - -export const getDnsSettings = () => async (dispatch) => { - dispatch(getDnsSettingsRequest()); - try { - const dnsStatus = await apiClient.getGlobalStatus(); - dispatch(getDnsSettingsSuccess(dnsStatus)); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(getDnsSettingsFailure()); - } -}; - -export const handleUpstreamChange = createAction('HANDLE_UPSTREAM_CHANGE'); -export const setUpstreamRequest = createAction('SET_UPSTREAM_REQUEST'); -export const setUpstreamFailure = createAction('SET_UPSTREAM_FAILURE'); -export const setUpstreamSuccess = createAction('SET_UPSTREAM_SUCCESS'); - -export const setUpstream = config => async (dispatch) => { - dispatch(setUpstreamRequest()); - try { - const values = { ...config }; - values.bootstrap_dns = normalizeTextarea(values.bootstrap_dns); - values.upstream_dns = normalizeTextarea(values.upstream_dns); - - await apiClient.setUpstream(values); - dispatch(addSuccessToast('updated_upstream_dns_toast')); - dispatch(setUpstreamSuccess(config)); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(setUpstreamFailure()); - } -}; - export const testUpstreamRequest = createAction('TEST_UPSTREAM_REQUEST'); export const testUpstreamFailure = createAction('TEST_UPSTREAM_FAILURE'); export const testUpstreamSuccess = createAction('TEST_UPSTREAM_SUCCESS'); diff --git a/client/src/api/Api.js b/client/src/api/Api.js index 94a05b19..f678d7c3 100644 --- a/client/src/api/Api.js +++ b/client/src/api/Api.js @@ -32,7 +32,6 @@ class Api { // Global methods GLOBAL_STATUS = { path: 'status', method: 'GET' }; - GLOBAL_SET_UPSTREAM_DNS = { path: 'set_upstreams_config', method: 'POST' }; GLOBAL_TEST_UPSTREAM_DNS = { path: 'test_upstream_dns', method: 'POST' }; GLOBAL_VERSION = { path: 'version.json', method: 'POST' }; GLOBAL_UPDATE = { path: 'update', method: 'POST' }; @@ -42,15 +41,6 @@ class Api { return this.makeRequest(path, method); } - setUpstream(url) { - const { path, method } = this.GLOBAL_SET_UPSTREAM_DNS; - const config = { - data: url, - headers: { 'Content-Type': 'application/json' }, - }; - return this.makeRequest(path, method, config); - } - testUpstream(servers) { const { path, method } = this.GLOBAL_TEST_UPSTREAM_DNS; const config = { diff --git a/client/src/components/Settings/Dns/Config/Form.js b/client/src/components/Settings/Dns/Config/Form.js index 1db400e8..46fae81e 100644 --- a/client/src/components/Settings/Dns/Config/Form.js +++ b/client/src/components/Settings/Dns/Config/Form.js @@ -17,26 +17,55 @@ import { } from '../../../../helpers/form'; import { BLOCKING_MODES } from '../../../../helpers/constants'; -const getFields = (processing, t) => Object.values(BLOCKING_MODES).map(mode => ( - -)); +const checkboxes = [{ + name: 'edns_cs_enabled', + placeholder: 'edns_enable', + subtitle: 'edns_cs_desc', +}, +{ + name: 'dnssec_enabled', + placeholder: 'dnssec_enable', + subtitle: 'dnssec_enable_desc', +}, +{ + name: 'disable_ipv6', + placeholder: 'disable_ipv6', + subtitle: 'disable_ipv6_desc', +}]; + +const customIps = [{ + description: 'blocking_ipv4_desc', + name: 'blocking_ipv4', + validateIp: ipv4, +}, +{ + description: 'blocking_ipv6_desc', + name: 'blocking_ipv6', + validateIp: ipv6, +}]; + +const getFields = (processing, t) => Object.values(BLOCKING_MODES) + .map(mode => ( + + )); let Form = ({ handleSubmit, submitting, invalid, processing, blockingMode, t, -}) => ( +}) =>
-
-
-
- -
-
-
-
- -
-
-
-
- -
-
+ {checkboxes.map(({ name, placeholder, subtitle }) => +
+
+ +
+
)}
- {Object.values(BLOCKING_MODES).map(mode => ( -
  • - {`blocking_mode_${mode}`} -
  • - ))} + {Object.values(BLOCKING_MODES) + .map(mode => ( +
  • + {`blocking_mode_${mode}`} +
  • + ))}
    {getFields(processing, t)} @@ -108,40 +115,27 @@ let Form = ({
    {blockingMode === BLOCKING_MODES.custom_ip && ( -
    + {customIps.map(({ + description, + name, + validateIp, + }) =>
    -
    -
    -
    -
    - -
    - blocking_ipv6_desc -
    - -
    -
    +
    )}
    )}
    @@ -152,8 +146,7 @@ let Form = ({ > save_btn - -); + ; Form.propTypes = { blockingMode: PropTypes.string.isRequired, diff --git a/client/src/components/Settings/Dns/Upstream/Form.js b/client/src/components/Settings/Dns/Upstream/Form.js index e6164818..961ac252 100644 --- a/client/src/components/Settings/Dns/Upstream/Form.js +++ b/client/src/components/Settings/Dns/Upstream/Form.js @@ -6,21 +6,50 @@ import { Trans, withNamespaces } from 'react-i18next'; import flow from 'lodash/flow'; import classnames from 'classnames'; -import { renderSelectField } from '../../../../helpers/form'; import Examples from './Examples'; +import { renderSelectField } from '../../../../helpers/form'; + +const getInputFields = (parallel_requests_selected, fastest_addr_selected) => [{ + // eslint-disable-next-line react/display-name + getTitle: () => , + name: 'upstream_dns', + type: 'text', + component: 'textarea', + className: 'form-control form-control--textarea', + placeholder: 'upstream_dns', +}, +{ + name: 'parallel_requests', + placeholder: 'parallel_requests', + component: renderSelectField, + type: 'checkbox', + subtitle: 'upstream_parallel', + disabled: fastest_addr_selected, +}, +{ + name: 'fastest_addr', + placeholder: 'fastest_addr', + component: renderSelectField, + type: 'checkbox', + subtitle: 'fastest_addr_desc', + disabled: parallel_requests_selected, +}]; let Form = (props) => { const { t, handleSubmit, testUpstream, - upstreamDns, - bootstrapDns, - allServers, submitting, invalid, - processingSetUpstream, + processingSetConfig, processingTestUpstream, + fastest_addr, + parallel_requests, + upstream_dns, + bootstrap_dns, } = props; const testButtonClass = classnames({ @@ -28,61 +57,49 @@ let Form = (props) => { 'btn btn-primary btn-standard mr-2 btn-loading': processingTestUpstream, }); + const INPUT_FIELDS = getInputFields(parallel_requests, fastest_addr); + return (
    -
    -
    - - -
    -
    -
    -
    - -
    -
    + {INPUT_FIELDS.map(({ + name, component, type, className, placeholder, getTitle, subtitle, disabled, + }) =>
    + {typeof getTitle === 'function' && getTitle()} + +
    )}

    -
    -
    - -
    - bootstrap_dns_desc -
    - +
    + +
    + bootstrap_dns_desc
    +
    @@ -92,12 +109,11 @@ let Form = (props) => { className={testButtonClass} onClick={() => testUpstream({ - upstream_dns: upstreamDns, - bootstrap_dns: bootstrapDns, - all_servers: allServers, + upstream_dns, + bootstrap_dns, }) } - disabled={!upstreamDns || processingTestUpstream} + disabled={!upstream_dns || processingTestUpstream} > test_upstream_btn @@ -105,7 +121,7 @@ let Form = (props) => { type="submit" className="btn btn-success btn-standard" disabled={ - submitting || invalid || processingSetUpstream || processingTestUpstream + submitting || invalid || processingSetConfig || processingTestUpstream } > apply_btn @@ -122,24 +138,28 @@ Form.propTypes = { submitting: PropTypes.bool, invalid: PropTypes.bool, initialValues: PropTypes.object, - upstreamDns: PropTypes.string, - bootstrapDns: PropTypes.string, - allServers: PropTypes.bool, + upstream_dns: PropTypes.string, + bootstrap_dns: PropTypes.string, + fastest_addr: PropTypes.bool, + parallel_requests: PropTypes.bool, processingTestUpstream: PropTypes.bool, - processingSetUpstream: PropTypes.bool, + processingSetConfig: PropTypes.bool, t: PropTypes.func, }; const selector = formValueSelector('upstreamForm'); Form = connect((state) => { - const upstreamDns = selector(state, 'upstream_dns'); - const bootstrapDns = selector(state, 'bootstrap_dns'); - const allServers = selector(state, 'all_servers'); + const upstream_dns = selector(state, 'upstream_dns'); + const bootstrap_dns = selector(state, 'bootstrap_dns'); + const fastest_addr = selector(state, 'fastest_addr'); + const parallel_requests = selector(state, 'parallel_requests'); + return { - upstreamDns, - bootstrapDns, - allServers, + upstream_dns, + bootstrap_dns, + fastest_addr, + parallel_requests, }; })(Form); diff --git a/client/src/components/Settings/Dns/Upstream/index.js b/client/src/components/Settings/Dns/Upstream/index.js index 292c2cfd..62a93795 100644 --- a/client/src/components/Settings/Dns/Upstream/index.js +++ b/client/src/components/Settings/Dns/Upstream/index.js @@ -7,7 +7,7 @@ import Card from '../../../ui/Card'; class Upstream extends Component { handleSubmit = (values) => { - this.props.setUpstream(values); + this.props.setDnsConfig(values); }; handleTest = (values) => { @@ -17,11 +17,14 @@ class Upstream extends Component { render() { const { t, - upstreamDns: upstream_dns, - bootstrapDns: bootstrap_dns, - allServers: all_servers, - processingSetUpstream, processingTestUpstream, + dnsConfig: { + upstream_dns, + bootstrap_dns, + fastest_addr, + parallel_requests, + processingSetConfig, + }, } = this.props; return ( @@ -36,12 +39,13 @@ class Upstream extends Component { initialValues={{ upstream_dns, bootstrap_dns, - all_servers, + fastest_addr, + parallel_requests, }} testUpstream={this.handleTest} onSubmit={this.handleSubmit} processingTestUpstream={processingTestUpstream} - processingSetUpstream={processingSetUpstream} + processingSetConfig={processingSetConfig} />
    @@ -51,14 +55,11 @@ class Upstream extends Component { } Upstream.propTypes = { - upstreamDns: PropTypes.string, - bootstrapDns: PropTypes.string, - allServers: PropTypes.bool, - setUpstream: PropTypes.func.isRequired, testUpstream: PropTypes.func.isRequired, - processingSetUpstream: PropTypes.bool.isRequired, processingTestUpstream: PropTypes.bool.isRequired, t: PropTypes.func.isRequired, + dnsConfig: PropTypes.object.isRequired, + setDnsConfig: PropTypes.func.isRequired, }; export default withNamespaces()(Upstream); diff --git a/client/src/components/Settings/Dns/index.js b/client/src/components/Settings/Dns/index.js index aae1fdab..85a4bcf5 100644 --- a/client/src/components/Settings/Dns/index.js +++ b/client/src/components/Settings/Dns/index.js @@ -10,7 +10,6 @@ import Loading from '../../ui/Loading'; class Dns extends Component { componentDidMount() { - this.props.getDnsSettings(); this.props.getAccessList(); this.props.getDnsConfig(); } @@ -18,59 +17,45 @@ class Dns extends Component { render() { const { t, - dashboard, settings, access, setAccessList, testUpstream, - setUpstream, dnsConfig, setDnsConfig, } = this.props; - const isDataLoading = dashboard.processingDnsSettings - || access.processing - || dnsConfig.processingGetConfig; - const isDataReady = !dashboard.processingDnsSettings - && !access.processing - && !dnsConfig.processingGetConfig; + const isDataLoading = access.processing || dnsConfig.processingGetConfig; return ( - {isDataLoading && } - {isDataReady && ( + {isDataLoading ? + : - - )} + } ); } } Dns.propTypes = { - dashboard: PropTypes.object.isRequired, settings: PropTypes.object.isRequired, - setUpstream: PropTypes.func.isRequired, testUpstream: PropTypes.func.isRequired, getAccessList: PropTypes.func.isRequired, setAccessList: PropTypes.func.isRequired, access: PropTypes.object.isRequired, - getDnsSettings: PropTypes.func.isRequired, dnsConfig: PropTypes.object.isRequired, setDnsConfig: PropTypes.func.isRequired, getDnsConfig: PropTypes.func.isRequired, diff --git a/client/src/components/ui/Checkbox.css b/client/src/components/ui/Checkbox.css index ff657898..bab88c79 100644 --- a/client/src/components/ui/Checkbox.css +++ b/client/src/components/ui/Checkbox.css @@ -83,6 +83,7 @@ .checkbox__input:disabled + .checkbox__label { cursor: default; + color: var(--gray); } .checkbox__input:disabled + .checkbox__label:before { diff --git a/client/src/containers/Dns.js b/client/src/containers/Dns.js index f32e1510..961c2f3e 100644 --- a/client/src/containers/Dns.js +++ b/client/src/containers/Dns.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { handleUpstreamChange, setUpstream, testUpstream, getDnsSettings } from '../actions'; +import { testUpstream } from '../actions'; import { getAccessList, setAccessList } from '../actions/access'; import { getRewritesList, @@ -25,8 +25,6 @@ const mapStateToProps = (state) => { }; const mapDispatchToProps = { - handleUpstreamChange, - setUpstream, testUpstream, getAccessList, setAccessList, @@ -34,7 +32,6 @@ const mapDispatchToProps = { addRewrite, deleteRewrite, toggleRewritesModal, - getDnsSettings, getDnsConfig, setDnsConfig, }; diff --git a/client/src/helpers/form.js b/client/src/helpers/form.js index d075feee..97c0daa2 100644 --- a/client/src/helpers/form.js +++ b/client/src/helpers/form.js @@ -117,29 +117,30 @@ export const renderSelectField = ({ placeholder, subtitle, disabled, + onClick, modifier = 'checkbox--form', meta: { touched, error }, }) => ( - - + {!disabled && + touched && + (error && {error})} + ); export const renderServiceField = ({ diff --git a/client/src/reducers/dnsConfig.js b/client/src/reducers/dnsConfig.js index ad45e631..9f55bee7 100644 --- a/client/src/reducers/dnsConfig.js +++ b/client/src/reducers/dnsConfig.js @@ -15,6 +15,8 @@ const dnsConfig = handleActions( const { blocking_ipv4, blocking_ipv6, + upstream_dns, + bootstrap_dns, ...values } = payload; @@ -23,6 +25,8 @@ const dnsConfig = handleActions( ...values, blocking_ipv4: blocking_ipv4 || DEFAULT_BLOCKING_IPV4, blocking_ipv6: blocking_ipv6 || DEFAULT_BLOCKING_IPV6, + upstream_dns: (upstream_dns && upstream_dns.join('\n')) || '', + bootstrap_dns: (bootstrap_dns && bootstrap_dns.join('\n')) || '', processingGetConfig: false, }; }, diff --git a/client/src/reducers/index.js b/client/src/reducers/index.js index 046fce67..245cf17d 100644 --- a/client/src/reducers/index.js +++ b/client/src/reducers/index.js @@ -35,14 +35,6 @@ const settings = handleActions( const newSettingsList = { ...settingsList, [settingKey]: newSetting }; return { ...state, settingsList: newSettingsList }; }, - [actions.setUpstreamRequest]: state => ({ ...state, processingSetUpstream: true }), - [actions.setUpstreamFailure]: state => ({ ...state, processingSetUpstream: false }), - [actions.setUpstreamSuccess]: (state, { payload }) => ({ - ...state, - ...payload, - processingSetUpstream: false, - }), - [actions.testUpstreamRequest]: state => ({ ...state, processingTestUpstream: true }), [actions.testUpstreamFailure]: state => ({ ...state, processingTestUpstream: false }), [actions.testUpstreamSuccess]: state => ({ ...state, processingTestUpstream: false }), @@ -50,7 +42,6 @@ const settings = handleActions( { processing: true, processingTestUpstream: false, - processingSetUpstream: false, processingDhcpStatus: false, settingsList: {}, }, @@ -67,12 +58,9 @@ const dashboard = handleActions( version, dns_port: dnsPort, dns_addresses: dnsAddresses, - upstream_dns: upstreamDns, - bootstrap_dns: bootstrapDns, - all_servers: allServers, protection_enabled: protectionEnabled, - language, http_port: httpPort, + language, } = payload; const newState = { ...state, @@ -81,9 +69,6 @@ const dashboard = handleActions( dnsVersion: version, dnsPort, dnsAddresses, - upstreamDns: (upstreamDns && upstreamDns.join('\n')) || '', - bootstrapDns: (bootstrapDns && bootstrapDns.join('\n')) || '', - allServers, protectionEnabled, language, httpPort, @@ -138,11 +123,6 @@ const dashboard = handleActions( return newState; }, - [actions.handleUpstreamChange]: (state, { payload }) => { - const { upstreamDns } = payload; - return { ...state, upstreamDns }; - }, - [actions.getLanguageSuccess]: (state, { payload }) => { const newState = { ...state, language: payload }; return newState; @@ -159,24 +139,6 @@ const dashboard = handleActions( return newState; }, - [actions.getDnsSettingsRequest]: state => ({ ...state, processingDnsSettings: true }), - [actions.getDnsSettingsFailure]: state => ({ ...state, processingDnsSettings: false }), - [actions.getDnsSettingsSuccess]: (state, { payload }) => { - const { - upstream_dns: upstreamDns, - bootstrap_dns: bootstrapDns, - all_servers: allServers, - } = payload; - - return { - ...state, - allServers, - upstreamDns: (upstreamDns && upstreamDns.join('\n')) || '', - bootstrapDns: (bootstrapDns && bootstrapDns.join('\n')) || '', - processingDnsSettings: false, - }; - }, - [actions.getProfileRequest]: state => ({ ...state, processingProfile: true }), [actions.getProfileFailure]: state => ({ ...state, processingProfile: false }), [actions.getProfileSuccess]: (state, { payload }) => ({ @@ -191,11 +153,7 @@ const dashboard = handleActions( processingVersion: true, processingClients: true, processingUpdate: false, - processingDnsSettings: true, processingProfile: true, - upstreamDns: '', - bootstrapDns: '', - allServers: false, protectionEnabled: false, processingProtection: false, httpPort: 80,