import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { Controller, useForm } from 'react-hook-form'; import i18next from 'i18next'; import { validateServerName, validateIsSafePort, validatePort, validatePortQuic, validatePortTLS, validatePlainDns, } from '../../../helpers/validators'; import KeyStatus from './KeyStatus'; import CertificateStatus from './CertificateStatus'; import { DNS_OVER_QUIC_PORT, DNS_OVER_TLS_PORT, STANDARD_HTTPS_PORT, ENCRYPTION_SOURCE, } from '../../../helpers/constants'; import { Checkbox } from '../../ui/Controls/Checkbox'; import { Radio } from '../../ui/Controls/Radio'; import { Input } from '../../ui/Controls/Input'; import { Textarea } from '../../ui/Controls/Textarea'; import { EncryptionData } from '../../../initialState'; import { toNumber } from '../../../helpers/form'; const certificateSourceOptions = [ { label: i18next.t('encryption_certificates_source_path'), value: ENCRYPTION_SOURCE.PATH, }, { label: i18next.t('encryption_certificates_source_content'), value: ENCRYPTION_SOURCE.CONTENT, }, ]; const keySourceOptions = [ { label: i18next.t('encryption_key_source_path'), value: ENCRYPTION_SOURCE.PATH, }, { label: i18next.t('encryption_key_source_content'), value: ENCRYPTION_SOURCE.CONTENT, }, ]; const validationMessage = (warningValidation: string, isWarning: boolean) => { if (!warningValidation) { return null; } if (isWarning) { return (

encryption_warning: {warningValidation}

); } return (

{warningValidation}

); }; export type EncryptionFormValues = { enabled?: boolean; serve_plain_dns?: boolean; server_name?: string; force_https?: boolean; port_https?: number; port_dns_over_tls?: number; port_dns_over_quic?: number; certificate_chain?: string; private_key?: string; certificate_path?: string; private_key_path?: string; certificate_source?: string; key_source?: string; private_key_saved?: boolean; }; type Props = { initialValues: EncryptionFormValues; encryption: EncryptionData; onSubmit: (values: EncryptionFormValues) => void; debouncedConfigValidation: (values: EncryptionFormValues) => void; setTlsConfig: (values: Partial) => void; validateTlsConfig: (values: Partial) => void; }; const defaultValues = { enabled: false, serve_plain_dns: true, server_name: '', force_https: false, port_https: STANDARD_HTTPS_PORT, port_dns_over_tls: DNS_OVER_TLS_PORT, port_dns_over_quic: DNS_OVER_QUIC_PORT, certificate_chain: '', private_key: '', certificate_path: '', private_key_path: '', certificate_source: ENCRYPTION_SOURCE.PATH, key_source: ENCRYPTION_SOURCE.PATH, private_key_saved: false, }; export const Form = ({ initialValues, encryption, onSubmit, setTlsConfig, debouncedConfigValidation, validateTlsConfig, }: Props) => { const { t } = useTranslation(); const { not_after, valid_chain, valid_key, valid_cert, valid_pair, dns_names, key_type, issuer, subject, warning_validation, processingConfig, processingValidate, } = encryption; const { control, handleSubmit, watch, reset, setValue, setError, getValues, formState: { isSubmitting, isValid }, } = useForm({ defaultValues: { ...defaultValues, ...initialValues, }, mode: 'onBlur', }); const { enabled: isEnabled, serve_plain_dns: servePlainDns, certificate_chain: certificateChain, private_key: privateKey, private_key_path: privateKeyPath, key_source: privateKeySource, private_key_saved: privateKeySaved, certificate_path: certificatePath, certificate_source: certificateSource, } = watch(); const handleBlur = () => { debouncedConfigValidation(getValues()); }; const isSavingDisabled = () => { const processing = isSubmitting || processingConfig || processingValidate; if (servePlainDns && !isEnabled) { return !isValid || processing; } return !isValid || processing || !valid_key || !valid_cert || !valid_pair; }; const clearFields = () => { if (window.confirm(t('encryption_reset'))) { reset(); setTlsConfig(defaultValues); validateTlsConfig(defaultValues); } }; const validatePorts = (values: EncryptionFormValues) => { 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 = i18next.t('form_error_equal'); errors.port_https = i18next.t('form_error_equal'); } } return errors; }; const onFormSubmit = (data: EncryptionFormValues) => { const validationErrors = validatePorts(data); if (Object.keys(validationErrors).length > 0) { Object.entries(validationErrors).forEach(([field, message]) => { setError(field as keyof EncryptionFormValues, { type: 'manual', message }); }); } else { onSubmit(data); } }; const isDisabled = isSavingDisabled(); const isWarning = valid_key && valid_cert && valid_pair; return (
( )} />
encryption_enable_desc
validatePlainDns(value, getValues()), }} render={({ field }) => } />
encryption_plain_dns_desc

( )} />
encryption_server_desc
( )} />
encryption_redirect_desc
( { const { value } = e.target; field.onChange(toNumber(value)); }} onBlur={handleBlur} /> )} />
encryption_https_desc
( { const { value } = e.target; field.onChange(toNumber(value)); }} onBlur={handleBlur} /> )} />
encryption_dot_desc
( { const { value } = e.target; field.onChange(toNumber(value)); }} onBlur={handleBlur} /> )} />
encryption_doq_desc
link , ]}> encryption_certificates_desc
( )} />
{certificateSource === ENCRYPTION_SOURCE.CONTENT ? ( (