From b027fff103c0d2070d6de3b554af2dc86474ffe6 Mon Sep 17 00:00:00 2001 From: Vladyslav Miachkov <34045014+fameowner99@users.noreply.github.com> Date: Sat, 25 May 2024 13:00:51 +0300 Subject: [PATCH] Add clickable docs url on error (#806) --- client/amnezia_application.cpp | 8 +- client/core/controllers/apiController.cpp | 9 +- client/core/controllers/apiController.h | 2 +- client/core/defs.h | 14 ++- client/core/errorstrings.cpp | 86 +++++++++---------- .../platforms/android/android_controller.cpp | 2 +- .../ui/controllers/connectionController.cpp | 11 ++- client/ui/controllers/connectionController.h | 3 +- client/ui/controllers/exportController.cpp | 21 +++-- client/ui/controllers/exportController.h | 1 + client/ui/controllers/importController.cpp | 9 +- client/ui/controllers/importController.h | 1 + client/ui/controllers/installController.cpp | 33 ++++--- client/ui/controllers/installController.h | 1 + client/ui/controllers/pageController.cpp | 14 +++ client/ui/controllers/pageController.h | 5 ++ client/ui/qml/Controls2/PopupType.qml | 10 +++ client/ui/qml/Pages2/PageSetupWizardStart.qml | 8 +- client/ui/qml/Pages2/PageShare.qml | 5 +- client/ui/qml/Pages2/PageStart.qml | 9 +- client/utils/converter.h | 25 ++++++ 21 files changed, 173 insertions(+), 104 deletions(-) create mode 100644 client/utils/converter.h diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 06d2f9ac..db4061eb 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -363,10 +363,16 @@ void AmneziaApplication::initControllers() new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings)); m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get()); - connect(m_connectionController.get(), &ConnectionController::connectionErrorOccurred, this, [this](const QString &errorMessage) { + connect(m_connectionController.get(), qOverload(&ConnectionController::connectionErrorOccurred), this, [this](const QString &errorMessage) { emit m_pageController->showErrorMessage(errorMessage); emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); }); + + connect(m_connectionController.get(), qOverload(&ConnectionController::connectionErrorOccurred), this, [this](ErrorCode errorCode) { + emit m_pageController->showErrorMessage(errorCode); + emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected); + }); + connect(m_connectionController.get(), &ConnectionController::connectButtonClicked, m_connectionController.get(), &ConnectionController::toggleConnection, Qt::QueuedConnection); diff --git a/client/core/controllers/apiController.cpp b/client/core/controllers/apiController.cpp index fa0fcaec..1a8dc8eb 100644 --- a/client/core/controllers/apiController.cpp +++ b/client/core/controllers/apiController.cpp @@ -8,7 +8,6 @@ #include "amnezia_application.h" #include "configurators/wireguard_configurator.h" #include "version.h" -#include "core/errorstrings.h" namespace { @@ -110,7 +109,7 @@ void ApiController::updateServerConfigFromApi(const QString &installationUuid, c QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); if (ba.isEmpty()) { - emit errorOccurred(errorString(ErrorCode::ApiConfigEmptyError)); + emit errorOccurred(ErrorCode::ApiConfigEmptyError); return; } @@ -135,14 +134,14 @@ void ApiController::updateServerConfigFromApi(const QString &installationUuid, c } else { if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError || reply->error() == QNetworkReply::NetworkError::TimeoutError) { - emit errorOccurred(errorString(ErrorCode::ApiConfigTimeoutError)); + emit errorOccurred(ErrorCode::ApiConfigTimeoutError); } else { QString err = reply->errorString(); qDebug() << QString::fromUtf8(reply->readAll()); qDebug() << reply->error(); qDebug() << err; qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); - emit errorOccurred(errorString(ErrorCode::ApiConfigDownloadError)); + emit errorOccurred(ErrorCode::ApiConfigDownloadError); } } @@ -153,7 +152,7 @@ void ApiController::updateServerConfigFromApi(const QString &installationUuid, c [this, reply](QNetworkReply::NetworkError error) { qDebug() << reply->errorString() << error; }); connect(reply, &QNetworkReply::sslErrors, [this, reply](const QList &errors) { qDebug().noquote() << errors; - emit errorOccurred(errorString(ErrorCode::ApiConfigSslError)); + emit errorOccurred(ErrorCode::ApiConfigSslError); }); } } diff --git a/client/core/controllers/apiController.h b/client/core/controllers/apiController.h index fad00c99..cc5d9f31 100644 --- a/client/core/controllers/apiController.h +++ b/client/core/controllers/apiController.h @@ -20,7 +20,7 @@ public slots: void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig); signals: - void errorOccurred(const QString &errorMessage); + void errorOccurred(ErrorCode errorCode); void configUpdated(const bool updateConfig, const QJsonObject &config, const int serverIndex); private: diff --git a/client/core/defs.h b/client/core/defs.h index 87b03824..a441ee1c 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -36,7 +36,11 @@ namespace amnezia } }; - enum ErrorCode { + namespace error_code_ns + { + Q_NAMESPACE + // TODO: change to enum class + enum ErrorCode { // General error codes NoError = 0, UnknownError = 100, @@ -75,7 +79,7 @@ namespace amnezia AmneziaServiceConnectionFailed = 603, ExecutableMissing = 604, XrayExecutableMissing = 605, - Tun2SockExecutableMissing = 606, + Tun2SockExecutableMissing = 606, // VPN errors OpenVpnAdaptersInUseError = 700, @@ -110,7 +114,11 @@ namespace amnezia UnspecifiedError = 1203, FatalError = 1204, AbortError = 1205 - }; + }; + Q_ENUM_NS(ErrorCode) + } + + using ErrorCode = error_code_ns::ErrorCode; } // namespace amnezia diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index bf47f2b9..9bba1f27 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -8,68 +8,68 @@ QString errorString(ErrorCode code) { switch (code) { // General error codes - case(NoError): errorMessage = QObject::tr("No error"); break; - case(UnknownError): errorMessage = QObject::tr("Unknown Error"); break; - case(NotImplementedError): errorMessage = QObject::tr("Function not implemented"); break; - case(AmneziaServiceNotRunning): errorMessage = QObject::tr("Background service is not running"); break; + case(ErrorCode::NoError): errorMessage = QObject::tr("No error"); break; + case(ErrorCode::UnknownError): errorMessage = QObject::tr("Unknown Error"); break; + case(ErrorCode::NotImplementedError): errorMessage = QObject::tr("Function not implemented"); break; + case(ErrorCode::AmneziaServiceNotRunning): errorMessage = QObject::tr("Background service is not running"); break; // Server errors - case(ServerCheckFailed): errorMessage = QObject::tr("Server check failed"); break; - case(ServerPortAlreadyAllocatedError): errorMessage = QObject::tr("Server port already used. Check for another software"); break; - case(ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break; - case(ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break; - case(ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break; - case(ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break; - case(ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break; + case(ErrorCode::ServerCheckFailed): errorMessage = QObject::tr("Server check failed"); break; + case(ErrorCode::ServerPortAlreadyAllocatedError): errorMessage = QObject::tr("Server port already used. Check for another software"); break; + case(ErrorCode::ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break; + case(ErrorCode::ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break; + case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break; + case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break; + case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break; // Libssh errors - case(SshRequestDeniedError): errorMessage = QObject::tr("Ssh request was denied"); break; - case(SshInterruptedError): errorMessage = QObject::tr("Ssh request was interrupted"); break; - case(SshInternalError): errorMessage = QObject::tr("Ssh internal error"); break; - case(SshPrivateKeyError): errorMessage = QObject::tr("Invalid private key or invalid passphrase entered"); break; - case(SshPrivateKeyFormatError): errorMessage = QObject::tr("The selected private key format is not supported, use openssh ED25519 key types or PEM key types"); break; - case(SshTimeoutError): errorMessage = QObject::tr("Timeout connecting to server"); break; + case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("Ssh request was denied"); break; + case(ErrorCode::SshInterruptedError): errorMessage = QObject::tr("Ssh request was interrupted"); break; + case(ErrorCode::SshInternalError): errorMessage = QObject::tr("Ssh internal error"); break; + case(ErrorCode::SshPrivateKeyError): errorMessage = QObject::tr("Invalid private key or invalid passphrase entered"); break; + case(ErrorCode::SshPrivateKeyFormatError): errorMessage = QObject::tr("The selected private key format is not supported, use openssh ED25519 key types or PEM key types"); break; + case(ErrorCode::SshTimeoutError): errorMessage = QObject::tr("Timeout connecting to server"); break; // Ssh scp errors - case(SshScpFailureError): errorMessage = QObject::tr("Scp error: Generic failure"); break; + case(ErrorCode::SshScpFailureError): errorMessage = QObject::tr("Scp error: Generic failure"); break; // Local errors - case (OpenVpnConfigMissing): errorMessage = QObject::tr("OpenVPN config missing"); break; - case (OpenVpnManagementServerError): errorMessage = QObject::tr("OpenVPN management server error"); break; + case (ErrorCode::OpenVpnConfigMissing): errorMessage = QObject::tr("OpenVPN config missing"); break; + case (ErrorCode::OpenVpnManagementServerError): errorMessage = QObject::tr("OpenVPN management server error"); break; // Distro errors - case (OpenVpnExecutableMissing): errorMessage = QObject::tr("OpenVPN executable missing"); break; - case (ShadowSocksExecutableMissing): errorMessage = QObject::tr("ShadowSocks (ss-local) executable missing"); break; - case (CloakExecutableMissing): errorMessage = QObject::tr("Cloak (ck-client) executable missing"); break; - case (AmneziaServiceConnectionFailed): errorMessage = QObject::tr("Amnezia helper service error"); break; - case (OpenSslFailed): errorMessage = QObject::tr("OpenSSL failed"); break; + case (ErrorCode::OpenVpnExecutableMissing): errorMessage = QObject::tr("OpenVPN executable missing"); break; + case (ErrorCode::ShadowSocksExecutableMissing): errorMessage = QObject::tr("ShadowSocks (ss-local) executable missing"); break; + case (ErrorCode::CloakExecutableMissing): errorMessage = QObject::tr("Cloak (ck-client) executable missing"); break; + case (ErrorCode::AmneziaServiceConnectionFailed): errorMessage = QObject::tr("Amnezia helper service error"); break; + case (ErrorCode::OpenSslFailed): errorMessage = QObject::tr("OpenSSL failed"); break; // VPN errors - case (OpenVpnAdaptersInUseError): errorMessage = QObject::tr("Can't connect: another VPN connection is active"); break; - case (OpenVpnTapAdapterError): errorMessage = QObject::tr("Can't setup OpenVPN TAP network adapter"); break; - case (AddressPoolError): errorMessage = QObject::tr("VPN pool error: no available addresses"); break; + case (ErrorCode::OpenVpnAdaptersInUseError): errorMessage = QObject::tr("Can't connect: another VPN connection is active"); break; + case (ErrorCode::OpenVpnTapAdapterError): errorMessage = QObject::tr("Can't setup OpenVPN TAP network adapter"); break; + case (ErrorCode::AddressPoolError): errorMessage = QObject::tr("VPN pool error: no available addresses"); break; - case (ImportInvalidConfigError): errorMessage = QObject::tr("The config does not contain any containers and credentials for connecting to the server"); break; + case (ErrorCode::ImportInvalidConfigError): errorMessage = QObject::tr("The config does not contain any containers and credentials for connecting to the server"); break; // Android errors - case (AndroidError): errorMessage = QObject::tr("VPN connection error"); break; + case (ErrorCode::AndroidError): errorMessage = QObject::tr("VPN connection error"); break; // Api errors - case (ApiConfigDownloadError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break; - case (ApiConfigAlreadyAdded): errorMessage = QObject::tr("This config has already been added to the application"); break; - case (ApiConfigEmptyError): errorMessage = QObject::tr("In the response from the server, an empty config was received"); break; - case (ApiConfigSslError): errorMessage = QObject::tr("SSL error occurred"); break; - case (ApiConfigTimeoutError): errorMessage = QObject::tr("Server response timeout on api request"); break; - + case (ErrorCode::ApiConfigDownloadError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break; + case (ErrorCode::ApiConfigAlreadyAdded): errorMessage = QObject::tr("This config has already been added to the application"); break; + case (ErrorCode::ApiConfigEmptyError): errorMessage = QObject::tr("In the response from the server, an empty config was received"); break; + case (ErrorCode::ApiConfigSslError): errorMessage = QObject::tr("SSL error occurred"); break; + case (ErrorCode::ApiConfigTimeoutError): errorMessage = QObject::tr("Server response timeout on api request"); break; + // QFile errors - case(OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; - case(ReadError): errorMessage = QObject::tr("QFile error: An error occurred when reading from the file"); break; - case(PermissionsError): errorMessage = QObject::tr("QFile error: The file could not be accessed"); break; - case(UnspecifiedError): errorMessage = QObject::tr("QFile error: An unspecified error occurred"); break; - case(FatalError): errorMessage = QObject::tr("QFile error: A fatal error occurred"); break; - case(AbortError): errorMessage = QObject::tr("QFile error: The operation was aborted"); break; + case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; + case(ErrorCode::ReadError): errorMessage = QObject::tr("QFile error: An error occurred when reading from the file"); break; + case(ErrorCode::PermissionsError): errorMessage = QObject::tr("QFile error: The file could not be accessed"); break; + case(ErrorCode::UnspecifiedError): errorMessage = QObject::tr("QFile error: An unspecified error occurred"); break; + case(ErrorCode::FatalError): errorMessage = QObject::tr("QFile error: A fatal error occurred"); break; + case(ErrorCode::AbortError): errorMessage = QObject::tr("QFile error: The operation was aborted"); break; - case(InternalError): + case(ErrorCode::InternalError): default: errorMessage = QObject::tr("Internal error"); break; } diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index 0311fd0a..be404a15 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -136,7 +136,7 @@ ErrorCode AndroidController::start(const QJsonObject &vpnConfig) callActivityMethod("start", "(Ljava/lang/String;)V", QJniObject::fromString(config).object()); - return NoError; + return ErrorCode::NoError; } void AndroidController::stop() diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp index 2308f7ad..df9e2dd4 100644 --- a/client/ui/controllers/connectionController.cpp +++ b/client/ui/controllers/connectionController.cpp @@ -8,7 +8,6 @@ #include #include "core/controllers/vpnConfigurationController.h" -#include "core/errorstrings.h" #include "version.h" ConnectionController::ConnectionController(const QSharedPointer &serversModel, @@ -30,7 +29,7 @@ ConnectionController::ConnectionController(const QSharedPointer &s connect(&m_apiController, &ApiController::configUpdated, this, static_cast(&ConnectionController::openConnection)); - connect(&m_apiController, &ApiController::errorOccurred, this, &ConnectionController::connectionErrorOccurred); + connect(&m_apiController, qOverload(&ApiController::errorOccurred), this, qOverload(&ConnectionController::connectionErrorOccurred)); m_state = Vpn::ConnectionState::Disconnected; } @@ -40,7 +39,7 @@ void ConnectionController::openConnection() #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true)) { - emit connectionErrorOccurred(errorString(ErrorCode::AmneziaServiceNotRunning)); + emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning); return; } #endif @@ -63,9 +62,9 @@ void ConnectionController::closeConnection() emit disconnectFromVpn(); } -QString ConnectionController::getLastConnectionError() +ErrorCode ConnectionController::getLastConnectionError() { - return errorString(m_vpnConnection->lastError()); + return m_vpnConnection->lastError(); } void ConnectionController::onConnectionStateChanged(Vpn::ConnectionState state) @@ -218,7 +217,7 @@ void ConnectionController::openConnection(const bool updateConfig, const QJsonOb ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex); ErrorCode errorCode = updateProtocolConfig(container, credentials, containerConfig, serverController); if (errorCode != ErrorCode::NoError) { - emit connectionErrorOccurred(errorString(errorCode)); + emit connectionErrorOccurred(errorCode); return; } diff --git a/client/ui/controllers/connectionController.h b/client/ui/controllers/connectionController.h index a35d45b5..bc8f7e2f 100644 --- a/client/ui/controllers/connectionController.h +++ b/client/ui/controllers/connectionController.h @@ -34,7 +34,7 @@ public slots: void openConnection(); void closeConnection(); - QString getLastConnectionError(); + ErrorCode getLastConnectionError(); void onConnectionStateChanged(Vpn::ConnectionState state); void onCurrentContainerUpdated(); @@ -50,6 +50,7 @@ signals: void connectionStateChanged(); void connectionErrorOccurred(const QString &errorMessage); + void connectionErrorOccurred(ErrorCode errorCode); void reconnectWithUpdatedContainer(const QString &message); void noInstalledContainers(); diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index 4549f78c..a5605674 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -9,7 +9,6 @@ #include #include "core/controllers/vpnConfigurationController.h" -#include "core/errorstrings.h" #include "systemController.h" #ifdef Q_OS_ANDROID #include "platforms/android/android_utils.h" @@ -101,7 +100,7 @@ void ExportController::generateConnectionConfig(const QString &clientName) errorCode = m_clientManagementModel->appendClient(container, credentials, containerConfig, clientName, serverController); if (errorCode != ErrorCode::NoError) { - emit exportErrorOccurred(errorString(errorCode)); + emit exportErrorOccurred(errorCode); return; } @@ -171,7 +170,7 @@ void ExportController::generateOpenVpnConfig(const QString &clientName) } if (errorCode) { - emit exportErrorOccurred(errorString(errorCode)); + emit exportErrorOccurred(errorCode); return; } @@ -189,7 +188,7 @@ void ExportController::generateWireGuardConfig(const QString &clientName) QJsonObject nativeConfig; ErrorCode errorCode = generateNativeConfig(DockerContainer::WireGuard, clientName, Proto::WireGuard, nativeConfig); if (errorCode) { - emit exportErrorOccurred(errorString(errorCode)); + emit exportErrorOccurred(errorCode); return; } @@ -209,7 +208,7 @@ void ExportController::generateAwgConfig(const QString &clientName) QJsonObject nativeConfig; ErrorCode errorCode = generateNativeConfig(DockerContainer::Awg, clientName, Proto::Awg, nativeConfig); if (errorCode) { - emit exportErrorOccurred(errorString(errorCode)); + emit exportErrorOccurred(errorCode); return; } @@ -237,7 +236,7 @@ void ExportController::generateShadowSocksConfig() } if (errorCode) { - emit exportErrorOccurred(errorString(errorCode)); + emit exportErrorOccurred(errorCode); return; } @@ -263,7 +262,7 @@ void ExportController::generateCloakConfig() QJsonObject nativeConfig; ErrorCode errorCode = generateNativeConfig(DockerContainer::Cloak, "", Proto::Cloak, nativeConfig); if (errorCode) { - emit exportErrorOccurred(errorString(errorCode)); + emit exportErrorOccurred(errorCode); return; } @@ -283,7 +282,7 @@ void ExportController::generateXrayConfig() QJsonObject nativeConfig; ErrorCode errorCode = generateNativeConfig(DockerContainer::Xray, "", Proto::Xray, nativeConfig); if (errorCode) { - emit exportErrorOccurred(errorString(errorCode)); + emit exportErrorOccurred(errorCode); return; } @@ -320,7 +319,7 @@ void ExportController::updateClientManagementModel(const DockerContainer contain QSharedPointer serverController(new ServerController(m_settings)); ErrorCode errorCode = m_clientManagementModel->updateModel(container, credentials, serverController); if (errorCode != ErrorCode::NoError) { - emit exportErrorOccurred(errorString(errorCode)); + emit exportErrorOccurred(errorCode); } } @@ -330,7 +329,7 @@ void ExportController::revokeConfig(const int row, const DockerContainer contain ErrorCode errorCode = m_clientManagementModel->revokeClient(row, container, credentials, m_serversModel->getProcessedServerIndex(), serverController); if (errorCode != ErrorCode::NoError) { - emit exportErrorOccurred(errorString(errorCode)); + emit exportErrorOccurred(errorCode); } } @@ -339,7 +338,7 @@ void ExportController::renameClient(const int row, const QString &clientName, co QSharedPointer serverController(new ServerController(m_settings)); ErrorCode errorCode = m_clientManagementModel->renameClient(row, clientName, container, credentials, serverController); if (errorCode != ErrorCode::NoError) { - emit exportErrorOccurred(errorString(errorCode)); + emit exportErrorOccurred(errorCode); } } diff --git a/client/ui/controllers/exportController.h b/client/ui/controllers/exportController.h index b978137e..023f22cf 100644 --- a/client/ui/controllers/exportController.h +++ b/client/ui/controllers/exportController.h @@ -49,6 +49,7 @@ public slots: signals: void generateConfig(int type); void exportErrorOccurred(const QString &errorMessage); + void exportErrorOccurred(ErrorCode errorCode); void exportConfigChanged(); diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index 8d1ff53e..aaffe47e 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -6,7 +6,6 @@ #include #include -#include "core/errorstrings.h" #ifdef Q_OS_ANDROID #include "platforms/android/android_controller.h" #endif @@ -221,7 +220,7 @@ void ImportController::importConfig() } else if (m_config.contains(config_key::configVersion)) { quint16 crc = qChecksum(QJsonDocument(m_config).toJson()); if (m_serversModel->isServerFromApiAlreadyExists(crc)) { - emit importErrorOccurred(errorString(ErrorCode::ApiConfigAlreadyAdded), true); + emit importErrorOccurred(ErrorCode::ApiConfigAlreadyAdded, true); } else { m_config.insert(config_key::crc, crc); @@ -231,7 +230,7 @@ void ImportController::importConfig() } else { qDebug() << "Failed to import profile"; qDebug().noquote() << QJsonDocument(m_config).toJson(); - emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError), false); + emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false); } m_config = {}; @@ -308,7 +307,7 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data) hostName = hostNameAndPortMatch.captured(1); } else { qDebug() << "Key parameter 'Endpoint' is missing"; - emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError), false); + emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false); return QJsonObject(); } @@ -334,7 +333,7 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data) lastConfig[config_key::server_pub_key] = configMap.value("PublicKey"); } else { qDebug() << "One of the key parameters is missing (PrivateKey, Address, PublicKey)"; - emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError), false); + emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false); return QJsonObject(); } diff --git a/client/ui/controllers/importController.h b/client/ui/controllers/importController.h index 509c4631..9bbb9e0c 100644 --- a/client/ui/controllers/importController.h +++ b/client/ui/controllers/importController.h @@ -54,6 +54,7 @@ public slots: signals: void importFinished(); void importErrorOccurred(const QString &errorMessage, bool goToPageHome); + void importErrorOccurred(ErrorCode errorCode, bool goToPageHome); void qrDecodingFinished(); diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index 7240adf6..e743d22c 100644 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -9,7 +9,6 @@ #include "core/controllers/serverController.h" #include "core/controllers/vpnConfigurationController.h" -#include "core/errorstrings.h" #include "core/networkUtilities.h" #include "logger.h" #include "ui/models/protocols/awgConfigModel.h" @@ -149,7 +148,7 @@ void InstallController::install(DockerContainer container, int port, TransportPr QMap installedContainers; ErrorCode errorCode = getAlreadyInstalledContainers(serverCredentials, serverController, installedContainers); if (errorCode) { - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); return; } @@ -158,7 +157,7 @@ void InstallController::install(DockerContainer container, int port, TransportPr if (!installedContainers.contains(container)) { errorCode = serverController->setupContainer(serverCredentials, container, config); if (errorCode) { - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); return; } @@ -169,7 +168,7 @@ void InstallController::install(DockerContainer container, int port, TransportPr } if (errorCode) { - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); return; } @@ -204,7 +203,7 @@ void InstallController::installServer(const DockerContainer container, const QMa auto errorCode = vpnConfigurationController.createProtocolConfigForContainer(m_processedServerCredentials, iterator.key(), containerConfig); if (errorCode) { - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); return; } containerConfigs.append(containerConfig); @@ -212,7 +211,7 @@ void InstallController::installServer(const DockerContainer container, const QMa errorCode = m_clientManagementModel->appendClient(iterator.key(), serverCredentials, containerConfig, QString("Admin [%1]").arg(QSysInfo::prettyProductName()), serverController); if (errorCode) { - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); return; } } else { @@ -244,7 +243,7 @@ void InstallController::installContainer(const DockerContainer container, const auto errorCode = vpnConfigurationController.createProtocolConfigForContainer(serverCredentials, iterator.key(), containerConfig); if (errorCode) { - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); return; } m_serversModel->addContainerConfig(iterator.key(), containerConfig); @@ -252,7 +251,7 @@ void InstallController::installContainer(const DockerContainer container, const errorCode = m_clientManagementModel->appendClient(iterator.key(), serverCredentials, containerConfig, QString("Admin [%1]").arg(QSysInfo::prettyProductName()), serverController); if (errorCode) { - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); return; } } else { @@ -310,7 +309,7 @@ void InstallController::scanServerForInstalledContainers() auto errorCode = vpnConfigurationController.createProtocolConfigForContainer(serverCredentials, container, containerConfig); if (errorCode) { - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); return; } m_serversModel->addContainerConfig(container, containerConfig); @@ -319,7 +318,7 @@ void InstallController::scanServerForInstalledContainers() QString("Admin [%1]").arg(QSysInfo::prettyProductName()), serverController); if (errorCode) { - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); return; } } else { @@ -334,7 +333,7 @@ void InstallController::scanServerForInstalledContainers() return; } - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); } ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentials &credentials, @@ -515,7 +514,7 @@ void InstallController::updateContainer(QJsonObject config) return; } - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); } void InstallController::rebootProcessedServer() @@ -528,7 +527,7 @@ void InstallController::rebootProcessedServer() if (errorCode == ErrorCode::NoError) { emit rebootProcessedServerFinished(tr("Server '%1' was rebooted").arg(serverName)); } else { - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); } } @@ -552,7 +551,7 @@ void InstallController::removeAllContainers() emit removeAllContainersFinished(tr("All containers from server '%1' have been removed").arg(serverName)); return; } - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); } void InstallController::removeProcessedContainer() @@ -570,7 +569,7 @@ void InstallController::removeProcessedContainer() emit removeProcessedContainerFinished(tr("%1 has been removed from the server '%2'").arg(containerName, serverName)); return; } - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); } void InstallController::removeApiConfig(const int serverIndex) @@ -738,7 +737,7 @@ bool InstallController::checkSshConnection(QSharedPointer serv if (errorCode == ErrorCode::NoError) { m_processedServerCredentials.secretData = decryptedPrivateKey; } else { - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); return false; } } @@ -747,7 +746,7 @@ bool InstallController::checkSshConnection(QSharedPointer serv output = serverController->checkSshConnection(m_processedServerCredentials, errorCode); if (errorCode != ErrorCode::NoError) { - emit installationErrorOccurred(errorString(errorCode)); + emit installationErrorOccurred(errorCode); return false; } else { if (output.contains(tr("Please login as the user"))) { diff --git a/client/ui/controllers/installController.h b/client/ui/controllers/installController.h index c79bd162..5e7fd41d 100644 --- a/client/ui/controllers/installController.h +++ b/client/ui/controllers/installController.h @@ -64,6 +64,7 @@ signals: void removeProcessedContainerFinished(const QString &finishedMessage); void installationErrorOccurred(const QString &errorMessage); + void installationErrorOccurred(ErrorCode errorCode); void serverAlreadyExists(int serverIndex); diff --git a/client/ui/controllers/pageController.cpp b/client/ui/controllers/pageController.cpp index 56268b2c..3c6583d3 100644 --- a/client/ui/controllers/pageController.cpp +++ b/client/ui/controllers/pageController.cpp @@ -1,4 +1,6 @@ #include "pageController.h" +#include "utils/converter.h" +#include "core/errorstrings.h" #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) #include @@ -38,6 +40,8 @@ PageController::PageController(const QSharedPointer &serversModel, connect(this, &PageController::raiseMainWindow, []() { setDockIconVisible(true); }); connect(this, &PageController::hideMainWindow, []() { setDockIconVisible(false); }); #endif + + connect(this, qOverload(&PageController::showErrorMessage), this, &PageController::onShowErrorMessage); m_isTriggeredByConnectButton = false; } @@ -161,3 +165,13 @@ int PageController::getDrawerDepth() { return m_drawerDepth; } + +void PageController::onShowErrorMessage(ErrorCode errorCode) +{ + const auto fullErrorMessage = errorString(errorCode); + const auto errorMessage = fullErrorMessage.mid(fullErrorMessage.indexOf(". ") + 1); // remove ErrorCode %1. + const auto errorUrl = QStringLiteral("https://docs.amnezia.org/troubleshooting/error-codes/#error-%1-%2").arg(static_cast(errorCode)).arg(utils::enumToString(errorCode).toLower()); + const auto fullMessage = QStringLiteral("ErrorCode: %2. %3").arg(errorUrl).arg(static_cast(errorCode)).arg(errorMessage); + + emit showErrorMessage(fullMessage); +} diff --git a/client/ui/controllers/pageController.h b/client/ui/controllers/pageController.h index b286b1b1..1fdb1e81 100644 --- a/client/ui/controllers/pageController.h +++ b/client/ui/controllers/pageController.h @@ -4,6 +4,7 @@ #include #include +#include "core/defs.h" #include "ui/models/servers_model.h" namespace PageLoader @@ -93,6 +94,9 @@ public slots: void setDrawerDepth(const int depth); int getDrawerDepth(); + private slots: + void onShowErrorMessage(amnezia::ErrorCode errorCode); + signals: void goToPage(PageLoader::PageEnum page, bool slide = true); void goToStartPage(); @@ -107,6 +111,7 @@ signals: void restorePageHomeState(bool isContainerInstalled = false); void replaceStartPage(); + void showErrorMessage(amnezia::ErrorCode); void showErrorMessage(const QString &errorMessage); void showNotificationMessage(const QString &message); diff --git a/client/ui/qml/Controls2/PopupType.qml b/client/ui/qml/Controls2/PopupType.qml index f3771cd4..fb0f0c3a 100644 --- a/client/ui/qml/Controls2/PopupType.qml +++ b/client/ui/qml/Controls2/PopupType.qml @@ -57,7 +57,17 @@ Popup { horizontalAlignment: Text.AlignLeft Layout.fillWidth: true + onLinkActivated: function(link) { + Qt.openUrlExternally(link) + } + text: root.text + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + } } Item { diff --git a/client/ui/qml/Pages2/PageSetupWizardStart.qml b/client/ui/qml/Pages2/PageSetupWizardStart.qml index 4be04255..4473b730 100644 --- a/client/ui/qml/Pages2/PageSetupWizardStart.qml +++ b/client/ui/qml/Pages2/PageSetupWizardStart.qml @@ -76,9 +76,9 @@ PageType { Connections { target: InstallController - function onInstallationErrorOccurred(errorMessage) { + function onInstallationErrorOccurred(error) { PageController.showBusyIndicator(false) - PageController.showErrorMessage(errorMessage) + PageController.showErrorMessage(error) var currentPageName = stackView.currentItem.objectName @@ -97,8 +97,8 @@ PageType { PageController.showBusyIndicator(false) } - function onImportErrorOccurred(errorMessage, goToPageHome) { - PageController.showErrorMessage(errorMessage) + function onImportErrorOccurred(error, goToPageHome) { + PageController.showErrorMessage(error) } } diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 12e11ff6..3bdca1bb 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -102,9 +102,10 @@ PageType { PageController.showBusyIndicator(false) } - function onExportErrorOccurred(errorMessage) { + function onExportErrorOccurred(error) { shareConnectionDrawer.close() - PageController.showErrorMessage(errorMessage) + + PageController.showErrorMessage(error) } } diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index aca26566..5585631e 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -99,9 +99,10 @@ PageType { Connections { target: InstallController - function onInstallationErrorOccurred(errorMessage) { + function onInstallationErrorOccurred(error) { PageController.showBusyIndicator(false) - PageController.showErrorMessage(errorMessage) + + PageController.showErrorMessage(error) var needCloseCurrentPage = false var currentPageName = tabBarStackView.currentItem.objectName @@ -146,8 +147,8 @@ PageType { Connections { target: ImportController - function onImportErrorOccurred(errorMessage, goToPageHome) { - PageController.showErrorMessage(errorMessage) + function onImportErrorOccurred(error, goToPageHome) { + PageController.showErrorMessage(error) } } diff --git a/client/utils/converter.h b/client/utils/converter.h new file mode 100644 index 00000000..27739bb9 --- /dev/null +++ b/client/utils/converter.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace utils +{ + template + QString enumToString(Enum value) + { + auto metaEnum = QMetaEnum::fromType(); + return metaEnum.valueToKey(static_cast(value)); + } + + template + Enum enumFromString(const QString &str, Enum defaultValue = {}) + { + auto metaEnum = QMetaEnum::fromType(); + bool isOk; + auto value = metaEnum.keyToValue(str.toLatin1(), &isOk); + if (isOk) { + return static_cast(value); + } + return defaultValue; + } +}