Added select for listen interfaces
This commit is contained in:
parent
5abe5af707
commit
f379d34813
|
@ -164,10 +164,11 @@
|
|||
"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 <0>{{link}}</0>",
|
||||
"install_settings_interface_link": "Your AdGuard Home admin web interface will be available on the following addresses: <0>{{link}}</0>",
|
||||
"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 <0>{{ip}}</0>",
|
||||
"install_settings_all_interfaces": "All interfaces",
|
||||
"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",
|
||||
|
@ -182,6 +183,7 @@
|
|||
"install_submit_desc": "The setup procedure is finished and you are ready to start using AdGuard Home.",
|
||||
"install_devices_router": "Router",
|
||||
"install_devices_router_desc": "This setup will automatically cover all the devices connected to your home router and you will not need to configure each of them manually.",
|
||||
"install_devices_address": "AdGuard Home DNS server is listening to the following addresses",
|
||||
"install_devices_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 often 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_devices_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_devices_router_list_3": "Enter your AdGuard Home server addresses there.",
|
||||
|
|
|
@ -48,8 +48,10 @@ export const setAllSettings = values => async (dispatch) => {
|
|||
await apiClient.setAllSettings(config);
|
||||
dispatch(setAllSettingsSuccess());
|
||||
dispatch(addSuccessToast('install_saved'));
|
||||
dispatch(nextStep());
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
dispatch(setAllSettingsFailure());
|
||||
dispatch(prevStep());
|
||||
}
|
||||
};
|
||||
|
|
|
@ -338,16 +338,16 @@ export default class Api {
|
|||
}
|
||||
|
||||
// Installation
|
||||
GET_DEFAULT_ADDRESSES = { path: 'install/get_default_addresses', method: 'GET' };
|
||||
SET_ALL_SETTINGS = { path: 'install/set_all_settings', method: 'POST' };
|
||||
INSTALL_GET_ADDRESSES = { path: 'install/get_addresses', method: 'GET' };
|
||||
INSTALL_CONFIGURE = { path: 'install/configure', method: 'POST' };
|
||||
|
||||
getDefaultAddresses() {
|
||||
const { path, method } = this.GET_DEFAULT_ADDRESSES;
|
||||
const { path, method } = this.INSTALL_GET_ADDRESSES;
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setAllSettings(config) {
|
||||
const { path, method } = this.SET_ALL_SETTINGS;
|
||||
const { path, method } = this.INSTALL_CONFIGURE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
|
|
|
@ -19,7 +19,26 @@ class Controls extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
renderButtons(step) {
|
||||
renderPrevButton(step) {
|
||||
switch (step) {
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary btn-standard btn-lg"
|
||||
onClick={this.props.prevStep}
|
||||
>
|
||||
<Trans>back</Trans>
|
||||
</button>
|
||||
);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
renderNextButton(step) {
|
||||
switch (step) {
|
||||
case 1:
|
||||
return (
|
||||
|
@ -34,14 +53,6 @@ class Controls extends Component {
|
|||
case 2:
|
||||
case 3:
|
||||
return (
|
||||
<div className="btn-list">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary btn-standard btn-lg"
|
||||
onClick={this.props.prevStep}
|
||||
>
|
||||
<Trans>back</Trans>
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-success btn-standard btn-lg"
|
||||
|
@ -49,18 +60,9 @@ class Controls extends Component {
|
|||
>
|
||||
<Trans>next</Trans>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
case 4:
|
||||
return (
|
||||
<div className="btn-list">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary btn-standard btn-lg"
|
||||
onClick={this.props.prevStep}
|
||||
>
|
||||
<Trans>back</Trans>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-success btn-standard btn-lg"
|
||||
|
@ -68,14 +70,13 @@ class Controls extends Component {
|
|||
>
|
||||
<Trans>next</Trans>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
case 5:
|
||||
return (
|
||||
<button
|
||||
type="submit"
|
||||
type="button"
|
||||
className="btn btn-success btn-standard btn-lg"
|
||||
disabled={this.props.submitting || this.props.pristine}
|
||||
onClick={this.props.openDashboard}
|
||||
>
|
||||
<Trans>open_dashboard</Trans>
|
||||
</button>
|
||||
|
@ -88,7 +89,10 @@ class Controls extends Component {
|
|||
render() {
|
||||
return (
|
||||
<div className="setup__nav">
|
||||
{this.renderButtons(this.props.step)}
|
||||
<div className="btn-list">
|
||||
{this.renderPrevButton(this.props.step)}
|
||||
{this.renderNextButton(this.props.step)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -98,9 +102,10 @@ Controls.propTypes = {
|
|||
step: PropTypes.number.isRequired,
|
||||
nextStep: PropTypes.func,
|
||||
prevStep: PropTypes.func,
|
||||
pristine: PropTypes.bool,
|
||||
openDashboard: PropTypes.func,
|
||||
submitting: PropTypes.bool,
|
||||
invalid: PropTypes.bool,
|
||||
pristine: PropTypes.bool,
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
|
|
|
@ -1,19 +1,29 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
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 Controls from './Controls';
|
||||
|
||||
const Devices = () => (
|
||||
let Devices = props => (
|
||||
<div className="setup__step">
|
||||
<div className="setup__group">
|
||||
<div className="setup__subtitle">
|
||||
<Trans>install_devices_title</Trans>
|
||||
</div>
|
||||
<p className="setup__desc">
|
||||
<div className="setup__desc">
|
||||
<Trans>install_devices_desc</Trans>
|
||||
</p>
|
||||
<div className="mt-1">
|
||||
<Trans>install_devices_address</Trans>:
|
||||
</div>
|
||||
<div>
|
||||
<strong>{`${props.dnsIp}:${props.dnsPort}`}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<Icons />
|
||||
<Tabs>
|
||||
<div label="Router">
|
||||
|
@ -90,4 +100,28 @@ const Devices = () => (
|
|||
</div>
|
||||
);
|
||||
|
||||
export default withNamespaces()(Devices);
|
||||
Devices.propTypes = {
|
||||
dnsIp: PropTypes.string.isRequired,
|
||||
dnsPort: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
const selector = formValueSelector('install');
|
||||
|
||||
Devices = connect((state) => {
|
||||
const dnsIp = selector(state, 'dns.ip');
|
||||
const dnsPort = selector(state, 'dns.port');
|
||||
|
||||
return {
|
||||
dnsIp,
|
||||
dnsPort,
|
||||
};
|
||||
})(Devices);
|
||||
|
||||
export default flow([
|
||||
withNamespaces(),
|
||||
reduxForm({
|
||||
form: 'install',
|
||||
destroyOnUnmount: false,
|
||||
forceUnregisterOnUnmount: true,
|
||||
}),
|
||||
])(Devices);
|
||||
|
|
|
@ -7,7 +7,6 @@ import flow from 'lodash/flow';
|
|||
|
||||
import Controls from './Controls';
|
||||
import renderField from './renderField';
|
||||
import { R_IPV4 } from '../../helpers/constants';
|
||||
|
||||
const required = (value) => {
|
||||
if (value || value === 0) {
|
||||
|
@ -16,13 +15,6 @@ const required = (value) => {
|
|||
return <Trans>form_error_required</Trans>;
|
||||
};
|
||||
|
||||
const ipv4 = (value) => {
|
||||
if (value && !new RegExp(R_IPV4).test(value)) {
|
||||
return <Trans>form_error_ip_format</Trans>;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const port = (value) => {
|
||||
if (value < 1 || value > 65535) {
|
||||
return <Trans>form_error_port</Trans>;
|
||||
|
@ -32,6 +24,29 @@ const port = (value) => {
|
|||
|
||||
const toNumber = value => value && parseInt(value, 10);
|
||||
|
||||
const renderInterfaces = (interfaces => (
|
||||
Object.keys(interfaces).map((item) => {
|
||||
const option = interfaces[item];
|
||||
const { name } = option;
|
||||
const onlyIPv6 = option.ip_addresses.every(ip => ip.includes(':'));
|
||||
let interfaceIP = option.ip_addresses[0];
|
||||
|
||||
if (!onlyIPv6) {
|
||||
option.ip_addresses.forEach((ip) => {
|
||||
if (!ip.includes(':')) {
|
||||
interfaceIP = ip;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<option value={interfaceIP} key={name}>
|
||||
{name} - {interfaceIP}
|
||||
</option>
|
||||
);
|
||||
})
|
||||
));
|
||||
|
||||
let Settings = (props) => {
|
||||
const {
|
||||
handleSubmit,
|
||||
|
@ -39,7 +54,10 @@ let Settings = (props) => {
|
|||
interfacePort,
|
||||
dnsIp,
|
||||
dnsPort,
|
||||
interfaces,
|
||||
invalid,
|
||||
webWarning,
|
||||
dnsWarning,
|
||||
} = props;
|
||||
const dnsAddress = dnsPort && dnsPort !== 53 ? `${dnsIp}:${dnsPort}` : dnsIp;
|
||||
const interfaceAddress = interfacePort ? `http://${interfaceIp}:${interfacePort}` : `http://${interfaceIp}`;
|
||||
|
@ -58,12 +76,14 @@ let Settings = (props) => {
|
|||
</label>
|
||||
<Field
|
||||
name="web.ip"
|
||||
component={renderField}
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="0.0.0.0"
|
||||
validate={[ipv4, required]}
|
||||
/>
|
||||
component="select"
|
||||
className="form-control custom-select"
|
||||
>
|
||||
<option value="0.0.0.0">
|
||||
<Trans>install_settings_all_interfaces</Trans>
|
||||
</option>
|
||||
{renderInterfaces(interfaces)}
|
||||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-4">
|
||||
|
@ -90,6 +110,11 @@ let Settings = (props) => {
|
|||
>
|
||||
install_settings_interface_link
|
||||
</Trans>
|
||||
{webWarning &&
|
||||
<div className="text-danger mt-2">
|
||||
{webWarning}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className="setup__group">
|
||||
|
@ -104,12 +129,14 @@ let Settings = (props) => {
|
|||
</label>
|
||||
<Field
|
||||
name="dns.ip"
|
||||
component={renderField}
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="0.0.0.0"
|
||||
validate={[ipv4, required]}
|
||||
/>
|
||||
component="select"
|
||||
className="form-control custom-select"
|
||||
>
|
||||
<option value="0.0.0.0" defaultValue>
|
||||
<Trans>install_settings_all_interfaces</Trans>
|
||||
</option>
|
||||
{renderInterfaces(interfaces)}
|
||||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-4">
|
||||
|
@ -129,14 +156,19 @@ let Settings = (props) => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="setup__desc">
|
||||
<div className="setup__desc">
|
||||
<Trans
|
||||
components={[<strong key="0">ip</strong>]}
|
||||
values={{ ip: dnsAddress }}
|
||||
>
|
||||
install_settings_dns_desc
|
||||
</Trans>
|
||||
</p>
|
||||
{dnsWarning &&
|
||||
<div className="text-danger mt-2">
|
||||
{dnsWarning}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<Controls invalid={invalid} />
|
||||
</form>
|
||||
|
@ -155,17 +187,13 @@ Settings.propTypes = {
|
|||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
]),
|
||||
webWarning: PropTypes.string.isRequired,
|
||||
dnsWarning: PropTypes.string.isRequired,
|
||||
interfaces: PropTypes.object.isRequired,
|
||||
invalid: PropTypes.bool.isRequired,
|
||||
initialValues: PropTypes.object,
|
||||
};
|
||||
|
||||
Settings.defaultProps = {
|
||||
interfaceIp: '192.168.0.1',
|
||||
interfacePort: 3000,
|
||||
dnsIp: '192.168.0.1',
|
||||
dnsPort: 53,
|
||||
};
|
||||
|
||||
const selector = formValueSelector('install');
|
||||
|
||||
Settings = connect((state) => {
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
}
|
||||
|
||||
.setup__desc {
|
||||
margin-bottom: 20px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,13 @@
|
|||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { reduxForm } from 'redux-form';
|
||||
import { reduxForm, formValueSelector } from 'redux-form';
|
||||
import { Trans, withNamespaces } from 'react-i18next';
|
||||
import flow from 'lodash/flow';
|
||||
|
||||
import Controls from './Controls';
|
||||
|
||||
class Submit extends Component {
|
||||
render() {
|
||||
const {
|
||||
handleSubmit,
|
||||
pristine,
|
||||
submitting,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
let Submit = props => (
|
||||
<div className="setup__step">
|
||||
<div className="setup__group">
|
||||
<h1 className="setup__title">
|
||||
|
@ -24,20 +17,37 @@ class Submit extends Component {
|
|||
<Trans>install_submit_desc</Trans>
|
||||
</p>
|
||||
</div>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Controls submitting={submitting} pristine={pristine} />
|
||||
<form onSubmit={props.handleSubmit}>
|
||||
<Controls
|
||||
submitting={props.submitting}
|
||||
pristine={props.pristine}
|
||||
address={`http://${props.interfaceIp}`}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Submit.propTypes = {
|
||||
interfaceIp: PropTypes.string.isRequired,
|
||||
interfacePort: PropTypes.number.isRequired,
|
||||
handleSubmit: PropTypes.func.isRequired,
|
||||
pristine: PropTypes.bool.isRequired,
|
||||
submitting: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
const selector = formValueSelector('install');
|
||||
|
||||
Submit = connect((state) => {
|
||||
const interfaceIp = selector(state, 'web.ip');
|
||||
const interfacePort = selector(state, 'web.port');
|
||||
|
||||
return {
|
||||
interfaceIp,
|
||||
interfacePort,
|
||||
};
|
||||
})(Submit);
|
||||
|
||||
|
||||
export default flow([
|
||||
withNamespaces(),
|
||||
reduxForm({
|
||||
|
|
|
@ -29,6 +29,10 @@ class Setup extends Component {
|
|||
this.props.setAllSettings(values);
|
||||
};
|
||||
|
||||
openDashboard = () => {
|
||||
console.log('Open dashboard');
|
||||
}
|
||||
|
||||
nextStep = () => {
|
||||
if (this.props.install.step < INSTALL_TOTAL_STEPS) {
|
||||
this.props.nextStep();
|
||||
|
@ -41,7 +45,7 @@ class Setup extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
renderPage(step, config) {
|
||||
renderPage(step, config, interfaces) {
|
||||
switch (step) {
|
||||
case 1:
|
||||
return <Greeting />;
|
||||
|
@ -49,17 +53,20 @@ class Setup extends Component {
|
|||
return (
|
||||
<Settings
|
||||
initialValues={config}
|
||||
interfaces={interfaces}
|
||||
webWarning={config.web.warning}
|
||||
dnsWarning={config.dns.warning}
|
||||
onSubmit={this.nextStep}
|
||||
/>
|
||||
);
|
||||
case 3:
|
||||
return (
|
||||
<Auth onSubmit={this.nextStep} />
|
||||
<Auth onSubmit={this.handleFormSubmit} />
|
||||
);
|
||||
case 4:
|
||||
return <Devices />;
|
||||
case 5:
|
||||
return <Submit onSubmit={this.handleFormSubmit} />;
|
||||
return <Submit onSubmit={this.openDashboard} />;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -71,6 +78,7 @@ class Setup extends Component {
|
|||
step,
|
||||
web,
|
||||
dns,
|
||||
interfaces,
|
||||
} = this.props.install;
|
||||
|
||||
return (
|
||||
|
@ -81,7 +89,7 @@ class Setup extends Component {
|
|||
<div className="setup">
|
||||
<div className="setup__container">
|
||||
<img src={logo} className="setup__logo" alt="logo" />
|
||||
{this.renderPage(step, { web, dns })}
|
||||
{this.renderPage(step, { web, dns }, interfaces)}
|
||||
<Progress step={step} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,7 +10,10 @@ const install = handleActions({
|
|||
[actions.getDefaultAddressesRequest]: state => ({ ...state, processingDefault: true }),
|
||||
[actions.getDefaultAddressesFailure]: state => ({ ...state, processingDefault: false }),
|
||||
[actions.getDefaultAddressesSuccess]: (state, { payload }) => {
|
||||
const newState = { ...state, ...payload, processingDefault: false };
|
||||
const values = payload;
|
||||
values.web.ip = state.web.ip;
|
||||
values.dns.ip = state.dns.ip;
|
||||
const newState = { ...state, ...values, processingDefault: false };
|
||||
return newState;
|
||||
},
|
||||
|
||||
|
@ -23,6 +26,17 @@ const install = handleActions({
|
|||
}, {
|
||||
step: INSTALL_FIRST_STEP,
|
||||
processingDefault: true,
|
||||
web: {
|
||||
ip: '0.0.0.0',
|
||||
port: 80,
|
||||
warning: '',
|
||||
},
|
||||
dns: {
|
||||
ip: '0.0.0.0',
|
||||
port: 53,
|
||||
warning: '',
|
||||
},
|
||||
interfaces: {},
|
||||
});
|
||||
|
||||
const toasts = handleActions({
|
||||
|
|
Loading…
Reference in New Issue