From c36a7895ada28cd20a3f19cb1e58d9b9c05383a6 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Thu, 17 Jan 2019 17:16:49 +0300 Subject: [PATCH 01/45] Add install page API stubs --- control.go | 42 ++++++++++++++++++++++++++++++++++++++++++ go.mod | 1 + 2 files changed, 43 insertions(+) diff --git a/control.go b/control.go index 4600c184..533e535f 100644 --- a/control.go +++ b/control.go @@ -13,6 +13,7 @@ import ( "github.com/AdguardTeam/AdGuardHome/dnsforward" "github.com/AdguardTeam/dnsproxy/upstream" + "github.com/davecgh/go-spew/spew" "github.com/hmage/golibs/log" "github.com/miekg/dns" govalidator "gopkg.in/asaskevich/govalidator.v4" @@ -693,6 +694,43 @@ func handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) { } } +func handleGetDefaultAddresses(w http.ResponseWriter, r *http.Request) { + data := struct { + Web struct { + IP string + Port int + } + DNS struct { + IP string + Port int + } + }{} + + // TODO: replace mockup with actual data + data.Web.IP = "192.168.104.104" + data.Web.Port = 3000 + data.DNS.IP = "192.168.104.104" + data.DNS.Port = 53 + + w.Header().Set("Content-Type", "application/json") + err := json.NewEncoder(w).Encode(data) + if err != nil { + httpError(w, http.StatusInternalServerError, "Unable to marshal default addresses to json: %s", err) + return + } +} + +func handleSetAllSettings(w http.ResponseWriter, r *http.Request) { + newSettings := map[string]interface{}{} + err := json.NewDecoder(r.Body).Decode(&newSettings) + if err != nil { + httpError(w, http.StatusBadRequest, "Failed to parse new DHCP config json: %s", err) + return + } + + spew.Dump(newSettings) +} + func registerControlHandlers() { http.HandleFunc("/control/status", optionalAuth(ensureGET(handleStatus))) http.HandleFunc("/control/enable_protection", optionalAuth(ensurePOST(handleProtectionEnable))) @@ -731,4 +769,8 @@ func registerControlHandlers() { http.HandleFunc("/control/dhcp/interfaces", optionalAuth(ensureGET(handleDHCPInterfaces))) http.HandleFunc("/control/dhcp/set_config", optionalAuth(ensurePOST(handleDHCPSetConfig))) http.HandleFunc("/control/dhcp/find_active_dhcp", optionalAuth(ensurePOST(handleDHCPFindActiveServer))) + + // TODO: move to registerInstallHandlers() + http.HandleFunc("/control/install/get_default_addresses", ensureGET(handleGetDefaultAddresses)) + http.HandleFunc("/control/install/set_all_settings", ensurePOST(handleSetAllSettings)) } diff --git a/go.mod b/go.mod index cc8f3327..6e9e5c4f 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ require ( github.com/AdguardTeam/dnsproxy v0.9.10 github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f // indirect github.com/bluele/gcache v0.0.0-20171010155617-472614239ac7 + github.com/davecgh/go-spew v1.1.1 github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-test/deep v1.0.1 github.com/gobuffalo/packr v1.19.0 From f21aebd1cffe45d67e09ba12b27dc516d899a6ec Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Thu, 17 Jan 2019 19:29:54 +0300 Subject: [PATCH 02/45] /install/get_default_addresses -- make fields lowercase --- control.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/control.go b/control.go index 533e535f..f869a8c0 100644 --- a/control.go +++ b/control.go @@ -695,15 +695,13 @@ func handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) { } func handleGetDefaultAddresses(w http.ResponseWriter, r *http.Request) { + type ipport struct { + IP string `json:"ip"` + Port int `json:"port"` + } data := struct { - Web struct { - IP string - Port int - } - DNS struct { - IP string - Port int - } + Web ipport `json:"web"` + DNS ipport `json:"dns"` }{} // TODO: replace mockup with actual data From 71259c5f19cd27ecb2db4be5166832c4e819dd03 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Fri, 18 Jan 2019 19:29:51 +0300 Subject: [PATCH 03/45] Added web setup entry point --- client/public/index.html | 26 +++++++++++++------------- client/public/install.html | 16 ++++++++++++++++ client/webpack.common.js | 13 ++++++++++++- client/webpack.dev.js | 1 - 4 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 client/public/install.html diff --git a/client/public/index.html b/client/public/index.html index e6b99e20..6b003a23 100644 --- a/client/public/index.html +++ b/client/public/index.html @@ -1,16 +1,16 @@ - - - - - - AdGuard Home - - - -
- + + + + + + AdGuard Home + + + +
+ diff --git a/client/public/install.html b/client/public/install.html new file mode 100644 index 00000000..45065fe3 --- /dev/null +++ b/client/public/install.html @@ -0,0 +1,16 @@ + + + + + + + + Setup AdGuard Home + + + +
+ + diff --git a/client/webpack.common.js b/client/webpack.common.js index 4ca3b1e4..aa1b2383 100644 --- a/client/webpack.common.js +++ b/client/webpack.common.js @@ -8,7 +8,9 @@ const CleanWebpackPlugin = require('clean-webpack-plugin'); const RESOURCES_PATH = path.resolve(__dirname); const ENTRY_REACT = path.resolve(RESOURCES_PATH, 'src/index.js'); +const ENTRY_INSTALL = path.resolve(RESOURCES_PATH, 'src/install/index.js'); const HTML_PATH = path.resolve(RESOURCES_PATH, 'public/index.html'); +const HTML_INSTALL_PATH = path.resolve(RESOURCES_PATH, 'public/install.html'); const PUBLIC_PATH = path.resolve(__dirname, '../build/static'); @@ -16,7 +18,8 @@ const config = { target: 'web', context: RESOURCES_PATH, entry: { - bundle: ENTRY_REACT, + main: ENTRY_REACT, + install: ENTRY_INSTALL, }, output: { path: PUBLIC_PATH, @@ -101,8 +104,16 @@ const config = { new HtmlWebpackPlugin({ inject: true, cache: false, + chunks: ['main'], template: HTML_PATH, }), + new HtmlWebpackPlugin({ + inject: true, + cache: false, + chunks: ['install'], + filename: 'install.html', + template: HTML_INSTALL_PATH, + }), new ExtractTextPlugin({ filename: '[name].[contenthash].css', }), diff --git a/client/webpack.dev.js b/client/webpack.dev.js index 977e6c9f..79589a08 100644 --- a/client/webpack.dev.js +++ b/client/webpack.dev.js @@ -2,7 +2,6 @@ const merge = require('webpack-merge'); const common = require('./webpack.common.js'); module.exports = merge(common, { - devtool: 'inline-source-map', module: { rules: [{ test: /\.js$/, From 5349ec76fd21dff3b25201f17311875744b4f21b Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Fri, 18 Jan 2019 20:17:48 +0300 Subject: [PATCH 04/45] Added components for web setup --- client/src/__locales/en.json | 32 +++- client/src/actions/install.js | 54 ++++++ client/src/api/Api.js | 18 ++ client/src/components/App/index.css | 6 +- client/src/components/Filters/UserRules.js | 2 +- client/src/components/Filters/index.js | 4 +- client/src/components/Header/index.js | 2 +- client/src/components/Settings/Dhcp/Form.js | 2 +- client/src/components/Settings/Dhcp/index.js | 8 +- client/src/components/Settings/Settings.css | 2 +- client/src/components/Settings/Upstream.js | 6 +- client/src/components/ui/Icons.js | Bin 0 -> 7992 bytes client/src/components/ui/Tab.js | 41 +++++ client/src/components/ui/Tabs.css | 42 +++++ client/src/components/ui/Tabs.js | 59 +++++++ client/src/components/ui/svg/icons.svg | 21 +++ .../components/{Header => ui/svg}/logo.svg | 0 client/src/helpers/constants.js | 3 + client/src/install/Setup/Auth.js | 98 +++++++++++ client/src/install/Setup/Controls.js | 115 +++++++++++++ client/src/install/Setup/Devices.js | 68 ++++++++ client/src/install/Setup/Greeting.js | 23 +++ client/src/install/Setup/Progress.js | 25 +++ client/src/install/Setup/Settings.js | 160 ++++++++++++++++++ client/src/install/Setup/Setup.css | 105 ++++++++++++ client/src/install/Setup/Submit.js | 44 +++++ client/src/install/Setup/index.js | 115 +++++++++++++ client/src/install/Setup/renderField.js | 19 +++ client/src/install/Setup/validate.js | 11 ++ client/src/install/index.js | 18 ++ client/src/reducers/install.js | 29 ++++ 31 files changed, 1117 insertions(+), 15 deletions(-) create mode 100644 client/src/actions/install.js create mode 100644 client/src/components/ui/Icons.js create mode 100644 client/src/components/ui/Tab.js create mode 100644 client/src/components/ui/Tabs.css create mode 100644 client/src/components/ui/Tabs.js create mode 100644 client/src/components/ui/svg/icons.svg rename client/src/components/{Header => ui/svg}/logo.svg (100%) create mode 100644 client/src/install/Setup/Auth.js create mode 100644 client/src/install/Setup/Controls.js create mode 100644 client/src/install/Setup/Devices.js create mode 100644 client/src/install/Setup/Greeting.js create mode 100644 client/src/install/Setup/Progress.js create mode 100644 client/src/install/Setup/Settings.js create mode 100644 client/src/install/Setup/Setup.css create mode 100644 client/src/install/Setup/Submit.js create mode 100644 client/src/install/Setup/index.js create mode 100644 client/src/install/Setup/renderField.js create mode 100644 client/src/install/Setup/validate.js create mode 100644 client/src/install/index.js create mode 100644 client/src/reducers/install.js diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 10471501..2a9d73eb 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -157,5 +157,35 @@ "category_label": "Category", "rule_label": "Rule", "filter_label": "Filter", - "unknown_filter": "Unknown filter {{filterId}}" + "unknown_filter": "Unknown filter {{filterId}}", + "install_welcome_title": "Welcome to AdGuard Home!", + "install_welcome_desc": "Lorem ipsum dolor sit amet consectetur adipisicing elit.", + "install_settings_title": "Admin Web Interface", + "install_settings_listen": "Listen interface", + "install_settings_port": "Port", + "install_settings_interface_link": "Your AdGuard Home admin web interface is available on {{link}}", + "form_error_port": "Enter valid port value", + "install_settings_dns": "DNS server", + "install_settings_dns_desc": "You will need to configure your devices or router to use the DNS server at {{ip}}", + "install_auth_title": "Authentication", + "install_auth_desc": "It is highly recommended to configure password authentication to your AdGuard Home admin web interface. Even if it is accessible only in your local network, it is still important to have it protected from unrestricted access.", + "install_auth_username": "Username", + "install_auth_password": "Password", + "install_auth_confirm": "Confirm password", + "install_auth_username_enter": "Enter username", + "install_auth_password_enter": "Enter password", + "install_step": "Step", + "install_devices_title": "Configure your devices", + "install_devices_desc": "In order for AdGuard Home to start working, you need to configure your devices to use it.", + "install_submit_title": "Congratulations!", + "install_submit_desc": "The setup procedure is finished and you are ready to start using AdGuard Home.", + "install_decices_router": "Router", + "install_decices_router_desc": "This setup will automatically cover all the devices connected to your home routerm and you will not need to configure each of them manually.", + "install_decices_router_list_1": "Open the preferences for your router. Usually, you can access it from your browser via a URL (like http://192.168.0.1/ or http://192.168.1.1/). You may be asked to enter the password. If you don t remember it, you can ofter reset the password by pressing a button on the router itself. Some routers require a specific application, which in that case should be already installed on your computer/phone.", + "install_decices_router_list_2": " Find the DHCP/DNS settings. Look for the DNS letters next to a field which allows two or three sets of numbers, each broken into four groups of one to three digits.", + "install_decices_router_list_3": "Enter your AdGuard Home server addresses there.", + "get_started": "Get Started", + "next": "Next", + "open_dashboard": "Open Dashboard", + "install_saved": "All settings saved" } \ No newline at end of file diff --git a/client/src/actions/install.js b/client/src/actions/install.js new file mode 100644 index 00000000..508aa8da --- /dev/null +++ b/client/src/actions/install.js @@ -0,0 +1,54 @@ +import { createAction } from 'redux-actions'; +import Api from '../api/Api'; + +const apiClient = new Api(); + +export const addErrorToast = createAction('ADD_ERROR_TOAST'); +export const addSuccessToast = createAction('ADD_SUCCESS_TOAST'); +export const nextStep = createAction('NEXT_STEP'); +export const prevStep = createAction('PREV_STEP'); + +export const getDefaultAddressesRequest = createAction('GET_DEFAULT_ADDRESSES_REQUEST'); +export const getDefaultAddressesFailure = createAction('GET_DEFAULT_ADDRESSES_FAILURE'); +export const getDefaultAddressesSuccess = createAction('GET_DEFAULT_ADDRESSES_SUCCESS'); + +export const getDefaultAddresses = () => async (dispatch) => { + dispatch(getDefaultAddressesRequest()); + try { + const addresses = await apiClient.getDefaultAddresses(); + dispatch(getDefaultAddressesSuccess(addresses)); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(getDefaultAddressesFailure()); + } +}; + +export const setAllSettingsRequest = createAction('SET_ALL_SETTINGS_REQUEST'); +export const setAllSettingsFailure = createAction('SET_ALL_SETTINGS_FAILURE'); +export const setAllSettingsSuccess = createAction('SET_ALL_SETTINGS_SUCCESS'); + +export const setAllSettings = values => async (dispatch) => { + dispatch(setAllSettingsRequest()); + try { + const { + web, + dns, + username, + password, + } = values; + + const config = { + web, + dns, + username, + password, + }; + + await apiClient.setAllSettings(config); + dispatch(setAllSettingsSuccess()); + dispatch(addSuccessToast('install_saved')); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(setAllSettingsFailure()); + } +}; diff --git a/client/src/api/Api.js b/client/src/api/Api.js index 4592fae7..7f1da9c5 100644 --- a/client/src/api/Api.js +++ b/client/src/api/Api.js @@ -336,4 +336,22 @@ export default class Api { }; return this.makeRequest(path, method, parameters); } + + // Installation + GET_DEFAULT_ADDRESSES = { path: 'install/get_default_addresses', method: 'GET' }; + SET_ALL_SETTINGS = { path: 'install/set_all_settings', method: 'POST' }; + + getDefaultAddresses() { + const { path, method } = this.GET_DEFAULT_ADDRESSES; + return this.makeRequest(path, method); + } + + setAllSettings(config) { + const { path, method } = this.SET_ALL_SETTINGS; + const parameters = { + data: config, + headers: { 'Content-Type': 'application/json' }, + }; + return this.makeRequest(path, method, parameters); + } } diff --git a/client/src/components/App/index.css b/client/src/components/App/index.css index 61ee53a4..391ddabd 100644 --- a/client/src/components/App/index.css +++ b/client/src/components/App/index.css @@ -1,7 +1,7 @@ body { margin: 0; padding: 0; - font-family: sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif; } .status { @@ -26,3 +26,7 @@ body { height: 3px; background: linear-gradient(45deg, rgba(99, 125, 120, 1) 0%, rgba(88, 177, 101, 1) 100%); } + +.hidden { + display: none; +} diff --git a/client/src/components/Filters/UserRules.js b/client/src/components/Filters/UserRules.js index 9aae0281..7a251f3a 100644 --- a/client/src/components/Filters/UserRules.js +++ b/client/src/components/Filters/UserRules.js @@ -25,7 +25,7 @@ class UserRules extends Component {