Improved LE support and CSR is now generted in modal window with cert prepopulating feature.

This commit is contained in:
Alexander 2021-11-01 23:28:10 +02:00
parent 8f8dbd5b25
commit 62d9652437
15 changed files with 182 additions and 83 deletions

View file

@ -12,7 +12,7 @@ const TextArea = ({ id, name, defaultValue = '', title, optionalTitle = '', rows
id={id}
rows={rows}
name={name}
disabled={disabled}
readOnly={disabled}
defaultValue={defaultValue}
{...rest}
>

View file

@ -19,18 +19,18 @@ const MainNav = () => {
});
const { userName } = useSelector(state => state.session);
const { session: { look } } = useSelector(state => state.userSession);
const { session } = useSelector(state => state.userSession);
const { user } = useSelector(state => state.menuCounters);
const { activeElement, focusedElement, adminMenuTabs, userMenuTabs } = useSelector(state => state.mainNavigation);
const { controlPanelFocusedElement } = useSelector(state => state.controlPanelContent);
const dispatch = useDispatch();
useEffect(() => {
if (!userName || !Object.entries(user).length) {
if (!userName || !Object.entries(user).length || !Object.entries(session).length) {
return history.push('/login');
}
if (look) {
if (session.look) {
const commonUserRoutes = ['package', 'ip', 'rrd', 'updates', 'firewall', 'server'];
const splitPath = history.location.pathname.split('/')[2];
@ -41,11 +41,11 @@ const MainNav = () => {
}
}
const tabs = look ? userMenuTabs : adminMenuTabs;
const tabs = session.look ? userMenuTabs : adminMenuTabs;
setState({ ...state, tabs });
setLoading(false);
}, [userName, user, history, look]);
}, [userName, user, history, session]);
const controlFocusedTabWithCallback = useCallback(event => {
let isSearchInputFocused = document.querySelector('input:focus') || document.querySelector('textarea:focus') || document.querySelector('textarea:focus');

View file

@ -10,7 +10,7 @@ import './Panel.scss';
const Panel = props => {
const { i18n, userName } = useSelector(state => state.session);
const { session: { look, user, FIREWALL_SYSTEM, FILEMANAGER_KEY, SOFTACULOUS } } = useSelector(state => state.userSession);
const { session } = useSelector(state => state.userSession);
const { activeElement, focusedElement } = useSelector(state => state.mainNavigation);
const dispatch = useDispatch();
const [loading, setLoading] = useState(false);
@ -67,7 +67,7 @@ const Panel = props => {
<div className="panel-wrapper">
{loading && <Spinner />}
<div className={`top-panel ${look ? 'long-profile' : ''}`}>
<div className={`top-panel ${session.look ? 'long-profile' : ''}`}>
<div className="container left-menu">
<div className="logo">
<Link to="/list/user/" onClick={() => dispatch(addActiveElement('/list/user/'))}>
@ -97,14 +97,14 @@ const Panel = props => {
<div className={className("/list/updates/")}>
<Link to="/list/updates/" onClick={event => handleState("/list/updates/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Updates}</Link>
</div>
{FIREWALL_SYSTEM && <div className={className("/list/firewall/")}>
{session.FIREWALL_SYSTEM && <div className={className("/list/firewall/")}>
<Link to="/list/firewall/" onClick={event => handleState("/list/firewall/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Firewall}</Link>
</div>}
</>)}
{FILEMANAGER_KEY && <div className={className("/list/directory/")}>
{session.FILEMANAGER_KEY && <div className={className("/list/directory/")}>
<Link to="/list/directory/">{i18n['File Manager']}</Link>
</div>}
{SOFTACULOUS === "yes" && <div className={className("/softaculous/")}><Link to="/softaculous/" target="_blank">{i18n.Apps ?? 'Apps'}</Link>
{session.SOFTACULOUS === "yes" && <div className={className("/softaculous/")}><Link to="/softaculous/" target="_blank">{i18n.Apps ?? 'Apps'}</Link>
</div>}
{userName === 'admin' && (
<div className={className("/list/server/")}>
@ -116,11 +116,11 @@ const Panel = props => {
<Notifications />
<div>
<Link to={`/edit/user?user=${userName}`}>
{look
{session.look
? <div className="long-username">
<span>{user}</span>
<span>{session.user}</span>
<FontAwesomeIcon icon="long-arrow-alt-right" />
<span>{look}</span>
<span>{session.look}</span>
</div>
: userName
}

View file

@ -28,7 +28,7 @@ const style = ({ menuHeight, mobile }) => {
const Menu = props => {
const { activeElement, focusedElement } = useSelector(state => state.mainNavigation);
const { i18n } = useSelector(state => state.session);
const { session: { look } } = useSelector(state => state.userSession);
const { session } = useSelector(state => state.userSession);
const { user } = useSelector(state => state.menuCounters);
const dispatch = useDispatch();
@ -72,7 +72,7 @@ const Menu = props => {
<h3>{i18n.USER}</h3>
<div className="stats">
{
look
session.look
? (<>
<div><span>{i18n.Disk}:</span> <span>{sizeFormatter(user.U_DISK)}</span></div>
<div><span>{i18n.Bandwidth}:</span> <span>{sizeFormatter(user.U_BANDWIDTH)}</span></div>

View file

@ -19,7 +19,6 @@ import HtmlParser from 'react-html-parser';
const AddUser = props => {
const { i18n } = useSelector(state => state.session);
const { session } = useSelector(state => state.userSession);
const userLanguage = localStorage.getItem("language");
const history = useHistory();
const dispatch = useDispatch();
const [state, setState] = useState({

View file

@ -6,7 +6,6 @@ import { addWeb, getWebDomainInfo } from '../../../ControlPanelService/Web';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import AdvancedOptions from './AdvancedOptions/AdvancedOptions';
import Checkbox from 'src/components/ControlPanel/AddItemLayout/Form/Checkbox/Checkbox';
import SelectInput from 'src/components/ControlPanel/AddItemLayout/Form/SelectInput/SelectInput';
import TextArea from 'src/components/ControlPanel/AddItemLayout/Form/TextArea/TextArea';
import Toolbar from '../../MainNav/Toolbar/Toolbar';
import { useHistory } from 'react-router-dom';
@ -14,6 +13,8 @@ import Spinner from '../../Spinner/Spinner';
import { useDispatch, useSelector } from 'react-redux';
import './AddWebDomain.scss';
import GenerateSSL from 'src/containers/GenerateCSR';
import 'src/components/Modal/Modal.scss';
import { Helmet } from 'react-helmet';
import { refreshCounters } from 'src/actions/MenuCounters/menuCounterActions';
import HtmlParser from 'react-html-parser';
@ -23,6 +24,7 @@ const AddWebDomain = props => {
const { session } = useSelector(state => state.userSession);
const dispatch = useDispatch();
const token = localStorage.getItem("token");
const [modalVisible, setModalVisible] = useState(false);
const history = useHistory();
const [state, setState] = useState({
loading: false,
@ -31,6 +33,8 @@ const AddWebDomain = props => {
proxySupport: true,
showAdvancedOptions: false,
okMessage: '',
ssl_crt: '',
ssl_key: '',
domain: '',
errorMessage: '',
webStats: [],
@ -83,7 +87,14 @@ const AddWebDomain = props => {
const renderAdvancedOptions = () => {
if (state.showAdvancedOptions) {
return <AdvancedOptions prefixI18N={state.prefixI18N} domain={state.domain} webStats={state.webStats} prePath={state.prePath} />;
return <AdvancedOptions
prefixI18N={state.prefixI18N}
setModalVisible={bool => setModalVisible(bool)}
sslCertificate={state.ssl_crt}
sslKey={state.ssl_key}
domain={state.domain}
webStats={state.webStats}
prePath={state.prePath} />;
}
}
@ -239,8 +250,27 @@ const AddWebDomain = props => {
</form>
)}
</AddItemLayout>
<div className={`modal fade ${modalVisible ? 'show' : ''}`} id="c-panel-modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" style={{ display: modalVisible ? 'block' : 'none' }}>
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5>{i18n['Generating CSR']}</h5>
<button type="button" onClick={() => setModalVisible(false)} className="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<GenerateSSL
domain={state.domain}
closeModal={() => setModalVisible(false)}
prePopulateInputs={({ crt, key }) => {
setState({ ...state, ssl_crt: crt, ssl_key: key });
setModalVisible(false);
}} />
</div>
</div>
</div>
</div >
);
}
export default AddWebDomain;
export default AddWebDomain;

View file

@ -23,4 +23,28 @@
}
}
}
#c-panel-modal {
padding: 2rem;
form .form-group input[type="text"] {
width: 90% !important;
}
label {
color: white;
}
.form-group {
padding-left: 1.5rem;
textarea {
width: 90% !important;
}
}
.l-col {
display: none;
}
}
}

View file

@ -23,7 +23,7 @@ const AdvancedOptions = ({ prefixI18N, prePath, ...props }) => {
const renderSslSupport = () => {
if (state.sslSupport) {
return <SslSupport />;
return <SslSupport sslCertificate={props.sslCertificate} sslKey={props.sslKey} setModalVisible={bool => props.setModalVisible(bool)} />;
}
}

View file

@ -1,5 +1,7 @@
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import TextArea from 'src/components/ControlPanel/AddItemLayout/Form/TextArea/TextArea';
import './SslSupport.scss';
@ -22,30 +24,27 @@ const SslSupport = props => {
<span className="lets-encrypt-span">{letsEncrypt ? i18n['Your certificate will be automatically issued in 5 minutes'] : null}</span>
</div>
<div class="form-group">
<label htmlFor="package">{i18n['SSL Home Directory']}</label>
<select class="form-control" id="ssl_home" name="v_ssl_home">
<option value="same">public_html</option>
<option value="single">public_shtml</option>
</select>
</div>
<input type="hidden" value="same" name="v_ssl_home" />
<div class="form-group">
<label htmlFor="aliases">{i18n['SSL Certificate']}</label>
<textarea class="form-control" id="aliases" rows="3" name="v_ssl_crt" disabled={letsEncrypt}></textarea>
</div>
<TextArea
id="ssl-certificate"
name="v_ssl_crt"
title={i18n['SSL Certificate']}
value={props.sslCertificate}
disabled={letsEncrypt}
optionalTitle={<>/ <button type="button" onClick={() => props.setModalVisible(true)} className="generate-csr">{i18n['Generate CSR']}</button></>} />
<div class="form-group">
<label htmlFor="aliases">{i18n['SSL Key']}</label>
<textarea class="form-control" id="aliases" rows="3" name="v_ssl_key" disabled={letsEncrypt}></textarea>
<textarea class="form-control" id="ssl_key" rows="3" name="v_ssl_key" defaultValue={props.sslKey} disabled={letsEncrypt}></textarea>
</div>
<div class="form-group">
<label htmlFor="aliases">{i18n['SSL Certificate Authority / Intermediate']}</label>
<textarea class="form-control" id="aliases" rows="3" name="v_ssl_ca" disabled={letsEncrypt}></textarea>
<textarea class="form-control" id="ssl_ca" rows="3" name="v_ssl_ca" disabled={letsEncrypt}></textarea>
</div>
</div>
);
}
export default SslSupport;
export default SslSupport;

View file

@ -13,6 +13,8 @@ import Toolbar from '../../MainNav/Toolbar/Toolbar';
import SslSupport from './SslSupport/SslSupport';
import { useHistory } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import GenerateSSL from 'src/containers/GenerateCSR';
import 'src/components/Modal/Modal.scss';
import QS from 'qs';
import './EditWeb.scss';
@ -28,6 +30,7 @@ const EditWeb = props => {
const history = useHistory();
const dispatch = useDispatch();
const [errorMessage, setErrorMessage] = useState('');
const [modalVisible, setModalVisible] = useState(false);
const [okMessage, setOkMessage] = useState('');
const [state, setState] = useState({
data: {},
@ -85,6 +88,26 @@ const EditWeb = props => {
if (updatedDomain['v_ssl'] === 'on') {
updatedDomain['v_ssl'] = 'yes';
} else {
delete updatedDomain['v_ssl'];
}
if (updatedDomain['v_letsencrypt'] === 'on') {
updatedDomain['v_letsencrypt'] = 'yes';
} else {
delete updatedDomain['v_letsencrypt'];
}
if (!updatedDomain['v_ssl_ca']) {
delete updatedDomain['v_ssl_ca'];
}
if (!updatedDomain['v_ssl_crt']) {
delete updatedDomain['v_ssl_crt'];
}
if (!updatedDomain['v_ssl_key']) {
delete updatedDomain['v_ssl_key'];
}
if (Object.keys(updatedDomain).length !== 0 && updatedDomain.constructor === Object) {
@ -246,6 +269,7 @@ const EditWeb = props => {
sslIssuer={state.data.ssl_issuer}
sslCertificate={state.data.ssl_crt}
sslKey={state.data.ssl_key}
setModalVisible={bool => setModalVisible(bool)}
sslCertificateAuthority={state.data.ssl_ca}
domain={state.domain}
sslHome={state.data.ssl_home}
@ -307,6 +331,26 @@ const EditWeb = props => {
</form>
}
</AddItemLayout>
<div className={`modal fade ${modalVisible ? 'show' : ''}`} id="c-panel-modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" style={{ display: modalVisible ? 'block' : 'none' }}>
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5>{i18n['Generating CSR']}</h5>
<button type="button" onClick={() => setModalVisible(false)} className="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<GenerateSSL
domain={state.domain}
closeModal={() => setModalVisible(false)}
prePopulateInputs={({ crt, key }) => {
setState({ ...state, data: { ...state.data, ssl_crt: crt, ssl_key: key } });
setModalVisible(false);
}} />
</div>
</div>
</div>
</div>
);
}

View file

@ -2,4 +2,28 @@
.web-stat-additional {
transform: translateX(3rem);
}
#c-panel-modal {
padding: 2rem;
form .form-group input[type="text"] {
width: 90% !important;
}
label {
color: white;
}
.form-group {
padding-left: 1.5rem;
textarea {
width: 90% !important;
}
}
.l-col {
display: none;
}
}
}

View file

@ -1,16 +1,13 @@
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import Checkbox from '../../../ControlPanel/AddItemLayout/Form/Checkbox/Checkbox';
import SelectInput from '../../../ControlPanel/AddItemLayout/Form/SelectInput/SelectInput';
import TextArea from '../../../ControlPanel/AddItemLayout/Form/TextArea/TextArea';
import './SslSupport.scss';
const SslSupport = props => {
const { i18n } = useSelector(state => state.session);
const [letsEncrypt, setLetsEncrypt] = useState(false);
const [sslHomeOptions, setSslHomeOptions] = useState(['public_html', 'public_shtml']);
const [letsEncrypt, setLetsEncrypt] = useState(props.letsEncrypt);
useEffect(() => {
setLetsEncrypt(props.letsEncrypt);
@ -22,19 +19,18 @@ const SslSupport = props => {
return (
<div className="ssl-support">
<Checkbox
onChange={onChangeLetsEncrypt}
name="v_letsencrypt"
id="lets-encrypt"
title={i18n['Lets Encrypt Support']}
defaultChecked={letsEncrypt} />
<>
<Checkbox
onChange={onChangeLetsEncrypt}
name="v_letsencrypt"
id="lets-encrypt"
title={i18n['Lets Encrypt Support']}
defaultChecked={letsEncrypt} />
<SelectInput
options={sslHomeOptions}
selected={props.sslHome === 'same' ? 'public_html' : 'public_shtml'}
name="v_ssl_home"
id="ssl_home"
title={i18n['SSL Home Directory']} />
{!props.letsEncrypt && <span className="lets-encrypt-span">{letsEncrypt ? i18n['Your certificate will be automatically issued in 5 minutes'] : null}</span>}
</>
<input type="hidden" value="same" name="v_ssl_home" />
<TextArea
id="ssl-certificate"
@ -42,11 +38,7 @@ const SslSupport = props => {
title={i18n['SSL Certificate']}
defaultValue={props.sslCertificate}
disabled={letsEncrypt}
optionalTitle={
!letsEncrypt
? (<>/ <Link className="generate-csr" target="_blank" to={`/generate/ssl/?domain=${props.domain}`}>{i18n['Generate CSR']}</Link></>)
: ''
} />
optionalTitle={<>/ <button type="button" onClick={() => props.setModalVisible(true)} className="generate-csr">{i18n['Generate CSR']}</button></>} />
<TextArea
id="ssl-key"
@ -141,4 +133,4 @@ const SslSupport = props => {
);
}
export default SslSupport;
export default SslSupport;

View file

@ -52,7 +52,6 @@ import Users from '../../containers/Users/Users';
import RRDs from '../../containers/RRDs/RRDs';
import BanList from '../Firewalls/Banlist';
import Web from '../../containers/Web/Web';
import GenerateCSR from '../GenerateCSR';
import Search from '../Search/Search';
import Logs from '../Logs/Logs';
@ -190,7 +189,6 @@ const ControlPanelContent = props => {
<Route path="/list/user" component={props => <Users changeSearchTerm={handleSearchTerm} {...props} />} />
<Route path="/add/user" component={() => <AddUser />} />
<Route path="/generate/ssl" component={GenerateCSR} />
<Route path="/edit/user" component={() => <EditUser />} />
<Route path="/list/web" component={props => <Web {...props} changeSearchTerm={handleSearchTerm} />} />
<Route path="/add/web" component={() => <AddWebDomain />} />

View file

@ -3,15 +3,11 @@ import { addActiveElement, removeFocusedElement } from "src/actions/MainNavigati
import TextInput from 'src/components/ControlPanel/AddItemLayout/Form/TextInput/TextInput';
import AddItemLayout from 'src/components/ControlPanel/AddItemLayout/AddItemLayout';
import TextArea from 'src/components/ControlPanel/AddItemLayout/Form/TextArea/TextArea';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import Toolbar from 'src/components/MainNav/Toolbar/Toolbar';
import { generateCSR, getCsrInitialData } from 'src/ControlPanelService/Web';
import { useDispatch, useSelector } from 'react-redux';
import Spinner from 'src/components/Spinner/Spinner';
import { useHistory } from 'react-router-dom';
import HtmlParser from 'react-html-parser';
import { Helmet } from 'react-helmet';
import QS from 'qs';
const GenerateSSL = props => {
const token = localStorage.getItem("token");
@ -28,8 +24,7 @@ const GenerateSSL = props => {
});
useEffect(() => {
let queryParams = QS.parse(history.location.search, { ignoreQueryPrefix: true });
const { domain } = queryParams;
const { domain } = props;
dispatch(addActiveElement('/list/web/'));
dispatch(removeFocusedElement());
@ -94,17 +89,6 @@ const GenerateSSL = props => {
return (
<div className="edit-template edit-user">
<Helmet>
<title>{`Vesta - ${i18n.WEB}`}</title>
</Helmet>
<Toolbar mobile={false}>
<div></div>
<div className="search-toolbar-name">{i18n['Generating CSR']}</div>
<div className="error"><span className="error-message">{errorMessage ? <FontAwesomeIcon icon="long-arrow-alt-right" /> : ''} {errorMessage}</span></div>
<div className="success">
<span className="ok-message">{okMessage ? <FontAwesomeIcon icon="long-arrow-alt-right" /> : ''} <span>{HtmlParser(okMessage)}</span> </span>
</div>
</Toolbar>
<AddItemLayout date={state.data.date} time={state.data.time} status={state.data.status}>
{state.loading ? <Spinner /> :
<form onSubmit={event => submitFormHandler(event)} id="add-user">
@ -132,7 +116,8 @@ const GenerateSSL = props => {
defaultValue={state.generatedData.key} />
<div className="buttons-wrapper">
<button type="button" className="back" onClick={() => history.push(`/edit/web/?domain=${state.domain}`)}>{i18n.Back}</button>
<button type="button" className="add" onClick={() => props.prePopulateInputs(state.generatedData)}>{i18n.Add}</button>
<button type="button" className="back" onClick={props.closeModal}>{i18n.Back}</button>
</div>
</>)
: (<>
@ -149,12 +134,16 @@ const GenerateSSL = props => {
<TextInput id="org" name="v_org" title={i18n['Organization']} value={state.data.org} />
<div className="buttons-wrapper">
<button type="submit" className="add">{i18n.Save}</button>
<button type="button" className="back" onClick={() => history.push(`/edit/web/?domain=${state.domain}`)}>{i18n.Back}</button>
<button type="submit" className="add">{i18n.Generate}</button>
<button type="button" className="back" onClick={props.closeModal}>{i18n.Back}</button>
</div>
</>)
}
<div className="error"><span className="error-message">{errorMessage}</span></div>
<div className="success">
<span className="ok-message"><span>{HtmlParser(okMessage)}</span> </span>
</div>
</form>
}
</AddItemLayout>

View file

@ -21,7 +21,7 @@ import { useHistory } from 'react-router';
const Users = props => {
const { userName, i18n } = useSelector(state => state.session);
const { session: { look } } = useSelector(state => state.userSession);
const { session } = useSelector(state => state.userSession);
const { controlPanelFocusedElement } = useSelector(state => state.controlPanelContent);
const { focusedElement } = useSelector(state => state.mainNavigation);
const dispatch = useDispatch();
@ -74,7 +74,7 @@ const Users = props => {
if (event.keyCode === 65) {
switch (history.location.pathname) {
case '/list/user/': return look ? history.push('/add/web/') : history.push('/add/user/');
case '/list/user/': return session.look ? history.push('/add/web/') : history.push('/add/user/');
default: break;
}
}
@ -403,8 +403,8 @@ const Users = props => {
</Helmet>
<Toolbar mobile={false} >
<LeftButton
name={look ? i18n['Add Web Domain'] : i18n['Add User']}
href={look ? "/add/web/" : "/add/user/"}
name={session.look ? i18n['Add Web Domain'] : i18n['Add User']}
href={session.look ? "/add/web/" : "/add/user/"}
showLeftMenu={true} />
<div className="r-menu">
<div className="input-group input-group-sm">