all: sync with master
This commit is contained in:
@@ -1,31 +1,27 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTranslation, Trans } from 'react-i18next';
|
||||
import format from 'date-fns/format';
|
||||
|
||||
import { EMPTY_DATE } from '../../../helpers/constants';
|
||||
|
||||
const CertificateStatus = ({
|
||||
validChain,
|
||||
validCert,
|
||||
subject,
|
||||
issuer,
|
||||
notAfter,
|
||||
dnsNames,
|
||||
}) => (
|
||||
interface CertificateStatusProps {
|
||||
validChain: boolean;
|
||||
validCert: boolean;
|
||||
subject?: string;
|
||||
issuer?: string;
|
||||
notAfter?: string;
|
||||
dnsNames?: string[];
|
||||
}
|
||||
|
||||
const CertificateStatus = ({ validChain, validCert, subject, issuer, notAfter, dnsNames }: CertificateStatusProps) => (
|
||||
<Fragment>
|
||||
<div className="form__label form__label--bold">
|
||||
<Trans>encryption_status</Trans>:
|
||||
</div>
|
||||
|
||||
<ul className="encryption__list">
|
||||
<li
|
||||
className={validChain ? 'text-success' : 'text-danger'}
|
||||
>
|
||||
{validChain ? (
|
||||
<Trans>encryption_chain_valid</Trans>
|
||||
) : (
|
||||
<Trans>encryption_chain_invalid</Trans>
|
||||
)}
|
||||
<li className={validChain ? 'text-success' : 'text-danger'}>
|
||||
{validChain ? <Trans>encryption_chain_valid</Trans> : <Trans>encryption_chain_invalid</Trans>}
|
||||
</li>
|
||||
{validCert && (
|
||||
<Fragment>
|
||||
@@ -59,13 +55,4 @@ const CertificateStatus = ({
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
CertificateStatus.propTypes = {
|
||||
validChain: PropTypes.bool.isRequired,
|
||||
validCert: PropTypes.bool.isRequired,
|
||||
subject: PropTypes.string,
|
||||
issuer: PropTypes.string,
|
||||
notAfter: PropTypes.string,
|
||||
dnsNames: PropTypes.arrayOf(PropTypes.string),
|
||||
};
|
||||
|
||||
export default withTranslation()(CertificateStatus);
|
||||
@@ -1,32 +1,39 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Field, reduxForm, formValueSelector } from 'redux-form';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import flow from 'lodash/flow';
|
||||
|
||||
import { renderInputField, CheckboxField, renderRadioField, toNumber } from '../../../helpers/form';
|
||||
import {
|
||||
renderInputField,
|
||||
CheckboxField,
|
||||
renderRadioField,
|
||||
toNumber,
|
||||
} from '../../../helpers/form';
|
||||
import {
|
||||
validateServerName, validateIsSafePort, validatePort, validatePortQuic, validatePortTLS, validatePlainDns,
|
||||
validateServerName,
|
||||
validateIsSafePort,
|
||||
validatePort,
|
||||
validatePortQuic,
|
||||
validatePortTLS,
|
||||
validatePlainDns,
|
||||
} from '../../../helpers/validators';
|
||||
import i18n from '../../../i18n';
|
||||
|
||||
import KeyStatus from './KeyStatus';
|
||||
|
||||
import CertificateStatus from './CertificateStatus';
|
||||
import {
|
||||
DNS_OVER_QUIC_PORT, DNS_OVER_TLS_PORT, FORM_NAME, STANDARD_HTTPS_PORT, ENCRYPTION_SOURCE,
|
||||
DNS_OVER_QUIC_PORT,
|
||||
DNS_OVER_TLS_PORT,
|
||||
FORM_NAME,
|
||||
STANDARD_HTTPS_PORT,
|
||||
ENCRYPTION_SOURCE,
|
||||
} from '../../../helpers/constants';
|
||||
|
||||
const validate = (values) => {
|
||||
const errors = {};
|
||||
const validate = (values: any) => {
|
||||
const errors: { port_dns_over_tls?: string; port_https?: string } = {};
|
||||
|
||||
if (values.port_dns_over_tls && values.port_https) {
|
||||
if (values.port_dns_over_tls === values.port_https) {
|
||||
errors.port_dns_over_tls = i18n.t('form_error_equal');
|
||||
|
||||
errors.port_https = i18n.t('form_error_equal');
|
||||
}
|
||||
}
|
||||
@@ -34,7 +41,7 @@ const validate = (values) => {
|
||||
return errors;
|
||||
};
|
||||
|
||||
const clearFields = (change, setTlsConfig, validateTlsConfig, t) => {
|
||||
const clearFields = (change: any, setTlsConfig: any, validateTlsConfig: any, t: any) => {
|
||||
const fields = {
|
||||
private_key: '',
|
||||
certificate_chain: '',
|
||||
@@ -52,13 +59,14 @@ const clearFields = (change, setTlsConfig, validateTlsConfig, t) => {
|
||||
// eslint-disable-next-line no-alert
|
||||
if (window.confirm(t('encryption_reset'))) {
|
||||
Object.keys(fields)
|
||||
|
||||
.forEach((field) => change(field, fields[field]));
|
||||
setTlsConfig(fields);
|
||||
validateTlsConfig(fields);
|
||||
}
|
||||
};
|
||||
|
||||
const validationMessage = (warningValidation, isWarning) => {
|
||||
const validationMessage = (warningValidation: any, isWarning: any) => {
|
||||
if (!warningValidation) {
|
||||
return null;
|
||||
}
|
||||
@@ -66,7 +74,9 @@ const validationMessage = (warningValidation, isWarning) => {
|
||||
if (isWarning) {
|
||||
return (
|
||||
<div className="col-12">
|
||||
<p><Trans>encryption_warning</Trans>: {warningValidation}</p>
|
||||
<p>
|
||||
<Trans>encryption_warning</Trans>: {warningValidation}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -78,7 +88,41 @@ const validationMessage = (warningValidation, isWarning) => {
|
||||
);
|
||||
};
|
||||
|
||||
let Form = (props) => {
|
||||
interface FormProps {
|
||||
handleSubmit: (...args: unknown[]) => string;
|
||||
handleChange?: (...args: unknown[]) => unknown;
|
||||
isEnabled: boolean;
|
||||
servePlainDns: boolean;
|
||||
certificateChain: string;
|
||||
privateKey: string;
|
||||
certificatePath: string;
|
||||
privateKeyPath: string;
|
||||
change: (...args: unknown[]) => unknown;
|
||||
submitting: boolean;
|
||||
invalid: boolean;
|
||||
initialValues: object;
|
||||
processingConfig: boolean;
|
||||
processingValidate: boolean;
|
||||
status_key?: string;
|
||||
not_after?: string;
|
||||
warning_validation?: string;
|
||||
valid_chain?: boolean;
|
||||
valid_key?: boolean;
|
||||
valid_cert?: boolean;
|
||||
valid_pair?: boolean;
|
||||
dns_names?: string[];
|
||||
key_type?: string;
|
||||
issuer?: string;
|
||||
subject?: string;
|
||||
t: (...args: unknown[]) => string;
|
||||
setTlsConfig: (...args: unknown[]) => unknown;
|
||||
validateTlsConfig: (...args: unknown[]) => unknown;
|
||||
certificateSource?: string;
|
||||
privateKeySource?: string;
|
||||
privateKeySaved?: boolean;
|
||||
}
|
||||
|
||||
let Form = (props: FormProps) => {
|
||||
const {
|
||||
t,
|
||||
handleSubmit,
|
||||
@@ -137,9 +181,11 @@ let Form = (props) => {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form__desc">
|
||||
<Trans>encryption_enable_desc</Trans>
|
||||
</div>
|
||||
|
||||
<div className="form__group mb-3 mt-5">
|
||||
<Field
|
||||
name="serve_plain_dns"
|
||||
@@ -150,16 +196,20 @@ let Form = (props) => {
|
||||
validate={validatePlainDns}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form__desc">
|
||||
<Trans>encryption_plain_dns_desc</Trans>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<label className="form__label" htmlFor="server_name">
|
||||
<Trans>encryption_server</Trans>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="col-lg-6">
|
||||
<div className="form__group form__group--settings">
|
||||
<Field
|
||||
@@ -173,11 +223,13 @@ let Form = (props) => {
|
||||
disabled={!isEnabled}
|
||||
validate={validateServerName}
|
||||
/>
|
||||
|
||||
<div className="form__desc">
|
||||
<Trans>encryption_server_desc</Trans>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-lg-6">
|
||||
<div className="form__group form__group--settings">
|
||||
<Field
|
||||
@@ -188,18 +240,21 @@ let Form = (props) => {
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
|
||||
<div className="form__desc">
|
||||
<Trans>encryption_redirect_desc</Trans>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row">
|
||||
<div className="col-lg-6">
|
||||
<div className="form__group form__group--settings">
|
||||
<label className="form__label" htmlFor="port_https">
|
||||
<Trans>encryption_https</Trans>
|
||||
</label>
|
||||
|
||||
<Field
|
||||
id="port_https"
|
||||
name="port_https"
|
||||
@@ -212,16 +267,19 @@ let Form = (props) => {
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
|
||||
<div className="form__desc">
|
||||
<Trans>encryption_https_desc</Trans>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-lg-6">
|
||||
<div className="form__group form__group--settings">
|
||||
<label className="form__label" htmlFor="port_dns_over_tls">
|
||||
<Trans>encryption_dot</Trans>
|
||||
</label>
|
||||
|
||||
<Field
|
||||
id="port_dns_over_tls"
|
||||
name="port_dns_over_tls"
|
||||
@@ -234,16 +292,19 @@ let Form = (props) => {
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
|
||||
<div className="form__desc">
|
||||
<Trans>encryption_dot_desc</Trans>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-lg-6">
|
||||
<div className="form__group form__group--settings">
|
||||
<label className="form__label" htmlFor="port_dns_over_quic">
|
||||
<Trans>encryption_doq</Trans>
|
||||
</label>
|
||||
|
||||
<Field
|
||||
id="port_dns_over_quic"
|
||||
name="port_dns_over_quic"
|
||||
@@ -256,30 +317,35 @@ let Form = (props) => {
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
|
||||
<div className="form__desc">
|
||||
<Trans>encryption_doq_desc</Trans>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="form__group form__group--settings">
|
||||
<label
|
||||
className="form__label form__label--with-desc form__label--bold"
|
||||
htmlFor="certificate_chain"
|
||||
>
|
||||
htmlFor="certificate_chain">
|
||||
<Trans>encryption_certificates</Trans>
|
||||
</label>
|
||||
|
||||
<div className="form__desc form__desc--top">
|
||||
<Trans
|
||||
values={{ link: 'letsencrypt.org' }}
|
||||
components={[
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://letsencrypt.org/" key="0">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://letsencrypt.org/"
|
||||
key="0">
|
||||
link
|
||||
</a>,
|
||||
]}
|
||||
>
|
||||
]}>
|
||||
encryption_certificates_desc
|
||||
</Trans>
|
||||
</div>
|
||||
@@ -295,6 +361,7 @@ let Form = (props) => {
|
||||
placeholder={t('encryption_certificates_source_path')}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
|
||||
<Field
|
||||
name="certificate_source"
|
||||
component={renderRadioField}
|
||||
@@ -332,6 +399,7 @@ let Form = (props) => {
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="form__status">
|
||||
{(certificateChain || certificatePath) && (
|
||||
<CertificateStatus
|
||||
@@ -346,6 +414,7 @@ let Form = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="form__group form__group--settings mt-3">
|
||||
@@ -364,6 +433,7 @@ let Form = (props) => {
|
||||
placeholder={t('encryption_key_source_path')}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
|
||||
<Field
|
||||
name="key_source"
|
||||
component={renderRadioField}
|
||||
@@ -396,7 +466,7 @@ let Form = (props) => {
|
||||
component={CheckboxField}
|
||||
disabled={!isEnabled}
|
||||
placeholder={t('use_saved_key')}
|
||||
onChange={(event) => {
|
||||
onChange={(event: any) => {
|
||||
if (event.target.checked) {
|
||||
change('private_key', '');
|
||||
}
|
||||
@@ -405,6 +475,7 @@ let Form = (props) => {
|
||||
}
|
||||
}}
|
||||
/>,
|
||||
|
||||
<Field
|
||||
id="private_key"
|
||||
key="private_key"
|
||||
@@ -418,29 +489,24 @@ let Form = (props) => {
|
||||
/>,
|
||||
]}
|
||||
</div>
|
||||
|
||||
<div className="form__status">
|
||||
{(privateKey || privateKeyPath) && (
|
||||
<KeyStatus validKey={valid_key} keyType={key_type} />
|
||||
)}
|
||||
{(privateKey || privateKeyPath) && <KeyStatus validKey={valid_key} keyType={key_type} />}
|
||||
</div>
|
||||
</div>
|
||||
{validationMessage(warning_validation, isWarning)}
|
||||
</div>
|
||||
|
||||
<div className="btn-list mt-2">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isDisabled}
|
||||
className="btn btn-success btn-standart"
|
||||
>
|
||||
<button type="submit" disabled={isDisabled} className="btn btn-success btn-standart">
|
||||
<Trans>save_config</Trans>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary btn-standart"
|
||||
disabled={submitting || processingConfig}
|
||||
onClick={() => clearFields(change, setTlsConfig, validateTlsConfig, t)}
|
||||
>
|
||||
onClick={() => clearFields(change, setTlsConfig, validateTlsConfig, t)}>
|
||||
<Trans>reset_settings</Trans>
|
||||
</button>
|
||||
</div>
|
||||
@@ -448,40 +514,6 @@ let Form = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
Form.propTypes = {
|
||||
handleSubmit: PropTypes.func.isRequired,
|
||||
handleChange: PropTypes.func,
|
||||
isEnabled: PropTypes.bool.isRequired,
|
||||
servePlainDns: PropTypes.bool.isRequired,
|
||||
certificateChain: PropTypes.string.isRequired,
|
||||
privateKey: PropTypes.string.isRequired,
|
||||
certificatePath: PropTypes.string.isRequired,
|
||||
privateKeyPath: PropTypes.string.isRequired,
|
||||
change: PropTypes.func.isRequired,
|
||||
submitting: PropTypes.bool.isRequired,
|
||||
invalid: PropTypes.bool.isRequired,
|
||||
initialValues: PropTypes.object.isRequired,
|
||||
processingConfig: PropTypes.bool.isRequired,
|
||||
processingValidate: PropTypes.bool.isRequired,
|
||||
status_key: PropTypes.string,
|
||||
not_after: PropTypes.string,
|
||||
warning_validation: PropTypes.string,
|
||||
valid_chain: PropTypes.bool,
|
||||
valid_key: PropTypes.bool,
|
||||
valid_cert: PropTypes.bool,
|
||||
valid_pair: PropTypes.bool,
|
||||
dns_names: PropTypes.arrayOf(PropTypes.string),
|
||||
key_type: PropTypes.string,
|
||||
issuer: PropTypes.string,
|
||||
subject: PropTypes.string,
|
||||
t: PropTypes.func.isRequired,
|
||||
setTlsConfig: PropTypes.func.isRequired,
|
||||
validateTlsConfig: PropTypes.func.isRequired,
|
||||
certificateSource: PropTypes.string,
|
||||
privateKeySource: PropTypes.string,
|
||||
privateKeySaved: PropTypes.bool,
|
||||
};
|
||||
|
||||
const selector = formValueSelector(FORM_NAME.ENCRYPTION);
|
||||
|
||||
Form = connect((state) => {
|
||||
@@ -1,31 +1,27 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTranslation, Trans } from 'react-i18next';
|
||||
|
||||
const KeyStatus = ({ validKey, keyType }) => (
|
||||
interface KeyStatusProps {
|
||||
validKey: boolean;
|
||||
keyType: string;
|
||||
}
|
||||
|
||||
const KeyStatus = ({ validKey, keyType }: KeyStatusProps) => (
|
||||
<Fragment>
|
||||
<div className="form__label form__label--bold">
|
||||
<Trans>encryption_status</Trans>:
|
||||
</div>
|
||||
|
||||
<ul className="encryption__list">
|
||||
<li className={validKey ? 'text-success' : 'text-danger'}>
|
||||
{validKey ? (
|
||||
<Trans values={{ type: keyType }}>
|
||||
encryption_key_valid
|
||||
</Trans>
|
||||
<Trans values={{ type: keyType }}>encryption_key_valid</Trans>
|
||||
) : (
|
||||
<Trans values={{ type: keyType }}>
|
||||
encryption_key_invalid
|
||||
</Trans>
|
||||
<Trans values={{ type: keyType }}>encryption_key_invalid</Trans>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
KeyStatus.propTypes = {
|
||||
validKey: PropTypes.bool.isRequired,
|
||||
keyType: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(KeyStatus);
|
||||
@@ -1,15 +1,26 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
import { DEBOUNCE_TIMEOUT, ENCRYPTION_SOURCE } from '../../../helpers/constants';
|
||||
import Form from './Form';
|
||||
import Card from '../../ui/Card';
|
||||
import PageTitle from '../../ui/PageTitle';
|
||||
import Loading from '../../ui/Loading';
|
||||
|
||||
class Encryption extends Component {
|
||||
import Form from './Form';
|
||||
|
||||
import Card from '../../ui/Card';
|
||||
|
||||
import PageTitle from '../../ui/PageTitle';
|
||||
|
||||
import Loading from '../../ui/Loading';
|
||||
import { EncryptionData } from '../../../initialState';
|
||||
|
||||
interface EncryptionProps {
|
||||
setTlsConfig: (...args: unknown[]) => unknown;
|
||||
validateTlsConfig: (...args: unknown[]) => unknown;
|
||||
encryption: EncryptionData;
|
||||
t: (...args: unknown[]) => string;
|
||||
}
|
||||
|
||||
class Encryption extends Component<EncryptionProps> {
|
||||
componentDidMount() {
|
||||
const { validateTlsConfig, encryption } = this.props;
|
||||
|
||||
@@ -18,27 +29,24 @@ class Encryption extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleFormSubmit = (values) => {
|
||||
handleFormSubmit = (values: any) => {
|
||||
const submitValues = this.getSubmitValues(values);
|
||||
|
||||
this.props.setTlsConfig(submitValues);
|
||||
};
|
||||
|
||||
handleFormChange = debounce((values) => {
|
||||
const submitValues = this.getSubmitValues(values);
|
||||
|
||||
if (submitValues.enabled || submitValues.serve_plain_dns) {
|
||||
if (submitValues.enabled) {
|
||||
this.props.validateTlsConfig(submitValues);
|
||||
}
|
||||
}, DEBOUNCE_TIMEOUT);
|
||||
|
||||
getInitialValues = (data) => {
|
||||
getInitialValues = (data: any) => {
|
||||
const { certificate_chain, private_key, private_key_saved } = data;
|
||||
const certificate_source = certificate_chain
|
||||
? ENCRYPTION_SOURCE.CONTENT
|
||||
: ENCRYPTION_SOURCE.PATH;
|
||||
const key_source = private_key || private_key_saved
|
||||
? ENCRYPTION_SOURCE.CONTENT
|
||||
: ENCRYPTION_SOURCE.PATH;
|
||||
const certificate_source = certificate_chain ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH;
|
||||
const key_source = private_key || private_key_saved ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH;
|
||||
|
||||
return {
|
||||
...data,
|
||||
@@ -47,10 +55,8 @@ class Encryption extends Component {
|
||||
};
|
||||
};
|
||||
|
||||
getSubmitValues = (values) => {
|
||||
const {
|
||||
certificate_source, key_source, private_key_saved, ...config
|
||||
} = values;
|
||||
getSubmitValues = (values: any) => {
|
||||
const { certificate_source, key_source, private_key_saved, ...config } = values;
|
||||
|
||||
if (certificate_source === ENCRYPTION_SOURCE.PATH) {
|
||||
config.certificate_chain = '';
|
||||
@@ -107,13 +113,13 @@ class Encryption extends Component {
|
||||
return (
|
||||
<div className="encryption">
|
||||
<PageTitle title={t('encryption_settings')} />
|
||||
|
||||
{encryption.processing && <Loading />}
|
||||
{!encryption.processing && (
|
||||
<Card
|
||||
title={t('encryption_title')}
|
||||
subtitle={t('encryption_desc')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
bodyType="card-body box-body--settings">
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
onSubmit={this.handleFormSubmit}
|
||||
@@ -129,11 +135,4 @@ class Encryption extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
Encryption.propTypes = {
|
||||
setTlsConfig: PropTypes.func.isRequired,
|
||||
validateTlsConfig: PropTypes.func.isRequired,
|
||||
encryption: PropTypes.object.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(Encryption);
|
||||
Reference in New Issue
Block a user