diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 92540ea6..ba99db2d 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -1,4 +1,7 @@ { + "upstream_parallel": "Use parallel queries to speed up resolving by simultaneously querying all upstream servers", + "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.", "url_added_successfully": "URL added successfully", "check_dhcp_servers": "Check for DHCP servers", "save_config": "Save config", @@ -246,8 +249,7 @@ "form_error_equal": "Shouldn't be equal", "form_error_password": "Password mismatched", "reset_settings": "Reset settings", - "update_announcement": "AdGuard Home {{version}} is now available! <0>Click here for more info.", - "upstream_parallel": "Use parallel queries to speed up resolving by simultaneously querying all upstream servers", - "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." -} + "update_announcement": "AdGuard Home {{version}} is now available! <0>Click here<\/0> for more info.", + "setup_guide": "Setup guide", + "dns_addresses": "DNS addresses" +} \ No newline at end of file diff --git a/client/src/__locales/zh-cn.json b/client/src/__locales/zh-cn.json index 7ac6abf3..03d87da9 100644 --- a/client/src/__locales/zh-cn.json +++ b/client/src/__locales/zh-cn.json @@ -1,4 +1,7 @@ { + "upstream_parallel": "\u901a\u8fc7\u540c\u65f6\u67e5\u8be2\u6240\u6709\u4e0a\u6d41\u670d\u52a1\u5668\u4ee5\u4f7f\u7528\u5e76\u884c\u67e5\u8be2\u52a0\u901f\u89e3\u6790", + "bootstrap_dns": "Bootstrap DNS \u670d\u52a1\u5668", + "bootstrap_dns_desc": "Bootstrap DNS servers are used to resolve IP addresses of the DoH\/DoT resolvers you specify as upstreams.", "url_added_successfully": "\u7f51\u5740\u6dfb\u52a0\u6210\u529f", "check_dhcp_servers": "\u68c0\u67e5 DHCP \u670d\u52a1\u5668", "save_config": "\u4fdd\u5b58\u914d\u7f6e", diff --git a/client/src/__locales/zh-tw.json b/client/src/__locales/zh-tw.json index 4d3b7b85..177fd8df 100644 --- a/client/src/__locales/zh-tw.json +++ b/client/src/__locales/zh-tw.json @@ -1,4 +1,7 @@ { + "upstream_parallel": "\u900f\u904e\u540c\u6642\u5730\u67e5\u8a62\u6240\u6709\u4e0a\u6e38\u7684\u4f3a\u670d\u5668\uff0c\u4f7f\u7528\u4e26\u884c\u7684\u67e5\u8a62\u4ee5\u52a0\u901f\u89e3\u6790", + "bootstrap_dns": "\u81ea\u6211\u555f\u52d5\uff08Bootstrap\uff09DNS \u4f3a\u670d\u5668", + "bootstrap_dns_desc": "\u81ea\u6211\u555f\u52d5\uff08Bootstrap\uff09DNS\u4f3a\u670d\u5668\u88ab\u7528\u65bc\u89e3\u6790\u60a8\u660e\u78ba\u6307\u5b9a\u4f5c\u70ba\u4e0a\u6e38\u7684DoH\/DoT\u89e3\u6790\u5668\u4e4bIP\u4f4d\u5740\u3002", "url_added_successfully": "\u7db2\u5740\u88ab\u6210\u529f\u5730\u52a0\u5165", "check_dhcp_servers": "\u6aa2\u67e5\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668", "save_config": "\u5132\u5b58\u914d\u7f6e", @@ -79,7 +82,7 @@ "no_settings": "\u7121\u8a2d\u5b9a", "general_settings": "\u4e00\u822c\u7684\u8a2d\u5b9a", "upstream_dns": "\u4e0a\u6e38\u7684DNS\u4f3a\u670d\u5668", - "upstream_dns_hint": "\u5982\u679c\u60a8\u4fdd\u7559\u8a72\u6b04\u4f4d\u7a7a\u767d\u7684\uff0cAdGuard Home\u5c07\u4f7f\u7528Cloudflare DNS<\/a>\u4f5c\u70ba\u4e0a\u6e38\u3002\u5c0d\u65bcDNS over TLS\u4f3a\u670d\u5668\u4f7f\u7528 tls:\/\/ \u524d\u7db4\u3002", + "upstream_dns_hint": "\u5982\u679c\u60a8\u5c07\u8a72\u6b04\u4f4d\u7559\u7a7a\uff0cAdGuard Home\u5c07\u4f7f\u7528Cloudflare DNS<\/a>\u4f5c\u70ba\u4e0a\u6e38\u3002", "test_upstream_btn": "\u6e2c\u8a66\u4e0a\u884c\u8cc7\u6599\u6d41", "apply_btn": "\u5957\u7528", "disabled_filtering_toast": "\u5df2\u7981\u7528\u904e\u6ffe", diff --git a/client/src/components/App/index.js b/client/src/components/App/index.js index 91c5e512..a2676dd0 100644 --- a/client/src/components/App/index.js +++ b/client/src/components/App/index.js @@ -14,8 +14,9 @@ import Dashboard from '../../containers/Dashboard'; import Settings from '../../containers/Settings'; import Filters from '../../containers/Filters'; import Logs from '../../containers/Logs'; -import Footer from '../ui/Footer'; +import SetupGuide from '../../containers/SetupGuide'; import Toasts from '../Toasts'; +import Footer from '../ui/Footer'; import Status from '../ui/Status'; import UpdateTopline from '../ui/UpdateTopline'; import EncryptionTopline from '../ui/EncryptionTopline'; @@ -86,6 +87,7 @@ class App extends Component { + } diff --git a/client/src/components/Header/Header.css b/client/src/components/Header/Header.css index dbbcf08d..a8273e78 100644 --- a/client/src/components/Header/Header.css +++ b/client/src/components/Header/Header.css @@ -76,6 +76,13 @@ font-weight: 600; } +.nav-version__link { + position: relative; + display: inline-block; + border-bottom: 1px dashed #495057; + cursor: pointer; +} + .header-brand-img { height: 32px; } diff --git a/client/src/components/Header/Menu.js b/client/src/components/Header/Menu.js index ef465378..2800d768 100644 --- a/client/src/components/Header/Menu.js +++ b/client/src/components/Header/Menu.js @@ -4,7 +4,6 @@ import PropTypes from 'prop-types'; import enhanceWithClickOutside from 'react-click-outside'; import classnames from 'classnames'; import { Trans, withNamespaces } from 'react-i18next'; -import { REPOSITORY } from '../../helpers/constants'; class Menu extends Component { handleClickOutside = () => { @@ -56,10 +55,10 @@ class Menu extends Component {
  • - + - faq - + setup_guide +
  • diff --git a/client/src/components/Header/Version.js b/client/src/components/Header/Version.js index 0faedbcc..be2158e9 100644 --- a/client/src/components/Header/Version.js +++ b/client/src/components/Header/Version.js @@ -2,24 +2,35 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Trans, withNamespaces } from 'react-i18next'; +import { getDnsAddress } from '../../helpers/helpers'; + function Version(props) { - const { dnsVersion, dnsAddress, dnsPort } = props; + const { dnsVersion, dnsAddresses, dnsPort } = props; return (
    version: {dnsVersion}
    -
    - address: {dnsAddress}:{dnsPort} +
    +
    + dns_addresses +
    +
    +
    + {dnsAddresses + .map(ip =>
  • {getDnsAddress(ip, dnsPort)}
  • ) + } +
    +
    ); } Version.propTypes = { - dnsVersion: PropTypes.string, - dnsAddress: PropTypes.string, - dnsPort: PropTypes.number, + dnsVersion: PropTypes.string.isRequired, + dnsAddresses: PropTypes.array.isRequired, + dnsPort: PropTypes.number.isRequired, }; export default withNamespaces()(Version); diff --git a/client/src/components/Header/index.js b/client/src/components/Header/index.js index ecc1a2af..e2d47fe6 100644 --- a/client/src/components/Header/index.js +++ b/client/src/components/Header/index.js @@ -56,11 +56,13 @@ class Header extends Component { toggleMenuOpen={this.toggleMenuOpen} closeMenu={this.closeMenu} /> -
    - -
    + {!dashboard.processing && +
    + +
    + }
    diff --git a/client/src/components/SetupGuide/Guide.css b/client/src/components/SetupGuide/Guide.css new file mode 100644 index 00000000..821f1798 --- /dev/null +++ b/client/src/components/SetupGuide/Guide.css @@ -0,0 +1,15 @@ +.guide { + max-width: 768px; + margin: 0 auto; +} + +.guide__title { + margin-bottom: 10px; + font-size: 17px; + font-weight: 700; +} + +.guide__desc { + margin-bottom: 20px; + font-size: 15px; +} diff --git a/client/src/components/SetupGuide/index.js b/client/src/components/SetupGuide/index.js new file mode 100644 index 00000000..bea2bbb1 --- /dev/null +++ b/client/src/components/SetupGuide/index.js @@ -0,0 +1,46 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Trans, withNamespaces } from 'react-i18next'; + +import { getDnsAddress } from '../../helpers/helpers'; + +import Guide from '../ui/Guide'; +import Card from '../ui/Card'; +import PageTitle from '../ui/PageTitle'; +import './Guide.css'; + +const SetupGuide = ({ + t, + dashboard: { + dnsAddresses, + dnsPort, + }, +}) => ( +
    + + +
    + install_devices_title +
    +
    + install_devices_desc +
    + install_devices_address: +
    +
    + {dnsAddresses + .map(ip =>
  • {getDnsAddress(ip, dnsPort)}
  • ) + } +
    +
    + +
    +
    +); + +SetupGuide.propTypes = { + dashboard: PropTypes.object.isRequired, + t: PropTypes.func.isRequired, +}; + +export default withNamespaces()(SetupGuide); diff --git a/client/src/components/ui/Guide.js b/client/src/components/ui/Guide.js new file mode 100644 index 00000000..9e89f7ec --- /dev/null +++ b/client/src/components/ui/Guide.js @@ -0,0 +1,83 @@ +import React from 'react'; +import { Trans, withNamespaces } from 'react-i18next'; + +import Tabs from '../ui/Tabs'; +import Icons from '../ui/Icons'; + +const Guide = () => ( +
    + + +
    +
    + install_devices_router +
    +
    +

    install_devices_router_desc

    +
      +
    1. install_devices_router_list_1
    2. +
    3. install_devices_router_list_2
    4. +
    5. install_devices_router_list_3
    6. +
    +
    +
    +
    +
    + Windows +
    +
    +
      +
    1. install_devices_windows_list_1
    2. +
    3. install_devices_windows_list_2
    4. +
    5. install_devices_windows_list_3
    6. +
    7. install_devices_windows_list_4
    8. +
    9. install_devices_windows_list_5
    10. +
    11. install_devices_windows_list_6
    12. +
    +
    +
    +
    +
    + macOS +
    +
    +
      +
    1. install_devices_macos_list_1
    2. +
    3. install_devices_macos_list_2
    4. +
    5. install_devices_macos_list_3
    6. +
    7. install_devices_macos_list_4
    8. +
    +
    +
    +
    +
    + Android +
    +
    +
      +
    1. install_devices_android_list_1
    2. +
    3. install_devices_android_list_2
    4. +
    5. install_devices_android_list_3
    6. +
    7. install_devices_android_list_4
    8. +
    9. install_devices_android_list_5
    10. +
    +
    +
    +
    +
    + iOS +
    +
    +
      +
    1. install_devices_ios_list_1
    2. +
    3. install_devices_ios_list_2
    4. +
    5. install_devices_ios_list_3
    6. +
    7. install_devices_ios_list_4
    8. +
    +
    +
    +
    +
    +); + +export default withNamespaces()(Guide); diff --git a/client/src/components/ui/Popover.css b/client/src/components/ui/Popover.css index dc83e4cb..f7e23836 100644 --- a/client/src/components/ui/Popover.css +++ b/client/src/components/ui/Popover.css @@ -22,6 +22,16 @@ height: 24px; } +.popover__trigger--address { + top: 0; + margin: 0; + line-height: 1.2; +} + +.popover__trigger--address:after { + display: none; +} + .popover__body { content: ""; display: flex; @@ -57,6 +67,38 @@ border-top: 6px solid #585965; } +.popover__body--address { + top: calc(100% + 10px); + right: 0; + left: initial; + bottom: initial; + z-index: 1; + min-width: 100px; + padding: 12px 18px; + font-weight: 700; + text-align: left; + white-space: nowrap; + transform: none; + cursor: default; +} + +.popover__body--address:after { + top: -11px; + left: initial; + right: 40px; + border-top: 6px solid transparent; + border-bottom: 6px solid #585965; +} + +.popover__body--address:before { + content: ""; + position: absolute; + top: -7px; + left: 0; + width: 100%; + height: 10px; +} + .popover__trigger:hover + .popover__body, .popover__body:hover { visibility: visible; @@ -73,6 +115,10 @@ stroke: #66b574; } +.popover__list--bold { + font-weight: 700; +} + .popover__list-title { margin-bottom: 3px; } diff --git a/client/src/containers/SetupGuide.js b/client/src/containers/SetupGuide.js new file mode 100644 index 00000000..1e7ecd12 --- /dev/null +++ b/client/src/containers/SetupGuide.js @@ -0,0 +1,14 @@ +import { connect } from 'react-redux'; +import * as actionCreators from '../actions'; +import SetupGuide from '../components/SetupGuide'; + +const mapStateToProps = (state) => { + const { dashboard } = state; + const props = { dashboard }; + return props; +}; + +export default connect( + mapStateToProps, + actionCreators, +)(SetupGuide); diff --git a/client/src/install/Setup/Devices.js b/client/src/install/Setup/Devices.js index 4a5f7c13..f3755b3d 100644 --- a/client/src/install/Setup/Devices.js +++ b/client/src/install/Setup/Devices.js @@ -5,8 +5,7 @@ import { reduxForm, formValueSelector } from 'redux-form'; import { Trans, withNamespaces } from 'react-i18next'; import flow from 'lodash/flow'; -import Tabs from '../../components/ui/Tabs'; -import Icons from '../../components/ui/Icons'; +import Guide from '../../components/ui/Guide'; import Controls from './Controls'; import AddressList from './AddressList'; @@ -30,77 +29,7 @@ let Devices = props => ( /> - - -
    -
    - install_devices_router -
    -
    -

    install_devices_router_desc

    -
      -
    1. install_devices_router_list_1
    2. -
    3. install_devices_router_list_2
    4. -
    5. install_devices_router_list_3
    6. -
    -
    -
    -
    -
    - Windows -
    -
    -
      -
    1. install_devices_windows_list_1
    2. -
    3. install_devices_windows_list_2
    4. -
    5. install_devices_windows_list_3
    6. -
    7. install_devices_windows_list_4
    8. -
    9. install_devices_windows_list_5
    10. -
    11. install_devices_windows_list_6
    12. -
    -
    -
    -
    -
    - macOS -
    -
    -
      -
    1. install_devices_macos_list_1
    2. -
    3. install_devices_macos_list_2
    4. -
    5. install_devices_macos_list_3
    6. -
    7. install_devices_macos_list_4
    8. -
    -
    -
    -
    -
    - Android -
    -
    -
      -
    1. install_devices_android_list_1
    2. -
    3. install_devices_android_list_2
    4. -
    5. install_devices_android_list_3
    6. -
    7. install_devices_android_list_4
    8. -
    9. install_devices_android_list_5
    10. -
    -
    -
    -
    -
    - iOS -
    -
    -
      -
    1. install_devices_ios_list_1
    2. -
    3. install_devices_ios_list_2
    4. -
    5. install_devices_ios_list_3
    6. -
    7. install_devices_ios_list_4
    8. -
    -
    -
    -
    + diff --git a/client/src/reducers/index.js b/client/src/reducers/index.js index 90ace4d6..a6daef82 100644 --- a/client/src/reducers/index.js +++ b/client/src/reducers/index.js @@ -48,7 +48,7 @@ const dashboard = handleActions({ version, running, dns_port: dnsPort, - dns_address: dnsAddress, + dns_addresses: dnsAddresses, querylog_enabled: queryLogEnabled, upstream_dns: upstreamDns, bootstrap_dns: bootstrapDns, @@ -63,7 +63,7 @@ const dashboard = handleActions({ processing: false, dnsVersion: version, dnsPort, - dnsAddress, + dnsAddresses, queryLogEnabled, upstreamDns: upstreamDns.join('\n'), bootstrapDns: bootstrapDns.join('\n'), @@ -181,6 +181,9 @@ const dashboard = handleActions({ protectionEnabled: false, processingProtection: false, httpPort: 80, + dnsPort: 53, + dnsAddresses: [], + dnsVersion: '', }); const queryLogs = handleActions({ diff --git a/control.go b/control.go index 3657b5c6..ad2da0ca 100644 --- a/control.go +++ b/control.go @@ -79,8 +79,25 @@ func httpUpdateConfigReloadDNSReturnOK(w http.ResponseWriter, r *http.Request) { func handleStatus(w http.ResponseWriter, r *http.Request) { log.Tracef("%s %v", r.Method, r.URL) + + dnsAddresses := []string{} + if config.DNS.BindHost == "0.0.0.0" { + ifaces, e := getValidNetInterfacesForWeb() + if e != nil { + log.Error("Couldn't get network interfaces: %v", e) + } + for _, iface := range ifaces { + for _, addr := range iface.Addresses { + dnsAddresses = append(dnsAddresses, addr) + } + } + } + if len(dnsAddresses) == 0 { + dnsAddresses = append(dnsAddresses, config.DNS.BindHost) + } + data := map[string]interface{}{ - "dns_address": config.DNS.BindHost, + "dns_addresses": dnsAddresses, "http_port": config.BindPort, "dns_port": config.DNS.Port, "protection_enabled": config.DNS.ProtectionEnabled,