added a "Cancel" button to interrupt the server configuration process when it is found that it is busy installing other software

This commit is contained in:
vladimir.kuznetsov 2023-01-02 17:32:27 +03:00
parent 08a8eadb49
commit 6ec090ea0d
17 changed files with 193 additions and 48 deletions

View file

@ -31,6 +31,7 @@ enum ErrorCode
ServerPortAlreadyAllocatedError,
ServerContainerMissingError,
ServerDockerFailedError,
ServerCancelInstallation,
// Ssh connection errors
SshSocketError, SshTimeoutError, SshProtocolError,

View file

@ -15,6 +15,7 @@ QString errorString(ErrorCode code){
case(ServerPortAlreadyAllocatedError): return QObject::tr("Server port already used. Check for another software");
case(ServerContainerMissingError): return QObject::tr("Server error: Docker container missing");
case(ServerDockerFailedError): return QObject::tr("Server error: Docker failed");
case(ServerCancelInstallation): return QObject::tr("Installation canceled by user");
// Ssh connection errors
case(SshSocketError): return QObject::tr("Ssh connection error");

View file

@ -530,10 +530,13 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
stdOut += data + "\n";
};
QFutureWatcher<void> watcher;
QFutureWatcher<ErrorCode> watcher;
QFuture<void> future = QtConcurrent::run([this, &stdOut, &cbReadStdOut, &cbReadStdErr, &credentials]() {
QFuture<ErrorCode> future = QtConcurrent::run([this, &stdOut, &cbReadStdOut, &cbReadStdErr, &credentials]() {
do {
if (m_cancelInstallation) {
return ErrorCode::ServerCancelInstallation;
}
stdOut.clear();
runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy),
@ -543,16 +546,22 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
QThread::msleep(1000);
}
} while (!stdOut.isEmpty());
return ErrorCode::NoError;
});
watcher.setFuture(future);
QEventLoop wait;
QObject::connect(&watcher, &QFutureWatcher<void>::finished, &wait, &QEventLoop::quit);
QObject::connect(&watcher, &QFutureWatcher<ErrorCode>::finished, &wait, &QEventLoop::quit);
wait.exec();
m_cancelInstallation = false;
emit serverIsBusy(false);
if (future.result() != ErrorCode::NoError) {
return future.result();
}
ErrorCode error = runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::install_docker),
genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
@ -820,6 +829,11 @@ SshConnection *ServerController::connectToHost(const SshConnectionParameters &ss
return client;
}
void ServerController::setCancelInstallation(const bool cancel)
{
m_cancelInstallation = cancel;
}
void ServerController::disconnectFromHost(const ServerCredentials &credentials)
{
SshConnection *client = acquireConnection(sshParams(credentials));

View file

@ -73,6 +73,7 @@ public:
QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr);
QSsh::SshConnection *connectToHost(const QSsh::SshConnectionParameters &sshParams);
void setCancelInstallation(const bool cancel);
private:
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container);
@ -85,6 +86,7 @@ private:
std::shared_ptr<Settings> m_settings;
std::shared_ptr<VpnConfigurator> m_configurator;
bool m_cancelInstallation = false;
signals:
void serverIsBusy(const bool isBusy);
};

View file

@ -59,22 +59,28 @@ ErrorCode ServerConfiguringProgressLogic::doInstallAction(const std::function<Er
set_labelServerBusyVisible(visible);
};
return doInstallAction(action, page, progress, noButton, noWaitInfo, busyInfo);
ButtonFunc cancelButton;
cancelButton.setVisibleFunc = [this] (bool visible) -> void {
set_pushButtonCancelVisible(visible);
};
return doInstallAction(action, page, progress, noButton, noWaitInfo, busyInfo, cancelButton);
}
ErrorCode ServerConfiguringProgressLogic::doInstallAction(const std::function<ErrorCode()> &action,
const PageFunc &page,
const ProgressFunc &progress,
const ButtonFunc &button,
const ButtonFunc &saveButton,
const LabelFunc &waitInfo,
const LabelFunc &serverBusyInfo)
const LabelFunc &serverBusyInfo,
const ButtonFunc &cancelButton)
{
progress.setVisibleFunc(true);
if (page.setEnabledFunc) {
page.setEnabledFunc(false);
}
if (button.setVisibleFunc) {
button.setVisibleFunc(false);
if (saveButton.setVisibleFunc) {
saveButton.setVisibleFunc(false);
}
if (waitInfo.setVisibleFunc) {
waitInfo.setVisibleFunc(true);
@ -91,33 +97,47 @@ ErrorCode ServerConfiguringProgressLogic::doInstallAction(const std::function<Er
progress.setValueFunc(0);
timer.start(1000);
QMetaObject::Connection connection;
if (serverBusyInfo.setTextFunc) {
connection = connect(m_serverController.get(),
QMetaObject::Connection cancelDoInstallActionConnection;
if (cancelButton.setVisibleFunc) {
cancelDoInstallActionConnection = connect(this, &ServerConfiguringProgressLogic::cancelDoInstallAction,
m_serverController.get(), &ServerController::setCancelInstallation);
}
QMetaObject::Connection serverBusyConnection;
if (serverBusyInfo.setVisibleFunc && serverBusyInfo.setTextFunc) {
serverBusyConnection = connect(m_serverController.get(),
&ServerController::serverIsBusy,
this,
[&serverBusyInfo, &timer](const bool isBusy) {
[&serverBusyInfo, &timer, &cancelButton](const bool isBusy) {
isBusy ? timer.stop() : timer.start(1000);
serverBusyInfo.setVisibleFunc(isBusy);
serverBusyInfo.setTextFunc(isBusy ? "Amnesia has detected that your server is currently "
"busy installing other software. Amnesia installation "
"will pause until the server finishes installing other software"
: "");
if (cancelButton.setVisibleFunc) {
cancelButton.setVisibleFunc(isBusy ? true : false);
}
});
}
ErrorCode e = action();
qDebug() << "doInstallAction finished with code" << e;
if (serverBusyInfo.setTextFunc) {
disconnect(connection);
if (cancelButton.setVisibleFunc) {
disconnect(cancelDoInstallActionConnection);
}
if (serverBusyInfo.setVisibleFunc && serverBusyInfo.setTextFunc) {
disconnect(serverBusyConnection);
}
if (e) {
if (page.setEnabledFunc) {
page.setEnabledFunc(true);
}
if (button.setVisibleFunc) {
button.setVisibleFunc(true);
if (saveButton.setVisibleFunc) {
saveButton.setVisibleFunc(true);
}
if (waitInfo.setVisibleFunc) {
waitInfo.setVisibleFunc(false);
@ -152,8 +172,8 @@ ErrorCode ServerConfiguringProgressLogic::doInstallAction(const std::function<Er
progress.setVisibleFunc(false);
if (button.setVisibleFunc) {
button.setVisibleFunc(true);
if (saveButton.setVisibleFunc) {
saveButton.setVisibleFunc(true);
}
if (page.setEnabledFunc) {
page.setEnabledFunc(true);
@ -163,3 +183,8 @@ ErrorCode ServerConfiguringProgressLogic::doInstallAction(const std::function<Er
}
return ErrorCode::NoError;
}
void ServerConfiguringProgressLogic::onPushButtonCancelClicked()
{
cancelDoInstallAction(true);
}

View file

@ -22,6 +22,10 @@ class ServerConfiguringProgressLogic : public PageLogicBase
AUTO_PROPERTY(QString, progressBarText)
AUTO_PROPERTY(bool, labelServerBusyVisible)
AUTO_PROPERTY(QString, labelServerBusyText)
AUTO_PROPERTY(bool, pushButtonCancelVisible)
public:
Q_INVOKABLE void onPushButtonCancelClicked();
private:
struct ProgressFunc {
@ -57,9 +61,13 @@ public:
ErrorCode doInstallAction(const std::function<ErrorCode()> &action,
const PageFunc &page,
const ProgressFunc &progress,
const ButtonFunc &button,
const ButtonFunc &saveButton,
const LabelFunc &waitInfo,
const LabelFunc &serverBusyInfo);
const LabelFunc &serverBusyInfo,
const ButtonFunc &cancelButton);
signals:
void cancelDoInstallAction(const bool cancel);
};
#endif // SERVER_CONFIGURING_PROGRESS_LOGIC_H

View file

@ -105,6 +105,11 @@ void CloakLogic::onPushButtonSaveClicked()
set_labelServerBusyVisible(visible);
};
ServerConfiguringProgressLogic::ButtonFunc cancelButtonFunc;
cancelButtonFunc.setVisibleFunc = [this] (bool visible) -> void {
set_pushButtonCancelVisible(visible);
};
progressBarFunc.setTextVisibleFunc(true);
progressBarFunc.setTextFunc(QString("Configuring..."));
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction([this, containerConfig, &newContainerConfig](){
@ -114,7 +119,8 @@ void CloakLogic::onPushButtonSaveClicked()
newContainerConfig);
},
pageFunc, progressBarFunc,
saveButtonFunc, waitInfoFunc, busyInfoFuncy);
saveButtonFunc, waitInfoFunc,
busyInfoFuncy, cancelButtonFunc);
if (!e) {
m_settings->setContainerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, newContainerConfig);
@ -123,3 +129,8 @@ void CloakLogic::onPushButtonSaveClicked()
qDebug() << "Protocol saved with code:" << e << "for" << uiLogic()->selectedServerIndex << uiLogic()->selectedDockerContainer;
}
void CloakLogic::onPushButtonCancelClicked()
{
emit uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->cancelDoInstallAction(true);
}

View file

@ -25,8 +25,12 @@ class CloakLogic : public PageProtocolLogicBase
AUTO_PROPERTY(bool, labelServerBusyVisible)
AUTO_PROPERTY(QString, labelServerBusyText)
AUTO_PROPERTY(bool, pushButtonCancelVisible)
public:
Q_INVOKABLE void onPushButtonSaveClicked();
Q_INVOKABLE void onPushButtonCancelClicked();
public:
explicit CloakLogic(UiLogic *uiLogic, QObject *parent = nullptr);

View file

@ -92,7 +92,7 @@ void OpenVpnLogic::updateProtocolPage(const QJsonObject &openvpnConfig, DockerCo
set_lineEditPortEnabled(container == DockerContainer::OpenVpn);
}
void OpenVpnLogic::onPushButtonProtoOpenVpnSaveClicked()
void OpenVpnLogic::onPushButtonSaveClicked()
{
QJsonObject protocolConfig = m_settings->protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, Proto::OpenVpn);
protocolConfig = getProtocolConfigFromPage(protocolConfig);
@ -144,6 +144,11 @@ void OpenVpnLogic::onPushButtonProtoOpenVpnSaveClicked()
set_labelServerBusyVisible(visible);
};
ServerConfiguringProgressLogic::ButtonFunc cancelButtonFunc;
cancelButtonFunc.setVisibleFunc = [this] (bool visible) -> void {
set_pushButtonCancelVisible(visible);
};
progressBarFunc.setTextVisibleFunc(true);
progressBarFunc.setTextFunc(QString("Configuring..."));
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction([this, containerConfig, &newContainerConfig](){
@ -153,7 +158,8 @@ void OpenVpnLogic::onPushButtonProtoOpenVpnSaveClicked()
newContainerConfig);
},
pageFunc, progressBarFunc,
saveButtonFunc, waitInfoFunc, busyInfoFuncy);
saveButtonFunc, waitInfoFunc,
busyInfoFuncy, cancelButtonFunc);
if (!e) {
m_settings->setContainerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, newContainerConfig);
@ -178,3 +184,8 @@ QJsonObject OpenVpnLogic::getProtocolConfigFromPage(QJsonObject oldConfig)
oldConfig.insert(config_key::additional_server_config, textAreaAdditionalServerConfig());
return oldConfig;
}
void OpenVpnLogic::onPushButtonCancelClicked()
{
emit uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->cancelDoInstallAction(true);
}

View file

@ -40,8 +40,11 @@ class OpenVpnLogic : public PageProtocolLogicBase
AUTO_PROPERTY(bool, labelServerBusyVisible)
AUTO_PROPERTY(QString, labelServerBusyText)
AUTO_PROPERTY(bool, pushButtonCancelVisible)
public:
Q_INVOKABLE void onPushButtonProtoOpenVpnSaveClicked();
Q_INVOKABLE void onPushButtonSaveClicked();
Q_INVOKABLE void onPushButtonCancelClicked();
public:
explicit OpenVpnLogic(UiLogic *uiLogic, QObject *parent = nullptr);

View file

@ -97,6 +97,11 @@ void ShadowSocksLogic::onPushButtonSaveClicked()
set_labelServerBusyVisible(visible);
};
ServerConfiguringProgressLogic::ButtonFunc cancelButtonFunc;
cancelButtonFunc.setVisibleFunc = [this] (bool visible) -> void {
set_pushButtonCancelVisible(visible);
};
progressBarFunc.setTextVisibleFunc(true);
progressBarFunc.setTextFunc(QString("Configuring..."));
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction([this, containerConfig, &newContainerConfig](){
@ -106,7 +111,8 @@ void ShadowSocksLogic::onPushButtonSaveClicked()
newContainerConfig);
},
pageFunc, progressBarFunc,
saveButtonFunc, waitInfoFunc, busyInfoFuncy);
saveButtonFunc, waitInfoFunc,
busyInfoFuncy, cancelButtonFunc);
if (!e) {
m_settings->setContainerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, newContainerConfig);
@ -114,3 +120,8 @@ void ShadowSocksLogic::onPushButtonSaveClicked()
}
qDebug() << "Protocol saved with code:" << e << "for" << uiLogic()->selectedServerIndex << uiLogic()->selectedDockerContainer;
}
void ShadowSocksLogic::onPushButtonCancelClicked()
{
emit uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->cancelDoInstallAction(true);
}

View file

@ -23,8 +23,12 @@ class ShadowSocksLogic : public PageProtocolLogicBase
AUTO_PROPERTY(bool, labelServerBusyVisible)
AUTO_PROPERTY(QString, labelServerBusyText)
AUTO_PROPERTY(bool, pushButtonCancelVisible)
public:
Q_INVOKABLE void onPushButtonSaveClicked();
Q_INVOKABLE void onPushButtonCancelClicked();
public:
explicit ShadowSocksLogic(UiLogic *uiLogic, QObject *parent = nullptr);

View file

@ -10,7 +10,6 @@ PageBase {
page: PageEnum.ServerConfiguringProgress
logic: ServerConfiguringProgressLogic
enabled: ServerConfiguringProgressLogic.pageEnabled
Caption {
id: caption
text: qsTr("Configuring...")
@ -56,14 +55,27 @@ PageBase {
visible: ServerConfiguringProgressLogic.labelWaitInfoVisible
}
ProgressBar {
id: pr
BlueButtonType {
id: pb_cancel
z: 1
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: logo.bottom
anchors.bottomMargin: 40
width: parent.width - 40
width: root.width - 60
height: 40
text: qsTr("Cancel")
visible: ServerConfiguringProgressLogic.pushButtonCancelVisible
enabled: ServerConfiguringProgressLogic.pushButtonCancelVisible
onClicked: {
ServerConfiguringProgressLogic.onPushButtonCancelClicked()
}
}
ProgressBar {
id: pr
enabled: ServerConfiguringProgressLogic.pageEnabled
anchors.fill: pb_cancel
from: 0
to: ServerConfiguringProgressLogic.progressBarMaximium
value: ServerConfiguringProgressLogic.progressBarValue

View file

@ -129,7 +129,6 @@ PageProtocolBase {
text: logic.labelInfoText
}
ProgressBar {
id: progressBar_proto_cloak_reset
anchors.horizontalCenter: parent.horizontalCenter
@ -184,4 +183,13 @@ PageProtocolBase {
}
}
BlueButtonType {
anchors.fill: pb_save
text: qsTr("Cancel")
visible: logic.pushButtonCancelVisible
enabled: logic.pushButtonCancelVisible
onClicked: {
logic.onPushButtonCancelClicked()
}
}
}

View file

@ -37,7 +37,6 @@ PageProtocolBase {
ColumnLayout {
id: content
enabled: logic.pageEnabled
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
@ -45,12 +44,13 @@ PageProtocolBase {
LabelType {
id: lb_subnet
enabled: logic.pageEnabled
height: 21
text: qsTr("VPN Addresses Subnet")
}
TextFieldType {
id: tf_subnet
enabled: logic.pageEnabled
implicitWidth: parent.width
height: 31
text: logic.lineEditSubnetText
@ -59,15 +59,16 @@ PageProtocolBase {
}
}
//
LabelType {
id: lb_proto
enabled: logic.pageEnabled
Layout.topMargin: 20
height: 21
text: qsTr("Network protocol")
}
Rectangle {
id: rect_proto
enabled: logic.pageEnabled
implicitWidth: root.width - 60
height: 71
border.width: 1
@ -99,8 +100,8 @@ PageProtocolBase {
}
}
//
RowLayout {
enabled: logic.pageEnabled
Layout.topMargin: 10
Layout.fillWidth: true
LabelType {
@ -122,12 +123,9 @@ PageProtocolBase {
}
}
//
CheckBoxType {
id: check_auto_enc
enabled: logic.pageEnabled
implicitWidth: parent.width
height: 21
text: qsTr("Auto-negotiate encryption")
@ -140,15 +138,16 @@ PageProtocolBase {
}
}
//
LabelType {
id: lb_cipher
enabled: logic.pageEnabled
height: 21
text: qsTr("Cipher")
}
ComboBoxType {
id: cb_cipher
enabled: logic.pageEnabled && !check_auto_enc.checked
implicitWidth: parent.width
height: 31
@ -175,18 +174,18 @@ PageProtocolBase {
onCurrentTextChanged: {
logic.comboBoxVpnCipherText = currentText
}
enabled: !check_auto_enc.checked
}
//
LabelType {
id: lb_hash
enabled: logic.pageEnabled
height: 21
Layout.topMargin: 20
text: qsTr("Hash")
}
ComboBoxType {
id: cb_hash
enabled: logic.pageEnabled && !check_auto_enc.checked
height: 31
implicitWidth: parent.width
model: [
@ -212,11 +211,11 @@ PageProtocolBase {
onCurrentTextChanged: {
logic.comboBoxVpnHashText = currentText
}
enabled: !check_auto_enc.checked
}
CheckBoxType {
id: check_tls
enabled: logic.pageEnabled
implicitWidth: parent.width
Layout.topMargin: 20
height: 21
@ -230,6 +229,7 @@ PageProtocolBase {
CheckBoxType {
id: check_block_dns
enabled: logic.pageEnabled
implicitWidth: parent.width
height: 21
text: qsTr("Block DNS requests outside of VPN")
@ -242,7 +242,7 @@ PageProtocolBase {
BasicButtonType {
id: pb_client_config
enabled: logic.pageEnabled
implicitWidth: parent.width
height: 21
text: qsTr("Additional client config commands →")
@ -267,6 +267,7 @@ PageProtocolBase {
Rectangle {
id: rect_client_conf
enabled: logic.pageEnabled
implicitWidth: root.width - 60
height: 101
border.width: 1
@ -288,14 +289,12 @@ PageProtocolBase {
}
}
}
}
BasicButtonType {
id: pb_server_config
enabled: logic.pageEnabled
implicitWidth: parent.width
height: 21
text: qsTr("Additional server config commands →")
@ -320,6 +319,7 @@ PageProtocolBase {
Rectangle {
id: rect_server_conf
enabled: logic.pageEnabled
implicitWidth: root.width - 60
height: 101
border.width: 1
@ -347,6 +347,7 @@ PageProtocolBase {
LabelType {
id: label_server_busy
enabled: logic.pageEnabled
horizontalAlignment: Text.AlignHCenter
Layout.maximumWidth: parent.width
Layout.fillWidth: true
@ -356,6 +357,7 @@ PageProtocolBase {
LabelType {
id: label_proto_openvpn_info
enabled: logic.pageEnabled
horizontalAlignment: Text.AlignHCenter
Layout.maximumWidth: parent.width
Layout.fillWidth: true
@ -371,18 +373,31 @@ PageProtocolBase {
BlueButtonType {
id: pb_save
enabled: logic.pageEnabled
z: 1
height: 40
text: qsTr("Save and restart VPN")
width: parent.width
visible: logic.pushButtonSaveVisible
onClicked: {
logic.onPushButtonProtoOpenVpnSaveClicked()
logic.onPushButtonSaveClicked()
}
}
BlueButtonType {
z: 1
anchors.fill: pb_save
text: qsTr("Cancel")
visible: logic.pushButtonCancelVisible
enabled: logic.pushButtonCancelVisible
onClicked: {
logic.onPushButtonCancelClicked()
}
}
ProgressBar {
id: progress_save
enabled: logic.pageEnabled
anchors.fill: pb_save
from: 0
to: logic.progressBarResetMaximium

View file

@ -162,4 +162,14 @@ PageProtocolBase {
logic.onPushButtonSaveClicked()
}
}
BlueButtonType {
anchors.fill: pb_save
text: qsTr("Cancel")
visible: logic.pushButtonCancelVisible
enabled: logic.pushButtonCancelVisible
onClicked: {
logic.onPushButtonCancelClicked()
}
}
}

View file

@ -346,6 +346,11 @@ void UiLogic::installServer(QMap<DockerContainer, QJsonObject> &containers)
pageLogic<ServerConfiguringProgressLogic>()->set_labelServerBusyVisible(visible);
};
ServerConfiguringProgressLogic::ButtonFunc cancelButtonFunc;
cancelButtonFunc.setVisibleFunc = [this] (bool visible) -> void {
pageLogic<ServerConfiguringProgressLogic>()->set_pushButtonCancelVisible(visible);
};
int count = 0;
ErrorCode error;
for (QMap<DockerContainer, QJsonObject>::iterator i = containers.begin(); i != containers.end(); i++, count++) {
@ -353,7 +358,7 @@ void UiLogic::installServer(QMap<DockerContainer, QJsonObject> &containers)
error = pageLogic<ServerConfiguringProgressLogic>()->doInstallAction([&] () {
return m_serverController->setupContainer(installCredentials, i.key(), i.value());
}, pageFunc, progressBarFunc, noButton, waitInfoFunc, busyInfoFunc);
}, pageFunc, progressBarFunc, noButton, waitInfoFunc, busyInfoFunc, cancelButtonFunc);
m_serverController->disconnectFromHost(installCredentials);
}