From 06372c8fd7f735158b335198c02da9be46152116 Mon Sep 17 00:00:00 2001 From: vkamn Date: Fri, 15 May 2026 12:33:36 +0800 Subject: [PATCH 1/9] refactor: remove serverConfig struct (#2595) * refactor: remove serverConfig struct * refactor: add warnings for api v1 configs * refactor: moved the server type definition to a separate namespace * refactor: simplified gateway stacks * fix: fixed server description * fix: fixed postAsync reply usage * fix: fixed validateConfig call * fix: fixed server name in notifications * fix: fixed initPrepareConfigHandler for lagacy configs --- client/cmake/sources.cmake | 2 +- .../core/controllers/api/newsController.cpp | 83 ++- client/core/controllers/api/newsController.h | 10 +- .../api/servicesCatalogController.cpp | 2 +- .../api/subscriptionController.cpp | 376 ++++--------- .../controllers/api/subscriptionController.h | 38 +- .../core/controllers/connectionController.cpp | 105 +++- .../core/controllers/connectionController.h | 11 +- client/core/controllers/coreController.cpp | 17 +- client/core/controllers/coreController.h | 2 - .../core/controllers/coreSignalHandlers.cpp | 130 +++-- client/core/controllers/coreSignalHandlers.h | 2 +- client/core/controllers/gatewayController.cpp | 2 +- .../selfhosted/exportController.cpp | 150 +++--- .../controllers/selfhosted/exportController.h | 28 +- .../selfhosted/importController.cpp | 32 +- .../selfhosted/installController.cpp | 225 +++++--- .../selfhosted/installController.h | 29 +- .../selfhosted/usersController.cpp | 79 ++- .../controllers/selfhosted/usersController.h | 15 +- client/core/controllers/serversController.cpp | 489 ++++++++++++----- client/core/controllers/serversController.h | 68 +-- .../core/controllers/settingsController.cpp | 7 +- client/core/models/api/apiConfig.h | 2 +- client/core/models/api/apiV1ServerConfig.cpp | 140 ----- client/core/models/api/apiV1ServerConfig.h | 47 -- client/core/models/api/apiV2ServerConfig.cpp | 8 + client/core/models/api/apiV2ServerConfig.h | 3 +- client/core/models/api/authData.h | 2 +- .../core/models/api/legacyApiServerConfig.cpp | 43 ++ .../core/models/api/legacyApiServerConfig.h | 38 ++ .../models/selfhosted/nativeServerConfig.cpp | 8 + .../models/selfhosted/nativeServerConfig.h | 1 + .../selfHostedAdminServerConfig.cpp | 170 ++++++ .../selfhosted/selfHostedAdminServerConfig.h | 53 ++ ...fig.cpp => selfHostedUserServerConfig.cpp} | 100 ++-- ...rConfig.h => selfHostedUserServerConfig.h} | 20 +- client/core/models/serverConfig.cpp | 234 -------- client/core/models/serverConfig.h | 92 ---- client/core/models/serverDescription.cpp | 187 +++++++ client/core/models/serverDescription.h | 64 +++ .../secureAppSettingsRepository.cpp | 5 +- .../repositories/secureServersRepository.cpp | 466 ++++++++++------ .../repositories/secureServersRepository.h | 78 +-- client/core/utils/api/apiEnums.h | 25 - client/core/utils/api/apiUtils.cpp | 71 +-- client/core/utils/api/apiUtils.h | 7 +- client/core/utils/constants/apiConstants.h | 8 +- client/core/utils/constants/apiKeys.h | 5 +- client/core/utils/constants/configKeys.h | 3 + client/core/utils/errorCodes.h | 5 +- client/core/utils/errorStrings.cpp | 1 + client/core/utils/serverConfigUtils.cpp | 122 +++++ client/core/utils/serverConfigUtils.h | 40 ++ client/tests/CMakeLists.txt | 10 - client/tests/testAdminSelfHostedExport.cpp | 6 +- client/tests/testComplexOperations.cpp | 31 +- client/tests/testDefaultServerChange.cpp | 31 +- client/tests/testGatewayStacks.cpp | 75 --- client/tests/testMultipleImports.cpp | 37 +- client/tests/testSelfHostedServerSetup.cpp | 84 +-- client/tests/testServerEdgeCases.cpp | 38 +- client/tests/testServerEdit.cpp | 38 +- client/tests/testServerRepositoryHelpers.h | 93 ++++ client/tests/testServersModelSync.cpp | 17 +- client/tests/testSettingsSignals.cpp | 1 - client/tests/testSignalOrder.cpp | 9 +- .../tests/testUiServersModelAndController.cpp | 42 +- .../api/subscriptionUiController.cpp | 135 +++-- .../api/subscriptionUiController.h | 29 +- .../ui/controllers/connectionUiController.cpp | 17 +- .../ui/controllers/connectionUiController.h | 2 - client/ui/controllers/qml/pageController.cpp | 12 +- client/ui/controllers/qml/pageController.h | 5 +- .../selfhosted/exportUiController.cpp | 36 +- .../selfhosted/exportUiController.h | 22 +- .../selfhosted/installUiController.cpp | 164 +++--- .../selfhosted/installUiController.h | 34 +- client/ui/controllers/serversUiController.cpp | 499 ++++++++++-------- client/ui/controllers/serversUiController.h | 70 ++- client/ui/models/api/apiAccountInfoModel.cpp | 18 +- client/ui/models/api/apiAccountInfoModel.h | 4 +- client/ui/models/api/apiCountryModel.cpp | 2 +- client/ui/models/api/apiDevicesModel.cpp | 2 +- client/ui/models/serversModel.cpp | 301 +++-------- client/ui/models/serversModel.h | 17 +- client/ui/qml/Components/ConnectButton.qml | 2 +- client/ui/qml/Components/GamepadLoader.qml | 2 +- .../qml/Components/HomeContainersListView.qml | 2 +- .../ui/qml/Components/RenameServerDrawer.qml | 2 +- client/ui/qml/Components/ServersListView.qml | 12 +- .../Components/SettingsContainersListView.qml | 6 +- .../Components/SubscriptionExpiredDrawer.qml | 4 +- client/ui/qml/Pages2/PageHome.qml | 4 +- .../Pages2/PageProtocolAwgClientSettings.qml | 2 +- .../ui/qml/Pages2/PageProtocolAwgSettings.qml | 2 +- .../Pages2/PageProtocolOpenVpnSettings.qml | 2 +- client/ui/qml/Pages2/PageProtocolRaw.qml | 2 +- .../PageProtocolWireGuardClientSettings.qml | 2 +- .../Pages2/PageProtocolWireGuardSettings.qml | 2 +- .../qml/Pages2/PageProtocolXraySettings.qml | 2 +- .../ui/qml/Pages2/PageServiceDnsSettings.qml | 2 +- .../ui/qml/Pages2/PageServiceSftpSettings.qml | 2 +- .../Pages2/PageServiceSocksProxySettings.qml | 2 +- .../PageSettingsApiAvailableCountries.qml | 6 +- .../ui/qml/Pages2/PageSettingsApiDevices.qml | 10 +- .../Pages2/PageSettingsApiNativeConfigs.qml | 486 ++++++++--------- .../qml/Pages2/PageSettingsApiServerInfo.qml | 20 +- .../Pages2/PageSettingsApiSubscriptionKey.qml | 6 +- client/ui/qml/Pages2/PageSettingsDns.qml | 2 +- .../ui/qml/Pages2/PageSettingsServerData.qml | 10 +- .../qml/Pages2/PageSettingsServerProtocol.qml | 8 +- .../ui/qml/Pages2/PageSettingsServersList.qml | 4 +- .../qml/Pages2/PageSetupWizardCredentials.qml | 2 +- client/ui/qml/Pages2/PageSetupWizardEasy.qml | 6 +- .../qml/Pages2/PageSetupWizardInstalling.qml | 8 +- .../PageSetupWizardProtocolSettings.qml | 2 +- client/ui/qml/Pages2/PageShare.qml | 30 +- client/ui/qml/Pages2/PageShareFullAccess.qml | 6 +- client/ui/qml/Pages2/PageStart.qml | 12 +- client/ui/qml/main2.qml | 30 ++ client/vpnConnection.cpp | 49 +- client/vpnConnection.h | 2 +- 123 files changed, 3558 insertions(+), 3026 deletions(-) delete mode 100644 client/core/models/api/apiV1ServerConfig.cpp delete mode 100644 client/core/models/api/apiV1ServerConfig.h create mode 100644 client/core/models/api/legacyApiServerConfig.cpp create mode 100644 client/core/models/api/legacyApiServerConfig.h create mode 100644 client/core/models/selfhosted/selfHostedAdminServerConfig.cpp create mode 100644 client/core/models/selfhosted/selfHostedAdminServerConfig.h rename client/core/models/selfhosted/{selfHostedServerConfig.cpp => selfHostedUserServerConfig.cpp} (53%) rename client/core/models/selfhosted/{selfHostedServerConfig.h => selfHostedUserServerConfig.h} (66%) delete mode 100644 client/core/models/serverConfig.cpp delete mode 100644 client/core/models/serverConfig.h create mode 100644 client/core/models/serverDescription.cpp create mode 100644 client/core/models/serverDescription.h delete mode 100644 client/core/utils/api/apiEnums.h create mode 100644 client/core/utils/serverConfigUtils.cpp create mode 100644 client/core/utils/serverConfigUtils.h delete mode 100644 client/tests/testGatewayStacks.cpp create mode 100644 client/tests/testServerRepositoryHelpers.h diff --git a/client/cmake/sources.cmake b/client/cmake/sources.cmake index 497757757..7c5767549 100644 --- a/client/cmake/sources.cmake +++ b/client/cmake/sources.cmake @@ -15,7 +15,6 @@ set(HEADERS ${HEADERS} ${CLIENT_ROOT_DIR}/core/utils/constants/protocolConstants.h ${CLIENT_ROOT_DIR}/core/utils/constants/apiKeys.h ${CLIENT_ROOT_DIR}/core/utils/constants/apiConstants.h - ${CLIENT_ROOT_DIR}/core/utils/api/apiEnums.h ${CLIENT_ROOT_DIR}/core/utils/errorStrings.h ${CLIENT_ROOT_DIR}/core/utils/selfhosted/scriptsRegistry.h ${CLIENT_ROOT_DIR}/core/utils/qrCodeUtils.h @@ -138,6 +137,7 @@ set(SOURCES ${SOURCES} ${CLIENT_ROOT_DIR}/../common/logger/logger.cpp ${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.cpp ${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.cpp + ${CLIENT_ROOT_DIR}/core/utils/serverConfigUtils.cpp ${CLIENT_ROOT_DIR}/core/utils/osSignalHandler.cpp ${CLIENT_ROOT_DIR}/core/utils/utilities.cpp ${CLIENT_ROOT_DIR}/core/utils/managementServer.cpp diff --git a/client/core/controllers/api/newsController.cpp b/client/core/controllers/api/newsController.cpp index abc045947..4529f29f9 100644 --- a/client/core/controllers/api/newsController.cpp +++ b/client/core/controllers/api/newsController.cpp @@ -1,51 +1,93 @@ #include "newsController.h" #include "core/controllers/gatewayController.h" -#include "core/utils/api/apiEnums.h" +#include "core/repositories/secureServersRepository.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" -#include "core/utils/constants/configKeys.h" #include +#include #include #include +#include #include using namespace amnezia; -NewsController::NewsController(SecureAppSettingsRepository* appSettingsRepository, - ServersController* serversController) - : m_appSettingsRepository(appSettingsRepository), m_serversController(serversController) +NewsController::NewsController(SecureAppSettingsRepository *appSettingsRepository, + SecureServersRepository *serversRepository) + : m_appSettingsRepository(appSettingsRepository), + m_serversRepository(serversRepository) { } +QJsonObject NewsController::getServicesList() const +{ + if (!m_serversRepository) { + return {}; + } + QSet userCountryCodes; + QSet serviceTypes; + const QVector ids = m_serversRepository->orderedServerIds(); + for (const QString &id : ids) { + const auto apiV2 = m_serversRepository->apiV2Config(id); + if (!apiV2.has_value()) { + continue; + } + if (!apiV2->apiConfig.userCountryCode.isEmpty()) { + userCountryCodes.insert(apiV2->apiConfig.userCountryCode); + } + const QString serviceType = apiV2->serviceType(); + if (!serviceType.isEmpty()) { + serviceTypes.insert(serviceType); + } + } + if (userCountryCodes.isEmpty() && serviceTypes.isEmpty()) { + return {}; + } + QJsonObject json; + + QJsonArray userCountryCodesArray; + for (const QString &code : userCountryCodes) { + userCountryCodesArray.append(code); + } + json[apiDefs::key::userCountryCode] = userCountryCodesArray; + + QJsonArray serviceTypesArray; + for (const QString &type : serviceTypes) { + serviceTypesArray.append(type); + } + json[apiDefs::key::serviceType] = serviceTypesArray; + + return json; +} + QFuture> NewsController::fetchNews() { - if (!m_serversController) { - qWarning() << "ServersController is null, skip fetchNews"; + if (!m_serversRepository) { + qWarning() << "SecureServersRepository is null, skip fetchNews"; return QtFuture::makeReadyFuture(qMakePair(ErrorCode::InternalError, QJsonArray())); } - - const auto stacks = m_serversController->gatewayStacks(); - if (stacks.isEmpty()) { + + const QJsonObject services = getServicesList(); + if (services.isEmpty()) { qDebug() << "No Gateway stacks, skip fetchNews"; return QtFuture::makeReadyFuture(qMakePair(ErrorCode::NoError, QJsonArray())); } auto gatewayController = QSharedPointer::create( - m_appSettingsRepository->getGatewayEndpoint(), - m_appSettingsRepository->isDevGatewayEnv(), - apiDefs::requestTimeoutMsecs, - m_appSettingsRepository->isStrictKillSwitchEnabled()); - + m_appSettingsRepository->getGatewayEndpoint(), + m_appSettingsRepository->isDevGatewayEnv(), + apiDefs::requestTimeoutMsecs, + m_appSettingsRepository->isStrictKillSwitchEnabled()); + QJsonObject payload; payload.insert("locale", m_appSettingsRepository->getAppLanguage().name().split("_").first()); - const QJsonObject stacksJson = stacks.toJson(); - if (stacksJson.contains(apiDefs::key::userCountryCode)) { - payload.insert(apiDefs::key::userCountryCode, stacksJson.value(apiDefs::key::userCountryCode)); + if (services.contains(apiDefs::key::userCountryCode)) { + payload.insert(apiDefs::key::userCountryCode, services.value(apiDefs::key::userCountryCode)); } - if (stacksJson.contains(apiDefs::key::serviceType)) { - payload.insert(apiDefs::key::serviceType, stacksJson.value(apiDefs::key::serviceType)); + if (services.contains(apiDefs::key::serviceType)) { + payload.insert(apiDefs::key::serviceType, services.value(apiDefs::key::serviceType)); } auto future = gatewayController->postAsync(QString("%1v1/news"), payload); @@ -69,4 +111,3 @@ QFuture> NewsController::fetchNews() return qMakePair(ErrorCode::NoError, newsArray); }); } - diff --git a/client/core/controllers/api/newsController.h b/client/core/controllers/api/newsController.h index 15ffd67b1..42f249276 100644 --- a/client/core/controllers/api/newsController.h +++ b/client/core/controllers/api/newsController.h @@ -3,26 +3,28 @@ #include #include +#include #include #include "core/utils/errorCodes.h" #include "core/utils/routeModes.h" #include "core/utils/commonStructs.h" #include "core/repositories/secureAppSettingsRepository.h" -#include "core/controllers/serversController.h" +#include "core/repositories/secureServersRepository.h" class NewsController { public: explicit NewsController(SecureAppSettingsRepository* appSettingsRepository, - ServersController* serversController); + SecureServersRepository* serversRepository); QFuture> fetchNews(); private: + QJsonObject getServicesList() const; + SecureAppSettingsRepository* m_appSettingsRepository; - ServersController* m_serversController; + SecureServersRepository* m_serversRepository; }; #endif // NEWSCONTROLLER_H - diff --git a/client/core/controllers/api/servicesCatalogController.cpp b/client/core/controllers/api/servicesCatalogController.cpp index afdcfac6d..0d767d8de 100644 --- a/client/core/controllers/api/servicesCatalogController.cpp +++ b/client/core/controllers/api/servicesCatalogController.cpp @@ -11,7 +11,7 @@ #include #include "core/controllers/gatewayController.h" -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" #include "version.h" diff --git a/client/core/controllers/api/subscriptionController.cpp b/client/core/controllers/api/subscriptionController.cpp index ce34d4690..d7f149f43 100644 --- a/client/core/controllers/api/subscriptionController.cpp +++ b/client/core/controllers/api/subscriptionController.cpp @@ -16,7 +16,7 @@ #include "core/utils/containerEnum.h" #include "core/utils/containers/containerUtils.h" #include "core/utils/protocolEnum.h" -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" #include "core/utils/api/apiUtils.h" @@ -26,7 +26,6 @@ #include "core/utils/constants/configKeys.h" #include "core/utils/constants/protocolConstants.h" #include "version.h" -#include "core/models/serverConfig.h" #include "core/models/containerConfig.h" #include "core/models/api/apiConfig.h" @@ -196,7 +195,7 @@ void SubscriptionController::updateApiConfigInJson(QJsonObject &serverConfigJson apiConfig[apiDefs::key::serviceProtocol] = serviceProtocol; apiConfig[apiDefs::key::userCountryCode] = userCountryCode; - if (serverConfigJson.value(configKey::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) { + if (serverConfigJson.value(configKey::configVersion).toInt() == serverConfigUtils::ConfigSource::AmneziaGateway) { QJsonObject responseObj = QJsonDocument::fromJson(apiResponseBody).object(); if (responseObj.contains(apiDefs::key::supportedProtocols)) { apiConfig.insert(apiDefs::key::supportedProtocols, responseObj.value(apiDefs::key::supportedProtocols).toArray()); @@ -217,8 +216,7 @@ ErrorCode SubscriptionController::executeRequest(const QString &endpoint, const } ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCountryCode, const QString &serviceType, - const QString &serviceProtocol, const ProtocolData &protocolData, - ServerConfig &serverConfig) + const QString &serviceProtocol, const ProtocolData &protocolData) { GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString(APP_VERSION), @@ -247,20 +245,18 @@ ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCo updateApiConfigInJson(serverConfigJson, serviceType, serviceProtocol, userCountryCode, responseBody); - ServerConfig serverConfigModel = ServerConfig::fromJson(serverConfigJson); - - if (!serverConfigModel.isApiV2()) { + if (serverConfigJson.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) { return ErrorCode::InternalError; } - m_serversRepository->addServer(serverConfigModel); - serverConfig = serverConfigModel; + ApiV2ServerConfig apiV2ServerConfig = ApiV2ServerConfig::fromJson(serverConfigJson); + m_serversRepository->addServer(QString(), apiV2ServerConfig.toJson(), + serverConfigUtils::configTypeFromJson(apiV2ServerConfig.toJson())); return ErrorCode::NoError; } ErrorCode SubscriptionController::importTrialFromGateway(const QString &userCountryCode, const QString &serviceType, - const QString &serviceProtocol, const QString &email, - ServerConfig &serverConfig) + const QString &serviceProtocol, const QString &email) { const QString trimmedEmail = email.trimmed(); if (trimmedEmail.isEmpty()) { @@ -306,16 +302,19 @@ ErrorCode SubscriptionController::importTrialFromGateway(const QString &userCoun } QJsonObject configObject = QJsonDocument::fromJson(configBytes).object(); - ServerConfig serverConfigModel = ServerConfig::fromJson(configObject); - m_serversRepository->addServer(serverConfigModel); - serverConfig = serverConfigModel; + if (configObject.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) { + return ErrorCode::InternalError; + } + + ApiV2ServerConfig apiV2ServerConfig = ApiV2ServerConfig::fromJson(configObject); + m_serversRepository->addServer(QString(), apiV2ServerConfig.toJson(), + serverConfigUtils::configTypeFromJson(apiV2ServerConfig.toJson())); return ErrorCode::NoError; } ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol, const ProtocolData &protocolData, const QString &transactionId, bool isTestPurchase, - ServerConfig &serverConfig, int *duplicateServerIndex) { GatewayRequestData gatewayRequestData { QSysInfo::productType(), @@ -351,15 +350,8 @@ ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userC // Check if server with this VPN key already exists for (int i = 0; i < m_serversRepository->serversCount(); ++i) { - ServerConfig existingServerConfig = m_serversRepository->server(i); - QString existingVpnKey; - if (existingServerConfig.isApiV1()) { - const ApiV1ServerConfig* apiV1 = existingServerConfig.as(); - existingVpnKey = apiV1 ? apiV1->vpnKey() : QString(); - } else if (existingServerConfig.isApiV2()) { - const ApiV2ServerConfig* apiV2 = existingServerConfig.as(); - existingVpnKey = apiV2 ? apiV2->vpnKey() : QString(); - } + const auto apiV2 = m_serversRepository->apiV2Config(m_serversRepository->serverIdAt(i)); + QString existingVpnKey = apiV2.has_value() ? apiV2->vpnKey() : QString(); existingVpnKey.replace(QStringLiteral("vpn://"), QString()); if (!existingVpnKey.isEmpty() && existingVpnKey == normalizedKey) { if (duplicateServerIndex) { @@ -385,38 +377,28 @@ ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userC quint16 crc = qChecksum(QJsonDocument(configObject).toJson()); - ServerConfig serverConfigModel = ServerConfig::fromJson(configObject); - - if (!serverConfigModel.isApiV2()) { + if (configObject.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) { return ErrorCode::InternalError; } - ApiV2ServerConfig* apiV2 = serverConfigModel.as(); - if (!apiV2) { - return ErrorCode::InternalError; - } + ApiV2ServerConfig apiV2ServerConfig = ApiV2ServerConfig::fromJson(configObject); + ApiV2ServerConfig* apiV2 = &apiV2ServerConfig; apiV2->apiConfig.vpnKey = normalizedKey; apiV2->apiConfig.isTestPurchase = isTestPurchase; apiV2->apiConfig.isInAppPurchase = true; apiV2->apiConfig.subscriptionExpiredByServer = false; apiV2->crc = crc; - m_serversRepository->addServer(serverConfigModel); - serverConfig = serverConfigModel; + m_serversRepository->addServer(QString(), apiV2ServerConfig.toJson(), + serverConfigUtils::configTypeFromJson(apiV2ServerConfig.toJson())); return ErrorCode::NoError; } -ErrorCode SubscriptionController::updateServiceFromGateway(int serverIndex, const QString &newCountryCode, bool isConnectEvent) +ErrorCode SubscriptionController::updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, bool isConnectEvent) { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - - if (!serverConfigModel.isApiV2()) { - return ErrorCode::InternalError; - } - - const ApiV2ServerConfig* apiV2 = serverConfigModel.as(); - if (!apiV2) { + auto apiV2 = m_serversRepository->apiV2Config(serverId); + if (!apiV2.has_value()) { return ErrorCode::InternalError; } const bool isTestPurchase = apiV2->apiConfig.isTestPurchase; @@ -445,12 +427,10 @@ ErrorCode SubscriptionController::updateServiceFromGateway(int serverIndex, cons ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody, isTestPurchase); if (errorCode != ErrorCode::NoError) { if (errorCode == ErrorCode::ApiSubscriptionExpiredError && !apiV2->apiConfig.isInAppPurchase) { - ServerConfig expiredServerConfig = serverConfigModel; - ApiV2ServerConfig *expiredApiV2 = expiredServerConfig.as(); - if (expiredApiV2) { - expiredApiV2->apiConfig.subscriptionExpiredByServer = true; - m_serversRepository->editServer(serverIndex, expiredServerConfig); - } + ApiV2ServerConfig expiredApiV2 = *apiV2; + expiredApiV2.apiConfig.subscriptionExpiredByServer = true; + m_serversRepository->editServer(serverId, expiredApiV2.toJson(), + serverConfigUtils::configTypeFromJson(expiredApiV2.toJson())); } return errorCode; } @@ -463,16 +443,12 @@ ErrorCode SubscriptionController::updateServiceFromGateway(int serverIndex, cons updateApiConfigInJson(serverConfigJson, apiV2->apiConfig.serviceType, serviceProtocol, apiV2->apiConfig.userCountryCode, responseBody); - ServerConfig newServerConfigModel = ServerConfig::fromJson(serverConfigJson); - - if (!newServerConfigModel.isApiV2()) { + if (serverConfigJson.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) { return ErrorCode::InternalError; } - ApiV2ServerConfig* newApiV2 = newServerConfigModel.as(); - if (!newApiV2) { - return ErrorCode::InternalError; - } + ApiV2ServerConfig newApiV2Config = ApiV2ServerConfig::fromJson(serverConfigJson); + ApiV2ServerConfig* newApiV2 = &newApiV2Config; newApiV2->apiConfig.vpnKey = apiV2->apiConfig.vpnKey; newApiV2->apiConfig.isTestPurchase = apiV2->apiConfig.isTestPurchase; @@ -487,20 +463,15 @@ ErrorCode SubscriptionController::updateServiceFromGateway(int serverIndex, cons newApiV2->nameOverriddenByUser = true; } - m_serversRepository->editServer(serverIndex, newServerConfigModel); + m_serversRepository->editServer(serverId, newApiV2Config.toJson(), + serverConfigUtils::configTypeFromJson(newApiV2Config.toJson())); return ErrorCode::NoError; } -ErrorCode SubscriptionController::deactivateDevice(int serverIndex) +ErrorCode SubscriptionController::deactivateDevice(const QString &serverId) { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - - if (!serverConfigModel.isApiV2()) { - return ErrorCode::NoError; - } - - const ApiV2ServerConfig* apiV2 = serverConfigModel.as(); - if (!apiV2) { + auto apiV2 = m_serversRepository->apiV2Config(serverId); + if (!apiV2.has_value()) { return ErrorCode::NoError; } @@ -528,23 +499,16 @@ ErrorCode SubscriptionController::deactivateDevice(int serverIndex) return errorCode; } - serverConfigModel.visit([](auto& arg) { - arg.containers.clear(); - }); - m_serversRepository->editServer(serverIndex, serverConfigModel); + apiV2->containers.clear(); + m_serversRepository->editServer(serverId, apiV2->toJson(), + serverConfigUtils::configTypeFromJson(apiV2->toJson())); return ErrorCode::NoError; } -ErrorCode SubscriptionController::deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode) +ErrorCode SubscriptionController::deactivateExternalDevice(const QString &serverId, const QString &uuid, const QString &serverCountryCode) { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - - if (!serverConfigModel.isApiV2()) { - return ErrorCode::NoError; - } - - const ApiV2ServerConfig* apiV2 = serverConfigModel.as(); - if (!apiV2) { + auto apiV2 = m_serversRepository->apiV2Config(serverId); + if (!apiV2.has_value()) { return ErrorCode::NoError; } @@ -573,25 +537,18 @@ ErrorCode SubscriptionController::deactivateExternalDevice(int serverIndex, cons } if (uuid == m_appSettingsRepository->getInstallationUuid(true)) { - serverConfigModel.visit([](auto& arg) { - arg.containers.clear(); - }); - m_serversRepository->editServer(serverIndex, serverConfigModel); + apiV2->containers.clear(); + m_serversRepository->editServer(serverId, apiV2->toJson(), + serverConfigUtils::configTypeFromJson(apiV2->toJson())); } return ErrorCode::NoError; } -ErrorCode SubscriptionController::exportNativeConfig(int serverIndex, const QString &serverCountryCode, QString &nativeConfig) +ErrorCode SubscriptionController::exportNativeConfig(const QString &serverId, const QString &serverCountryCode, QString &nativeConfig) { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - - if (!serverConfigModel.isApiV2()) { - return ErrorCode::InternalError; - } - - const ApiV2ServerConfig* apiV2 = serverConfigModel.as(); - if (!apiV2) { + auto apiV2 = m_serversRepository->apiV2Config(serverId); + if (!apiV2.has_value()) { return ErrorCode::InternalError; } const bool isTestPurchase = apiV2->apiConfig.isTestPurchase; @@ -624,16 +581,10 @@ ErrorCode SubscriptionController::exportNativeConfig(int serverIndex, const QStr return ErrorCode::NoError; } -ErrorCode SubscriptionController::revokeNativeConfig(int serverIndex, const QString &serverCountryCode) +ErrorCode SubscriptionController::revokeNativeConfig(const QString &serverId, const QString &serverCountryCode) { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - - if (!serverConfigModel.isApiV2()) { - return ErrorCode::InternalError; - } - - const ApiV2ServerConfig* apiV2 = serverConfigModel.as(); - if (!apiV2) { + auto apiV2 = m_serversRepository->apiV2Config(serverId); + if (!apiV2.has_value()) { return ErrorCode::InternalError; } const bool isTestPurchase = apiV2->apiConfig.isTestPurchase; @@ -661,126 +612,54 @@ ErrorCode SubscriptionController::revokeNativeConfig(int serverIndex, const QStr return ErrorCode::NoError; } -ErrorCode SubscriptionController::updateServiceFromTelegram(int serverIndex) +ErrorCode SubscriptionController::prepareVpnKeyExport(const QString &serverId, QString &vpnKey) { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - - if (!serverConfigModel.isApiV1()) { - return ErrorCode::InternalError; - } - - const ApiV1ServerConfig* apiV1 = serverConfigModel.as(); - if (!apiV1) { - return ErrorCode::InternalError; - } - QString serviceProtocol = apiV1->protocol; - ProtocolData protocolData = generateProtocolData(serviceProtocol); - QString installationUuid = m_appSettingsRepository->getInstallationUuid(true); - - GatewayController gatewayController(m_appSettingsRepository->getGatewayEndpoint(), m_appSettingsRepository->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_appSettingsRepository->isStrictKillSwitchEnabled()); - - QJsonObject apiPayload; - appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload); - apiPayload[apiDefs::key::uuid] = installationUuid; - apiPayload[apiDefs::key::osVersion] = QSysInfo::productType(); - apiPayload[apiDefs::key::appVersion] = QString(APP_VERSION); - apiPayload[configKey::accessToken] = apiV1->apiKey; - apiPayload[apiDefs::key::apiEndpoint] = apiV1->apiEndpoint; - - QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/proxy_config"), apiPayload, responseBody); - if (errorCode != ErrorCode::NoError) { - return errorCode; - } - - QJsonObject serverConfigJson; - errorCode = extractServerConfigJsonFromResponse(responseBody, serviceProtocol, protocolData, serverConfigJson); - if (errorCode != ErrorCode::NoError) { - return errorCode; - } - - ServerConfig newServerConfigModel = ServerConfig::fromJson(serverConfigJson); - - if (!newServerConfigModel.isApiV1()) { - return ErrorCode::InternalError; - } - - ApiV1ServerConfig* newApiV1 = newServerConfigModel.as(); - if (!newApiV1) { - return ErrorCode::InternalError; - } - newApiV1->apiKey = apiV1->apiKey; - newApiV1->apiEndpoint = apiV1->apiEndpoint; - newApiV1->crc = apiV1->crc; - - m_serversRepository->editServer(serverIndex, newServerConfigModel); - return ErrorCode::NoError; -} - -ErrorCode SubscriptionController::prepareVpnKeyExport(int serverIndex, QString &vpnKey) -{ - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - - if (serverConfigModel.isApiV1()) { - const ApiV1ServerConfig* apiV1 = serverConfigModel.as(); - vpnKey = apiV1 ? apiV1->vpnKey() : QString(); - } else if (serverConfigModel.isApiV2()) { - ApiV2ServerConfig* apiV2 = serverConfigModel.as(); - vpnKey = apiV2 ? apiV2->vpnKey() : QString(); - if (vpnKey.isEmpty()) { - QJsonObject serverJson = serverConfigModel.toJson(); - vpnKey = apiUtils::getPremiumV2VpnKey(serverJson); - if (vpnKey.isEmpty()) { - return ErrorCode::ApiConfigEmptyError; - } - apiV2->apiConfig.vpnKey = vpnKey; - m_serversRepository->editServer(serverIndex, serverConfigModel); - } - } else { + auto apiV2 = m_serversRepository->apiV2Config(serverId); + if (!apiV2.has_value()) { return ErrorCode::ApiConfigEmptyError; } + vpnKey = apiV2->vpnKey(); + if (vpnKey.isEmpty()) { + vpnKey = apiUtils::getPremiumV2VpnKey(apiV2->toJson()); + if (vpnKey.isEmpty()) { + return ErrorCode::ApiConfigEmptyError; + } + apiV2->apiConfig.vpnKey = vpnKey; + m_serversRepository->editServer(serverId, apiV2->toJson(), + serverConfigUtils::configTypeFromJson(apiV2->toJson())); + } return ErrorCode::NoError; } -ErrorCode SubscriptionController::validateAndUpdateConfig(int serverIndex, bool hasInstalledContainers) +ErrorCode SubscriptionController::validateAndUpdateConfig(const QString &serverId, bool hasInstalledContainers) { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - - apiDefs::ConfigSource configSource; - if (serverConfigModel.isApiV1()) { - configSource = apiDefs::ConfigSource::Telegram; - } else if (serverConfigModel.isApiV2()) { - configSource = apiDefs::ConfigSource::AmneziaGateway; - } else { + if (!m_serversRepository->apiV2Config(serverId).has_value()) { return ErrorCode::NoError; } - if (configSource == apiDefs::ConfigSource::Telegram && !hasInstalledContainers) { - removeApiConfig(serverIndex); - return updateServiceFromTelegram(serverIndex); - } else if (configSource == apiDefs::ConfigSource::AmneziaGateway && !hasInstalledContainers) { - return updateServiceFromGateway(serverIndex, "", true); - } else if (configSource && isApiKeyExpired(serverIndex)) { - qDebug() << "attempt to update api config by expires_at event"; - if (configSource == apiDefs::ConfigSource::AmneziaGateway) { - return updateServiceFromGateway(serverIndex, "", true); - } else { - removeApiConfig(serverIndex); - return updateServiceFromTelegram(serverIndex); - } + if (!hasInstalledContainers) { + return updateServiceFromGateway(serverId, "", true); } + + if (isApiKeyExpired(serverId)) { + qDebug() << "attempt to update api config by expires_at event"; + return updateServiceFromGateway(serverId, "", true); + } + return ErrorCode::NoError; } -void SubscriptionController::removeApiConfig(int serverIndex) +void SubscriptionController::removeApiConfig(const QString &serverId) { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); + auto apiV2 = m_serversRepository->apiV2Config(serverId); + if (!apiV2.has_value()) { + return; + } #if defined(Q_OS_IOS) || defined(MACOS_NE) - QString description = serverConfigModel.description(); - QString hostName = serverConfigModel.hostName(); + QString description = apiV2->description; + QString hostName = apiV2->hostName; QString vpncName = QString("%1 (%2) %3") .arg(description) .arg(hostName) @@ -789,34 +668,21 @@ void SubscriptionController::removeApiConfig(int serverIndex) AmneziaVPN::removeVPNC(vpncName.toStdString()); #endif - serverConfigModel.visit([](auto& arg) { - arg.dns1.clear(); - arg.dns2.clear(); - arg.containers.clear(); - arg.hostName.clear(); - arg.defaultContainer = DockerContainer::None; - }); + apiV2->dns1.clear(); + apiV2->dns2.clear(); + apiV2->containers.clear(); + apiV2->hostName.clear(); + apiV2->defaultContainer = DockerContainer::None; + apiV2->apiConfig.publicKey = ApiConfig::PublicKeyInfo{}; - if (serverConfigModel.isApiV2()) { - ApiV2ServerConfig* apiV2 = serverConfigModel.as(); - if (apiV2) { - apiV2->apiConfig.publicKey = ApiConfig::PublicKeyInfo{}; - } - } - - m_serversRepository->editServer(serverIndex, serverConfigModel); + m_serversRepository->editServer(serverId, apiV2->toJson(), + serverConfigUtils::configTypeFromJson(apiV2->toJson())); } -bool SubscriptionController::isApiKeyExpired(int serverIndex) const +bool SubscriptionController::isApiKeyExpired(const QString &serverId) const { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - - if (!serverConfigModel.isApiV2()) { - return false; - } - - const ApiV2ServerConfig* apiV2 = serverConfigModel.as(); - if (!apiV2) { + auto apiV2 = m_serversRepository->apiV2Config(serverId); + if (!apiV2.has_value()) { return false; } const QString expiresAt = apiV2->apiConfig.publicKey.expiresAt; @@ -833,31 +699,24 @@ bool SubscriptionController::isApiKeyExpired(int serverIndex) const return false; } -void SubscriptionController::setCurrentProtocol(int serverIndex, const QString &protocolName) +void SubscriptionController::setCurrentProtocol(const QString &serverId, const QString &protocolName) { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - if (serverConfigModel.isApiV2()) { - ApiV2ServerConfig* apiV2 = serverConfigModel.as(); - if (apiV2) { - apiV2->apiConfig.serviceProtocol = protocolName; - } - m_serversRepository->editServer(serverIndex, serverConfigModel); + auto apiV2 = m_serversRepository->apiV2Config(serverId); + if (apiV2.has_value()) { + apiV2->apiConfig.serviceProtocol = protocolName; + m_serversRepository->editServer(serverId, apiV2->toJson(), + serverConfigUtils::configTypeFromJson(apiV2->toJson())); } } -bool SubscriptionController::isVlessProtocol(int serverIndex) const +bool SubscriptionController::isVlessProtocol(const QString &serverId) const { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - if (serverConfigModel.isApiV2()) { - const ApiV2ServerConfig* apiV2 = serverConfigModel.as(); - return apiV2 && apiV2->serviceProtocol() == "vless"; - } - return false; + auto apiV2 = m_serversRepository->apiV2Config(serverId); + return apiV2.has_value() && apiV2->serviceProtocol() == "vless"; } ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol, const QString &productId, - ServerConfig &serverConfig, int *duplicateServerIndex) { #if defined(Q_OS_IOS) || defined(MACOS_NE) @@ -891,13 +750,12 @@ ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCou ProtocolData protocolData = generateProtocolData(serviceProtocol); return importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData, - originalTransactionId, isTestPurchase, serverConfig, duplicateServerIndex); + originalTransactionId, isTestPurchase, duplicateServerIndex); #else Q_UNUSED(userCountryCode); Q_UNUSED(serviceType); Q_UNUSED(serviceProtocol); Q_UNUSED(productId); - Q_UNUSED(serverConfig); return ErrorCode::ApiPurchaseError; #endif } @@ -956,10 +814,9 @@ SubscriptionController::AppStoreRestoreResult SubscriptionController::processApp << "originalTransactionId =" << originalTransactionId << "productId =" << transactionProductId; ProtocolData protocolData = generateProtocolData(serviceProtocol); - ServerConfig serverConfig; int currentDuplicateServerIndex = -1; ErrorCode errorCode = importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData, - originalTransactionId, isTestPurchase, serverConfig, + originalTransactionId, isTestPurchase, ¤tDuplicateServerIndex); if (errorCode == ErrorCode::ApiConfigAlreadyAdded) { @@ -991,16 +848,10 @@ SubscriptionController::AppStoreRestoreResult SubscriptionController::processApp #endif } -ErrorCode SubscriptionController::getAccountInfo(int serverIndex, QJsonObject &accountInfo) +ErrorCode SubscriptionController::getAccountInfo(const QString &serverId, QJsonObject &accountInfo) { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - - if (!serverConfigModel.isApiV2()) { - return ErrorCode::InternalError; - } - - const ApiV2ServerConfig* apiV2 = serverConfigModel.as(); - if (!apiV2) { + auto apiV2 = m_serversRepository->apiV2Config(serverId); + if (!apiV2.has_value()) { return ErrorCode::InternalError; } bool isTestPurchase = apiV2->apiConfig.isTestPurchase; @@ -1030,20 +881,13 @@ ErrorCode SubscriptionController::getAccountInfo(int serverIndex, QJsonObject &a return ErrorCode::NoError; } -QFuture> SubscriptionController::getRenewalLink(int serverIndex) +QFuture> SubscriptionController::getRenewalLink(const QString &serverId) { auto promise = QSharedPointer>>::create(); promise->start(); - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - if (!serverConfigModel.isApiV2()) { - promise->addResult(qMakePair(ErrorCode::InternalError, QString())); - promise->finish(); - return promise->future(); - } - - const ApiV2ServerConfig *apiV2 = serverConfigModel.as(); - if (!apiV2) { + auto apiV2 = m_serversRepository->apiV2Config(serverId); + if (!apiV2.has_value()) { promise->addResult(qMakePair(ErrorCode::InternalError, QString())); promise->finish(); return promise->future(); diff --git a/client/core/controllers/api/subscriptionController.h b/client/core/controllers/api/subscriptionController.h index 2780ab1da..5b0f65da6 100644 --- a/client/core/controllers/api/subscriptionController.h +++ b/client/core/controllers/api/subscriptionController.h @@ -12,7 +12,6 @@ #include "core/utils/commonStructs.h" #include "core/repositories/secureServersRepository.h" #include "core/repositories/secureAppSettingsRepository.h" -#include "core/models/serverConfig.h" class ServersController; @@ -48,44 +47,38 @@ public: ProtocolData generateProtocolData(const QString &protocol); void appendProtocolDataToApiPayload(const QString &protocol, const ProtocolData &protocolData, QJsonObject &apiPayload); - ErrorCode fillServerConfig(const QJsonObject &serverConfigJson, ServerConfig &serverConfig); ErrorCode importServiceFromGateway(const QString &userCountryCode, const QString &serviceType, - const QString &serviceProtocol, const ProtocolData &protocolData, - ServerConfig &serverConfig); + const QString &serviceProtocol, const ProtocolData &protocolData); ErrorCode importTrialFromGateway(const QString &userCountryCode, const QString &serviceType, - const QString &serviceProtocol, const QString &email, - ServerConfig &serverConfig); + const QString &serviceProtocol, const QString &email); ErrorCode importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol, const ProtocolData &protocolData, const QString &transactionId, bool isTestPurchase, - ServerConfig &serverConfig, int *duplicateServerIndex = nullptr); - ErrorCode updateServiceFromGateway(int serverIndex, const QString &newCountryCode, bool isConnectEvent); + ErrorCode updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, bool isConnectEvent); - ErrorCode deactivateDevice(int serverIndex); + ErrorCode deactivateDevice(const QString &serverId); - ErrorCode deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode); + ErrorCode deactivateExternalDevice(const QString &serverId, const QString &uuid, const QString &serverCountryCode); - ErrorCode exportNativeConfig(int serverIndex, const QString &serverCountryCode, QString &nativeConfig); + ErrorCode exportNativeConfig(const QString &serverId, const QString &serverCountryCode, QString &nativeConfig); - ErrorCode revokeNativeConfig(int serverIndex, const QString &serverCountryCode); + ErrorCode revokeNativeConfig(const QString &serverId, const QString &serverCountryCode); - ErrorCode updateServiceFromTelegram(int serverIndex); + ErrorCode prepareVpnKeyExport(const QString &serverId, QString &vpnKey); - ErrorCode prepareVpnKeyExport(int serverIndex, QString &vpnKey); + ErrorCode validateAndUpdateConfig(const QString &serverId, bool hasInstalledContainers); - ErrorCode validateAndUpdateConfig(int serverIndex, bool hasInstalledContainers); + void removeApiConfig(const QString &serverId); - void removeApiConfig(int serverIndex); + void setCurrentProtocol(const QString &serverId, const QString &protocolName); + bool isVlessProtocol(const QString &serverId) const; - void setCurrentProtocol(int serverIndex, const QString &protocolName); - bool isVlessProtocol(int serverIndex) const; - - ErrorCode getAccountInfo(int serverIndex, QJsonObject &accountInfo); - QFuture> getRenewalLink(int serverIndex); + ErrorCode getAccountInfo(const QString &serverId, QJsonObject &accountInfo); + QFuture> getRenewalLink(const QString &serverId); struct AppStoreRestoreResult { @@ -98,7 +91,6 @@ public: ErrorCode processAppStorePurchase(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol, const QString &productId, - ServerConfig &serverConfig, int *duplicateServerIndex = nullptr); AppStoreRestoreResult processAppStoreRestore(const QString &userCountryCode, const QString &serviceType, @@ -106,7 +98,7 @@ public: private: ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody, bool isTestPurchase = false); - bool isApiKeyExpired(int serverIndex) const; + bool isApiKeyExpired(const QString &serverId) const; ErrorCode extractServerConfigJsonFromResponse(const QByteArray &apiResponseBody, const QString &protocol, const ProtocolData &protocolData, QJsonObject &serverConfigJson); diff --git a/client/core/controllers/connectionController.cpp b/client/core/controllers/connectionController.cpp index 122727e76..f360cf2f4 100644 --- a/client/core/controllers/connectionController.cpp +++ b/client/core/controllers/connectionController.cpp @@ -9,11 +9,11 @@ #include "core/utils/constants/protocolConstants.h" #include "core/utils/utilities.h" #include "core/utils/networkUtilities.h" +#include "core/utils/serverConfigUtils.h" #include "version.h" #include "core/utils/containerEnum.h" #include "core/utils/containers/containerUtils.h" #include "core/utils/protocolEnum.h" -#include "core/models/serverConfig.h" #include "core/models/containerConfig.h" #include "core/models/protocolConfig.h" @@ -51,7 +51,7 @@ void ConnectionController::setConnectionState(Vpn::ConnectionState state) } } -ErrorCode ConnectionController::prepareConnection(int serverIndex, +ErrorCode ConnectionController::prepareConnection(const QString &serverId, QJsonObject& vpnConfiguration, DockerContainer& container) { @@ -59,35 +59,98 @@ ErrorCode ConnectionController::prepareConnection(int serverIndex, return ErrorCode::AmneziaServiceNotRunning; } - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - container = serverConfigModel.defaultContainer(); + ContainerConfig containerConfigModel; + QPair dns; + QString hostName; + QString description; + int configVersion = 0; + bool isApiConfig = false; + + const auto kind = m_serversRepository->serverKind(serverId); + switch (kind) { + case serverConfigUtils::ConfigType::SelfHostedAdmin: { + const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId); + if (!cfg.has_value()) return ErrorCode::InternalError; + container = cfg->defaultContainer; + containerConfigModel = cfg->containerConfig(container); + dns = { cfg->dns1, cfg->dns2 }; + hostName = cfg->hostName; + description = cfg->description; + break; + } + case serverConfigUtils::ConfigType::SelfHostedUser: { + const auto cfg = m_serversRepository->selfHostedUserConfig(serverId); + if (!cfg.has_value()) return ErrorCode::InternalError; + container = cfg->defaultContainer; + containerConfigModel = cfg->containerConfig(container); + dns = { cfg->dns1, cfg->dns2 }; + hostName = cfg->hostName; + description = cfg->description; + break; + } + case serverConfigUtils::ConfigType::Native: { + const auto cfg = m_serversRepository->nativeConfig(serverId); + if (!cfg.has_value()) return ErrorCode::InternalError; + container = cfg->defaultContainer; + containerConfigModel = cfg->containerConfig(container); + dns = { cfg->dns1, cfg->dns2 }; + hostName = cfg->hostName; + description = cfg->description; + break; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV2: + case serverConfigUtils::ConfigType::AmneziaFreeV3: + case serverConfigUtils::ConfigType::ExternalPremium: { + const auto cfg = m_serversRepository->apiV2Config(serverId); + if (!cfg.has_value()) return ErrorCode::InternalError; + container = cfg->defaultContainer; + containerConfigModel = cfg->containerConfig(container); + dns = { cfg->dns1, cfg->dns2 }; + hostName = cfg->hostName; + description = cfg->description; + configVersion = serverConfigUtils::ConfigSource::AmneziaGateway; + isApiConfig = true; + break; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV1: + case serverConfigUtils::ConfigType::AmneziaFreeV2: + return ErrorCode::InternalError; + case serverConfigUtils::ConfigType::Invalid: + default: + return ErrorCode::InternalError; + } if (!isContainerSupported(container)) { return ErrorCode::NotSupportedOnThisPlatform; } + if (dns.first.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.first)) { + if (m_appSettingsRepository->useAmneziaDns()) { + dns.first = protocols::dns::amneziaDnsIp; + } else { + dns.first = m_appSettingsRepository->primaryDns(); + } + } + if (dns.second.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.second)) { + dns.second = m_appSettingsRepository->secondaryDns(); + } - ContainerConfig containerConfigModel = m_serversRepository->containerConfig(serverIndex, container); - - auto dns = serverConfigModel.getDnsPair(m_appSettingsRepository->useAmneziaDns(), - m_appSettingsRepository->primaryDns(), - m_appSettingsRepository->secondaryDns()); - - vpnConfiguration = createConnectionConfiguration(dns, serverConfigModel, containerConfigModel, container); + vpnConfiguration = createConnectionConfiguration(dns, isApiConfig, hostName, description, configVersion, + containerConfigModel, container); return ErrorCode::NoError; } -ErrorCode ConnectionController::openConnection(int serverIndex) +ErrorCode ConnectionController::openConnection(const QString &serverId) { QJsonObject vpnConfiguration; DockerContainer container; - ErrorCode errorCode = prepareConnection(serverIndex, vpnConfiguration, container); + ErrorCode errorCode = prepareConnection(serverId, vpnConfiguration, container); if (errorCode != ErrorCode::NoError) { return errorCode; } - emit openConnectionRequested(serverIndex, container, vpnConfiguration); + emit openConnectionRequested(serverId, container, vpnConfiguration); return ErrorCode::NoError; } @@ -120,7 +183,10 @@ ErrorCode ConnectionController::lastConnectionError() const } QJsonObject ConnectionController::createConnectionConfiguration(const QPair &dns, - const ServerConfig &serverConfig, + bool isApiConfig, + const QString &hostName, + const QString &description, + int configVersion, const ContainerConfig &containerConfig, DockerContainer container) { @@ -134,7 +200,7 @@ QJsonObject ConnectionController::createConnectionConfiguration(const QPairisSitesSplitTunnelingEnabled(), m_appSettingsRepository->routeMode() @@ -160,10 +226,9 @@ QJsonObject ConnectionController::createConnectionConfiguration(const QPair &dns, - const ServerConfig &serverConfig, + bool isApiConfig, + const QString &hostName, + const QString &description, + int configVersion, const ContainerConfig &containerConfig, DockerContainer container); @@ -60,7 +63,7 @@ public: signals: void connectionStateChanged(Vpn::ConnectionState state); - void openConnectionRequested(int serverIndex, DockerContainer container, const QJsonObject &vpnConfiguration); + void openConnectionRequested(const QString &serverId, DockerContainer container, const QJsonObject &vpnConfiguration); void closeConnectionRequested(); void setConnectionStateRequested(Vpn::ConnectionState state); void killSwitchModeChangedRequested(bool enabled); diff --git a/client/core/controllers/coreController.cpp b/client/core/controllers/coreController.cpp index 227850b6d..b23f98180 100644 --- a/client/core/controllers/coreController.cpp +++ b/client/core/controllers/coreController.cpp @@ -8,7 +8,6 @@ #include "core/controllers/selfhosted/installController.h" #include "core/controllers/selfhosted/importController.h" #include "core/controllers/coreSignalHandlers.h" -#include "core/models/serverConfig.h" #include "logger.h" #include "secureQSettings.h" @@ -145,7 +144,7 @@ void CoreController::initCoreControllers() m_allowedDnsController = new AllowedDnsController(m_appSettingsRepository); m_servicesCatalogController = new ServicesCatalogController(m_appSettingsRepository); m_subscriptionController = new SubscriptionController(m_serversRepository, m_appSettingsRepository); - m_newsController = new NewsController(m_appSettingsRepository, m_serversController); + m_newsController = new NewsController(m_appSettingsRepository, m_serversRepository); m_updateController = new UpdateController(m_appSettingsRepository, this); m_installController = new InstallController(m_serversRepository, m_appSettingsRepository, this); @@ -165,7 +164,7 @@ void CoreController::initControllers() setQmlContextProperty("FocusController", m_focusController); } - m_installUiController = new InstallUiController(m_installController, m_serversController, m_settingsController, m_protocolsModel, m_usersController, + m_installUiController = new InstallUiController(m_installController, m_serversController, m_settingsController, m_protocolsModel, m_usersController, m_awgConfigModel, m_wireGuardConfigModel, m_openVpnConfigModel, m_xrayConfigModel, m_torConfigModel, #ifdef Q_OS_WINDOWS m_ikev2ConfigModel, @@ -262,9 +261,12 @@ void CoreController::initSignalHandlers() { m_signalHandlers = new CoreSignalHandlers(this, this); m_signalHandlers->initAllHandlers(); - + // Trigger initial update after handlers are connected m_serversUiController->updateModel(); + if (m_serversUiController->hasServersFromGatewayApi()) { + m_apiNewsUiController->fetchNews(false); + } } void CoreController::updateTranslator(const QLocale &locale) @@ -322,11 +324,16 @@ PageController* CoreController::pageController() const void CoreController::openConnectionByIndex(int serverIndex) { + const QString serverId = + m_serversUiController ? m_serversUiController->getServerId(serverIndex) : QString(); + if (serverId.isEmpty()) { + return; + } if (m_serversModel) { m_serversModel->setProcessedServerIndex(serverIndex); } if (m_serversController) { - m_serversController->setDefaultServerIndex(serverIndex); + m_serversController->setDefaultServer(serverId); } m_connectionUiController->toggleConnection(); } diff --git a/client/core/controllers/coreController.h b/client/core/controllers/coreController.h index 0d77c5167..4b3ed2831 100644 --- a/client/core/controllers/coreController.h +++ b/client/core/controllers/coreController.h @@ -84,7 +84,6 @@ class TestDefaultServerChange; class TestServerEdgeCases; class TestSignalOrder; class TestServersModelSync; -class TestGatewayStacks; class TestComplexOperations; class TestSettingsSignals; class TestUiServersModelAndController; @@ -101,7 +100,6 @@ class CoreController : public QObject friend class TestServerEdgeCases; friend class TestSignalOrder; friend class TestServersModelSync; - friend class TestGatewayStacks; friend class TestComplexOperations; friend class TestSettingsSignals; friend class TestUiServersModelAndController; diff --git a/client/core/controllers/coreSignalHandlers.cpp b/client/core/controllers/coreSignalHandlers.cpp index 449a8af1d..934f20f6a 100644 --- a/client/core/controllers/coreSignalHandlers.cpp +++ b/client/core/controllers/coreSignalHandlers.cpp @@ -7,6 +7,7 @@ #include "core/utils/routeModes.h" #include "core/controllers/coreController.h" #include "core/repositories/secureServersRepository.h" +#include "core/utils/serverConfigUtils.h" #include "core/repositories/secureAppSettingsRepository.h" #include "vpnConnection.h" #include "ui/controllers/qml/pageController.h" @@ -65,7 +66,6 @@ void CoreSignalHandlers::initAllHandlers() initImportControllerHandler(); initApiCountryModelUpdateHandler(); initSubscriptionRefreshHandler(); - initContainerModelUpdateHandler(); initAdminConfigRevokedHandler(); initPassphraseRequestHandler(); initTranslationsUpdatedHandler(); @@ -78,6 +78,7 @@ void CoreSignalHandlers::initAllHandlers() initAllowedDnsModelUpdateHandler(); initAppSplitTunnelingModelUpdateHandler(); initPrepareConfigHandler(); + initUnsupportedConnectDrawerHandler(); initStrictKillSwitchHandler(); initAndroidSettingsHandler(); initAndroidConnectionHandler(); @@ -124,11 +125,9 @@ void CoreSignalHandlers::initInstallControllerHandler() { connect(m_coreController->m_installController, &InstallController::serverIsBusy, m_coreController->m_installUiController, &InstallUiController::serverIsBusy); connect(m_coreController->m_installUiController, &InstallUiController::cancelInstallation, m_coreController->m_installController, &InstallController::cancelInstallation); - connect(m_coreController->m_installUiController, &InstallUiController::currentContainerUpdated, m_coreController->m_connectionUiController, - &ConnectionUiController::onCurrentContainerUpdated); connect(m_coreController->m_serversUiController, &ServersUiController::processedServerIndexChanged, - m_coreController->m_installUiController, [this](int index) { - if (index >= 0) { + m_coreController->m_installUiController, [this](int serverIndex) { + if (serverIndex >= 0) { m_coreController->m_installUiController->clearProcessedServerCredentials(); } }); @@ -137,20 +136,20 @@ void CoreSignalHandlers::initInstallControllerHandler() void CoreSignalHandlers::initExportControllerHandler() { connect(m_coreController->m_exportController, &ExportController::appendClientRequested, this, - [this](int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container) { - m_coreController->m_usersController->appendClient(serverIndex, clientId, clientName, container); + [this](const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container) { + m_coreController->m_usersController->appendClient(serverId, clientId, clientName, container); }); connect(m_coreController->m_exportController, &ExportController::updateClientsRequested, this, - [this](int serverIndex, DockerContainer container) { - m_coreController->m_usersController->updateClients(serverIndex, container); + [this](const QString &serverId, DockerContainer container) { + m_coreController->m_usersController->updateClients(serverId, container); }); connect(m_coreController->m_exportController, &ExportController::revokeClientRequested, this, - [this](int serverIndex, int row, DockerContainer container) { - m_coreController->m_usersController->revokeClient(serverIndex, row, container); + [this](const QString &serverId, int row, DockerContainer container) { + m_coreController->m_usersController->revokeClient(serverId, row, container); }); connect(m_coreController->m_exportController, &ExportController::renameClientRequested, this, - [this](int serverIndex, int row, const QString &clientName, DockerContainer container) { - m_coreController->m_usersController->renameClient(serverIndex, row, clientName, container); + [this](const QString &serverId, int row, const QString &clientName, DockerContainer container) { + m_coreController->m_usersController->renameClient(serverId, row, clientName, container); }); } @@ -159,9 +158,12 @@ void CoreSignalHandlers::initImportControllerHandler() connect(m_coreController->m_importCoreController, &ImportController::importFinished, this, [this]() { if (!m_coreController->m_connectionController->isConnected()) { int newServerIndex = m_coreController->m_serversController->getServersCount() - 1; - m_coreController->m_serversController->setDefaultServerIndex(newServerIndex); + const QString serverId = m_coreController->m_serversController->getServerId(newServerIndex); + if (!serverId.isEmpty()) { + m_coreController->m_serversController->setDefaultServer(serverId); + } if (m_coreController->m_serversUiController) { - m_coreController->m_serversUiController->setProcessedServerIndex(newServerIndex); + m_coreController->m_serversUiController->setProcessedServerId(serverId); } } }); @@ -170,21 +172,18 @@ void CoreSignalHandlers::initImportControllerHandler() void CoreSignalHandlers::initApiCountryModelUpdateHandler() { connect(m_coreController->m_serversUiController, &ServersUiController::updateApiCountryModel, this, [this]() { - int processedIndex = m_coreController->m_serversUiController->getProcessedServerIndex(); - if (processedIndex < 0 || processedIndex >= m_coreController->m_serversRepository->serversCount()) { + const QString processedServerId = m_coreController->m_serversUiController->getProcessedServerId(); + if (processedServerId.isEmpty()) { return; } - ServerConfig server = m_coreController->m_serversRepository->server(processedIndex); QJsonArray availableCountries; QString serverCountryCode; - - if (server.isApiV2()) { - const ApiV2ServerConfig* apiV2 = server.as(); - if (apiV2) { - availableCountries = apiV2->apiConfig.availableCountries; - serverCountryCode = apiV2->apiConfig.serverCountryCode; - } + + const auto apiV2 = m_coreController->m_serversRepository->apiV2Config(processedServerId); + if (apiV2.has_value()) { + availableCountries = apiV2->apiConfig.availableCountries; + serverCountryCode = apiV2->apiConfig.serverCountryCode; } m_coreController->m_apiCountryModel->updateModel(availableCountries, serverCountryCode); @@ -194,18 +193,9 @@ void CoreSignalHandlers::initApiCountryModelUpdateHandler() void CoreSignalHandlers::initSubscriptionRefreshHandler() { connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::subscriptionRefreshNeeded, this, [this]() { - const int defaultServerIndex = m_coreController->m_serversController->getDefaultServerIndex(); - if (defaultServerIndex >= 0) { - m_coreController->m_subscriptionUiController->getAccountInfo(defaultServerIndex, false); - } - }); -} - -void CoreSignalHandlers::initContainerModelUpdateHandler() -{ - connect(m_coreController->m_serversController, &ServersController::gatewayStacksExpanded, this, [this]() { - if (m_coreController->m_serversUiController->hasServersFromGatewayApi()) { - m_coreController->m_apiNewsUiController->fetchNews(false); + const QString defaultServerId = m_coreController->m_serversController->getDefaultServerId(); + if (!defaultServerId.isEmpty()) { + m_coreController->m_subscriptionUiController->getAccountInfo(defaultServerId, false); } }); } @@ -213,17 +203,17 @@ void CoreSignalHandlers::initContainerModelUpdateHandler() void CoreSignalHandlers::initAdminConfigRevokedHandler() { connect(m_coreController->m_installController, &InstallController::clientRevocationRequested, this, - [this](int serverIndex, const ContainerConfig &containerConfig, DockerContainer container) { - m_coreController->m_usersController->revokeClient(serverIndex, containerConfig, container); + [this](const QString &serverId, const ContainerConfig &containerConfig, DockerContainer container) { + m_coreController->m_usersController->revokeClient(serverId, containerConfig, container); }); connect(m_coreController->m_installController, &InstallController::clientAppendRequested, this, - [this](int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container) { - m_coreController->m_usersController->appendClient(serverIndex, clientId, clientName, container); + [this](const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container) { + m_coreController->m_usersController->appendClient(serverId, clientId, clientName, container); }); - connect(m_coreController->m_usersController, &UsersController::adminConfigRevoked, m_coreController->m_serversController, - &ServersController::clearCachedProfile); + connect(m_coreController->m_usersController, &UsersController::adminConfigRevoked, m_coreController->m_installController, + &InstallController::clearCachedProfile); } void CoreSignalHandlers::initPassphraseRequestHandler() @@ -251,7 +241,8 @@ void CoreSignalHandlers::initLanguageHandler() void CoreSignalHandlers::initAutoConnectHandler() { - if (m_coreController->m_settingsUiController->isAutoConnectEnabled() && m_coreController->m_serversController->getDefaultServerIndex() >= 0) { + if (m_coreController->m_settingsUiController->isAutoConnectEnabled() + && !m_coreController->m_serversController->getDefaultServerId().isEmpty()) { QTimer::singleShot(1000, this, [this]() { m_coreController->m_connectionUiController->openConnection(); }); } } @@ -271,16 +262,20 @@ void CoreSignalHandlers::initServersModelUpdateHandler() m_coreController->m_serversUiController, &ServersUiController::updateModel); connect(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged, m_coreController->m_serversUiController, &ServersUiController::onDefaultServerChanged); - - connect(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded, - m_coreController->m_serversController, &ServersController::recomputeGatewayStacks); - connect(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited, - m_coreController->m_serversController, &ServersController::recomputeGatewayStacks); - connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved, - m_coreController->m_serversController, &ServersController::recomputeGatewayStacks); - - connect(m_coreController->m_settingsUiController, &SettingsUiController::restoreBackupFinished, - m_coreController->m_serversUiController, &ServersUiController::updateModel); + + connect(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded, this, + [this](const QString &serverId) { + if (m_coreController->m_serversRepository->apiV2Config(serverId).has_value()) { + m_coreController->m_apiNewsUiController->fetchNews(false); + } + }); + + connect(m_coreController->m_settingsUiController, &SettingsUiController::restoreBackupFinished, this, [this]() { + m_coreController->m_serversUiController->updateModel(); + if (m_coreController->m_serversUiController->hasServersFromGatewayApi()) { + m_coreController->m_apiNewsUiController->fetchNews(false); + } + }); } void CoreSignalHandlers::initClientManagementModelUpdateHandler() @@ -315,7 +310,19 @@ void CoreSignalHandlers::initPrepareConfigHandler() connect(m_coreController->m_connectionUiController, &ConnectionUiController::prepareConfig, this, [this]() { m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Preparing); - m_coreController->m_subscriptionUiController->validateConfig(); + const QString serverId = m_coreController->m_serversController->getDefaultServerId(); + if (serverId.isEmpty()) { + m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Disconnected); + return; + } + + const serverConfigUtils::ConfigType kind = m_coreController->m_serversRepository->serverKind(serverId); + + if (serverConfigUtils::isApiV2Subscription(kind) || serverConfigUtils::isLegacyApiSubscription(kind)) { + m_coreController->m_subscriptionUiController->validateConfig(); + } else { + m_coreController->m_installUiController->validateConfig(); + } }); connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::configValidated, this, [this](bool isValid) { @@ -324,7 +331,7 @@ void CoreSignalHandlers::initPrepareConfigHandler() return; } - m_coreController->m_installUiController->validateConfig(); + m_coreController->m_connectionUiController->openConnection(); }); connect(m_coreController->m_installUiController, &InstallUiController::configValidated, this, [this](bool isValid) { @@ -337,6 +344,12 @@ void CoreSignalHandlers::initPrepareConfigHandler() }); } +void CoreSignalHandlers::initUnsupportedConnectDrawerHandler() +{ + connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::unsupportedConnectDrawerRequested, + m_coreController->m_pageController, &PageController::unsupportedConnectDrawerRequested); +} + void CoreSignalHandlers::initStrictKillSwitchHandler() { connect(m_coreController->m_settingsUiController, &SettingsUiController::strictKillSwitchEnabledChanged, m_coreController->m_connectionController, @@ -348,7 +361,10 @@ void CoreSignalHandlers::initAndroidSettingsHandler() #ifdef Q_OS_ANDROID connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs); connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled); - connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer); + connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved, this, + [](const QString &/*serverId*/, int removedIndex) { + AndroidController::instance()->resetLastServer(removedIndex); + }); connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); }); #endif } diff --git a/client/core/controllers/coreSignalHandlers.h b/client/core/controllers/coreSignalHandlers.h index 51f1d2f1d..2f5d59976 100644 --- a/client/core/controllers/coreSignalHandlers.h +++ b/client/core/controllers/coreSignalHandlers.h @@ -21,7 +21,6 @@ private: void initImportControllerHandler(); void initApiCountryModelUpdateHandler(); void initSubscriptionRefreshHandler(); - void initContainerModelUpdateHandler(); void initAdminConfigRevokedHandler(); void initPassphraseRequestHandler(); void initTranslationsUpdatedHandler(); @@ -34,6 +33,7 @@ private: void initAllowedDnsModelUpdateHandler(); void initAppSplitTunnelingModelUpdateHandler(); void initPrepareConfigHandler(); + void initUnsupportedConnectDrawerHandler(); void initStrictKillSwitchHandler(); void initAndroidSettingsHandler(); void initAndroidConnectionHandler(); diff --git a/client/core/controllers/gatewayController.cpp b/client/core/controllers/gatewayController.cpp index 23ced44f3..9b22ad6ad 100644 --- a/client/core/controllers/gatewayController.cpp +++ b/client/core/controllers/gatewayController.cpp @@ -239,7 +239,7 @@ QFuture> GatewayController::postAsync(const QString connect(reply, &QNetworkReply::sslErrors, [sslErrors](const QList &errors) { *sslErrors = errors; }); - connect(reply, &QNetworkReply::finished, reply, [promise, sslErrors, encRequestData, endpoint, apiPayload, reply, this]() mutable { + connect(reply, &QNetworkReply::finished, this, [promise, sslErrors, encRequestData, endpoint, apiPayload, reply, this]() mutable { QByteArray encryptedResponseBody = reply->readAll(); QString replyErrorString = reply->errorString(); auto replyError = reply->error(); diff --git a/client/core/controllers/selfhosted/exportController.cpp b/client/core/controllers/selfhosted/exportController.cpp index 917304c49..095b57353 100644 --- a/client/core/controllers/selfhosted/exportController.cpp +++ b/client/core/controllers/selfhosted/exportController.cpp @@ -5,14 +5,13 @@ #include "core/configurators/configuratorBase.h" #include "core/utils/selfhosted/sshSession.h" -#include "core/utils/networkUtilities.h" #include "core/utils/qrCodeUtils.h" #include "core/utils/serialization/serialization.h" #include "core/utils/protocolEnum.h" #include "core/protocols/protocolUtils.h" #include "core/utils/constants/configKeys.h" #include "core/utils/constants/protocolConstants.h" -#include "core/models/serverConfig.h" +#include "core/models/selfhosted/selfHostedAdminServerConfig.h" #include "core/models/containerConfig.h" #include "core/models/protocolConfig.h" @@ -27,18 +26,20 @@ ExportController::ExportController(SecureServersRepository* serversRepository, { } -ExportController::ExportResult ExportController::generateFullAccessConfig(int serverIndex) +ExportController::ExportResult ExportController::generateFullAccessConfig(const QString &serverId) { ExportResult result; - ServerConfig serverConfig = m_serversRepository->server(serverIndex); - serverConfig.visit([](auto& arg) { - for (auto it = arg.containers.begin(); it != arg.containers.end(); ++it) { - it.value().protocolConfig.clearClientConfig(); - } - }); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + result.errorCode = ErrorCode::InternalError; + return result; + } + for (auto it = adminConfig->containers.begin(); it != adminConfig->containers.end(); ++it) { + it.value().protocolConfig.clearClientConfig(); + } - QJsonObject serverJson = serverConfig.toJson(); + QJsonObject serverJson = adminConfig->toJson(); QByteArray compressedConfig = QJsonDocument(serverJson).toJson(); compressedConfig = qCompress(compressedConfig, 8); result.config = generateVpnUrl(compressedConfig); @@ -47,13 +48,22 @@ ExportController::ExportResult ExportController::generateFullAccessConfig(int se return result; } -ExportController::ExportResult ExportController::generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName) +ExportController::ExportResult ExportController::generateConnectionConfig(const QString &serverId, int containerIndex, const QString &clientName) { ExportResult result; DockerContainer container = static_cast(containerIndex); - ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); - ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + result.errorCode = ErrorCode::InternalError; + return result; + } + const ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + result.errorCode = ErrorCode::InternalError; + return result; + } + ContainerConfig containerConfig = adminConfig->containerConfig(container); if (ContainerUtils::containerService(container) != ServiceType::Other) { SshSession sshSession; @@ -74,35 +84,25 @@ ExportController::ExportResult ExportController::generateConnectionConfig(int se QString clientId = newProtocolConfig.clientId(); if (!clientId.isEmpty()) { - emit appendClientRequested(serverIndex, clientId, clientName, container); + emit appendClientRequested(serverId, clientId, clientName, container); } } - ServerConfig serverConfig = m_serversRepository->server(serverIndex); - serverConfig.visit([container, containerConfig](auto& arg) { - arg.containers.clear(); - arg.containers[container] = containerConfig; - arg.defaultContainer = container; - }); + const QPair dns = adminConfig->getDnsPair(m_appSettingsRepository->useAmneziaDns(), + m_appSettingsRepository->primaryDns(), + m_appSettingsRepository->secondaryDns()); - if (serverConfig.isSelfHosted()) { - SelfHostedServerConfig* selfHosted = serverConfig.as(); - if (selfHosted) { - selfHosted->userName.reset(); - selfHosted->password.reset(); - selfHosted->port.reset(); - } - } + adminConfig->containers.clear(); + adminConfig->containers[container] = containerConfig; + adminConfig->defaultContainer = container; + adminConfig->userName.clear(); + adminConfig->password.clear(); + adminConfig->port = 0; - auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(), - m_appSettingsRepository->primaryDns(), - m_appSettingsRepository->secondaryDns()); - serverConfig.visit([&dns](auto& arg) { - arg.dns1 = dns.first; - arg.dns2 = dns.second; - }); + adminConfig->dns1 = dns.first; + adminConfig->dns2 = dns.second; - QJsonObject serverJson = serverConfig.toJson(); + QJsonObject serverJson = adminConfig->toJson(); QByteArray compressedConfig = QJsonDocument(serverJson).toJson(); compressedConfig = qCompress(compressedConfig, 8); result.config = generateVpnUrl(compressedConfig); @@ -111,7 +111,7 @@ ExportController::ExportResult ExportController::generateConnectionConfig(int se return result; } -ExportController::NativeConfigResult ExportController::generateNativeConfig(int serverIndex, DockerContainer container, +ExportController::NativeConfigResult ExportController::generateNativeConfig(const QString &serverId, DockerContainer container, const ContainerConfig &containerConfig, const QString &clientName) { @@ -123,11 +123,19 @@ ExportController::NativeConfigResult ExportController::generateNativeConfig(int Proto protocol = ContainerUtils::defaultProtocol(container); - ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); - ServerConfig serverConfig = m_serversRepository->server(serverIndex); - auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(), - m_appSettingsRepository->primaryDns(), - m_appSettingsRepository->secondaryDns()); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + result.errorCode = ErrorCode::InternalError; + return result; + } + const ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + result.errorCode = ErrorCode::InternalError; + return result; + } + const QPair dns = adminConfig->getDnsPair(m_appSettingsRepository->useAmneziaDns(), + m_appSettingsRepository->primaryDns(), + m_appSettingsRepository->secondaryDns()); ContainerConfig modifiedContainerConfig = containerConfig; modifiedContainerConfig.container = container; @@ -157,20 +165,25 @@ ExportController::NativeConfigResult ExportController::generateNativeConfig(int if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg || protocol == Proto::Xray) { QString clientId = newProtocolConfig.clientId(); if (!clientId.isEmpty()) { - emit appendClientRequested(serverIndex, clientId, clientName, container); + emit appendClientRequested(serverId, clientId, clientName, container); } } return result; } -ExportController::ExportResult ExportController::generateOpenVpnConfig(int serverIndex, const QString &clientName) +ExportController::ExportResult ExportController::generateOpenVpnConfig(const QString &serverId, const QString &clientName) { ExportResult result; DockerContainer container = DockerContainer::OpenVpn; - ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + result.errorCode = ErrorCode::InternalError; + return result; + } + ContainerConfig containerConfig = adminConfig->containerConfig(container); - auto nativeResult = generateNativeConfig(serverIndex, container, containerConfig, clientName); + auto nativeResult = generateNativeConfig(serverId, container, containerConfig, clientName); if (nativeResult.errorCode != ErrorCode::NoError) { result.errorCode = nativeResult.errorCode; return result; @@ -185,13 +198,18 @@ ExportController::ExportResult ExportController::generateOpenVpnConfig(int serve return result; } -ExportController::ExportResult ExportController::generateWireGuardConfig(int serverIndex, const QString &clientName) +ExportController::ExportResult ExportController::generateWireGuardConfig(const QString &serverId, const QString &clientName) { ExportResult result; - ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, DockerContainer::WireGuard); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + result.errorCode = ErrorCode::InternalError; + return result; + } + ContainerConfig containerConfig = adminConfig->containerConfig(DockerContainer::WireGuard); - auto nativeResult = generateNativeConfig(serverIndex, DockerContainer::WireGuard, containerConfig, clientName); + auto nativeResult = generateNativeConfig(serverId, DockerContainer::WireGuard, containerConfig, clientName); if (nativeResult.errorCode != ErrorCode::NoError) { result.errorCode = nativeResult.errorCode; return result; @@ -206,7 +224,7 @@ ExportController::ExportResult ExportController::generateWireGuardConfig(int ser return result; } -ExportController::ExportResult ExportController::generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName) +ExportController::ExportResult ExportController::generateAwgConfig(const QString &serverId, int containerIndex, const QString &clientName) { ExportResult result; @@ -215,9 +233,14 @@ ExportController::ExportResult ExportController::generateAwgConfig(int serverInd result.errorCode = ErrorCode::InternalError; return result; } - ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + result.errorCode = ErrorCode::InternalError; + return result; + } + ContainerConfig containerConfig = adminConfig->containerConfig(container); - auto nativeResult = generateNativeConfig(serverIndex, container, containerConfig, clientName); + auto nativeResult = generateNativeConfig(serverId, container, containerConfig, clientName); if (nativeResult.errorCode != ErrorCode::NoError) { result.errorCode = nativeResult.errorCode; return result; @@ -233,13 +256,18 @@ ExportController::ExportResult ExportController::generateAwgConfig(int serverInd } -ExportController::ExportResult ExportController::generateXrayConfig(int serverIndex, const QString &clientName) +ExportController::ExportResult ExportController::generateXrayConfig(const QString &serverId, const QString &clientName) { ExportResult result; - ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, DockerContainer::Xray); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + result.errorCode = ErrorCode::InternalError; + return result; + } + ContainerConfig containerConfig = adminConfig->containerConfig(DockerContainer::Xray); - auto nativeResult = generateNativeConfig(serverIndex, DockerContainer::Xray, containerConfig, clientName); + auto nativeResult = generateNativeConfig(serverId, DockerContainer::Xray, containerConfig, clientName); if (nativeResult.errorCode != ErrorCode::NoError) { result.errorCode = nativeResult.errorCode; return result; @@ -302,22 +330,22 @@ ExportController::ExportResult ExportController::generateXrayConfig(int serverIn return result; } -void ExportController::updateClientManagementModel(int serverIndex, int containerIndex) +void ExportController::updateClientManagementModel(const QString &serverId, int containerIndex) { DockerContainer container = static_cast(containerIndex); - emit updateClientsRequested(serverIndex, container); + emit updateClientsRequested(serverId, container); } -void ExportController::revokeConfig(int row, int serverIndex, int containerIndex) +void ExportController::revokeConfig(int row, const QString &serverId, int containerIndex) { DockerContainer container = static_cast(containerIndex); - emit revokeClientRequested(serverIndex, row, container); + emit revokeClientRequested(serverId, row, container); } -void ExportController::renameClient(int row, const QString &clientName, int serverIndex, int containerIndex) +void ExportController::renameClient(int row, const QString &clientName, const QString &serverId, int containerIndex) { DockerContainer container = static_cast(containerIndex); - emit renameClientRequested(serverIndex, row, clientName, container); + emit renameClientRequested(serverId, row, clientName, container); } QString ExportController::generateVpnUrl(const QByteArray &compressedConfig) diff --git a/client/core/controllers/selfhosted/exportController.h b/client/core/controllers/selfhosted/exportController.h index 2f2648c1c..e89ef7336 100644 --- a/client/core/controllers/selfhosted/exportController.h +++ b/client/core/controllers/selfhosted/exportController.h @@ -37,23 +37,23 @@ public: SecureAppSettingsRepository* appSettingsRepository, QObject *parent = nullptr); - ExportResult generateFullAccessConfig(int serverIndex); - ExportResult generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName); - ExportResult generateOpenVpnConfig(int serverIndex, const QString &clientName); - ExportResult generateWireGuardConfig(int serverIndex, const QString &clientName); - ExportResult generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName); - ExportResult generateXrayConfig(int serverIndex, const QString &clientName); + ExportResult generateFullAccessConfig(const QString &serverId); + ExportResult generateConnectionConfig(const QString &serverId, int containerIndex, const QString &clientName); + ExportResult generateOpenVpnConfig(const QString &serverId, const QString &clientName); + ExportResult generateWireGuardConfig(const QString &serverId, const QString &clientName); + ExportResult generateAwgConfig(const QString &serverId, int containerIndex, const QString &clientName); + ExportResult generateXrayConfig(const QString &serverId, const QString &clientName); signals: - void appendClientRequested(int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container); - void updateClientsRequested(int serverIndex, DockerContainer container); - void revokeClientRequested(int serverIndex, int row, DockerContainer container); - void renameClientRequested(int serverIndex, int row, const QString &clientName, DockerContainer container); + void appendClientRequested(const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container); + void updateClientsRequested(const QString &serverId, DockerContainer container); + void revokeClientRequested(const QString &serverId, int row, DockerContainer container); + void renameClientRequested(const QString &serverId, int row, const QString &clientName, DockerContainer container); public slots: - void updateClientManagementModel(int serverIndex, int containerIndex); - void revokeConfig(int row, int serverIndex, int containerIndex); - void renameClient(int row, const QString &clientName, int serverIndex, int containerIndex); + void updateClientManagementModel(const QString &serverId, int containerIndex); + void revokeConfig(int row, const QString &serverId, int containerIndex); + void renameClient(int row, const QString &clientName, const QString &serverId, int containerIndex); private: struct NativeConfigResult @@ -62,7 +62,7 @@ private: QJsonObject jsonNativeConfig; }; - NativeConfigResult generateNativeConfig(int serverIndex, DockerContainer container, + NativeConfigResult generateNativeConfig(const QString &serverId, DockerContainer container, const ContainerConfig &containerConfig, const QString &clientName); diff --git a/client/core/controllers/selfhosted/importController.cpp b/client/core/controllers/selfhosted/importController.cpp index c1c7503eb..bf31e1312 100644 --- a/client/core/controllers/selfhosted/importController.cpp +++ b/client/core/controllers/selfhosted/importController.cpp @@ -16,7 +16,7 @@ #include "core/utils/containerEnum.h" #include "core/utils/containers/containerUtils.h" #include "core/utils/protocolEnum.h" -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" #include "core/utils/api/apiUtils.h" @@ -27,7 +27,6 @@ #include "core/utils/constants/configKeys.h" #include "core/utils/constants/protocolConstants.h" #include "core/utils/qrCodeUtils.h" -#include "core/models/serverConfig.h" using namespace amnezia; using namespace ProtocolUtils; @@ -208,12 +207,18 @@ ImportController::ImportResult ImportController::extractConfigFromData(const QSt case ConfigTypes::Amnezia: { result.config = QJsonDocument::fromJson(config.toUtf8()).object(); - if (apiUtils::isServerFromApi(result.config)) { + if (serverConfigUtils::isServerFromApi(result.config)) { auto apiConfig = result.config.value(apiDefs::key::apiConfig).toObject(); apiConfig[apiDefs::key::vpnKey] = data; result.config[apiDefs::key::apiConfig] = apiConfig; } + if (serverConfigUtils::isLegacyApiSubscription(serverConfigUtils::configTypeFromJson(result.config))) { + result.errorCode = ErrorCode::LegacyApiV1NotSupportedError; + result.config = {}; + return result; + } + processAmneziaConfig(result.config); if (!result.config.empty()) { checkForMaliciousStrings(result.config, result.maliciousWarningText); @@ -381,18 +386,29 @@ void ImportController::importConfig(const QJsonObject &config) credentials.secretData = config.value(configKey::password).toString(); if (credentials.isValid() || config.contains(configKey::containers)) { - ServerConfig serverConfig = ServerConfig::fromJson(config); - m_serversRepository->addServer(serverConfig); + m_serversRepository->addServer(QString(), config, serverConfigUtils::configTypeFromJson(config)); emit importFinished(); } else if (config.contains(configKey::configVersion)) { quint16 crc = qChecksum(QJsonDocument(config).toJson()); - if (m_serversRepository->hasServerWithCrc(crc)) { + bool hasServerWithCrc = false; + const QVector ids = m_serversRepository->orderedServerIds(); + for (const QString &id : ids) { + const auto apiV2 = m_serversRepository->apiV2Config(id); + if (!apiV2.has_value()) { + continue; + } + if (static_cast(apiV2->crc) == crc) { + hasServerWithCrc = true; + break; + } + } + + if (hasServerWithCrc) { emit importErrorOccurred(ErrorCode::ApiConfigAlreadyAdded, true); } else { QJsonObject configWithCrc = config; configWithCrc.insert(configKey::crc, crc); - ServerConfig serverConfig = ServerConfig::fromJson(configWithCrc); - m_serversRepository->addServer(serverConfig); + m_serversRepository->addServer(QString(), configWithCrc, serverConfigUtils::configTypeFromJson(configWithCrc)); emit importFinished(); } } else { diff --git a/client/core/controllers/selfhosted/installController.cpp b/client/core/controllers/selfhosted/installController.cpp index c862f4723..2a45ba789 100644 --- a/client/core/controllers/selfhosted/installController.cpp +++ b/client/core/controllers/selfhosted/installController.cpp @@ -33,7 +33,6 @@ #include "core/protocols/protocolUtils.h" #include "core/utils/constants/configKeys.h" #include "core/utils/constants/protocolConstants.h" -#include "core/models/serverConfig.h" #include "core/models/containerConfig.h" #include "core/models/protocols/awgProtocolConfig.h" #include "ui/models/protocols/wireguardConfigModel.h" @@ -129,15 +128,27 @@ ErrorCode InstallController::setupContainer(const ServerCredentials &credentials return startupContainerWorker(credentials, container, config, sshSession); } -ErrorCode InstallController::updateContainer(int serverIndex, DockerContainer container, const ContainerConfig &oldConfig, +ErrorCode InstallController::updateContainer(const QString &serverId, DockerContainer container, const ContainerConfig &oldConfig, ContainerConfig &newConfig) { if (!isUpdateDockerContainerRequired(container, oldConfig, newConfig)) { - m_serversRepository->setContainerConfig(serverIndex, container, newConfig); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + adminConfig->updateContainerConfig(container, newConfig); + m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin); return ErrorCode::NoError; } - ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + return ErrorCode::InternalError; + } SshSession sshSession(this); bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig); @@ -154,42 +165,51 @@ ErrorCode InstallController::updateContainer(int serverIndex, DockerContainer co } if (errorCode == ErrorCode::NoError) { - clearCachedProfile(serverIndex, container); - m_serversRepository->setContainerConfig(serverIndex, container, newConfig); + clearCachedProfile(serverId, container); + adminConfig->updateContainerConfig(container, newConfig); + m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin); } return errorCode; } -void InstallController::clearCachedProfile(int serverIndex, DockerContainer container) +void InstallController::clearCachedProfile(const QString &serverId, DockerContainer container) { if (ContainerUtils::containerService(container) == ServiceType::Other) { return; } - ContainerConfig containerConfigModel = m_serversRepository->containerConfig(serverIndex, container); - - m_serversRepository->clearLastConnectionConfig(serverIndex, container); - - emit clientRevocationRequested(serverIndex, containerConfigModel, container); -} - -ErrorCode InstallController::validateAndPrepareConfig(int serverIndex) -{ - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - - if (serverConfigModel.isApiConfig()) { - return ErrorCode::NoError; + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return; } - DockerContainer container = serverConfigModel.defaultContainer(); + adminConfig->clearCachedClientProfile(container); + const ContainerConfig containerConfigModel = adminConfig->containerConfig(container); + + m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin); + + emit clientRevocationRequested(serverId, containerConfigModel, container); +} + +ErrorCode InstallController::validateAndPrepareConfig(const QString &serverId) +{ + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + + DockerContainer container = adminConfig->defaultContainer; if (container == DockerContainer::None) { return ErrorCode::NoInstalledContainersError; } - ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container); - ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); + ContainerConfig containerConfig = adminConfig->containerConfig(container); + ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + return ErrorCode::InternalError; + } SshSession sshSession; auto isProtocolConfigExists = [](const ContainerConfig &cfg) { @@ -198,20 +218,21 @@ ErrorCode InstallController::validateAndPrepareConfig(int serverIndex) if (!isProtocolConfigExists(containerConfig)) { QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName()); - ErrorCode errorCode = processContainerForAdmin(container, containerConfig, credentials, sshSession, serverIndex, clientName); + ErrorCode errorCode = processContainerForAdmin(container, containerConfig, credentials, sshSession, serverId, clientName); if (errorCode != ErrorCode::NoError) { return errorCode; } - m_serversRepository->setContainerConfig(serverIndex, container, containerConfig); + adminConfig->updateContainerConfig(container, containerConfig); + m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin); } return ErrorCode::NoError; } -void InstallController::validateConfig(int serverIndex) +void InstallController::validateConfig(const QString &serverId) { - QFuture future = QtConcurrent::run([this, serverIndex]() { - return validateAndPrepareConfig(serverIndex); + QFuture future = QtConcurrent::run([this, serverId]() { + return validateAndPrepareConfig(serverId); }); auto *watcher = new QFutureWatcher(this); @@ -230,6 +251,21 @@ void InstallController::validateConfig(int serverIndex) watcher->setFuture(future); } +void InstallController::addEmptyServer(const ServerCredentials &credentials) +{ + SelfHostedAdminServerConfig serverConfig; + serverConfig.hostName = credentials.hostName; + serverConfig.userName = credentials.userName; + serverConfig.password = credentials.secretData; + serverConfig.port = credentials.port; + serverConfig.description = m_appSettingsRepository->nextAvailableServerName(); + serverConfig.displayName = serverConfig.description.isEmpty() ? serverConfig.hostName : serverConfig.description; + serverConfig.defaultContainer = DockerContainer::None; + + m_serversRepository->addServer(QString(), serverConfig.toJson(), + serverConfigUtils::ConfigType::SelfHostedAdmin); +} + ErrorCode InstallController::prepareContainerConfig(DockerContainer container, const ServerCredentials &credentials, ContainerConfig &containerConfig, SshSession &sshSession) { if (!ContainerUtils::isSupportedByCurrentPlatform(container)) { @@ -257,7 +293,7 @@ ErrorCode InstallController::prepareContainerConfig(DockerContainer container, c return ErrorCode::NoError; } -void InstallController::adminAppendRequested(int serverIndex, DockerContainer container, +void InstallController::adminAppendRequested(const QString &serverId, DockerContainer container, const ContainerConfig &containerConfig, const QString &clientName) { if (ContainerUtils::containerService(container) == ServiceType::Other @@ -266,13 +302,13 @@ void InstallController::adminAppendRequested(int serverIndex, DockerContainer co } QString clientId = containerConfig.protocolConfig.clientId(); if (!clientId.isEmpty()) { - emit clientAppendRequested(serverIndex, clientId, clientName, container); + emit clientAppendRequested(serverId, clientId, clientName, container); } } ErrorCode InstallController::processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig, const ServerCredentials &credentials, SshSession &sshSession, - int serverIndex, const QString &clientName) + const QString &serverId, const QString &clientName) { if (ContainerUtils::isSupportedByCurrentPlatform(container)) { ErrorCode errorCode = prepareContainerConfig(container, credentials, containerConfig, sshSession); @@ -280,7 +316,7 @@ ErrorCode InstallController::processContainerForAdmin(DockerContainer container, return errorCode; } } - adminAppendRequested(serverIndex, container, containerConfig, clientName); + adminAppendRequested(serverId, container, containerConfig, clientName); return ErrorCode::NoError; } @@ -688,9 +724,16 @@ ErrorCode InstallController::setupServerFirewall(const ServerCredentials &creden amnezia::genBaseVars(credentials, DockerContainer::None, QString(), QString()))); } -ErrorCode InstallController::rebootServer(int serverIndex) +ErrorCode InstallController::rebootServer(const QString &serverId) { - auto credentials = m_serversRepository->serverCredentials(serverIndex); + const auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + return ErrorCode::InternalError; + } SshSession sshSession(this); QString script = QString("sudo reboot"); @@ -709,27 +752,38 @@ ErrorCode InstallController::rebootServer(int serverIndex) return sshSession.runScript(credentials, script, cbReadStdOut, cbReadStdErr); } -ErrorCode InstallController::removeAllContainers(int serverIndex) +ErrorCode InstallController::removeAllContainers(const QString &serverId) { - auto credentials = m_serversRepository->serverCredentials(serverIndex); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + return ErrorCode::InternalError; + } SshSession sshSession(this); ErrorCode errorCode = sshSession.runScript(credentials, amnezia::scriptData(SharedScriptType::remove_all_containers)); if (errorCode == ErrorCode::NoError) { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - serverConfigModel.visit([](auto& arg) { - arg.containers.clear(); - arg.defaultContainer = DockerContainer::None; - }); - m_serversRepository->editServer(serverIndex, serverConfigModel); + adminConfig->containers.clear(); + adminConfig->defaultContainer = DockerContainer::None; + m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin); } return errorCode; } -ErrorCode InstallController::removeContainer(int serverIndex, DockerContainer container) +ErrorCode InstallController::removeContainer(const QString &serverId, DockerContainer container) { - auto credentials = m_serversRepository->serverCredentials(serverIndex); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + return ErrorCode::InternalError; + } SshSession sshSession(this); ErrorCode errorCode = sshSession.runScript( credentials, @@ -737,11 +791,10 @@ ErrorCode InstallController::removeContainer(int serverIndex, DockerContainer co amnezia::genBaseVars(credentials, container, QString(), QString()))); if (errorCode == ErrorCode::NoError) { - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - QMap containers = serverConfigModel.containers(); + QMap containers = adminConfig->containers; containers.remove(container); - - DockerContainer defaultContainer = serverConfigModel.defaultContainer(); + + DockerContainer defaultContainer = adminConfig->defaultContainer; if (defaultContainer == container) { if (containers.isEmpty()) { defaultContainer = DockerContainer::None; @@ -749,12 +802,10 @@ ErrorCode InstallController::removeContainer(int serverIndex, DockerContainer co defaultContainer = containers.begin().key(); } } - - serverConfigModel.visit([&containers, defaultContainer](auto& arg) { - arg.containers = containers; - arg.defaultContainer = defaultContainer; - }); - m_serversRepository->editServer(serverIndex, serverConfigModel); + + adminConfig->containers = containers; + adminConfig->defaultContainer = defaultContainer; + m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin); } return errorCode; @@ -815,9 +866,16 @@ bool InstallController::isUpdateDockerContainerRequired(DockerContainer containe return true; } -ErrorCode InstallController::scanServerForInstalledContainers(int serverIndex) +ErrorCode InstallController::scanServerForInstalledContainers(const QString &serverId) { - ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + return ErrorCode::InternalError; + } SshSession sshSession(this); QMap installedContainers; @@ -826,8 +884,7 @@ ErrorCode InstallController::scanServerForInstalledContainers(int serverIndex) return errorCode; } - ServerConfig serverConfigModel = m_serversRepository->server(serverIndex); - QMap containers = serverConfigModel.containers(); + QMap containers = adminConfig->containers; bool hasNewContainers = false; QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName()); @@ -835,29 +892,25 @@ ErrorCode InstallController::scanServerForInstalledContainers(int serverIndex) if (!containers.contains(iterator.key())) { ContainerConfig containerConfig = iterator.value(); errorCode = processContainerForAdmin(iterator.key(), containerConfig, credentials, sshSession, - serverIndex, clientName); + serverId, clientName); if (errorCode != ErrorCode::NoError) { return errorCode; } containers.insert(iterator.key(), containerConfig); hasNewContainers = true; - DockerContainer defaultContainer = serverConfigModel.defaultContainer(); + DockerContainer defaultContainer = adminConfig->defaultContainer; if (defaultContainer == DockerContainer::None && ContainerUtils::containerService(iterator.key()) != ServiceType::Other && ContainerUtils::isSupportedByCurrentPlatform(iterator.key())) { - serverConfigModel.visit([iterator](auto& arg) { - arg.defaultContainer = iterator.key(); - }); + adminConfig->defaultContainer = iterator.key(); } } } if (hasNewContainers) { - serverConfigModel.visit([&containers](auto& arg) { - arg.containers = containers; - }); - m_serversRepository->editServer(serverIndex, serverConfigModel); + adminConfig->containers = containers; + m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin); } return ErrorCode::NoError; @@ -899,7 +952,7 @@ ErrorCode InstallController::installServer(const ServerCredentials &credentials, preparedContainers.insert(container, containerConfig); } - SelfHostedServerConfig serverConfig; + SelfHostedAdminServerConfig serverConfig; serverConfig.hostName = credentials.hostName; serverConfig.userName = credentials.userName; serverConfig.password = credentials.secretData; @@ -912,21 +965,29 @@ ErrorCode InstallController::installServer(const ServerCredentials &credentials, serverConfig.defaultContainer = container; - m_serversRepository->addServer(ServerConfig(serverConfig)); + serverConfig.displayName = serverConfig.description.isEmpty() ? serverConfig.hostName : serverConfig.description; - int serverIndex = m_serversRepository->serversCount() - 1; + const QString newServerId = m_serversRepository->addServer(QString(), serverConfig.toJson(), + serverConfigUtils::ConfigType::SelfHostedAdmin); QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName()); for (auto iterator = preparedContainers.begin(); iterator != preparedContainers.end(); iterator++) { - adminAppendRequested(serverIndex, iterator.key(), iterator.value(), clientName); + adminAppendRequested(newServerId, iterator.key(), iterator.value(), clientName); } return ErrorCode::NoError; } -ErrorCode InstallController::installContainer(int serverIndex, DockerContainer container, int port, +ErrorCode InstallController::installContainer(const QString &serverId, DockerContainer container, int port, TransportProto transportProto, bool &wasContainerInstalled) { - ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + return ErrorCode::InternalError; + } SshSession sshSession(this); QMap installedContainers; @@ -949,15 +1010,17 @@ ErrorCode InstallController::installContainer(int serverIndex, DockerContainer c QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName()); for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) { - ContainerConfig existingConfigModel = m_serversRepository->containerConfig(serverIndex, iterator.key()); + ContainerConfig existingConfigModel = adminConfig->containerConfig(iterator.key()); if (existingConfigModel.container == DockerContainer::None) { ContainerConfig containerConfig = iterator.value(); errorCode = processContainerForAdmin(iterator.key(), containerConfig, credentials, sshSession, - serverIndex, clientName); + serverId, clientName); if (errorCode != ErrorCode::NoError) { return errorCode; } - m_serversRepository->setContainerConfig(serverIndex, iterator.key(), containerConfig); + adminConfig->updateContainerConfig(iterator.key(), containerConfig); + m_serversRepository->editServer(serverId, adminConfig->toJson(), + serverConfigUtils::ConfigType::SelfHostedAdmin); } } @@ -993,7 +1056,15 @@ bool InstallController::isServerAlreadyExists(const ServerCredentials &credentia { int serversCount = m_serversRepository->serversCount(); for (int i = 0; i < serversCount; i++) { - const ServerCredentials existingCredentials = m_serversRepository->serverCredentials(i); + const QString existingServerId = m_serversRepository->serverIdAt(i); + const auto adminConfig = m_serversRepository->selfHostedAdminConfig(existingServerId); + if (!adminConfig.has_value()) { + continue; + } + const ServerCredentials existingCredentials = adminConfig->credentials(); + if (!existingCredentials.isValid()) { + continue; + } if (credentials.hostName == existingCredentials.hostName && credentials.port == existingCredentials.port) { existingServerIndex = i; return true; diff --git a/client/core/controllers/selfhosted/installController.h b/client/core/controllers/selfhosted/installController.h index bf5701e27..acb61ca41 100644 --- a/client/core/controllers/selfhosted/installController.h +++ b/client/core/controllers/selfhosted/installController.h @@ -33,22 +33,22 @@ public: ~InstallController(); ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate = false); - ErrorCode updateContainer(int serverIndex, DockerContainer container, const ContainerConfig &oldConfig, ContainerConfig &newConfig); + ErrorCode updateContainer(const QString &serverId, DockerContainer container, const ContainerConfig &oldConfig, ContainerConfig &newConfig); - ErrorCode rebootServer(int serverIndex); - ErrorCode removeAllContainers(int serverIndex); - ErrorCode removeContainer(int serverIndex, DockerContainer container); + ErrorCode rebootServer(const QString &serverId); + ErrorCode removeAllContainers(const QString &serverId); + ErrorCode removeContainer(const QString &serverId, DockerContainer container); ContainerConfig generateConfig(DockerContainer container, int port, TransportProto transportProto); ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap &installedContainers, SshSession &sshSession); - ErrorCode scanServerForInstalledContainers(int serverIndex); + ErrorCode scanServerForInstalledContainers(const QString &serverId); ErrorCode installContainer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto, ContainerConfig &config); ErrorCode installServer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto, bool &wasContainerInstalled); - ErrorCode installContainer(int serverIndex, DockerContainer container, int port, TransportProto transportProto, + ErrorCode installContainer(const QString &serverId, DockerContainer container, int port, TransportProto transportProto, bool &wasContainerInstalled); bool isUpdateDockerContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig); @@ -62,11 +62,13 @@ public: void cancelInstallation(); - void clearCachedProfile(int serverIndex, DockerContainer container); + void clearCachedProfile(const QString &serverId, DockerContainer container); - ErrorCode validateAndPrepareConfig(int serverIndex); + ErrorCode validateAndPrepareConfig(const QString &serverId); - void validateConfig(int serverIndex); + void validateConfig(const QString &serverId); + + void addEmptyServer(const ServerCredentials &credentials); signals: void configValidated(bool isValid); @@ -74,8 +76,8 @@ signals: void serverIsBusy(const bool isBusy); void cancelInstallationRequested(); - void clientRevocationRequested(int serverIndex, const ContainerConfig &containerConfig, DockerContainer container); - void clientAppendRequested(int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container); + void clientRevocationRequested(const QString &serverId, const ContainerConfig &containerConfig, DockerContainer container); + void clientAppendRequested(const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container); private: ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container, SshSession &sshSession); @@ -95,9 +97,9 @@ private: ErrorCode processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig, const ServerCredentials &credentials, SshSession &sshSession, - int serverIndex, const QString &clientName); + const QString &serverId, const QString &clientName); - void adminAppendRequested(int serverIndex, DockerContainer container, + void adminAppendRequested(const QString &serverId, DockerContainer container, const ContainerConfig &containerConfig, const QString &clientName); static void updateContainerConfigAfterInstallation(DockerContainer container, ContainerConfig &containerConfig, const QString &stdOut); @@ -114,4 +116,3 @@ private: }; #endif // INSTALLCONTROLLER_H - diff --git a/client/core/controllers/selfhosted/usersController.cpp b/client/core/controllers/selfhosted/usersController.cpp index 3999f66d7..7180cbb8f 100644 --- a/client/core/controllers/selfhosted/usersController.cpp +++ b/client/core/controllers/selfhosted/usersController.cpp @@ -14,7 +14,6 @@ #include "core/protocols/protocolUtils.h" #include "core/utils/constants/configKeys.h" #include "core/utils/constants/protocolConstants.h" -#include "core/models/serverConfig.h" #include "core/models/containerConfig.h" using namespace amnezia; @@ -292,11 +291,18 @@ ErrorCode UsersController::getXrayClients(const DockerContainer container, const return error; } -ErrorCode UsersController::updateClients(int serverIndex, const DockerContainer container) +ErrorCode UsersController::updateClients(const QString &serverId, const DockerContainer container) { ErrorCode error = ErrorCode::NoError; SshSession sshSession; - ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + return ErrorCode::InternalError; + } QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable"); if (container == DockerContainer::OpenVpn) { @@ -381,20 +387,27 @@ ErrorCode UsersController::updateClients(int serverIndex, const DockerContainer } -ErrorCode UsersController::appendClient(int serverIndex, const QString &clientId, const QString &clientName, const DockerContainer container) +ErrorCode UsersController::appendClient(const QString &serverId, const QString &clientId, const QString &clientName, const DockerContainer container) { ErrorCode error = ErrorCode::NoError; SshSession sshSession; - ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + return ErrorCode::InternalError; + } - error = updateClients(serverIndex, container); + error = updateClients(serverId, container); if (error != ErrorCode::NoError) { return error; } int existingIndex = clientIndexById(clientId, m_clientsTable); if (existingIndex >= 0) { - return renameClient(serverIndex, existingIndex, clientName, container, true); + return renameClient(serverId, existingIndex, clientName, container, true); } QJsonObject client; @@ -426,7 +439,7 @@ ErrorCode UsersController::appendClient(int serverIndex, const QString &clientId return error; } -ErrorCode UsersController::renameClient(int serverIndex, const int row, const QString &clientName, +ErrorCode UsersController::renameClient(const QString &serverId, const int row, const QString &clientName, const DockerContainer container, bool addTimeStamp) { if (row < 0 || row >= m_clientsTable.size()) { @@ -434,7 +447,14 @@ ErrorCode UsersController::renameClient(int serverIndex, const int row, const QS } SshSession sshSession; - ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + return ErrorCode::InternalError; + } auto client = m_clientsTable.at(row).toObject(); auto userData = client[configKey::userData].toObject(); @@ -470,7 +490,7 @@ ErrorCode UsersController::renameClient(int serverIndex, const int row, const QS } ErrorCode UsersController::revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials, - const int serverIndex, SshSession* sshSession, QJsonArray &clientsTable) + SshSession* sshSession, QJsonArray &clientsTable) { if (row < 0 || row >= clientsTable.size()) { return ErrorCode::InternalError; @@ -689,14 +709,21 @@ ErrorCode UsersController::revokeXray(const int row, return error; } -ErrorCode UsersController::revokeClient(int serverIndex, const int index, const DockerContainer container) +ErrorCode UsersController::revokeClient(const QString &serverId, const int index, const DockerContainer container) { if (index < 0 || index >= m_clientsTable.size()) { return ErrorCode::InternalError; } SshSession sshSession; - ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + return ErrorCode::InternalError; + } QString clientId = m_clientsTable.at(index).toObject().value(configKey::clientId).toString(); ErrorCode errorCode = ErrorCode::NoError; @@ -704,7 +731,7 @@ ErrorCode UsersController::revokeClient(int serverIndex, const int index, const switch(container) { case DockerContainer::OpenVpn: { - errorCode = revokeOpenVpn(index, container, credentials, serverIndex, &sshSession, m_clientsTable); + errorCode = revokeOpenVpn(index, container, credentials, &sshSession, m_clientsTable); break; } case DockerContainer::WireGuard: @@ -724,12 +751,15 @@ ErrorCode UsersController::revokeClient(int serverIndex, const int index, const } if (errorCode == ErrorCode::NoError) { - ServerConfig serverConfig = m_serversRepository->server(serverIndex); - ContainerConfig containerCfg = m_serversRepository->containerConfig(serverIndex, container); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + ContainerConfig containerCfg = adminConfig->containerConfig(container); QString containerClientId = containerCfg.protocolConfig.clientId(); if (!clientId.isEmpty() && !containerClientId.isEmpty() && containerClientId.contains(clientId)) { - emit adminConfigRevoked(serverIndex, container); + emit adminConfigRevoked(serverId, container); } emit clientRevoked(index); @@ -739,13 +769,20 @@ ErrorCode UsersController::revokeClient(int serverIndex, const int index, const return errorCode; } -ErrorCode UsersController::revokeClient(int serverIndex, const ContainerConfig &containerConfig, const DockerContainer container) +ErrorCode UsersController::revokeClient(const QString &serverId, const ContainerConfig &containerConfig, const DockerContainer container) { SshSession sshSession; - ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex); + auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId); + if (!adminConfig.has_value()) { + return ErrorCode::InternalError; + } + ServerCredentials credentials = adminConfig->credentials(); + if (!credentials.isValid()) { + return ErrorCode::InternalError; + } ErrorCode errorCode = ErrorCode::NoError; - errorCode = updateClients(serverIndex, container); + errorCode = updateClients(serverId, container); if (errorCode != ErrorCode::NoError) { return errorCode; } @@ -778,7 +815,7 @@ ErrorCode UsersController::revokeClient(int serverIndex, const ContainerConfig & switch (container) { case DockerContainer::OpenVpn: { - errorCode = revokeOpenVpn(row, container, credentials, serverIndex, &sshSession, m_clientsTable); + errorCode = revokeOpenVpn(row, container, credentials, &sshSession, m_clientsTable); break; } case DockerContainer::WireGuard: @@ -797,7 +834,7 @@ ErrorCode UsersController::revokeClient(int serverIndex, const ContainerConfig & } if (errorCode == ErrorCode::NoError) { - emit adminConfigRevoked(serverIndex, container); + emit adminConfigRevoked(serverId, container); emit clientRevoked(row); emit clientsUpdated(m_clientsTable); } diff --git a/client/core/controllers/selfhosted/usersController.h b/client/core/controllers/selfhosted/usersController.h index 89bc94b90..b52a3f5e5 100644 --- a/client/core/controllers/selfhosted/usersController.h +++ b/client/core/controllers/selfhosted/usersController.h @@ -37,21 +37,21 @@ signals: void clientAdded(const QJsonObject &client); void clientRenamed(int row, const QString &newName); void clientRevoked(int row); - void adminConfigRevoked(int serverIndex, DockerContainer container); + void adminConfigRevoked(const QString &serverId, DockerContainer container); public slots: - ErrorCode updateClients(int serverIndex, const DockerContainer container); - ErrorCode appendClient(int serverIndex, const QString &clientId, const QString &clientName, const DockerContainer container); - ErrorCode renameClient(int serverIndex, const int row, const QString &userName, const DockerContainer container, bool addTimeStamp = false); - ErrorCode revokeClient(int serverIndex, const int index, const DockerContainer container); - ErrorCode revokeClient(int serverIndex, const ContainerConfig &containerConfig, const DockerContainer container); + ErrorCode updateClients(const QString &serverId, const DockerContainer container); + ErrorCode appendClient(const QString &serverId, const QString &clientId, const QString &clientName, const DockerContainer container); + ErrorCode renameClient(const QString &serverId, const int row, const QString &userName, const DockerContainer container, bool addTimeStamp = false); + ErrorCode revokeClient(const QString &serverId, const int index, const DockerContainer container); + ErrorCode revokeClient(const QString &serverId, const ContainerConfig &containerConfig, const DockerContainer container); private: bool isClientExists(const QString &clientId, const QJsonArray &clientsTable); int clientIndexById(const QString &clientId, const QJsonArray &clientsTable); void migration(const QByteArray &clientsTableString, QJsonArray &clientsTable); - ErrorCode revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials, const int serverIndex, + ErrorCode revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials, SshSession* sshSession, QJsonArray &clientsTable); ErrorCode revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials, SshSession* sshSession, QJsonArray &clientsTable); @@ -73,4 +73,3 @@ private: }; #endif // USERSCONTROLLER_H - diff --git a/client/core/controllers/serversController.cpp b/client/core/controllers/serversController.cpp index 1842775b8..7b406ae06 100644 --- a/client/core/controllers/serversController.cpp +++ b/client/core/controllers/serversController.cpp @@ -1,81 +1,268 @@ #include "serversController.h" -#include "core/utils/networkUtilities.h" -#include "core/utils/api/apiEnums.h" -#include "core/utils/constants/apiKeys.h" -#include "core/utils/constants/apiConstants.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/protocolEnum.h" #include "core/protocols/protocolUtils.h" #include "core/utils/constants/configKeys.h" -#include "core/utils/constants/protocolConstants.h" -#include "core/models/serverConfig.h" #include "core/models/containerConfig.h" +#include "core/models/serverDescription.h" + #if defined(Q_OS_IOS) || defined(MACOS_NE) #include #endif -ServersController::ServersController(SecureServersRepository* serversRepository, - SecureAppSettingsRepository* appSettingsRepository, - QObject *parent) +ServersController::ServersController(SecureServersRepository *serversRepository, + SecureAppSettingsRepository *appSettingsRepository, QObject *parent) : QObject(parent), m_serversRepository(serversRepository), m_appSettingsRepository(appSettingsRepository) { - recomputeGatewayStacks(); + ensureDefaultServerValid(); } -void ServersController::addServer(const ServerConfig &server) +void ServersController::ensureDefaultServerValid() { - m_serversRepository->addServer(server); -} - -void ServersController::editServer(int index, const ServerConfig &server) -{ - m_serversRepository->editServer(index, server); -} - -void ServersController::removeServer(int index) -{ - m_serversRepository->removeServer(index); -} - -void ServersController::setDefaultServerIndex(int index) -{ - m_serversRepository->setDefaultServer(index); -} - -void ServersController::setDefaultContainer(int serverIndex, DockerContainer container) -{ - m_serversRepository->setDefaultContainer(serverIndex, container); -} - -void ServersController::updateContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config) -{ - m_serversRepository->setContainerConfig(serverIndex, container, config); -} - -void ServersController::clearCachedProfile(int serverIndex, DockerContainer container) -{ - m_serversRepository->clearLastConnectionConfig(serverIndex, container); -} - -QJsonArray ServersController::getServersArray() const -{ - QJsonArray result; - QVector servers = m_serversRepository->servers(); - for (const ServerConfig& server : servers) { - result.append(server.toJson()); + if (!getServersCount()) { + return; + } + + const QString defaultId = getDefaultServerId(); + if (!defaultId.isEmpty() && indexOfServerId(defaultId) >= 0) { + return; + } + + const QString firstId = getServerId(0); + if (!firstId.isEmpty()) { + setDefaultServer(firstId); } - return result; } -QVector ServersController::getServers() const +bool ServersController::renameServer(const QString &serverId, const QString &name) { - return m_serversRepository->servers(); + const serverConfigUtils::ConfigType kind = m_serversRepository->serverKind(serverId); + switch (kind) { + case serverConfigUtils::ConfigType::SelfHostedAdmin: { + auto cfg = m_serversRepository->selfHostedAdminConfig(serverId); + if (!cfg.has_value()) return false; + cfg->description = name; + m_serversRepository->editServer(serverId, cfg->toJson(), kind); + return true; + } + case serverConfigUtils::ConfigType::SelfHostedUser: { + auto cfg = m_serversRepository->selfHostedUserConfig(serverId); + if (!cfg.has_value()) return false; + cfg->description = name; + m_serversRepository->editServer(serverId, cfg->toJson(), kind); + return true; + } + case serverConfigUtils::ConfigType::Native: { + auto cfg = m_serversRepository->nativeConfig(serverId); + if (!cfg.has_value()) return false; + cfg->description = name; + m_serversRepository->editServer(serverId, cfg->toJson(), kind); + return true; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV2: + case serverConfigUtils::ConfigType::AmneziaFreeV3: + case serverConfigUtils::ConfigType::ExternalPremium: { + auto cfg = m_serversRepository->apiV2Config(serverId); + if (!cfg.has_value()) return false; + cfg->name = name; + cfg->nameOverriddenByUser = true; + m_serversRepository->editServer(serverId, cfg->toJson(), kind); + return true; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV1: + case serverConfigUtils::ConfigType::AmneziaFreeV2: + case serverConfigUtils::ConfigType::Invalid: + default: + return false; + } } -ContainerConfig ServersController::getContainerConfig(int serverIndex, DockerContainer container) const +void ServersController::removeServer(const QString &serverId) { - return m_serversRepository->containerConfig(serverIndex, container); + m_serversRepository->removeServer(serverId); +} + +void ServersController::setDefaultServer(const QString &serverId) +{ + m_serversRepository->setDefaultServer(serverId); +} + +void ServersController::setDefaultContainer(const QString &serverId, DockerContainer container) +{ + const serverConfigUtils::ConfigType kind = m_serversRepository->serverKind(serverId); + switch (kind) { + case serverConfigUtils::ConfigType::SelfHostedAdmin: { + auto cfg = m_serversRepository->selfHostedAdminConfig(serverId); + if (!cfg.has_value()) return; + cfg->defaultContainer = container; + m_serversRepository->editServer(serverId, cfg->toJson(), kind); + return; + } + case serverConfigUtils::ConfigType::SelfHostedUser: { + auto cfg = m_serversRepository->selfHostedUserConfig(serverId); + if (!cfg.has_value()) return; + cfg->defaultContainer = container; + m_serversRepository->editServer(serverId, cfg->toJson(), kind); + return; + } + case serverConfigUtils::ConfigType::Native: { + auto cfg = m_serversRepository->nativeConfig(serverId); + if (!cfg.has_value()) return; + cfg->defaultContainer = container; + m_serversRepository->editServer(serverId, cfg->toJson(), kind); + return; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV2: + case serverConfigUtils::ConfigType::AmneziaFreeV3: + case serverConfigUtils::ConfigType::ExternalPremium: { + auto cfg = m_serversRepository->apiV2Config(serverId); + if (!cfg.has_value()) return; + cfg->defaultContainer = container; + m_serversRepository->editServer(serverId, cfg->toJson(), kind); + return; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV1: + case serverConfigUtils::ConfigType::AmneziaFreeV2: + case serverConfigUtils::ConfigType::Invalid: + default: + return; + } +} + +QVector ServersController::buildServerDescriptions(bool isAmneziaDnsEnabled) const +{ + QVector out; + const QVector ids = m_serversRepository->orderedServerIds(); + out.reserve(ids.size()); + + for (const QString &id : ids) { + ServerDescription d; + using Kind = serverConfigUtils::ConfigType; + const Kind kind = m_serversRepository->serverKind(id); + switch (kind) { + case Kind::SelfHostedAdmin: { + const auto cfg = m_serversRepository->selfHostedAdminConfig(id); + if (!cfg) { + continue; + } + d = buildServerDescription(*cfg, isAmneziaDnsEnabled); + break; + } + case Kind::SelfHostedUser: { + const auto cfg = m_serversRepository->selfHostedUserConfig(id); + if (!cfg) { + continue; + } + d = buildServerDescription(*cfg, isAmneziaDnsEnabled); + break; + } + case Kind::Native: { + const auto cfg = m_serversRepository->nativeConfig(id); + if (!cfg) { + continue; + } + d = buildServerDescription(*cfg, isAmneziaDnsEnabled); + break; + } + case Kind::AmneziaPremiumV2: + case Kind::AmneziaFreeV3: + case Kind::ExternalPremium: { + const auto cfg = m_serversRepository->apiV2Config(id); + if (!cfg) { + continue; + } + d = buildServerDescription(*cfg, isAmneziaDnsEnabled); + break; + } + case Kind::AmneziaPremiumV1: + case Kind::AmneziaFreeV2: { + const auto cfg = m_serversRepository->legacyApiConfig(id); + if (!cfg) { + continue; + } + d = buildServerDescription(*cfg, isAmneziaDnsEnabled); + break; + } + case Kind::Invalid: + default: + continue; + } + + d.serverId = id; + out.append(d); + } + return out; +} + +QMap ServersController::getServerContainersMap(const QString &serverId) const +{ + switch (m_serversRepository->serverKind(serverId)) { + case serverConfigUtils::ConfigType::SelfHostedAdmin: { + const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId); + return cfg.has_value() ? cfg->containers : QMap{}; + } + case serverConfigUtils::ConfigType::SelfHostedUser: { + const auto cfg = m_serversRepository->selfHostedUserConfig(serverId); + return cfg.has_value() ? cfg->containers : QMap{}; + } + case serverConfigUtils::ConfigType::Native: { + const auto cfg = m_serversRepository->nativeConfig(serverId); + return cfg.has_value() ? cfg->containers : QMap{}; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV2: + case serverConfigUtils::ConfigType::AmneziaFreeV3: + case serverConfigUtils::ConfigType::ExternalPremium: { + const auto cfg = m_serversRepository->apiV2Config(serverId); + return cfg.has_value() ? cfg->containers : QMap{}; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV1: + case serverConfigUtils::ConfigType::AmneziaFreeV2: { + const auto cfg = m_serversRepository->legacyApiConfig(serverId); + return cfg.has_value() ? cfg->containers : QMap{}; + } + case serverConfigUtils::ConfigType::Invalid: + default: + return {}; + } +} + +DockerContainer ServersController::getDefaultContainer(const QString &serverId) const +{ + switch (m_serversRepository->serverKind(serverId)) { + case serverConfigUtils::ConfigType::SelfHostedAdmin: { + const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId); + return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None; + } + case serverConfigUtils::ConfigType::SelfHostedUser: { + const auto cfg = m_serversRepository->selfHostedUserConfig(serverId); + return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None; + } + case serverConfigUtils::ConfigType::Native: { + const auto cfg = m_serversRepository->nativeConfig(serverId); + return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV2: + case serverConfigUtils::ConfigType::AmneziaFreeV3: + case serverConfigUtils::ConfigType::ExternalPremium: { + const auto cfg = m_serversRepository->apiV2Config(serverId); + return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV1: + case serverConfigUtils::ConfigType::AmneziaFreeV2: { + const auto cfg = m_serversRepository->legacyApiConfig(serverId); + return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None; + } + case serverConfigUtils::ConfigType::Invalid: + default: + return DockerContainer::None; + } +} + +ContainerConfig ServersController::getContainerConfig(const QString &serverId, DockerContainer container) const +{ + return getServerContainersMap(serverId).value(container); } int ServersController::getDefaultServerIndex() const @@ -83,114 +270,131 @@ int ServersController::getDefaultServerIndex() const return m_serversRepository->defaultServerIndex(); } +QString ServersController::getDefaultServerId() const +{ + return m_serversRepository->defaultServerId(); +} + int ServersController::getServersCount() const { return m_serversRepository->serversCount(); } -ServerConfig ServersController::getServerConfig(int serverIndex) const +QString ServersController::getServerId(int serverIndex) const { - return m_serversRepository->server(serverIndex); + return m_serversRepository->serverIdAt(serverIndex); } -ServerCredentials ServersController::getServerCredentials(int serverIndex) const +int ServersController::indexOfServerId(const QString &serverId) const { - return m_serversRepository->serverCredentials(serverIndex); + return m_serversRepository->indexOfServerId(serverId); } -QPair ServersController::getDnsPair(int serverIndex, bool isAmneziaDnsEnabled) const +QString ServersController::notificationDisplayName(const QString &serverId) const { - ServerConfig serverConfig = m_serversRepository->server(serverIndex); - return serverConfig.getDnsPair(isAmneziaDnsEnabled, - m_appSettingsRepository->primaryDns(), - m_appSettingsRepository->secondaryDns()); -} + if (serverId.isEmpty()) { + return {}; + } -ServersController::GatewayStacksData ServersController::gatewayStacks() const -{ - return m_gatewayStacks; -} - -void ServersController::recomputeGatewayStacks() -{ - GatewayStacksData computed; - bool hasNewTags = false; - QVector servers = m_serversRepository->servers(); - - for (const ServerConfig& serverConfig : servers) { - if (serverConfig.isApiV2()) { - const ApiV2ServerConfig* apiV2 = serverConfig.as(); - if (!apiV2) continue; - const QString userCountryCode = apiV2->apiConfig.userCountryCode; - const QString serviceType = apiV2->serviceType(); - - if (!userCountryCode.isEmpty()) { - if (!m_gatewayStacks.userCountryCodes.contains(userCountryCode)) { - hasNewTags = true; - } - computed.userCountryCodes.insert(userCountryCode); - } - - if (!serviceType.isEmpty()) { - if (!m_gatewayStacks.serviceTypes.contains(serviceType)) { - hasNewTags = true; - } - computed.serviceTypes.insert(serviceType); + using Kind = serverConfigUtils::ConfigType; + switch (m_serversRepository->serverKind(serverId)) { + case Kind::SelfHostedAdmin: { + if (const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId)) { + if (!cfg->displayName.isEmpty()) { + return cfg->displayName; } } + break; } - - m_gatewayStacks = std::move(computed); - if (hasNewTags) { - emit gatewayStacksExpanded(); - } -} - -bool ServersController::GatewayStacksData::operator==(const GatewayStacksData &other) const -{ - return userCountryCodes == other.userCountryCodes && serviceTypes == other.serviceTypes; -} - -QJsonObject ServersController::GatewayStacksData::toJson() const -{ - QJsonObject json; - - QJsonArray userCountryCodesArray; - for (const QString &code : userCountryCodes) { - userCountryCodesArray.append(code); - } - json[apiDefs::key::userCountryCode] = userCountryCodesArray; - - QJsonArray serviceTypesArray; - for (const QString &type : serviceTypes) { - serviceTypesArray.append(type); - } - json[apiDefs::key::serviceType] = serviceTypesArray; - - return json; -} - -bool ServersController::isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) const -{ - QVector servers = m_serversRepository->servers(); - for (const ServerConfig& serverConfig : servers) { - if (serverConfig.isApiV2()) { - const ApiV2ServerConfig* apiV2 = serverConfig.as(); - if (!apiV2) return false; - if (apiV2->apiConfig.userCountryCode == userCountryCode - && apiV2->serviceType() == serviceType - && apiV2->serviceProtocol() == serviceProtocol) { - return true; + case Kind::SelfHostedUser: { + if (const auto cfg = m_serversRepository->selfHostedUserConfig(serverId)) { + if (!cfg->displayName.isEmpty()) { + return cfg->displayName; } } + break; + } + case Kind::Native: { + if (const auto cfg = m_serversRepository->nativeConfig(serverId)) { + if (!cfg->displayName.isEmpty()) { + return cfg->displayName; + } + } + break; + } + case Kind::AmneziaPremiumV2: + case Kind::AmneziaFreeV3: + case Kind::ExternalPremium: { + if (const auto cfg = m_serversRepository->apiV2Config(serverId)) { + if (!cfg->displayName.isEmpty()) { + return cfg->displayName; + } + } + break; + } + case Kind::AmneziaPremiumV1: + case Kind::AmneziaFreeV2: { + if (const auto cfg = m_serversRepository->legacyApiConfig(serverId)) { + if (!cfg->displayName.isEmpty()) { + return cfg->displayName; + } + } + break; + } + default: + break; + } + + const int idx = indexOfServerId(serverId); + if (idx >= 0) { + return QString::number(idx + 1); + } + return serverId; +} + +std::optional ServersController::apiV2Config(const QString &serverId) const +{ + return m_serversRepository->apiV2Config(serverId); +} + +std::optional ServersController::selfHostedAdminConfig(const QString &serverId) const +{ + return m_serversRepository->selfHostedAdminConfig(serverId); +} + +ServerCredentials ServersController::getServerCredentials(const QString &serverId) const +{ + const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId); + if (cfg.has_value()) { + const ServerCredentials creds = cfg->credentials(); + if (creds.isValid()) { + return creds; + } + } + return ServerCredentials {}; +} + +bool ServersController::isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, + const QString &serviceProtocol) const +{ + const QVector ids = m_serversRepository->orderedServerIds(); + for (const QString &id : ids) { + const auto apiV2 = m_serversRepository->apiV2Config(id); + if (!apiV2.has_value()) { + continue; + } + if (apiV2->apiConfig.userCountryCode == userCountryCode && apiV2->serviceType() == serviceType + && apiV2->serviceProtocol() == serviceProtocol) { + return true; + } } return false; } -bool ServersController::hasInstalledContainers(int serverIndex) const +bool ServersController::hasInstalledContainers(const QString &serverId) const { - ServerConfig serverConfig = m_serversRepository->server(serverIndex); - QMap containers = serverConfig.containers(); + const QMap containers = getServerContainersMap(serverId); + for (auto it = containers.begin(); it != containers.end(); ++it) { DockerContainer container = it.key(); if (ContainerUtils::containerService(container) == ServiceType::Vpn) { @@ -203,3 +407,8 @@ bool ServersController::hasInstalledContainers(int serverIndex) const return false; } +bool ServersController::isLegacyApiV1Server(const QString &serverId) const +{ + return !serverId.isEmpty() + && serverConfigUtils::isLegacyApiSubscription(m_serversRepository->serverKind(serverId)); +} diff --git a/client/core/controllers/serversController.h b/client/core/controllers/serversController.h index 3b91e5558..e8286ed4c 100644 --- a/client/core/controllers/serversController.h +++ b/client/core/controllers/serversController.h @@ -1,11 +1,11 @@ #ifndef SERVERSCONTROLLER_H #define SERVERSCONTROLLER_H +#include + #include -#include -#include -#include #include +#include #include @@ -17,34 +17,18 @@ #include "core/utils/commonStructs.h" #include "core/repositories/secureServersRepository.h" #include "core/repositories/secureAppSettingsRepository.h" -#include "core/models/serverConfig.h" #include "core/models/containerConfig.h" +#include "core/models/serverDescription.h" class SshSession; class InstallController; using namespace amnezia; -/** - * @brief Core business logic controller for server operations - * - * This controller contains pure business logic for managing servers. - */ class ServersController : public QObject { Q_OBJECT - -public: - struct GatewayStacksData - { - QSet userCountryCodes; - QSet serviceTypes; - bool isEmpty() const { return userCountryCodes.isEmpty() && serviceTypes.isEmpty(); } - bool operator==(const GatewayStacksData &other) const; - QJsonObject toJson() const; - }; - public: explicit ServersController(SecureServersRepository* serversRepository, SecureAppSettingsRepository* appSettingsRepository = nullptr, @@ -52,44 +36,38 @@ public: ~ServersController() = default; // Server management - void addServer(const ServerConfig &server); - void editServer(int index, const ServerConfig &server); - void removeServer(int index); - void setDefaultServerIndex(int index); + bool renameServer(const QString &serverId, const QString &name); + void removeServer(const QString &serverId); + void setDefaultServer(const QString &serverId); // Container management - void setDefaultContainer(int serverIndex, DockerContainer container); - void updateContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config); - - // Cache management - void clearCachedProfile(int serverIndex, DockerContainer container); + void setDefaultContainer(const QString &serverId, DockerContainer container); // Getters - QJsonArray getServersArray() const; - QVector getServers() const; + QVector buildServerDescriptions(bool isAmneziaDnsEnabled) const; int getDefaultServerIndex() const; + QString getDefaultServerId() const; int getServersCount() const; - ServerConfig getServerConfig(int serverIndex) const; - ServerCredentials getServerCredentials(int serverIndex) const; - ContainerConfig getContainerConfig(int serverIndex, DockerContainer container) const; - QPair getDnsPair(int serverIndex, bool isAmneziaDnsEnabled) const; - - GatewayStacksData gatewayStacks() const; + QString getServerId(int serverIndex) const; + int indexOfServerId(const QString &serverId) const; + QString notificationDisplayName(const QString &serverId) const; + std::optional apiV2Config(const QString &serverId) const; + std::optional selfHostedAdminConfig(const QString &serverId) const; + ServerCredentials getServerCredentials(const QString &serverId) const; + QMap getServerContainersMap(const QString &serverId) const; + DockerContainer getDefaultContainer(const QString &serverId) const; + ContainerConfig getContainerConfig(const QString &serverId, DockerContainer container) const; // Validation bool isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) const; - bool hasInstalledContainers(int serverIndex) const; - -signals: - void gatewayStacksExpanded(); - -public slots: - void recomputeGatewayStacks(); + bool hasInstalledContainers(const QString &serverId) const; + bool isLegacyApiV1Server(const QString &serverId) const; private: + void ensureDefaultServerValid(); + SecureServersRepository* m_serversRepository; SecureAppSettingsRepository* m_appSettingsRepository; - GatewayStacksData m_gatewayStacks; }; #endif // SERVERSCONTROLLER_H diff --git a/client/core/controllers/settingsController.cpp b/client/core/controllers/settingsController.cpp index f4f2e4327..3e0aae76a 100644 --- a/client/core/controllers/settingsController.cpp +++ b/client/core/controllers/settingsController.cpp @@ -179,12 +179,9 @@ QString SettingsController::getAppVersion() const void SettingsController::clearSettings() { - int serverCount = m_serversRepository->serversCount(); - m_appSettingsRepository->clearSettings(); - - m_serversRepository->setServersArray(QJsonArray()); - m_serversRepository->setDefaultServer(0); + + m_serversRepository->clearServers(); emit siteSplitTunnelingRouteModeChanged(RouteMode::VpnOnlyForwardSites); emit siteSplitTunnelingToggled(false); diff --git a/client/core/models/api/apiConfig.h b/client/core/models/api/apiConfig.h index 05ecda478..3af65cf47 100644 --- a/client/core/models/api/apiConfig.h +++ b/client/core/models/api/apiConfig.h @@ -6,7 +6,7 @@ #include #include -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" diff --git a/client/core/models/api/apiV1ServerConfig.cpp b/client/core/models/api/apiV1ServerConfig.cpp deleted file mode 100644 index 9e4c0e6d2..000000000 --- a/client/core/models/api/apiV1ServerConfig.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "apiV1ServerConfig.h" - -#include -#include - -#include "core/utils/containerEnum.h" -#include "core/utils/containers/containerUtils.h" -#include "core/utils/protocolEnum.h" -#include "core/utils/protocolEnum.h" -#include "core/protocols/protocolUtils.h" -#include "core/utils/constants/apiKeys.h" -#include "core/utils/constants/configKeys.h" -#include "core/utils/constants/protocolConstants.h" -#include "core/utils/api/apiUtils.h" - -namespace amnezia -{ - -using namespace ContainerEnumNS; - -bool ApiV1ServerConfig::isPremium() const -{ - constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT); - return apiEndpoint.contains(premiumV1Endpoint); -} - -bool ApiV1ServerConfig::isFree() const -{ - constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT); - return apiEndpoint.contains(freeV2Endpoint); -} - -QString ApiV1ServerConfig::vpnKey() const -{ - QJsonObject json = toJson(); - return apiUtils::getPremiumV1VpnKey(json); -} - -bool ApiV1ServerConfig::hasContainers() const -{ - return !containers.isEmpty(); -} - -ContainerConfig ApiV1ServerConfig::containerConfig(DockerContainer container) const -{ - if (!containers.contains(container)) { - return ContainerConfig{}; - } - return containers.value(container); -} - -QJsonObject ApiV1ServerConfig::toJson() const -{ - QJsonObject obj; - - if (!name.isEmpty()) { - obj[configKey::name] = name; - } - if (!description.isEmpty()) { - obj[configKey::description] = description; - } - if (!protocol.isEmpty()) { - obj[apiDefs::key::protocol] = protocol; - } - if (!apiEndpoint.isEmpty()) { - obj[apiDefs::key::apiEndpoint] = apiEndpoint; - } - if (!apiKey.isEmpty()) { - obj[apiDefs::key::apiKey] = apiKey; - } - - obj[configKey::configVersion] = configVersion; - - if (!hostName.isEmpty()) { - obj[configKey::hostName] = hostName; - } - - QJsonArray containersArray; - for (auto it = containers.begin(); it != containers.end(); ++it) { - QJsonObject containerObj = it.value().toJson(); - containersArray.append(containerObj); - } - if (!containersArray.isEmpty()) { - obj[configKey::containers] = containersArray; - } - - if (defaultContainer != DockerContainer::None) { - obj[configKey::defaultContainer] = ContainerUtils::containerToString(defaultContainer); - } - - if (!dns1.isEmpty()) { - obj[configKey::dns1] = dns1; - } - if (!dns2.isEmpty()) { - obj[configKey::dns2] = dns2; - } - - if (crc > 0) { - obj[configKey::crc] = crc; - } - - return obj; -} - -ApiV1ServerConfig ApiV1ServerConfig::fromJson(const QJsonObject& json) -{ - ApiV1ServerConfig config; - - config.name = json.value(configKey::name).toString(); - config.description = json.value(configKey::description).toString(); - config.protocol = json.value(apiDefs::key::protocol).toString(); - config.apiEndpoint = json.value(apiDefs::key::apiEndpoint).toString(); - config.apiKey = json.value(apiDefs::key::apiKey).toString(); - config.configVersion = json.value(configKey::configVersion).toInt(1); - config.hostName = json.value(configKey::hostName).toString(); - - QJsonArray containersArray = json.value(configKey::containers).toArray(); - for (const QJsonValue& val : containersArray) { - QJsonObject containerObj = val.toObject(); - ContainerConfig containerConfig = ContainerConfig::fromJson(containerObj); - - QString containerStr = containerObj.value(configKey::container).toString(); - DockerContainer container = ContainerUtils::containerFromString(containerStr); - - config.containers.insert(container, containerConfig); - } - - QString defaultContainerStr = json.value(configKey::defaultContainer).toString(); - config.defaultContainer = ContainerUtils::containerFromString(defaultContainerStr); - - config.dns1 = json.value(configKey::dns1).toString(); - config.dns2 = json.value(configKey::dns2).toString(); - - config.crc = json.value(configKey::crc).toInt(0); - - return config; -} - -} // namespace amnezia - diff --git a/client/core/models/api/apiV1ServerConfig.h b/client/core/models/api/apiV1ServerConfig.h deleted file mode 100644 index be414cef5..000000000 --- a/client/core/models/api/apiV1ServerConfig.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef APIV1SERVERCONFIG_H -#define APIV1SERVERCONFIG_H - -#include -#include - -#include "core/utils/containerEnum.h" -#include "core/utils/containers/containerUtils.h" -#include "core/utils/protocolEnum.h" -#include "core/models/containerConfig.h" -#include "core/utils/api/apiEnums.h" -#include "core/utils/constants/apiKeys.h" -#include "core/utils/constants/apiConstants.h" - -namespace amnezia -{ - -using namespace ContainerEnumNS; - -struct ApiV1ServerConfig { - QString description; - QString hostName; - QMap containers; - DockerContainer defaultContainer; - QString dns1; - QString dns2; - - QString name; - QString protocol; - QString apiEndpoint; - QString apiKey; - int crc; - int configVersion; - - bool isPremium() const; - bool isFree() const; - QString vpnKey() const; - bool hasContainers() const; - ContainerConfig containerConfig(DockerContainer container) const; - QJsonObject toJson() const; - static ApiV1ServerConfig fromJson(const QJsonObject& json); -}; - -} // namespace amnezia - -#endif // APIV1SERVERCONFIG_H - diff --git a/client/core/models/api/apiV2ServerConfig.cpp b/client/core/models/api/apiV2ServerConfig.cpp index 08d6323f8..2809675e5 100644 --- a/client/core/models/api/apiV2ServerConfig.cpp +++ b/client/core/models/api/apiV2ServerConfig.cpp @@ -80,6 +80,9 @@ QJsonObject ApiV2ServerConfig::toJson() const if (!description.isEmpty()) { obj[configKey::description] = description; } + if (!displayName.isEmpty()) { + obj[configKey::displayName] = displayName; + } obj[configKey::configVersion] = configVersion; @@ -131,6 +134,7 @@ ApiV2ServerConfig ApiV2ServerConfig::fromJson(const QJsonObject& json) config.name = json.value(configKey::name).toString(); config.nameOverriddenByUser = json.value(configKey::nameOverriddenByUser).toBool(false); config.description = json.value(configKey::description).toString(); + config.displayName = json.value(configKey::displayName).toString(); config.configVersion = json.value(configKey::configVersion).toInt(2); config.hostName = json.value(configKey::hostName).toString(); @@ -163,6 +167,10 @@ ApiV2ServerConfig ApiV2ServerConfig::fromJson(const QJsonObject& json) config.authData = AuthData::fromJson(authDataObj); } + if (config.displayName.isEmpty()) { + config.displayName = config.name.isEmpty() ? config.description : config.name; + } + return config; } diff --git a/client/core/models/api/apiV2ServerConfig.h b/client/core/models/api/apiV2ServerConfig.h index e3625ae96..c2f9b8762 100644 --- a/client/core/models/api/apiV2ServerConfig.h +++ b/client/core/models/api/apiV2ServerConfig.h @@ -10,7 +10,7 @@ #include "core/models/containerConfig.h" #include "core/models/api/apiConfig.h" #include "core/models/api/authData.h" -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" @@ -21,6 +21,7 @@ using namespace ContainerEnumNS; struct ApiV2ServerConfig { QString description; + QString displayName; QString hostName; QMap containers; DockerContainer defaultContainer; diff --git a/client/core/models/api/authData.h b/client/core/models/api/authData.h index 1d2061ac9..b92ba1b86 100644 --- a/client/core/models/api/authData.h +++ b/client/core/models/api/authData.h @@ -4,7 +4,7 @@ #include #include -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" diff --git a/client/core/models/api/legacyApiServerConfig.cpp b/client/core/models/api/legacyApiServerConfig.cpp new file mode 100644 index 000000000..9d6bafff4 --- /dev/null +++ b/client/core/models/api/legacyApiServerConfig.cpp @@ -0,0 +1,43 @@ +#include "legacyApiServerConfig.h" + +#include "core/utils/constants/apiKeys.h" +#include "core/utils/constants/configKeys.h" + +namespace amnezia +{ + +bool LegacyApiServerConfig::hasContainers() const +{ + return !containers.isEmpty(); +} + +ContainerConfig LegacyApiServerConfig::containerConfig(DockerContainer container) const +{ + if (!containers.contains(container)) { + return ContainerConfig{}; + } + return containers.value(container); +} + +LegacyApiServerConfig LegacyApiServerConfig::fromJson(const QJsonObject &json) +{ + LegacyApiServerConfig config; + + config.name = json.value(configKey::name).toString(); + config.description = json.value(configKey::description).toString(); + config.displayName = json.value(configKey::displayName).toString(); + config.hostName = json.value(configKey::hostName).toString(); + + config.crc = json.value(configKey::crc).toInt(0); + + config.configVersion = json.value(configKey::configVersion).toInt(1); + config.apiEndpoint = json.value(apiDefs::key::apiEndpoint).toString(); + + if (config.displayName.isEmpty()) { + config.displayName = config.name.isEmpty() ? config.description : config.name; + } + + return config; +} + +} // namespace amnezia diff --git a/client/core/models/api/legacyApiServerConfig.h b/client/core/models/api/legacyApiServerConfig.h new file mode 100644 index 000000000..94f904981 --- /dev/null +++ b/client/core/models/api/legacyApiServerConfig.h @@ -0,0 +1,38 @@ +#ifndef LEGACYAPISERVERCONFIG_H +#define LEGACYAPISERVERCONFIG_H + +#include +#include + +#include "core/utils/containerEnum.h" +#include "core/utils/protocolEnum.h" +#include "core/models/containerConfig.h" + +namespace amnezia +{ + +using namespace ContainerEnumNS; + +struct LegacyApiServerConfig { + QString description; + QString displayName; + QString hostName; + QMap containers; + DockerContainer defaultContainer = DockerContainer::None; + QString dns1; + QString dns2; + + QString name; + int crc = 0; + + int configVersion = 0; + QString apiEndpoint; + + bool hasContainers() const; + ContainerConfig containerConfig(DockerContainer container) const; + static LegacyApiServerConfig fromJson(const QJsonObject &json); +}; + +} // namespace amnezia + +#endif // LEGACYAPISERVERCONFIG_H diff --git a/client/core/models/selfhosted/nativeServerConfig.cpp b/client/core/models/selfhosted/nativeServerConfig.cpp index 34577ac29..d8f0d80be 100644 --- a/client/core/models/selfhosted/nativeServerConfig.cpp +++ b/client/core/models/selfhosted/nativeServerConfig.cpp @@ -35,6 +35,9 @@ QJsonObject NativeServerConfig::toJson() const if (!description.isEmpty()) { obj[configKey::description] = this->description; } + if (!displayName.isEmpty()) { + obj[configKey::displayName] = displayName; + } if (!hostName.isEmpty()) { obj[configKey::hostName] = hostName; } @@ -67,6 +70,7 @@ NativeServerConfig NativeServerConfig::fromJson(const QJsonObject& json) NativeServerConfig config; config.description = json.value(configKey::description).toString(); + config.displayName = json.value(configKey::displayName).toString(); config.hostName = json.value(configKey::hostName).toString(); QJsonArray containersArray = json.value(configKey::containers).toArray(); @@ -86,6 +90,10 @@ NativeServerConfig NativeServerConfig::fromJson(const QJsonObject& json) config.dns1 = json.value(configKey::dns1).toString(); config.dns2 = json.value(configKey::dns2).toString(); + if (config.displayName.isEmpty()) { + config.displayName = config.description.isEmpty() ? config.hostName : config.description; + } + return config; } diff --git a/client/core/models/selfhosted/nativeServerConfig.h b/client/core/models/selfhosted/nativeServerConfig.h index 13982eb8b..87d11077a 100644 --- a/client/core/models/selfhosted/nativeServerConfig.h +++ b/client/core/models/selfhosted/nativeServerConfig.h @@ -16,6 +16,7 @@ using namespace ContainerEnumNS; struct NativeServerConfig { QString description; + QString displayName; QString hostName; QMap containers; DockerContainer defaultContainer; diff --git a/client/core/models/selfhosted/selfHostedAdminServerConfig.cpp b/client/core/models/selfhosted/selfHostedAdminServerConfig.cpp new file mode 100644 index 000000000..7729a3e46 --- /dev/null +++ b/client/core/models/selfhosted/selfHostedAdminServerConfig.cpp @@ -0,0 +1,170 @@ +#include "selfHostedAdminServerConfig.h" + +#include + +#include "core/protocols/protocolUtils.h" +#include "core/utils/constants/configKeys.h" +#include "core/utils/constants/protocolConstants.h" +#include "core/utils/containerEnum.h" +#include "core/utils/containers/containerUtils.h" +#include "core/utils/protocolEnum.h" +#include "core/utils/networkUtilities.h" + +namespace amnezia +{ + +using namespace ContainerEnumNS; + +bool SelfHostedAdminServerConfig::hasCredentials() const +{ + return !userName.isEmpty() && !password.isEmpty() && port > 0; +} + +bool SelfHostedAdminServerConfig::isReadOnly() const +{ + return !hasCredentials(); +} + +ServerCredentials SelfHostedAdminServerConfig::credentials() const +{ + ServerCredentials creds; + creds.hostName = hostName; + creds.userName = userName; + creds.secretData = password; + creds.port = port; + return creds; +} + +bool SelfHostedAdminServerConfig::hasContainers() const +{ + return !containers.isEmpty(); +} + +ContainerConfig SelfHostedAdminServerConfig::containerConfig(DockerContainer container) const +{ + if (!containers.contains(container)) { + return ContainerConfig{}; + } + return containers.value(container); +} + +void SelfHostedAdminServerConfig::updateContainerConfig(DockerContainer container, const ContainerConfig &config) +{ + containers[container] = config; +} + +void SelfHostedAdminServerConfig::clearCachedClientProfile(DockerContainer container) +{ + if (ContainerUtils::containerService(container) == ServiceType::Other) { + return; + } + + ContainerConfig cleared = containerConfig(container); + cleared.protocolConfig.clearClientConfig(); + containers[container] = cleared; +} + +QPair SelfHostedAdminServerConfig::getDnsPair(bool isAmneziaDnsEnabled, const QString &primaryDns, + const QString &secondaryDns) const +{ + QString d1 = dns1; + QString d2 = dns2; + const bool dnsOnServer = containers.contains(DockerContainer::Dns); + + if (d1.isEmpty() || !NetworkUtilities::checkIPv4Format(d1)) { + d1 = (isAmneziaDnsEnabled && dnsOnServer) ? protocols::dns::amneziaDnsIp : primaryDns; + } + if (d2.isEmpty() || !NetworkUtilities::checkIPv4Format(d2)) { + d2 = secondaryDns; + } + return { d1, d2 }; +} + +QJsonObject SelfHostedAdminServerConfig::toJson() const +{ + QJsonObject obj; + + if (!description.isEmpty()) { + obj[configKey::description] = this->description; + } + if (!displayName.isEmpty()) { + obj[configKey::displayName] = displayName; + } + if (!hostName.isEmpty()) { + obj[configKey::hostName] = hostName; + } + + QJsonArray containersArray; + for (auto it = containers.begin(); it != containers.end(); ++it) { + QJsonObject containerObj = it.value().toJson(); + containersArray.append(containerObj); + } + if (!containersArray.isEmpty()) { + obj[configKey::containers] = containersArray; + } + + if (defaultContainer != DockerContainer::None) { + obj[configKey::defaultContainer] = ContainerUtils::containerToString(defaultContainer); + } + + if (!dns1.isEmpty()) { + obj[configKey::dns1] = dns1; + } + if (!dns2.isEmpty()) { + obj[configKey::dns2] = dns2; + } + + if (!userName.isEmpty()) { + obj[configKey::userName] = userName; + } + if (!password.isEmpty()) { + obj[configKey::password] = password; + } + if (port > 0) { + obj[configKey::port] = port; + } + + return obj; +} + +SelfHostedAdminServerConfig SelfHostedAdminServerConfig::fromJson(const QJsonObject &json) +{ + SelfHostedAdminServerConfig config; + + config.description = json.value(configKey::description).toString(); + config.displayName = json.value(configKey::displayName).toString(); + config.hostName = json.value(configKey::hostName).toString(); + + QJsonArray containersArray = json.value(configKey::containers).toArray(); + for (const QJsonValue &val : containersArray) { + QJsonObject containerObj = val.toObject(); + ContainerConfig cc = ContainerConfig::fromJson(containerObj); + + QString containerStr = containerObj.value(configKey::container).toString(); + DockerContainer container = ContainerUtils::containerFromString(containerStr); + + config.containers.insert(container, cc); + } + + QString defaultContainerStr = json.value(configKey::defaultContainer).toString(); + config.defaultContainer = ContainerUtils::containerFromString(defaultContainerStr); + + config.dns1 = json.value(configKey::dns1).toString(); + config.dns2 = json.value(configKey::dns2).toString(); + + config.userName = json.value(configKey::userName).toString(); + config.password = json.value(configKey::password).toString(); + if (json.contains(configKey::port)) { + config.port = json.value(configKey::port).toInt(); + } else { + config.port = 0; + } + + if (config.displayName.isEmpty()) { + config.displayName = config.description.isEmpty() ? config.hostName : config.description; + } + + return config; +} + +} // namespace amnezia diff --git a/client/core/models/selfhosted/selfHostedAdminServerConfig.h b/client/core/models/selfhosted/selfHostedAdminServerConfig.h new file mode 100644 index 000000000..bd4a04716 --- /dev/null +++ b/client/core/models/selfhosted/selfHostedAdminServerConfig.h @@ -0,0 +1,53 @@ +#ifndef SELFHOSTEDADMINSERVERCONFIG_H +#define SELFHOSTEDADMINSERVERCONFIG_H + +#include +#include +#include + +#include "core/utils/containerEnum.h" +#include "core/utils/containers/containerUtils.h" +#include "core/utils/protocolEnum.h" +#include "core/models/containerConfig.h" +#include "core/utils/errorCodes.h" +#include "core/utils/routeModes.h" +#include "core/utils/commonStructs.h" + +namespace amnezia +{ + +using namespace ContainerEnumNS; + +struct SelfHostedAdminServerConfig { + QString description; + QString displayName; + QString hostName; + QMap containers; + DockerContainer defaultContainer; + QString dns1; + QString dns2; + + QString userName; + QString password; + int port = 0; + + bool hasCredentials() const; + bool isReadOnly() const; + ServerCredentials credentials() const; + bool hasContainers() const; + ContainerConfig containerConfig(DockerContainer container) const; + + void updateContainerConfig(DockerContainer container, const ContainerConfig &config); + + void clearCachedClientProfile(DockerContainer container); + + QPair getDnsPair(bool isAmneziaDnsEnabled, const QString &primaryDns, + const QString &secondaryDns) const; + + QJsonObject toJson() const; + static SelfHostedAdminServerConfig fromJson(const QJsonObject &json); +}; + +} // namespace amnezia + +#endif // SELFHOSTEDADMINSERVERCONFIG_H diff --git a/client/core/models/selfhosted/selfHostedServerConfig.cpp b/client/core/models/selfhosted/selfHostedUserServerConfig.cpp similarity index 53% rename from client/core/models/selfhosted/selfHostedServerConfig.cpp rename to client/core/models/selfhosted/selfHostedUserServerConfig.cpp index 535350936..982456ed2 100644 --- a/client/core/models/selfhosted/selfHostedServerConfig.cpp +++ b/client/core/models/selfhosted/selfHostedUserServerConfig.cpp @@ -1,53 +1,40 @@ -#include "selfHostedServerConfig.h" +#include "selfHostedUserServerConfig.h" #include -#include -#include -#include "core/utils/containerEnum.h" -#include "core/utils/containers/containerUtils.h" -#include "core/utils/protocolEnum.h" -#include "core/utils/protocolEnum.h" #include "core/protocols/protocolUtils.h" #include "core/utils/constants/configKeys.h" #include "core/utils/constants/protocolConstants.h" +#include "core/utils/containerEnum.h" +#include "core/utils/containers/containerUtils.h" +#include "core/utils/protocolEnum.h" namespace amnezia { using namespace ContainerEnumNS; -bool SelfHostedServerConfig::hasCredentials() const +bool SelfHostedUserServerConfig::hasCredentials() const { - return userName.has_value() && password.has_value() && port.has_value(); + return false; } -bool SelfHostedServerConfig::isReadOnly() const +bool SelfHostedUserServerConfig::isReadOnly() const { - return !hasCredentials(); + return true; } -std::optional SelfHostedServerConfig::credentials() const +std::optional SelfHostedUserServerConfig::credentials() const { - if (!hasCredentials()) { - return std::nullopt; - } - - ServerCredentials creds; - creds.hostName = hostName; - creds.userName = userName.value(); - creds.secretData = password.value(); - creds.port = port.value(); - - return creds; + return std::nullopt; } -bool SelfHostedServerConfig::hasContainers() const +bool SelfHostedUserServerConfig::hasContainers() const { return !containers.isEmpty(); } -ContainerConfig SelfHostedServerConfig::containerConfig(DockerContainer container) const +ContainerConfig SelfHostedUserServerConfig::containerConfig(DockerContainer container) const { if (!containers.contains(container)) { return ContainerConfig{}; @@ -55,17 +42,20 @@ ContainerConfig SelfHostedServerConfig::containerConfig(DockerContainer containe return containers.value(container); } -QJsonObject SelfHostedServerConfig::toJson() const +QJsonObject SelfHostedUserServerConfig::toJson() const { QJsonObject obj; - + if (!description.isEmpty()) { obj[configKey::description] = this->description; } + if (!displayName.isEmpty()) { + obj[configKey::displayName] = displayName; + } if (!hostName.isEmpty()) { obj[configKey::hostName] = hostName; } - + QJsonArray containersArray; for (auto it = containers.begin(); it != containers.end(); ++it) { QJsonObject containerObj = it.value().toJson(); @@ -74,67 +64,51 @@ QJsonObject SelfHostedServerConfig::toJson() const if (!containersArray.isEmpty()) { obj[configKey::containers] = containersArray; } - + if (defaultContainer != DockerContainer::None) { obj[configKey::defaultContainer] = ContainerUtils::containerToString(defaultContainer); } - + if (!dns1.isEmpty()) { obj[configKey::dns1] = dns1; } if (!dns2.isEmpty()) { obj[configKey::dns2] = dns2; } - - if (userName.has_value()) { - obj[configKey::userName] = userName.value(); - } - if (password.has_value()) { - obj[configKey::password] = password.value(); - } - if (port.has_value()) { - obj[configKey::port] = port.value(); - } - + return obj; } -SelfHostedServerConfig SelfHostedServerConfig::fromJson(const QJsonObject& json) +SelfHostedUserServerConfig SelfHostedUserServerConfig::fromJson(const QJsonObject &json) { - SelfHostedServerConfig config; - + SelfHostedUserServerConfig config; + config.description = json.value(configKey::description).toString(); + config.displayName = json.value(configKey::displayName).toString(); config.hostName = json.value(configKey::hostName).toString(); - + QJsonArray containersArray = json.value(configKey::containers).toArray(); - for (const QJsonValue& val : containersArray) { + for (const QJsonValue &val : containersArray) { QJsonObject containerObj = val.toObject(); - ContainerConfig containerConfig = ContainerConfig::fromJson(containerObj); - + ContainerConfig cc = ContainerConfig::fromJson(containerObj); + QString containerStr = containerObj.value(configKey::container).toString(); DockerContainer container = ContainerUtils::containerFromString(containerStr); - - config.containers.insert(container, containerConfig); + + config.containers.insert(container, cc); } - + QString defaultContainerStr = json.value(configKey::defaultContainer).toString(); config.defaultContainer = ContainerUtils::containerFromString(defaultContainerStr); - + config.dns1 = json.value(configKey::dns1).toString(); config.dns2 = json.value(configKey::dns2).toString(); - - if (json.contains(configKey::userName)) { - config.userName = json.value(configKey::userName).toString(); + + if (config.displayName.isEmpty()) { + config.displayName = config.description.isEmpty() ? config.hostName : config.description; } - if (json.contains(configKey::password)) { - config.password = json.value(configKey::password).toString(); - } - if (json.contains(configKey::port)) { - config.port = json.value(configKey::port).toInt(); - } - + return config; } } // namespace amnezia - diff --git a/client/core/models/selfhosted/selfHostedServerConfig.h b/client/core/models/selfhosted/selfHostedUserServerConfig.h similarity index 66% rename from client/core/models/selfhosted/selfHostedServerConfig.h rename to client/core/models/selfhosted/selfHostedUserServerConfig.h index 39dc88540..67d053a29 100644 --- a/client/core/models/selfhosted/selfHostedServerConfig.h +++ b/client/core/models/selfhosted/selfHostedUserServerConfig.h @@ -1,5 +1,5 @@ -#ifndef SELFHOSTEDSERVERCONFIG_H -#define SELFHOSTEDSERVERCONFIG_H +#ifndef SELFHOSTEDUSERSERVERCONFIG_H +#define SELFHOSTEDUSERSERVERCONFIG_H #include #include @@ -9,8 +9,6 @@ #include "core/utils/containers/containerUtils.h" #include "core/utils/protocolEnum.h" #include "core/models/containerConfig.h" -#include "core/utils/errorCodes.h" -#include "core/utils/routeModes.h" #include "core/utils/commonStructs.h" namespace amnezia @@ -18,28 +16,24 @@ namespace amnezia using namespace ContainerEnumNS; -struct SelfHostedServerConfig { +struct SelfHostedUserServerConfig { QString description; + QString displayName; QString hostName; QMap containers; DockerContainer defaultContainer; QString dns1; QString dns2; - - std::optional userName; - std::optional password; - std::optional port; - + bool hasCredentials() const; bool isReadOnly() const; std::optional credentials() const; bool hasContainers() const; ContainerConfig containerConfig(DockerContainer container) const; QJsonObject toJson() const; - static SelfHostedServerConfig fromJson(const QJsonObject& json); + static SelfHostedUserServerConfig fromJson(const QJsonObject &json); }; } // namespace amnezia -#endif // SELFHOSTEDSERVERCONFIG_H - +#endif // SELFHOSTEDUSERSERVERCONFIG_H diff --git a/client/core/models/serverConfig.cpp b/client/core/models/serverConfig.cpp deleted file mode 100644 index 7006fb07b..000000000 --- a/client/core/models/serverConfig.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include "serverConfig.h" - -#include "core/utils/api/apiUtils.h" -#include "core/utils/networkUtilities.h" -#include "core/models/selfhosted/selfHostedServerConfig.h" -#include "core/models/selfhosted/nativeServerConfig.h" -#include "core/models/api/apiV1ServerConfig.h" -#include "core/models/api/apiV2ServerConfig.h" -#include "core/utils/protocolEnum.h" -#include "core/protocols/protocolUtils.h" -#include "core/utils/constants/configKeys.h" -#include "core/utils/constants/protocolConstants.h" - -namespace amnezia -{ - -using namespace ContainerEnumNS; - -QString ServerConfig::description() const -{ - return std::visit([](const auto& v) { return v.description; }, data); -} - -QString ServerConfig::hostName() const -{ - return std::visit([](const auto& v) { return v.hostName; }, data); -} - -QString ServerConfig::displayName() const -{ - if (isApiV1()) { - const auto *apiV1 = as(); - return apiV1 ? apiV1->name : description(); - } - if (isApiV2()) { - const auto *apiV2 = as(); - return apiV2 ? apiV2->name : description(); - } - QString name = description(); - return name.isEmpty() ? hostName() : name; -} - -QMap ServerConfig::containers() const -{ - return std::visit([](const auto& v) { return v.containers; }, data); -} - -DockerContainer ServerConfig::defaultContainer() const -{ - return std::visit([](const auto& v) { return v.defaultContainer; }, data); -} - -QString ServerConfig::dns1() const -{ - return std::visit([](const auto& v) { return v.dns1; }, data); -} - -QString ServerConfig::dns2() const -{ - return std::visit([](const auto& v) { return v.dns2; }, data); -} - -bool ServerConfig::hasContainers() const -{ - return std::visit([](const auto& v) { return v.hasContainers(); }, data); -} - -ContainerConfig ServerConfig::containerConfig(DockerContainer container) const -{ - return std::visit([container](const auto& v) { return v.containerConfig(container); }, data); -} - -int ServerConfig::crc() const -{ - return std::visit([](const auto& v) -> int { - using T = std::decay_t; - if constexpr (std::is_same_v || - std::is_same_v) { - return v.crc; - } - return 0; - }, data); -} - -int ServerConfig::configVersion() const -{ - return std::visit([](const auto& v) -> int { - using T = std::decay_t; - if constexpr (std::is_same_v) { - return apiDefs::ConfigSource::Telegram; - } else if constexpr (std::is_same_v) { - return apiDefs::ConfigSource::AmneziaGateway; - } - return 0; // SelfHostedServerConfig or NativeServerConfig - }, data); -} - -bool ServerConfig::isSelfHosted() const -{ - return std::holds_alternative(data); -} - -bool ServerConfig::isNative() const -{ - return std::holds_alternative(data); -} - -bool ServerConfig::isApiV1() const -{ - return std::holds_alternative(data); -} - -bool ServerConfig::isApiV2() const -{ - return std::holds_alternative(data); -} - -bool ServerConfig::isApiConfig() const -{ - return isApiV1() || isApiV2(); -} - -QJsonObject ServerConfig::toJson() const -{ - return std::visit([](const auto& v) { return v.toJson(); }, data); -} - -ServerConfig ServerConfig::fromJson(const QJsonObject& json) -{ - apiDefs::ConfigType configType = apiUtils::getConfigType(json); - - switch (configType) { - case apiDefs::ConfigType::SelfHosted: { - bool hasThirdPartyConfig = false; - QJsonArray containersArray = json.value(configKey::containers).toArray(); - for (const QJsonValue& val : containersArray) { - QJsonObject containerObj = val.toObject(); - for (auto it = containerObj.begin(); it != containerObj.end(); ++it) { - QString key = it.key(); - if (key != configKey::container) { - QJsonObject protocolObj = it.value().toObject(); - if (protocolObj.contains(configKey::isThirdPartyConfig) && - protocolObj.value(configKey::isThirdPartyConfig).toBool()) { - hasThirdPartyConfig = true; - break; - } - } - } - if (hasThirdPartyConfig) { - break; - } - } - - if (hasThirdPartyConfig) { - return ServerConfig{NativeServerConfig::fromJson(json)}; - } else { - return ServerConfig{SelfHostedServerConfig::fromJson(json)}; - } - } - case apiDefs::ConfigType::AmneziaPremiumV1: - case apiDefs::ConfigType::AmneziaFreeV2: - return ServerConfig{ApiV1ServerConfig::fromJson(json)}; - case apiDefs::ConfigType::AmneziaPremiumV2: - case apiDefs::ConfigType::AmneziaFreeV3: - case apiDefs::ConfigType::ExternalPremium: - return ServerConfig{ApiV2ServerConfig::fromJson(json)}; - default: { - // Check if any container has isThirdPartyConfig - bool hasThirdPartyConfig = false; - QJsonArray containersArray = json.value(configKey::containers).toArray(); - for (const QJsonValue& val : containersArray) { - QJsonObject containerObj = val.toObject(); - // Check all protocol keys in the container object - for (auto it = containerObj.begin(); it != containerObj.end(); ++it) { - QString key = it.key(); - if (key != configKey::container) { - QJsonObject protocolObj = it.value().toObject(); - if (protocolObj.contains(configKey::isThirdPartyConfig) && - protocolObj.value(configKey::isThirdPartyConfig).toBool()) { - hasThirdPartyConfig = true; - break; - } - } - } - if (hasThirdPartyConfig) { - break; - } - } - - if (hasThirdPartyConfig) { - return ServerConfig{NativeServerConfig::fromJson(json)}; - } else { - return ServerConfig{SelfHostedServerConfig::fromJson(json)}; - } - } - } -} - -QPair ServerConfig::getDnsPair(bool isAmneziaDnsEnabled, - const QString &primaryDns, - const QString &secondaryDns) const -{ - QPair dns; - - QMap serverContainers = containers(); - - bool isDnsContainerInstalled = false; - for (auto it = serverContainers.begin(); it != serverContainers.end(); ++it) { - if (it.key() == DockerContainer::Dns) { - isDnsContainerInstalled = true; - break; - } - } - - dns.first = dns1(); - dns.second = dns2(); - - if (dns.first.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.first)) { - if (isAmneziaDnsEnabled && isDnsContainerInstalled) { - dns.first = protocols::dns::amneziaDnsIp; - } else { - dns.first = primaryDns; - } - } - - if (dns.second.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.second)) { - dns.second = secondaryDns; - } - - return dns; -} - -} // namespace amnezia - diff --git a/client/core/models/serverConfig.h b/client/core/models/serverConfig.h deleted file mode 100644 index 93ef1842d..000000000 --- a/client/core/models/serverConfig.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef SERVERCONFIG_H -#define SERVERCONFIG_H - -#include -#include -#include - -#include "core/utils/containerEnum.h" -#include "core/utils/containers/containerUtils.h" -#include "core/utils/protocolEnum.h" -#include "core/models/selfhosted/selfHostedServerConfig.h" -#include "core/models/selfhosted/nativeServerConfig.h" -#include "core/models/api/apiV1ServerConfig.h" -#include "core/models/api/apiV2ServerConfig.h" -#include "core/models/containerConfig.h" - -namespace amnezia -{ - -using namespace ContainerEnumNS; - -struct ServerConfig { - using Variant = std::variant< - SelfHostedServerConfig, - NativeServerConfig, - ApiV1ServerConfig, - ApiV2ServerConfig - >; - - Variant data; - - ServerConfig() = default; - ServerConfig(const Variant& v) : data(v) {} - ServerConfig(Variant&& v) : data(std::move(v)) {} - - template>, ServerConfig>::value>> - ServerConfig(const T& v) : data(v) {} - - template>, ServerConfig>::value>> - ServerConfig(T&& v) : data(std::forward(v)) {} - - QString description() const; - QString hostName() const; - QString displayName() const; - QMap containers() const; - DockerContainer defaultContainer() const; - QString dns1() const; - QString dns2() const; - bool hasContainers() const; - ContainerConfig containerConfig(DockerContainer container) const; - - int crc() const; - int configVersion() const; - - bool isSelfHosted() const; - bool isNative() const; - bool isApiV1() const; - bool isApiV2() const; - bool isApiConfig() const; - - template - T* as() { - return std::get_if(&data); - } - - template - const T* as() const { - return std::get_if(&data); - } - - QJsonObject toJson() const; - static ServerConfig fromJson(const QJsonObject& json); - - template - auto visit(Visitor&& visitor) { - return std::visit(std::forward(visitor), data); - } - - template - auto visit(Visitor&& visitor) const { - return std::visit(std::forward(visitor), data); - } - - QPair getDnsPair(bool isAmneziaDnsEnabled, - const QString &primaryDns, - const QString &secondaryDns) const; -}; - -} // namespace amnezia - -#endif // SERVERCONFIG_H - diff --git a/client/core/models/serverDescription.cpp b/client/core/models/serverDescription.cpp new file mode 100644 index 000000000..c533a094b --- /dev/null +++ b/client/core/models/serverDescription.cpp @@ -0,0 +1,187 @@ +#include "serverDescription.h" + +#include + +#include "core/utils/serverConfigUtils.h" +#include "core/utils/constants/apiKeys.h" +#include "core/utils/constants/apiConstants.h" +#include "core/utils/constants/protocolConstants.h" +#include "core/utils/api/apiUtils.h" +#include "core/utils/containers/containerUtils.h" +#include "core/protocols/protocolUtils.h" +#include "core/models/protocols/awgProtocolConfig.h" + +using namespace amnezia; + +namespace +{ + +bool computeHasInstalledVpnContainers(const QMap &containers) +{ + for (auto it = containers.begin(); it != containers.end(); ++it) { + const DockerContainer container = it.key(); + if (ContainerUtils::containerService(container) == ServiceType::Vpn || container == DockerContainer::SSXray) { + return true; + } + } + return false; +} + +template +ServerDescription buildBaseDescription(const T &server) +{ + ServerDescription row; + row.hostName = server.hostName; + row.defaultContainer = server.defaultContainer; + row.primaryDnsIsAmnezia = (server.dns1 == protocols::dns::amneziaDnsIp); + row.hasInstalledVpnContainers = computeHasInstalledVpnContainers(server.containers); + return row; +} + +QString getBaseDescription(const QMap &containers, + bool isAmneziaDnsEnabled, + bool hasWriteAccess, + bool primaryDnsIsAmnezia) +{ + QString description; + if (hasWriteAccess) { + const bool isDnsInstalled = containers.contains(DockerContainer::Dns); + if (isAmneziaDnsEnabled && isDnsInstalled) { + description += QStringLiteral("Amnezia DNS | "); + } + } else if (primaryDnsIsAmnezia) { + description += QStringLiteral("Amnezia DNS | "); + } + return description; +} + +QString getProtocolName(DockerContainer defaultContainer, const QMap &containers) +{ + QString containerName = ContainerUtils::containerHumanNames().value(defaultContainer); + QString protocolVersion; + + if (ContainerUtils::isAwgContainer(defaultContainer)) { + const auto it = containers.constFind(defaultContainer); + if (it != containers.cend()) { + if (const AwgProtocolConfig *awg = it->getAwgProtocolConfig()) { + protocolVersion = ProtocolUtils::getProtocolVersionString(awg->toJson()); + if (defaultContainer == DockerContainer::Awg && !awg->serverConfig.isThirdPartyConfig) { + containerName = QStringLiteral("AmneziaWG Legacy"); + } + } + } + } + + return containerName + protocolVersion + QStringLiteral(" | "); +} + +} // namespace + +namespace amnezia +{ + +ServerDescription buildServerDescription(const SelfHostedAdminServerConfig &server, bool isAmneziaDnsEnabled) +{ + ServerDescription row = buildBaseDescription(server); + row.selfHostedSshCredentials.hostName = server.hostName; + row.selfHostedSshCredentials.userName = server.userName; + row.selfHostedSshCredentials.secretData = server.password; + row.selfHostedSshCredentials.port = server.port > 0 ? server.port : 22; + + row.hasWriteAccess = !row.selfHostedSshCredentials.userName.isEmpty() + && !row.selfHostedSshCredentials.secretData.isEmpty(); + + row.serverName = server.displayName; + row.baseDescription = getBaseDescription(server.containers, isAmneziaDnsEnabled, row.hasWriteAccess, row.primaryDnsIsAmnezia); + + const QString protocolName = getProtocolName(server.defaultContainer, server.containers); + row.expandedServerDescription = row.baseDescription + row.hostName; + row.collapsedServerDescription = row.baseDescription + protocolName + row.hostName; + return row; +} + +ServerDescription buildServerDescription(const SelfHostedUserServerConfig &server, bool isAmneziaDnsEnabled) +{ + ServerDescription row = buildBaseDescription(server); + row.selfHostedSshCredentials.hostName = server.hostName; + row.selfHostedSshCredentials.port = 22; + row.hasWriteAccess = false; + + row.serverName = server.displayName; + row.baseDescription = getBaseDescription(server.containers, isAmneziaDnsEnabled, row.hasWriteAccess, row.primaryDnsIsAmnezia); + + const QString protocolName = getProtocolName(server.defaultContainer, server.containers); + row.expandedServerDescription = row.baseDescription + row.hostName; + row.collapsedServerDescription = row.baseDescription + protocolName + row.hostName; + return row; +} + +ServerDescription buildServerDescription(const NativeServerConfig &server, bool isAmneziaDnsEnabled) +{ + ServerDescription row = buildBaseDescription(server); + row.hasWriteAccess = false; + + row.serverName = server.displayName; + row.baseDescription = getBaseDescription(server.containers, isAmneziaDnsEnabled, row.hasWriteAccess, row.primaryDnsIsAmnezia); + + const QString protocolName = getProtocolName(server.defaultContainer, server.containers); + row.expandedServerDescription = row.baseDescription + row.hostName; + row.collapsedServerDescription = row.baseDescription + protocolName + row.hostName; + return row; +} + +ServerDescription buildServerDescription(const LegacyApiServerConfig &server, bool /*isAmneziaDnsEnabled*/) +{ + ServerDescription row = buildBaseDescription(server); + row.configVersion = serverConfigUtils::ConfigSource::Telegram; + row.isApiV1 = true; + row.isServerFromGatewayApi = false; + row.hasWriteAccess = false; + + row.serverName = server.displayName; + row.baseDescription = server.description; + + const QString fullDescriptionForCollapsed = row.baseDescription; + row.collapsedServerDescription = fullDescriptionForCollapsed; + row.expandedServerDescription = fullDescriptionForCollapsed; + return row; +} + +ServerDescription buildServerDescription(const ApiV2ServerConfig &server, bool /*isAmneziaDnsEnabled*/) +{ + ServerDescription row = buildBaseDescription(server); + row.configVersion = serverConfigUtils::ConfigSource::AmneziaGateway; + row.isApiV2 = true; + row.isServerFromGatewayApi = true; + row.isPremium = server.isPremium() || server.isExternalPremium(); + row.hasWriteAccess = false; + + row.serverName = server.displayName; + row.baseDescription = server.apiConfig.serverCountryCode.isEmpty() ? server.description : server.apiConfig.serverCountryName; + + row.isCountrySelectionAvailable = !server.apiConfig.availableCountries.isEmpty(); + row.apiAvailableCountries = server.apiConfig.availableCountries; + row.apiServerCountryCode = server.apiConfig.serverCountryCode; + + row.isAdVisible = server.apiConfig.serviceInfo.isAdVisible; + row.adHeader = server.apiConfig.serviceInfo.adHeader; + row.adDescription = server.apiConfig.serviceInfo.adDescription; + row.adEndpoint = server.apiConfig.serviceInfo.adEndpoint; + row.isRenewalAvailable = server.apiConfig.serviceInfo.isRenewalAvailable; + + if (!server.apiConfig.isInAppPurchase) { + if (server.apiConfig.subscriptionExpiredByServer) { + row.isSubscriptionExpired = true; + } else if (!server.apiConfig.subscription.endDate.isEmpty()) { + row.isSubscriptionExpired = apiUtils::isSubscriptionExpired(server.apiConfig.subscription.endDate); + row.isSubscriptionExpiringSoon = apiUtils::isSubscriptionExpiringSoon(server.apiConfig.subscription.endDate); + } + } + + const QString fullDescriptionForCollapsed = row.baseDescription; + row.collapsedServerDescription = fullDescriptionForCollapsed; + row.expandedServerDescription = fullDescriptionForCollapsed; + return row; +} + +} // namespace amnezia diff --git a/client/core/models/serverDescription.h b/client/core/models/serverDescription.h new file mode 100644 index 000000000..22a0ddb72 --- /dev/null +++ b/client/core/models/serverDescription.h @@ -0,0 +1,64 @@ +#ifndef SERVERDESCRIPTION_H +#define SERVERDESCRIPTION_H + +#include +#include + +#include "core/utils/containerEnum.h" +#include "core/utils/selfhosted/sshSession.h" +#include "core/models/selfhosted/selfHostedAdminServerConfig.h" +#include "core/models/selfhosted/selfHostedUserServerConfig.h" +#include "core/models/selfhosted/nativeServerConfig.h" +#include "core/models/api/legacyApiServerConfig.h" +#include "core/models/api/apiV2ServerConfig.h" + +namespace amnezia +{ + +struct ServerDescription +{ + QString serverId; + + QString serverName; + QString baseDescription; + QString hostName; + + int configVersion = 0; + + ServerCredentials selfHostedSshCredentials; + bool hasWriteAccess = false; + + bool primaryDnsIsAmnezia = false; + DockerContainer defaultContainer = DockerContainer::None; + bool hasInstalledVpnContainers = false; + + bool isApiV1 = false; + bool isApiV2 = false; + bool isServerFromGatewayApi = false; + bool isPremium = false; + + bool isCountrySelectionAvailable = false; + QJsonArray apiAvailableCountries; + QString apiServerCountryCode; + + bool isAdVisible = false; + QString adHeader; + QString adDescription; + QString adEndpoint; + bool isRenewalAvailable = false; + bool isSubscriptionExpired = false; + bool isSubscriptionExpiringSoon = false; + + QString collapsedServerDescription; + QString expandedServerDescription; +}; + +ServerDescription buildServerDescription(const SelfHostedAdminServerConfig &server, bool isAmneziaDnsEnabled); +ServerDescription buildServerDescription(const SelfHostedUserServerConfig &server, bool isAmneziaDnsEnabled); +ServerDescription buildServerDescription(const NativeServerConfig &server, bool isAmneziaDnsEnabled); +ServerDescription buildServerDescription(const LegacyApiServerConfig &server, bool isAmneziaDnsEnabled); +ServerDescription buildServerDescription(const ApiV2ServerConfig &server, bool isAmneziaDnsEnabled); + +} // namespace amnezia + +#endif diff --git a/client/core/repositories/secureAppSettingsRepository.cpp b/client/core/repositories/secureAppSettingsRepository.cpp index 3bc579c74..01f313b08 100644 --- a/client/core/repositories/secureAppSettingsRepository.cpp +++ b/client/core/repositories/secureAppSettingsRepository.cpp @@ -2,15 +2,16 @@ #include #include +#include #include #include "core/utils/errorCodes.h" #include "core/utils/routeModes.h" #include "core/utils/commonStructs.h" -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" -#include "core/models/serverConfig.h" +#include "core/utils/constants/configKeys.h" #include "core/utils/networkUtilities.h" using namespace amnezia; diff --git a/client/core/repositories/secureServersRepository.cpp b/client/core/repositories/secureServersRepository.cpp index 7107b8aa5..d59dfc7b7 100644 --- a/client/core/repositories/secureServersRepository.cpp +++ b/client/core/repositories/secureServersRepository.cpp @@ -1,26 +1,44 @@ #include "secureServersRepository.h" -#include #include +#include +#include +#include -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" -#include "core/utils/constants/apiConstants.h" -#include "core/models/serverConfig.h" -#include "core/models/containerConfig.h" -#include "core/utils/protocolEnum.h" -#include "core/protocols/protocolUtils.h" #include "core/utils/constants/configKeys.h" -#include "core/utils/constants/protocolConstants.h" -SecureServersRepository::SecureServersRepository(SecureQSettings* settings, QObject *parent) +using namespace amnezia; + +namespace { + +QString readStorageServerId(const QJsonObject &json) +{ + return json.value(QString(configKey::storageServerId)).toString().trimmed(); +} + +QJsonObject withoutStorageServerId(const QJsonObject &json) +{ + QJsonObject o = json; + o.remove(QString(configKey::storageServerId)); + return o; +} + +QJsonObject embedStorageServerId(const QString &serverId, const QJsonObject &payloadSansId) +{ + QJsonObject o = payloadSansId; + o.insert(QString(configKey::storageServerId), serverId); + return o; +} + +} // namespace + +SecureServersRepository::SecureServersRepository(SecureQSettings *settings, QObject *parent) : QObject(parent), m_settings(settings) { - QJsonArray arr = QJsonDocument::fromJson(value("Servers/serversList").toByteArray()).array(); - for (const QJsonValue &val : arr) { - m_servers.append(ServerConfig::fromJson(val.toObject())); - } - m_defaultServerIndex = value("Servers/defaultServerIndex", 0).toInt(); + loadFromStorage(); + persistDefaultServerFields(); } QVariant SecureServersRepository::value(const QString &key, const QVariant &defaultValue) const @@ -33,216 +51,322 @@ void SecureServersRepository::setValue(const QString &key, const QVariant &value m_settings->setValue(key, value); } +void SecureServersRepository::clearServerStateMaps() +{ + m_serverJsonById.clear(); + m_orderedServerIds.clear(); +} + +QString SecureServersRepository::normalizedOrGeneratedServerId(const QString &candidateId) const +{ + const QString trimmed = candidateId.trimmed(); + if (!trimmed.isEmpty() && !m_serverJsonById.contains(trimmed)) { + return trimmed; + } + return QUuid::createUuid().toString(QUuid::WithoutBraces); +} + +void SecureServersRepository::updateDefaultServerFromStorage() +{ + const QString storedDefaultId = value(QStringLiteral("Servers/defaultServerId"), QString()).toString(); + if (!storedDefaultId.isEmpty() && m_serverJsonById.contains(storedDefaultId)) { + m_defaultServerId = storedDefaultId; + return; + } + + const int storedDefaultIndex = value("Servers/defaultServerIndex", 0).toInt(); + if (storedDefaultIndex >= 0 && storedDefaultIndex < m_orderedServerIds.size()) { + m_defaultServerId = m_orderedServerIds.at(storedDefaultIndex); + return; + } + + if (!m_orderedServerIds.isEmpty()) { + m_defaultServerId = m_orderedServerIds.first(); + return; + } + + m_defaultServerId.clear(); +} + +void SecureServersRepository::persistDefaultServerFields() +{ + if (m_orderedServerIds.isEmpty()) { + m_defaultServerId.clear(); + } else if (!m_orderedServerIds.contains(m_defaultServerId)) { + m_defaultServerId = m_orderedServerIds.first(); + } + + setValue("Servers/defaultServerId", m_defaultServerId); +} + +void SecureServersRepository::loadFromStorage() +{ + clearServerStateMaps(); + + const QJsonArray serversArray = + QJsonDocument::fromJson(value(QStringLiteral("Servers/serversList"), QByteArray()).toByteArray()) + .array(); + + for (int i = 0; i < serversArray.size(); ++i) { + const QJsonObject json = serversArray.at(i).toObject(); + const QString candidateId = readStorageServerId(json); + const QString serverId = normalizedOrGeneratedServerId(candidateId); + const QJsonObject strippedJson = withoutStorageServerId(json); + const serverConfigUtils::ConfigType kind = serverConfigUtils::configTypeFromJson(strippedJson); + + if (m_serverJsonById.contains(serverId) || kind == serverConfigUtils::ConfigType::Invalid) { + continue; + } + m_serverJsonById.insert(serverId, embedStorageServerId(serverId, strippedJson)); + m_orderedServerIds.append(serverId); + } + + updateDefaultServerFromStorage(); +} + void SecureServersRepository::syncToStorage() { - QJsonArray arr; - for (const ServerConfig &cfg : m_servers) { - arr.append(cfg.toJson()); + QJsonArray serversArray; + + for (const QString &serverId : m_orderedServerIds) { + if (!m_serverJsonById.contains(serverId)) { + continue; + } + serversArray.append(m_serverJsonById.value(serverId)); } - setValue("Servers/serversList", QJsonDocument(arr).toJson()); + + setValue("Servers/serversList", QJsonDocument(serversArray).toJson()); + persistDefaultServerFields(); } void SecureServersRepository::invalidateCache() { - m_servers.clear(); - QJsonArray arr = QJsonDocument::fromJson(value("Servers/serversList").toByteArray()).array(); - for (const QJsonValue &val : arr) { - m_servers.append(ServerConfig::fromJson(val.toObject())); - } - m_defaultServerIndex = value("Servers/defaultServerIndex", 0).toInt(); + loadFromStorage(); } -void SecureServersRepository::setServersArray(const QJsonArray &servers) +void SecureServersRepository::clearServers() { - m_servers.clear(); - for (const QJsonValue &val : servers) { - m_servers.append(ServerConfig::fromJson(val.toObject())); - } + clearServerStateMaps(); + + m_defaultServerId.clear(); + syncToStorage(); } -void SecureServersRepository::addServer(const ServerConfig &server) +QString SecureServersRepository::addServer(const QString &serverId, const QJsonObject &serverJson, serverConfigUtils::ConfigType kind) { - m_servers.append(server); + const QString id = normalizedOrGeneratedServerId(serverId); + if (m_serverJsonById.contains(id) || kind == serverConfigUtils::ConfigType::Invalid) { + return id; + } + const QJsonObject strippedJson = withoutStorageServerId(serverJson); + if (serverConfigUtils::configTypeFromJson(strippedJson) != kind) { + return id; + } + m_serverJsonById.insert(id, embedStorageServerId(id, strippedJson)); + + m_orderedServerIds.append(id); + + if (m_defaultServerId.isEmpty()) { + m_defaultServerId = id; + } + syncToStorage(); - emit serverAdded(server); + emit serverAdded(id); + return id; } -void SecureServersRepository::editServer(int index, const ServerConfig &server) +void SecureServersRepository::editServer(const QString &serverId, const QJsonObject &serverJson, serverConfigUtils::ConfigType kind) { - if (index < 0 || index >= m_servers.size()) { + if (indexOfServerId(serverId) < 0 || kind == serverConfigUtils::ConfigType::Invalid) { return; } - m_servers.replace(index, server); - syncToStorage(); - emit serverEdited(index, server); -} - -void SecureServersRepository::removeServer(int index) -{ - if (index < 0 || index >= m_servers.size()) { + if (!m_serverJsonById.contains(serverId)) { return; } - int defaultIndex = m_defaultServerIndex; - m_servers.removeAt(index); - if (defaultIndex == index) { - setDefaultServer(0); - } else if (defaultIndex > index) { - setDefaultServer(defaultIndex - 1); + const QJsonObject oldJson = m_serverJsonById.value(serverId); + const serverConfigUtils::ConfigType oldKind = serverConfigUtils::configTypeFromJson(withoutStorageServerId(oldJson)); + + m_serverJsonById.remove(serverId); + + const QJsonObject strippedNew = withoutStorageServerId(serverJson); + if (serverConfigUtils::configTypeFromJson(strippedNew) != kind) { + const QJsonObject strippedOld = withoutStorageServerId(oldJson); + if (oldKind != serverConfigUtils::ConfigType::Invalid && serverConfigUtils::configTypeFromJson(strippedOld) == oldKind) { + m_serverJsonById.insert(serverId, embedStorageServerId(serverId, strippedOld)); + } + return; + } + m_serverJsonById.insert(serverId, embedStorageServerId(serverId, strippedNew)); + + syncToStorage(); + emit serverEdited(serverId); +} + +void SecureServersRepository::removeServer(const QString &serverId) +{ + const int removedIndex = indexOfServerId(serverId); + if (removedIndex < 0) { + return; + } + if (!m_serverJsonById.contains(serverId)) { + return; } - if (m_servers.isEmpty()) { - setDefaultServer(0); + const QString previousDefaultId = m_defaultServerId; + const int previousDefaultIndex = defaultServerIndex(); + + m_serverJsonById.remove(serverId); + m_orderedServerIds.removeAt(removedIndex); + + if (m_orderedServerIds.isEmpty()) { + m_defaultServerId.clear(); + } else if (m_defaultServerId == serverId) { + const int fallbackIndex = qMin(removedIndex, m_orderedServerIds.size() - 1); + m_defaultServerId = m_orderedServerIds.at(fallbackIndex); + } else if (!m_orderedServerIds.contains(m_defaultServerId)) { + m_defaultServerId = m_orderedServerIds.first(); + } + + const int newDefaultIndex = defaultServerIndex(); + if (previousDefaultId != m_defaultServerId || previousDefaultIndex != newDefaultIndex) { + emit defaultServerChanged(m_defaultServerId); } syncToStorage(); - emit serverRemoved(index); + emit serverRemoved(serverId, removedIndex); } -ServerConfig SecureServersRepository::server(int index) const +serverConfigUtils::ConfigType SecureServersRepository::serverKind(const QString &serverId) const { - if (index < 0 || index >= m_servers.size()) { - return SelfHostedServerConfig{}; + const auto it = m_serverJsonById.constFind(serverId); + if (it == m_serverJsonById.constEnd()) { + return serverConfigUtils::ConfigType::Invalid; } - return m_servers.at(index); + return serverConfigUtils::configTypeFromJson(withoutStorageServerId(it.value())); } -QVector SecureServersRepository::servers() const +std::optional SecureServersRepository::selfHostedAdminConfig(const QString &serverId) const { - return m_servers; + const auto it = m_serverJsonById.constFind(serverId); + if (it == m_serverJsonById.constEnd()) { + return std::nullopt; + } + const QJsonObject strippedJson = withoutStorageServerId(it.value()); + if (serverConfigUtils::configTypeFromJson(strippedJson) != serverConfigUtils::ConfigType::SelfHostedAdmin) { + return std::nullopt; + } + return SelfHostedAdminServerConfig::fromJson(strippedJson); +} + +std::optional SecureServersRepository::selfHostedUserConfig(const QString &serverId) const +{ + const auto it = m_serverJsonById.constFind(serverId); + if (it == m_serverJsonById.constEnd()) { + return std::nullopt; + } + const QJsonObject strippedJson = withoutStorageServerId(it.value()); + if (serverConfigUtils::configTypeFromJson(strippedJson) != serverConfigUtils::ConfigType::SelfHostedUser) { + return std::nullopt; + } + return SelfHostedUserServerConfig::fromJson(strippedJson); +} + +std::optional SecureServersRepository::nativeConfig(const QString &serverId) const +{ + const auto it = m_serverJsonById.constFind(serverId); + if (it == m_serverJsonById.constEnd()) { + return std::nullopt; + } + const QJsonObject strippedJson = withoutStorageServerId(it.value()); + if (serverConfigUtils::configTypeFromJson(strippedJson) != serverConfigUtils::ConfigType::Native) { + return std::nullopt; + } + return NativeServerConfig::fromJson(strippedJson); +} + +std::optional SecureServersRepository::apiV2Config(const QString &serverId) const +{ + const auto it = m_serverJsonById.constFind(serverId); + if (it == m_serverJsonById.constEnd()) { + return std::nullopt; + } + const QJsonObject strippedJson = withoutStorageServerId(it.value()); + if (!serverConfigUtils::isApiV2Subscription(serverConfigUtils::configTypeFromJson(strippedJson))) { + return std::nullopt; + } + return ApiV2ServerConfig::fromJson(strippedJson); +} + +std::optional SecureServersRepository::legacyApiConfig(const QString &serverId) const +{ + const auto it = m_serverJsonById.constFind(serverId); + if (it == m_serverJsonById.constEnd()) { + return std::nullopt; + } + const QJsonObject strippedJson = withoutStorageServerId(it.value()); + if (!serverConfigUtils::isLegacyApiSubscription(serverConfigUtils::configTypeFromJson(strippedJson))) { + return std::nullopt; + } + return LegacyApiServerConfig::fromJson(strippedJson); } int SecureServersRepository::serversCount() const { - return m_servers.size(); + return m_orderedServerIds.size(); +} + +QString SecureServersRepository::serverIdAt(int index) const +{ + if (index < 0 || index >= m_orderedServerIds.size()) { + return QString(); + } + return m_orderedServerIds.at(index); +} + +QVector SecureServersRepository::orderedServerIds() const +{ + return m_orderedServerIds; +} + +int SecureServersRepository::indexOfServerId(const QString &serverId) const +{ + return m_orderedServerIds.indexOf(serverId); } int SecureServersRepository::defaultServerIndex() const { - return m_defaultServerIndex; + if (m_orderedServerIds.isEmpty()) { + return 0; + } + const int idx = m_orderedServerIds.indexOf(m_defaultServerId); + return idx >= 0 ? idx : 0; } -void SecureServersRepository::setDefaultServer(int index) +QString SecureServersRepository::defaultServerId() const { - if (index < 0) { + return m_defaultServerId; +} + +void SecureServersRepository::setDefaultServer(const QString &serverId) +{ + if (m_orderedServerIds.isEmpty()) { return; } - if (m_servers.size() > 0 && index >= m_servers.size()) { + if (!m_serverJsonById.contains(serverId)) { return; } - if (m_servers.isEmpty() && index != 0) { + + if (indexOfServerId(serverId) < 0) { return; } - if (m_defaultServerIndex == index) { + + if (m_defaultServerId == serverId) { return; } - m_defaultServerIndex = index; - setValue("Servers/defaultServerIndex", index); - emit defaultServerChanged(index); -} -void SecureServersRepository::setDefaultContainer(int serverIndex, DockerContainer container) -{ - ServerConfig config = server(serverIndex); - config.visit([container](auto& arg) { - arg.defaultContainer = container; - }); - editServer(serverIndex, config); -} - -ContainerConfig SecureServersRepository::containerConfig(int serverIndex, DockerContainer container) const -{ - ServerConfig config = server(serverIndex); - return config.containerConfig(container); -} - -void SecureServersRepository::setContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config) -{ - ServerConfig serverConfig = server(serverIndex); - serverConfig.visit([container, &config](auto& arg) { - arg.containers[container] = config; - }); - editServer(serverIndex, serverConfig); -} - -void SecureServersRepository::clearLastConnectionConfig(int serverIndex, DockerContainer container) -{ - ServerConfig serverConfig = server(serverIndex); - ContainerConfig containerCfg = serverConfig.containerConfig(container); - - containerCfg.protocolConfig.clearClientConfig(); - - setContainerConfig(serverIndex, container, containerCfg); -} - -ServerCredentials SecureServersRepository::serverCredentials(int index) const -{ - ServerConfig config = server(index); - - if (config.isSelfHosted()) { - const SelfHostedServerConfig* selfHosted = config.as(); - if (!selfHosted) return ServerCredentials(); - auto creds = selfHosted->credentials(); - if (creds.has_value()) { - return creds.value(); - } - } - - return ServerCredentials{}; -} - -bool SecureServersRepository::hasServerWithVpnKey(const QString &vpnKey) const -{ - QString normalizedInput = vpnKey.trimmed(); - if (normalizedInput.startsWith(QStringLiteral("vpn://"), Qt::CaseInsensitive)) { - normalizedInput = normalizedInput.mid(QStringLiteral("vpn://").size()); - } - if (normalizedInput.isEmpty()) { - return false; - } - - QVector serversList = servers(); - for (const ServerConfig& serverConfig : serversList) { - if (serverConfig.isApiV1()) { - const ApiV1ServerConfig* apiV1 = serverConfig.as(); - if (!apiV1) continue; - QString storedKey = apiV1->vpnKey(); - if (storedKey.isEmpty()) { - continue; - } - QString normalizedStored = storedKey.trimmed(); - if (normalizedStored.startsWith(QStringLiteral("vpn://"), Qt::CaseInsensitive)) { - normalizedStored = normalizedStored.mid(QStringLiteral("vpn://").size()); - } - if (normalizedInput == normalizedStored) { - return true; - } - } else if (serverConfig.isApiV2()) { - const ApiV2ServerConfig* apiV2 = serverConfig.as(); - if (!apiV2) continue; - QString storedKey = apiV2->vpnKey(); - if (storedKey.isEmpty()) { - continue; - } - QString normalizedStored = storedKey.trimmed(); - if (normalizedStored.startsWith(QStringLiteral("vpn://"), Qt::CaseInsensitive)) { - normalizedStored = normalizedStored.mid(QStringLiteral("vpn://").size()); - } - if (normalizedInput == normalizedStored) { - return true; - } - } - } - return false; -} - -bool SecureServersRepository::hasServerWithCrc(quint16 crc) const -{ - for (const ServerConfig& serverConfig : m_servers) { - if (static_cast(serverConfig.crc()) == crc) { - return true; - } - } - return false; + m_defaultServerId = serverId; + persistDefaultServerFields(); + emit defaultServerChanged(m_defaultServerId); } diff --git a/client/core/repositories/secureServersRepository.h b/client/core/repositories/secureServersRepository.h index 03c876a71..a0542a62f 100644 --- a/client/core/repositories/secureServersRepository.h +++ b/client/core/repositories/secureServersRepository.h @@ -1,14 +1,20 @@ #ifndef SECURESERVERSREPOSITORY_H #define SECURESERVERSREPOSITORY_H +#include +#include #include #include -#include -#include #include +#include -#include "core/models/serverConfig.h" +#include "core/models/selfhosted/selfHostedAdminServerConfig.h" +#include "core/models/selfhosted/selfHostedUserServerConfig.h" +#include "core/models/selfhosted/nativeServerConfig.h" +#include "core/models/api/apiV2ServerConfig.h" +#include "core/models/api/legacyApiServerConfig.h" #include "core/models/containerConfig.h" +#include "core/utils/serverConfigUtils.h" #include "secureQSettings.h" using namespace amnezia; @@ -18,47 +24,57 @@ class SecureServersRepository : public QObject Q_OBJECT public: - explicit SecureServersRepository(SecureQSettings* settings, QObject *parent = nullptr); + explicit SecureServersRepository(SecureQSettings *settings, QObject *parent = nullptr); + + QString addServer(const QString &serverId, const QJsonObject &serverJson, serverConfigUtils::ConfigType kind); + void editServer(const QString &serverId, const QJsonObject &serverJson, serverConfigUtils::ConfigType kind); + void removeServer(const QString &serverId); + serverConfigUtils::ConfigType serverKind(const QString &serverId) const; + + std::optional selfHostedAdminConfig(const QString &serverId) const; + std::optional selfHostedUserConfig(const QString &serverId) const; + std::optional nativeConfig(const QString &serverId) const; + std::optional apiV2Config(const QString &serverId) const; + std::optional legacyApiConfig(const QString &serverId) const; - void addServer(const ServerConfig &server); - void editServer(int index, const ServerConfig &server); - void removeServer(int index); - ServerConfig server(int index) const; - QVector servers() const; int serversCount() const; + int indexOfServerId(const QString &serverId) const; + QString serverIdAt(int index) const; + QVector orderedServerIds() const; int defaultServerIndex() const; - void setDefaultServer(int index); + QString defaultServerId() const; + void setDefaultServer(const QString &serverId); - void setDefaultContainer(int serverIndex, DockerContainer container); - ContainerConfig containerConfig(int serverIndex, DockerContainer container) const; - void setContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config); - void clearLastConnectionConfig(int serverIndex, DockerContainer container); - - ServerCredentials serverCredentials(int index) const; - bool hasServerWithVpnKey(const QString &vpnKey) const; - bool hasServerWithCrc(quint16 crc) const; - - void setServersArray(const QJsonArray &servers); + void clearServers(); void invalidateCache(); signals: - void serverAdded(ServerConfig config); - void serverEdited(int index, ServerConfig config); - void serverRemoved(int index); - void defaultServerChanged(int index); + void serverAdded(const QString &serverId); + void serverEdited(const QString &serverId); + void serverRemoved(const QString &serverId, int removedIndex); + void defaultServerChanged(const QString &defaultServerId); private: + void loadFromStorage(); + void updateDefaultServerFromStorage(); + void persistDefaultServerFields(); + + QString normalizedOrGeneratedServerId(const QString &candidateId) const; + void syncToStorage(); - QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; + QVariant value(const QString &key, const QVariant &defaultValue) const; void setValue(const QString &key, const QVariant &value); - - SecureQSettings* m_settings; - - QVector m_servers; - int m_defaultServerIndex = 0; + + void clearServerStateMaps(); + + SecureQSettings *m_settings; + + QHash m_serverJsonById; + QVector m_orderedServerIds; + + QString m_defaultServerId; }; #endif // SECURESERVERSREPOSITORY_H - diff --git a/client/core/utils/api/apiEnums.h b/client/core/utils/api/apiEnums.h deleted file mode 100644 index c52ce98a0..000000000 --- a/client/core/utils/api/apiEnums.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef APIENUMS_H -#define APIENUMS_H - -namespace apiDefs -{ - enum ConfigType { - AmneziaFreeV2 = 0, - AmneziaFreeV3, - AmneziaPremiumV1, - AmneziaPremiumV2, - AmneziaTrialV2, - SelfHosted, - ExternalPremium, - ExternalTrial - }; - - enum ConfigSource { - Telegram = 1, - AmneziaGateway - }; -} - -#endif // APIENUMS_H - - diff --git a/client/core/utils/api/apiUtils.cpp b/client/core/utils/api/apiUtils.cpp index eca4689fb..60b78d565 100644 --- a/client/core/utils/api/apiUtils.cpp +++ b/client/core/utils/api/apiUtils.cpp @@ -1,5 +1,6 @@ #include "apiUtils.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/configKeys.h" #include #include @@ -75,63 +76,6 @@ bool apiUtils::isSubscriptionExpiringSoon(const QString &subscriptionEndDate, in return endDate <= nowUtc.addDays(withinDays); } -bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject) -{ - auto configVersion = serverConfigObject.value(configKey::configVersion).toInt(); - switch (configVersion) { - case apiDefs::ConfigSource::Telegram: return true; - case apiDefs::ConfigSource::AmneziaGateway: return true; - default: return false; - } -} - -apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject) -{ - auto configVersion = serverConfigObject.value(configKey::configVersion).toInt(); - - switch (configVersion) { - case apiDefs::ConfigSource::Telegram: { - constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT); - constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT); - - auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString(); - - if (apiEndpoint.contains(premiumV1Endpoint)) { - return apiDefs::ConfigType::AmneziaPremiumV1; - } else if (apiEndpoint.contains(freeV2Endpoint)) { - return apiDefs::ConfigType::AmneziaFreeV2; - } - }; - case apiDefs::ConfigSource::AmneziaGateway: { - constexpr QLatin1String servicePremium("amnezia-premium"); - constexpr QLatin1String serviceFree("amnezia-free"); - constexpr QLatin1String serviceExternalPremium("external-premium"); - constexpr QLatin1String serviceExternalTrial("external-trial"); - - auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject(); - auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString(); - - if (serviceType == servicePremium) { - return apiDefs::ConfigType::AmneziaPremiumV2; - } else if (serviceType == serviceFree) { - return apiDefs::ConfigType::AmneziaFreeV3; - } else if (serviceType == serviceExternalPremium) { - return apiDefs::ConfigType::ExternalPremium; - } else if (serviceType == serviceExternalTrial) { - return apiDefs::ConfigType::ExternalTrial; - } - } - default: { - return apiDefs::ConfigType::SelfHosted; - } - }; -} - -apiDefs::ConfigSource apiUtils::getConfigSource(const QJsonObject &serverConfigObject) -{ - return static_cast(serverConfigObject.value(configKey::configVersion).toInt()); -} - amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList &sslErrors, const QString &replyErrorString, const QNetworkReply::NetworkError &replyError, const int httpStatusCode, const QByteArray &responseBody) @@ -197,14 +141,14 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList &ssl bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject) { - static const QSet premiumTypes = { apiDefs::ConfigType::AmneziaPremiumV1, apiDefs::ConfigType::AmneziaPremiumV2, - apiDefs::ConfigType::ExternalPremium, apiDefs::ConfigType::ExternalTrial }; - return premiumTypes.contains(getConfigType(serverConfigObject)); + static const QSet premiumTypes = { serverConfigUtils::ConfigType::AmneziaPremiumV1, serverConfigUtils::ConfigType::AmneziaPremiumV2, + serverConfigUtils::ConfigType::ExternalPremium }; + return premiumTypes.contains(serverConfigUtils::configTypeFromJson(serverConfigObject)); } QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject) { - if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) { + if (serverConfigUtils::configTypeFromJson(serverConfigObject) != serverConfigUtils::ConfigType::AmneziaPremiumV1) { return {}; } @@ -242,9 +186,8 @@ QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject) QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject) { - auto configType = apiUtils::getConfigType(serverConfigObject); - if (configType != apiDefs::ConfigType::AmneziaPremiumV2 && configType != apiDefs::ConfigType::ExternalPremium - && configType != apiDefs::ConfigType::ExternalTrial) { + auto configType = serverConfigUtils::configTypeFromJson(serverConfigObject); + if (configType != serverConfigUtils::ConfigType::AmneziaPremiumV2 && configType != serverConfigUtils::ConfigType::ExternalPremium) { return {}; } diff --git a/client/core/utils/api/apiUtils.h b/client/core/utils/api/apiUtils.h index be770defa..e1ada61ae 100644 --- a/client/core/utils/api/apiUtils.h +++ b/client/core/utils/api/apiUtils.h @@ -4,7 +4,7 @@ #include #include -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" #include "core/utils/errorCodes.h" @@ -13,17 +13,12 @@ namespace apiUtils { - bool isServerFromApi(const QJsonObject &serverConfigObject); - bool isSubscriptionExpired(const QString &subscriptionEndDate); bool isSubscriptionExpiringSoon(const QString &subscriptionEndDate, int withinDays = 30); bool isPremiumServer(const QJsonObject &serverConfigObject); - apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject); - apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject); - amnezia::ErrorCode checkNetworkReplyErrors(const QList &sslErrors, const QString &replyErrorString, const QNetworkReply::NetworkError &replyError, const int httpStatusCode, const QByteArray &responseBody); diff --git a/client/core/utils/constants/apiConstants.h b/client/core/utils/constants/apiConstants.h index b9895bc82..148ae2e36 100644 --- a/client/core/utils/constants/apiConstants.h +++ b/client/core/utils/constants/apiConstants.h @@ -3,9 +3,9 @@ namespace apiDefs { - const int requestTimeoutMsecs = 12 * 1000; // 12 secs -} + +constexpr int requestTimeoutMsecs = 12 * 1000; // 12 secs + +} // namespace apiDefs #endif // APICONSTANTS_H - - diff --git a/client/core/utils/constants/apiKeys.h b/client/core/utils/constants/apiKeys.h index 2e037bca6..46833706c 100644 --- a/client/core/utils/constants/apiKeys.h +++ b/client/core/utils/constants/apiKeys.h @@ -2,7 +2,6 @@ #define APIKEYS_H #include -#include "core/utils/api/apiEnums.h" namespace apiDefs { @@ -82,7 +81,7 @@ namespace apiDefs constexpr QLatin1String expiresAt("expires_at"); constexpr QLatin1String isConnectEvent("is_connect_event"); constexpr QLatin1String certificate("certificate"); - } -} + } // namespace key +} // namespace apiDefs #endif // APIKEYS_H diff --git a/client/core/utils/constants/configKeys.h b/client/core/utils/constants/configKeys.h index 40bc842b1..3272eb2bb 100644 --- a/client/core/utils/constants/configKeys.h +++ b/client/core/utils/constants/configKeys.h @@ -18,6 +18,7 @@ namespace amnezia constexpr QLatin1String serverIndex("serverIndex"); constexpr QLatin1String description("description"); + constexpr QLatin1String displayName("displayName"); constexpr QLatin1String name("name"); constexpr QLatin1String cert("cert"); constexpr QLatin1String accessToken("api_key"); @@ -121,6 +122,8 @@ namespace amnezia constexpr QLatin1String latestHandshake("latestHandshake"); constexpr QLatin1String dataReceived("dataReceived"); constexpr QLatin1String dataSent("dataSent"); + + constexpr QLatin1String storageServerId("storageServerId"); } } diff --git a/client/core/utils/errorCodes.h b/client/core/utils/errorCodes.h index 00e8c6b20..0750553be 100644 --- a/client/core/utils/errorCodes.h +++ b/client/core/utils/errorCodes.h @@ -71,10 +71,11 @@ namespace amnezia // import and install errors ImportInvalidConfigError = 900, - ImportBackupFileUseRestoreInstead = 903, - RestoreBackupInvalidError = 904, ImportOpenConfigError = 901, NoInstalledContainersError = 902, + ImportBackupFileUseRestoreInstead = 903, + RestoreBackupInvalidError = 904, + LegacyApiV1NotSupportedError = 905, // Android errors AndroidError = 1000, diff --git a/client/core/utils/errorStrings.cpp b/client/core/utils/errorStrings.cpp index 591716ec9..05e481303 100644 --- a/client/core/utils/errorStrings.cpp +++ b/client/core/utils/errorStrings.cpp @@ -59,6 +59,7 @@ QString errorString(ErrorCode code) { case (ErrorCode::ImportInvalidConfigError): errorMessage = QObject::tr("The config does not contain any containers and credentials for connecting to the server"); break; case (ErrorCode::ImportBackupFileUseRestoreInstead): errorMessage = QObject::tr("Backup files cannot be imported here. Use 'Restore from backup' instead."); break; case (ErrorCode::RestoreBackupInvalidError): errorMessage = QObject::tr("Backup file is corrupted or has invalid format"); break; + case (ErrorCode::LegacyApiV1NotSupportedError): errorMessage = QObject::tr("This legacy Amnezia subscription format is no longer supported"); break; case (ErrorCode::ImportOpenConfigError): errorMessage = QObject::tr("Unable to open config file"); break; case (ErrorCode::NoInstalledContainersError): errorMessage = QObject::tr("VPN Protocols is not installed.\n Please install VPN container at first"); break; diff --git a/client/core/utils/serverConfigUtils.cpp b/client/core/utils/serverConfigUtils.cpp new file mode 100644 index 000000000..c6ec2a0fe --- /dev/null +++ b/client/core/utils/serverConfigUtils.cpp @@ -0,0 +1,122 @@ +#include "serverConfigUtils.h" + +#include +#include + +#include "core/models/selfhosted/selfHostedAdminServerConfig.h" +#include "core/utils/constants/apiKeys.h" +#include "core/utils/constants/configKeys.h" + +namespace +{ + +bool hasThirdPartyConfig(const QJsonObject &json) +{ + const QJsonArray containersArray = json.value(amnezia::configKey::containers).toArray(); + for (const QJsonValue &val : containersArray) { + const QJsonObject containerObj = val.toObject(); + for (auto it = containerObj.begin(); it != containerObj.end(); ++it) { + if (it.key() == amnezia::configKey::container) { + continue; + } + const QJsonObject protocolObj = it.value().toObject(); + if (protocolObj.contains(amnezia::configKey::isThirdPartyConfig) + && protocolObj.value(amnezia::configKey::isThirdPartyConfig).toBool()) { + return true; + } + } + } + return false; +} + +} // namespace + +namespace serverConfigUtils +{ + +bool isServerFromApi(const QJsonObject &serverConfigObject) +{ + const int configVersion = serverConfigObject.value(amnezia::configKey::configVersion).toInt(); + switch (configVersion) { + case ConfigSource::Telegram: + case ConfigSource::AmneziaGateway: + return true; + default: + return false; + } +} + +ConfigSource getConfigSource(const QJsonObject &serverConfigObject) +{ + return static_cast(serverConfigObject.value(amnezia::configKey::configVersion).toInt()); +} + +ConfigType configTypeFromJson(const QJsonObject &serverConfigObject) +{ + const int configVersion = serverConfigObject.value(amnezia::configKey::configVersion).toInt(); + + switch (configVersion) { + case ConfigSource::Telegram: { + constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT); + constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT); + + const QString apiEndpointValue = serverConfigObject.value(apiDefs::key::apiEndpoint).toString(); + + if (apiEndpointValue.contains(premiumV1Endpoint)) { + return ConfigType::AmneziaPremiumV1; + } + if (apiEndpointValue.contains(freeV2Endpoint)) { + return ConfigType::AmneziaFreeV2; + } + } + [[fallthrough]]; + case ConfigSource::AmneziaGateway: { + constexpr QLatin1String servicePremium("amnezia-premium"); + constexpr QLatin1String serviceFree("amnezia-free"); + constexpr QLatin1String serviceExternalPremium("external-premium"); + + const QJsonObject apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject(); + const QString serviceTypeStr = apiConfigObject.value(apiDefs::key::serviceType).toString(); + + if (serviceTypeStr == servicePremium) { + return ConfigType::AmneziaPremiumV2; + } + if (serviceTypeStr == serviceFree) { + return ConfigType::AmneziaFreeV3; + } + if (serviceTypeStr == serviceExternalPremium) { + return ConfigType::ExternalPremium; + } + break; + } + default: + break; + } + + if (hasThirdPartyConfig(serverConfigObject)) { + return ConfigType::Native; + } + + const amnezia::SelfHostedAdminServerConfig adminProbe = + amnezia::SelfHostedAdminServerConfig::fromJson(serverConfigObject); + return adminProbe.hasCredentials() ? ConfigType::SelfHostedAdmin : ConfigType::SelfHostedUser; +} + +bool isLegacyApiSubscription(ConfigType configType) +{ + return configType == ConfigType::AmneziaPremiumV1 || configType == ConfigType::AmneziaFreeV2; +} + +bool isApiV2Subscription(ConfigType configType) +{ + switch (configType) { + case ConfigType::AmneziaPremiumV2: + case ConfigType::AmneziaFreeV3: + case ConfigType::ExternalPremium: + return true; + default: + return false; + } +} + +} // namespace serverConfigUtils diff --git a/client/core/utils/serverConfigUtils.h b/client/core/utils/serverConfigUtils.h new file mode 100644 index 000000000..9657c0da9 --- /dev/null +++ b/client/core/utils/serverConfigUtils.h @@ -0,0 +1,40 @@ +#ifndef SERVERCONFIGUTILS_H +#define SERVERCONFIGUTILS_H + +#include + +namespace serverConfigUtils +{ + +enum ConfigType { + AmneziaFreeV2 = 0, + AmneziaFreeV3, + AmneziaPremiumV1, + AmneziaPremiumV2, + SelfHosted, + ExternalPremium, + + SelfHostedAdmin = 8, + SelfHostedUser, + Native, + Invalid +}; + +enum ConfigSource { + Telegram = 1, + AmneziaGateway +}; + +bool isServerFromApi(const QJsonObject &serverConfigObject); + +ConfigSource getConfigSource(const QJsonObject &serverConfigObject); + +ConfigType configTypeFromJson(const QJsonObject &serverConfigObject); + +bool isLegacyApiSubscription(ConfigType configType); + +bool isApiV2Subscription(ConfigType configType); + +} // namespace serverConfigUtils + +#endif // SERVERCONFIGUTILS_H diff --git a/client/tests/CMakeLists.txt b/client/tests/CMakeLists.txt index e541a4e6f..a8ba12246 100644 --- a/client/tests/CMakeLists.txt +++ b/client/tests/CMakeLists.txt @@ -95,15 +95,6 @@ target_link_libraries(test_servers_model_sync PRIVATE test_common ) -add_executable(test_gateway_stacks - testGatewayStacks.cpp -) - -target_link_libraries(test_gateway_stacks PRIVATE - Qt6::Test - test_common -) - add_executable(test_complex_operations testComplexOperations.cpp ) @@ -148,7 +139,6 @@ add_test(NAME DefaultServerChangeTest COMMAND test_default_server_change) add_test(NAME ServerEdgeCasesTest COMMAND test_server_edge_cases) add_test(NAME SignalOrderTest COMMAND test_signal_order) add_test(NAME ServersModelSyncTest COMMAND test_servers_model_sync) -add_test(NAME GatewayStacksTest COMMAND test_gateway_stacks) add_test(NAME ComplexOperationsTest COMMAND test_complex_operations) add_test(NAME SettingsSignalsTest COMMAND test_settings_signals) add_test(NAME UiServersModelAndControllerTest COMMAND test_ui_servers_model_and_controller) diff --git a/client/tests/testAdminSelfHostedExport.cpp b/client/tests/testAdminSelfHostedExport.cpp index 9cd8a6976..c2a4065f8 100644 --- a/client/tests/testAdminSelfHostedExport.cpp +++ b/client/tests/testAdminSelfHostedExport.cpp @@ -8,7 +8,7 @@ #include #include "core/controllers/coreController.h" -#include "core/models/serverConfig.h" +#include "core/utils/constants/configKeys.h" #include "vpnConnection.h" #include "secureQSettings.h" @@ -117,8 +117,8 @@ private slots: QVERIFY2(defaultServerChangedSpy.count() == 0, "defaultServerChanged signal should NOT be emitted (default is already 0)"); QVERIFY2(m_coreController->m_serversRepository->serversCount() > 0, "Server should be added"); - int serverIndex = m_coreController->m_serversRepository->defaultServerIndex(); - auto exportResult = m_coreController->m_exportController->generateFullAccessConfig(serverIndex); + const QString serverId = m_coreController->m_serversRepository->defaultServerId(); + auto exportResult = m_coreController->m_exportController->generateFullAccessConfig(serverId); QVERIFY2(exportResult.errorCode == ErrorCode::NoError, "Export should succeed"); QVERIFY2(!exportResult.config.isEmpty(), "Exported config should not be empty"); diff --git a/client/tests/testComplexOperations.cpp b/client/tests/testComplexOperations.cpp index 878a12510..1117b5e13 100644 --- a/client/tests/testComplexOperations.cpp +++ b/client/tests/testComplexOperations.cpp @@ -5,7 +5,8 @@ #include #include "core/controllers/coreController.h" -#include "core/models/serverConfig.h" +#include "core/models/serverDescription.h" +#include "tests/testServerRepositoryHelpers.h" #include "vpnConnection.h" #include "secureQSettings.h" @@ -37,7 +38,7 @@ private slots: void init() { m_settings->clearSettings(); if (m_coreController->m_serversModel) { - m_coreController->m_serversModel->updateModel(QVector(), -1, false); + m_coreController->m_serversModel->updateModel(QVector(), -1); } } @@ -65,35 +66,33 @@ private slots: QVERIFY2(m_coreController->m_serversRepository->serversCount() == 3, "Should have 3 servers"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 2, "Default should be index 2"); - ServerConfig server0 = m_coreController->m_serversController->getServerConfig(0); - server0.visit([](auto& arg) { - arg.description = "Edited First Server"; - }); - m_coreController->m_serversController->editServer(0, server0); + amnezia::test::setServerDescription(m_coreController->m_serversRepository, + m_coreController->m_serversController->getServerId(0), + QStringLiteral("Edited First Server")); QVERIFY2(serverEditedSpy.count() == 1, "serverEdited should be emitted"); - QString editedDesc0 = m_coreController->m_serversRepository->server(0).description(); + QString editedDesc0 = amnezia::test::serverDescription(m_coreController->m_serversRepository, + m_coreController->m_serversRepository->serverIdAt(0)); QVERIFY2(editedDesc0 == "Edited First Server", "First server should be edited"); - m_coreController->m_serversController->removeServer(1); + m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(1)); QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved should be emitted"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 2, "Should have 2 servers"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Default should be index 1 (was 2, removed 1)"); - m_coreController->m_serversController->setDefaultServerIndex(0); + m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(0)); QVERIFY2(defaultServerChangedSpy.count() == 4, "defaultServerChanged should be emitted again"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default should be index 0"); - ServerConfig server0After = m_coreController->m_serversController->getServerConfig(0); - server0After.visit([](auto& arg) { - arg.description = "Final Edited Server"; - }); - m_coreController->m_serversController->editServer(0, server0After); + amnezia::test::setServerDescription(m_coreController->m_serversRepository, + m_coreController->m_serversController->getServerId(0), + QStringLiteral("Final Edited Server")); QVERIFY2(serverEditedSpy.count() == 2, "serverEdited should be emitted again"); - QString finalDesc0 = m_coreController->m_serversRepository->server(0).description(); + QString finalDesc0 = amnezia::test::serverDescription(m_coreController->m_serversRepository, + m_coreController->m_serversRepository->serverIdAt(0)); QVERIFY2(finalDesc0 == "Final Edited Server", "First server should be edited again"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 2, "Final servers count should be 2"); diff --git a/client/tests/testDefaultServerChange.cpp b/client/tests/testDefaultServerChange.cpp index adffde62f..f0fb6163b 100644 --- a/client/tests/testDefaultServerChange.cpp +++ b/client/tests/testDefaultServerChange.cpp @@ -5,7 +5,8 @@ #include #include "core/controllers/coreController.h" -#include "core/models/serverConfig.h" +#include "core/models/serverDescription.h" +#include "tests/testServerRepositoryHelpers.h" #include "ui/models/serversModel.h" #include "vpnConnection.h" #include "secureQSettings.h" @@ -39,7 +40,7 @@ private slots: m_settings->clearSettings(); m_coreController->m_serversRepository->invalidateCache(); if (m_coreController->m_serversModel) { - m_coreController->m_serversModel->updateModel(QVector(), -1, false); + m_coreController->m_serversModel->updateModel(QVector(), -1); } } @@ -60,9 +61,10 @@ private slots: QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged); - m_coreController->m_serversController->setDefaultServerIndex(0); + m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(0)); QVERIFY2(defaultServerChangedSpy.count() == 1, "defaultServerChanged signal should be emitted"); - QVERIFY2(defaultServerChangedSpy.at(0).at(0).toInt() == 0, "defaultServerChanged should emit index 0"); + QVERIFY2(defaultServerChangedSpy.at(0).at(0).toString() == m_coreController->m_serversController->getServerId(0), + "defaultServerChanged should emit new default server id"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default server index should be 0"); if (m_coreController->m_serversModel) { @@ -70,9 +72,10 @@ private slots: QVERIFY2(modelDefaultIndex == 0, "Model should reflect default server"); } - m_coreController->m_serversController->setDefaultServerIndex(2); + m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(2)); QVERIFY2(defaultServerChangedSpy.count() == 2, "defaultServerChanged signal should be emitted again"); - QVERIFY2(defaultServerChangedSpy.at(1).at(0).toInt() == 2, "defaultServerChanged should emit index 2"); + QVERIFY2(defaultServerChangedSpy.at(1).at(0).toString() == m_coreController->m_serversController->getServerId(2), + "defaultServerChanged should emit new default server id"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 2, "Default server index should be 2"); } @@ -94,28 +97,28 @@ private slots: QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged); QSignalSpy serverRemovedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved); - m_coreController->m_serversController->removeServer(0); + m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(0)); QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 2, "Should have 2 servers"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Default should be index 1 (was 2, removed 0)"); - ServerConfig remainingServer1 = m_coreController->m_serversRepository->server(0); - ServerConfig remainingServer2 = m_coreController->m_serversRepository->server(1); - QString desc1 = remainingServer1.description(); - QString desc2 = remainingServer2.description(); + QString desc1 = amnezia::test::serverDescription(m_coreController->m_serversRepository, + m_coreController->m_serversRepository->serverIdAt(0)); + QString desc2 = amnezia::test::serverDescription(m_coreController->m_serversRepository, + m_coreController->m_serversRepository->serverIdAt(1)); QVERIFY2(desc1 == "Xray Server", "First remaining server should be Xray"); QVERIFY2(desc2 == "WireGuard Server", "Second remaining server should be WireGuard"); defaultServerChangedSpy.clear(); serverRemovedSpy.clear(); - m_coreController->m_serversController->removeServer(0); + m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(0)); QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 1, "Should have 1 server"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default should be index 0 (was 1, removed 0)"); - ServerConfig lastServer = m_coreController->m_serversRepository->server(0); - QString lastDesc = lastServer.description(); + QString lastDesc = amnezia::test::serverDescription(m_coreController->m_serversRepository, + m_coreController->m_serversRepository->serverIdAt(0)); QVERIFY2(lastDesc == "WireGuard Server", "Last server should be WireGuard"); } }; diff --git a/client/tests/testGatewayStacks.cpp b/client/tests/testGatewayStacks.cpp deleted file mode 100644 index acffe39bb..000000000 --- a/client/tests/testGatewayStacks.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include -#include -#include -#include - -#include "core/controllers/coreController.h" -#include "core/models/serverConfig.h" -#include "vpnConnection.h" -#include "secureQSettings.h" - -using namespace amnezia; - -class TestGatewayStacks : public QObject -{ - Q_OBJECT - -private: - CoreController* m_coreController; - SecureQSettings* m_settings; - -private slots: - void initTestCase() { - QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); - m_settings = new SecureQSettings(testOrg, "amnezia-client", nullptr, false); - - auto vpnConnection = QSharedPointer::create(nullptr, nullptr); - m_coreController = new CoreController(vpnConnection, m_settings, nullptr, this); - } - - void cleanupTestCase() { - m_settings->clearSettings(); - delete m_coreController; - delete m_settings; - } - - void init() { - m_settings->clearSettings(); - if (m_coreController->m_serversModel) { - m_coreController->m_serversModel->updateModel(QVector(), -1, false); - } - } - - void testGatewayStacksRecomputeOnServerOperations() { - QString awgKey = "vpn://AAABFHjadZBBT4QwEIX_ipkzS2wBJdyMB1cPXvbgwRgyQnclgZa0RTYS_rszXRa52Mt77TfzOu0EldEeG62sg-J9AhxPUEywF1CAuF3WTl4dRLCXhJIVpVuUEMpWdLdFKaH7FeUb9Mx3scpFk0XTRbOLvlSkKZsOz-Gi4BsdRiV_EGEydhwlg0tWynEZmd5Yz1bkoaK3xpvKtOU3_UFjOE3SsRs-tfIl1rVVzoWQOI9FzC3eonYcU4ZmgkPdwxz9fSYdYafVT4M7-lEJ80cEtTri0PrH_2q4wlW26f1lioe3p5uDsjQWoS_j_Ct2ipvGU6zO2PWtiivT8RPQudHYmqBXzl-3Yn2slBEMTtklgYt4C_Mv3ROMwA"; - - QSignalSpy gatewayStacksExpandedSpy(m_coreController->m_serversController, &ServersController::gatewayStacksExpanded); - QSignalSpy serverAddedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded); - QSignalSpy serverEditedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited); - QSignalSpy serverRemovedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved); - - auto importResult = m_coreController->m_importCoreController->extractConfigFromData(awgKey); - m_coreController->m_importCoreController->importConfig(importResult.config); - - QVERIFY2(serverAddedSpy.count() == 1, "serverAdded signal should be emitted"); - QVERIFY2(m_coreController->m_serversController->gatewayStacks().isEmpty(), "Gateway stacks should be empty for self-hosted servers"); - - ServerConfig serverConfig = m_coreController->m_serversController->getServerConfig(0); - serverConfig.visit([](auto& arg) { - arg.description = "Edited Server"; - }); - m_coreController->m_serversController->editServer(0, serverConfig); - - QVERIFY2(serverEditedSpy.count() == 1, "serverEdited signal should be emitted"); - - m_coreController->m_serversController->removeServer(0); - - QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted"); - QVERIFY2(m_coreController->m_serversController->gatewayStacks().isEmpty(), "Gateway stacks should remain empty"); - } -}; - -QTEST_MAIN(TestGatewayStacks) -#include "testGatewayStacks.moc" - diff --git a/client/tests/testMultipleImports.cpp b/client/tests/testMultipleImports.cpp index 44a0acd3c..932c0c902 100644 --- a/client/tests/testMultipleImports.cpp +++ b/client/tests/testMultipleImports.cpp @@ -6,7 +6,8 @@ #include #include "core/controllers/coreController.h" -#include "core/models/serverConfig.h" +#include "core/models/serverDescription.h" +#include "tests/testServerRepositoryHelpers.h" #include "vpnConnection.h" #include "secureQSettings.h" @@ -40,7 +41,7 @@ private slots: m_settings->clearSettings(); m_coreController->m_serversRepository->invalidateCache(); if (m_coreController->m_serversModel) { - m_coreController->m_serversModel->updateModel(QVector(), -1, false); + m_coreController->m_serversModel->updateModel(QVector(), -1); } } @@ -70,8 +71,8 @@ private slots: } QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "First server should be default"); - ServerConfig server1 = m_coreController->m_serversRepository->server(0); - QString desc1 = server1.description(); + QString desc1 = amnezia::test::serverDescription(m_coreController->m_serversRepository, + m_coreController->m_serversRepository->serverIdAt(0)); QVERIFY2(desc1 == "AWG Server", "First server description should match"); if (m_coreController->m_serversModel) { @@ -92,8 +93,8 @@ private slots: } QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Second server should be default"); - ServerConfig server2 = m_coreController->m_serversRepository->server(1); - QString desc2 = server2.description(); + QString desc2 = amnezia::test::serverDescription(m_coreController->m_serversRepository, + m_coreController->m_serversRepository->serverIdAt(1)); QVERIFY2(desc2 == "Xray Server", "Second server description should match"); if (m_coreController->m_serversModel) { @@ -114,8 +115,8 @@ private slots: } QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 2, "Third server should be default"); - ServerConfig server3 = m_coreController->m_serversRepository->server(2); - QString desc3 = server3.description(); + QString desc3 = amnezia::test::serverDescription(m_coreController->m_serversRepository, + m_coreController->m_serversRepository->serverIdAt(2)); QVERIFY2(desc3 == "WireGuard Server", "Third server description should match"); if (m_coreController->m_serversModel) { @@ -147,25 +148,25 @@ private slots: QVERIFY2(m_coreController->m_serversRepository->serversCount() == 2, "After two imports servers count should be 2"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Second server should be default"); - ServerConfig server0 = m_coreController->m_serversRepository->server(0); - ServerConfig server1 = m_coreController->m_serversRepository->server(1); - QString desc0 = server0.description(); - QString desc1 = server1.description(); + QString desc0 = amnezia::test::serverDescription(m_coreController->m_serversRepository, + m_coreController->m_serversRepository->serverIdAt(0)); + QString desc1 = amnezia::test::serverDescription(m_coreController->m_serversRepository, + m_coreController->m_serversRepository->serverIdAt(1)); QVERIFY2(desc0 == "AWG Server", "First server description should match"); QVERIFY2(desc1 == "Xray Server", "Second server description should match"); defaultServerChangedSpy.clear(); serverRemovedSpy.clear(); - m_coreController->m_serversController->removeServer(0); + m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(0)); QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted"); - QVERIFY2(serverRemovedSpy.at(0).at(0).toInt() == 0, "serverRemoved should emit index 0"); + QVERIFY2(serverRemovedSpy.at(0).at(1).toInt() == 0, "serverRemoved should emit removed index 0"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 1, "After removing first server, servers count should be 1"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "After removing first server, default index should be 0"); - ServerConfig remainingServer = m_coreController->m_serversRepository->server(0); - QString remainingDesc = remainingServer.description(); + QString remainingDesc = amnezia::test::serverDescription(m_coreController->m_serversRepository, + m_coreController->m_serversRepository->serverIdAt(0)); QVERIFY2(remainingDesc == "Xray Server", "Remaining server should be Xray Server"); if (m_coreController->m_serversModel) { @@ -177,10 +178,10 @@ private slots: defaultServerChangedSpy.clear(); serverRemovedSpy.clear(); - m_coreController->m_serversController->removeServer(0); + m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(0)); QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted"); - QVERIFY2(serverRemovedSpy.at(0).at(0).toInt() == 0, "serverRemoved should emit index 0"); + QVERIFY2(serverRemovedSpy.at(0).at(1).toInt() == 0, "serverRemoved should emit removed index 0"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 0, "After removing last server, servers count should be 0"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "After removing last server, default index should be 0"); diff --git a/client/tests/testSelfHostedServerSetup.cpp b/client/tests/testSelfHostedServerSetup.cpp index a44725556..fa1a783fb 100644 --- a/client/tests/testSelfHostedServerSetup.cpp +++ b/client/tests/testSelfHostedServerSetup.cpp @@ -7,8 +7,8 @@ #include #include "core/controllers/coreController.h" -#include "core/models/serverConfig.h" -#include "core/models/selfhosted/selfHostedServerConfig.h" +#include "core/models/serverDescription.h" +#include "core/models/selfhosted/selfHostedAdminServerConfig.h" #include "core/models/containerConfig.h" #include "core/models/protocols/awgProtocolConfig.h" #include "core/models/protocols/dnsProtocolConfig.h" @@ -60,21 +60,24 @@ private: qDebug() << "SSH connection successful. Output:" << sshOutput; } - void verifyAdminAccess(int serverIndex) { - ServerConfig server = m_coreController->m_serversRepository->server(serverIndex); - const SelfHostedServerConfig* selfHosted = server.as(); - QVERIFY2(selfHosted != nullptr, "Server config should be SelfHostedServerConfig"); + void verifyAdminAccess(int serverIndex) + { + const QString serverId = m_coreController->m_serversRepository->serverIdAt(serverIndex); + const auto adminCfg = m_coreController->m_serversRepository->selfHostedAdminConfig(serverId); + QVERIFY2(adminCfg.has_value(), "Server config should be SelfHostedAdminServerConfig"); + + const SelfHostedAdminServerConfig &selfHosted = *adminCfg; - QVERIFY2(selfHosted->hasCredentials(), + QVERIFY2(selfHosted.hasCredentials(), "Server should have credentials (admin access)"); - QVERIFY2(selfHosted->userName.has_value() && !selfHosted->userName.value().isEmpty(), + QVERIFY2(!selfHosted.userName.isEmpty(), "Server should have userName for admin access"); - QVERIFY2(selfHosted->password.has_value() && !selfHosted->password.value().isEmpty(), + QVERIFY2(!selfHosted.password.isEmpty(), "Server should have password for admin access"); - QVERIFY2(!selfHosted->isReadOnly(), + QVERIFY2(!selfHosted.isReadOnly(), "Server should not be read-only (should have admin access)"); if (m_coreController->m_serversModel) { @@ -143,7 +146,7 @@ private slots: void init() { m_settings->clearSettings(); if (m_coreController->m_serversModel) { - m_coreController->m_serversModel->updateModel(QVector(), -1, false); + m_coreController->m_serversModel->updateModel(QVector(), -1); } } @@ -177,10 +180,10 @@ private slots: int serverIndex = m_coreController->m_serversRepository->serversCount() - 1; qDebug() << "Server with Awg container added at index:" << serverIndex; - ServerConfig serverAfterAwg = m_coreController->m_serversRepository->server(serverIndex); - QVERIFY2(serverAfterAwg.isSelfHosted(), "Server should be self-hosted"); - const SelfHostedServerConfig* selfHostedAfterAwg = serverAfterAwg.as(); - QVERIFY2(selfHostedAfterAwg != nullptr, "Server config should be SelfHostedServerConfig"); + const auto adminAfterAwg = m_coreController->m_serversRepository->selfHostedAdminConfig( + m_coreController->m_serversRepository->serverIdAt(serverIndex)); + QVERIFY2(adminAfterAwg.has_value(), "Server should be self-hosted (admin)"); + const SelfHostedAdminServerConfig *selfHostedAfterAwg = &(*adminAfterAwg); QVERIFY2(selfHostedAfterAwg->defaultContainer == DockerContainer::Awg, "Default container should be Awg"); QVERIFY2(selfHostedAfterAwg->containers.contains(DockerContainer::Awg), "Server should have Awg container"); @@ -198,8 +201,9 @@ private slots: TransportProto dnsTransportProto = TransportProto::Udp; bool wasDnsInstalled = false; + const QString serverIdForOps = m_coreController->m_serversRepository->serverIdAt(serverIndex); ErrorCode installContainerError = m_coreController->m_installController->installContainer( - serverIndex, DockerContainer::Dns, dnsPort, dnsTransportProto, wasDnsInstalled); + serverIdForOps, DockerContainer::Dns, dnsPort, dnsTransportProto, wasDnsInstalled); QVERIFY2(installContainerError == ErrorCode::NoError, QString("installContainer for Dns should succeed. Error: %1") @@ -207,9 +211,10 @@ private slots: .toUtf8().constData()); qDebug() << "Dns container installed:" << wasDnsInstalled; - ServerConfig serverAfterDns = m_coreController->m_serversRepository->server(serverIndex); - const SelfHostedServerConfig* selfHostedAfterDns = serverAfterDns.as(); - QVERIFY2(selfHostedAfterDns != nullptr, "Server config should be SelfHostedServerConfig"); + const auto adminAfterDns = m_coreController->m_serversRepository->selfHostedAdminConfig( + m_coreController->m_serversRepository->serverIdAt(serverIndex)); + QVERIFY2(adminAfterDns.has_value(), "Server config should be SelfHostedAdminServerConfig"); + const SelfHostedAdminServerConfig *selfHostedAfterDns = &(*adminAfterDns); QVERIFY2(selfHostedAfterDns->containers.contains(DockerContainer::Awg), "Server should still have Awg container"); QVERIFY2(selfHostedAfterDns->containers.contains(DockerContainer::Dns), "Server should have Dns container"); QVERIFY2(selfHostedAfterDns->containers.size() == 2, @@ -242,16 +247,18 @@ private slots: verifySshConnection(credentials); - SelfHostedServerConfig serverConfig; + SelfHostedAdminServerConfig serverConfig; serverConfig.hostName = credentials.hostName; serverConfig.userName = credentials.userName; serverConfig.password = credentials.secretData; serverConfig.port = credentials.port; serverConfig.description = m_coreController->m_appSettingsRepository->nextAvailableServerName(); + serverConfig.displayName = serverConfig.description.isEmpty() ? serverConfig.hostName : serverConfig.description; serverConfig.defaultContainer = DockerContainer::None; QSignalSpy serverAddedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded); - m_coreController->m_serversController->addServer(ServerConfig(serverConfig)); + m_coreController->m_serversRepository->addServer(QString(), serverConfig.toJson(), + serverConfigUtils::ConfigType::SelfHostedAdmin); QVERIFY2(serverAddedSpy.count() == 1, "serverAdded signal should be emitted"); QVERIFY2(m_coreController->m_serversRepository->serversCount() > 0, "Server should be added"); @@ -259,23 +266,25 @@ private slots: int serverIndex = m_coreController->m_serversRepository->serversCount() - 1; qDebug() << "Empty server added at index:" << serverIndex; - ServerConfig addedServer = m_coreController->m_serversRepository->server(serverIndex); - QVERIFY2(addedServer.isSelfHosted(), "Added server should be self-hosted"); - const SelfHostedServerConfig* selfHosted = addedServer.as(); - QVERIFY2(selfHosted != nullptr, "Server config should be SelfHostedServerConfig"); + const auto addedAdmin = m_coreController->m_serversRepository->selfHostedAdminConfig( + m_coreController->m_serversRepository->serverIdAt(serverIndex)); + QVERIFY2(addedAdmin.has_value(), "Added server should be self-hosted admin"); + const SelfHostedAdminServerConfig *selfHosted = &(*addedAdmin); QVERIFY2(selfHosted->containers.isEmpty(), "Server should have no containers initially"); QVERIFY2(selfHosted->defaultContainer == DockerContainer::None, "Default container should be None"); - ErrorCode scanError = m_coreController->m_installController->scanServerForInstalledContainers(serverIndex); + const QString scanServerId = m_coreController->m_serversRepository->serverIdAt(serverIndex); + ErrorCode scanError = m_coreController->m_installController->scanServerForInstalledContainers(scanServerId); QVERIFY2(scanError == ErrorCode::NoError, QString("Server scan should succeed. Error: %1") .arg(static_cast(scanError)) .toUtf8().constData()); qDebug() << "Server scan completed successfully"; - ServerConfig scannedServer = m_coreController->m_serversRepository->server(serverIndex); - const SelfHostedServerConfig* scannedSelfHosted = scannedServer.as(); - QVERIFY2(scannedSelfHosted != nullptr, "Scanned server config should be SelfHostedServerConfig"); + const auto scannedAdmin = m_coreController->m_serversRepository->selfHostedAdminConfig( + m_coreController->m_serversRepository->serverIdAt(serverIndex)); + QVERIFY2(scannedAdmin.has_value(), "Scanned server config should be SelfHostedAdminServerConfig"); + const SelfHostedAdminServerConfig *scannedSelfHosted = &(*scannedAdmin); QMap containers = scannedSelfHosted->containers; int containersCount = containers.size(); @@ -336,24 +345,27 @@ private slots: int serverIndex = m_coreController->m_serversRepository->serversCount() - 1; qDebug() << "Server with Awg container added at index:" << serverIndex; - ServerConfig serverBeforeRemoval = m_coreController->m_serversRepository->server(serverIndex); - const SelfHostedServerConfig* selfHostedBeforeRemoval = serverBeforeRemoval.as(); - QVERIFY2(selfHostedBeforeRemoval != nullptr, "Server config should be SelfHostedServerConfig"); + const auto adminBeforeRemoval = m_coreController->m_serversRepository->selfHostedAdminConfig( + m_coreController->m_serversRepository->serverIdAt(serverIndex)); + QVERIFY2(adminBeforeRemoval.has_value(), "Server config should be SelfHostedAdminServerConfig"); + const SelfHostedAdminServerConfig *selfHostedBeforeRemoval = &(*adminBeforeRemoval); QVERIFY2(!selfHostedBeforeRemoval->containers.isEmpty(), "Server should have containers before removal"); QVERIFY2(selfHostedBeforeRemoval->defaultContainer != DockerContainer::None, "Server should have default container before removal"); qDebug() << "Containers before removal:" << selfHostedBeforeRemoval->containers.size(); - ErrorCode removeError = m_coreController->m_installController->removeAllContainers(serverIndex); + const QString removeServerId = m_coreController->m_serversRepository->serverIdAt(serverIndex); + ErrorCode removeError = m_coreController->m_installController->removeAllContainers(removeServerId); QVERIFY2(removeError == ErrorCode::NoError, QString("removeAllContainers should succeed. Error: %1") .arg(static_cast(removeError)) .toUtf8().constData()); qDebug() << "All containers removed successfully"; - ServerConfig serverAfterRemoval = m_coreController->m_serversRepository->server(serverIndex); - const SelfHostedServerConfig* selfHostedAfterRemoval = serverAfterRemoval.as(); - QVERIFY2(selfHostedAfterRemoval != nullptr, "Server config should be SelfHostedServerConfig"); + const auto adminAfterRemoval = m_coreController->m_serversRepository->selfHostedAdminConfig( + m_coreController->m_serversRepository->serverIdAt(serverIndex)); + QVERIFY2(adminAfterRemoval.has_value(), "Server config should be SelfHostedAdminServerConfig"); + const SelfHostedAdminServerConfig *selfHostedAfterRemoval = &(*adminAfterRemoval); QVERIFY2(selfHostedAfterRemoval->containers.isEmpty(), "Server should have no containers after removal"); diff --git a/client/tests/testServerEdgeCases.cpp b/client/tests/testServerEdgeCases.cpp index 53c358d20..91dba71fd 100644 --- a/client/tests/testServerEdgeCases.cpp +++ b/client/tests/testServerEdgeCases.cpp @@ -1,13 +1,15 @@ #include -#include #include #include #include #include "core/controllers/coreController.h" -#include "core/models/serverConfig.h" +#include "core/repositories/secureServersRepository.h" +#include "core/models/serverDescription.h" +#include "core/models/selfhosted/selfHostedAdminServerConfig.h" #include "vpnConnection.h" #include "secureQSettings.h" +#include "core/utils/serverConfigUtils.h" using namespace amnezia; @@ -38,7 +40,7 @@ private slots: m_settings->clearSettings(); m_coreController->m_serversRepository->invalidateCache(); if (m_coreController->m_serversModel) { - m_coreController->m_serversModel->updateModel(QVector(), -1, false); + m_coreController->m_serversModel->updateModel(QVector(), -1); } } @@ -54,27 +56,32 @@ private slots: QSignalSpy serverEditedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited); QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged); - m_coreController->m_serversController->removeServer(-1); + m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(-1)); QVERIFY2(serverRemovedSpy.count() == 0, "serverRemoved should NOT be emitted for invalid index"); - m_coreController->m_serversController->removeServer(10); + m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(10)); QVERIFY2(serverRemovedSpy.count() == 0, "serverRemoved should NOT be emitted for invalid index"); - m_coreController->m_serversController->removeServer(100); + m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(100)); QVERIFY2(serverRemovedSpy.count() == 0, "serverRemoved should NOT be emitted for invalid index"); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 1, "Server count should remain 1"); - ServerConfig serverConfig = m_coreController->m_serversController->getServerConfig(0); - m_coreController->m_serversController->editServer(-1, serverConfig); + const QString validServerId = m_coreController->m_serversController->getServerId(0); + const serverConfigUtils::ConfigType editKind = + m_coreController->m_serversRepository->serverKind(validServerId); + + m_coreController->m_serversRepository->editServer(m_coreController->m_serversController->getServerId(-1), + QJsonObject(), editKind); QVERIFY2(serverEditedSpy.count() == 0, "serverEdited should NOT be emitted for invalid index"); - m_coreController->m_serversController->editServer(10, serverConfig); + m_coreController->m_serversRepository->editServer(m_coreController->m_serversController->getServerId(10), + QJsonObject(), editKind); QVERIFY2(serverEditedSpy.count() == 0, "serverEdited should NOT be emitted for invalid index"); - m_coreController->m_serversController->setDefaultServerIndex(-1); + m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(-1)); QVERIFY2(defaultServerChangedSpy.count() == 0, "defaultServerChanged should NOT be emitted for invalid index"); - m_coreController->m_serversController->setDefaultServerIndex(10); + m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(10)); QVERIFY2(defaultServerChangedSpy.count() == 0, "defaultServerChanged should NOT be emitted for invalid index"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default server index should remain 0"); } @@ -86,14 +93,15 @@ private slots: QVERIFY2(m_coreController->m_serversRepository->serversCount() == 0, "Should start with 0 servers"); - ServerConfig emptyConfig = SelfHostedServerConfig{}; - m_coreController->m_serversController->removeServer(0); + m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(0)); QVERIFY2(serverRemovedSpy.count() == 0, "serverRemoved should NOT be emitted for empty repository"); - m_coreController->m_serversController->editServer(0, emptyConfig); + m_coreController->m_serversRepository->editServer(m_coreController->m_serversController->getServerId(0), + SelfHostedAdminServerConfig {}.toJson(), + serverConfigUtils::ConfigType::SelfHostedAdmin); QVERIFY2(serverEditedSpy.count() == 0, "serverEdited should NOT be emitted for empty repository"); - m_coreController->m_serversController->setDefaultServerIndex(0); + m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(0)); QVERIFY2(defaultServerChangedSpy.count() == 0, "defaultServerChanged should NOT be emitted for empty repository"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default server index should be 0 for empty repository"); diff --git a/client/tests/testServerEdit.cpp b/client/tests/testServerEdit.cpp index 57f18ef12..4e101eb6e 100644 --- a/client/tests/testServerEdit.cpp +++ b/client/tests/testServerEdit.cpp @@ -5,7 +5,8 @@ #include #include "core/controllers/coreController.h" -#include "core/models/serverConfig.h" +#include "core/models/serverDescription.h" +#include "tests/testServerRepositoryHelpers.h" #include "ui/models/serversModel.h" #include "vpnConnection.h" #include "secureQSettings.h" @@ -39,7 +40,7 @@ private slots: m_settings->clearSettings(); m_coreController->m_serversRepository->invalidateCache(); if (m_coreController->m_serversModel) { - m_coreController->m_serversModel->updateModel(QVector(), -1, false); + m_coreController->m_serversModel->updateModel(QVector(), -1); } } @@ -52,20 +53,17 @@ private slots: QVERIFY2(importFinishedSpy.count() == 1, "Import should succeed"); QSignalSpy serverEditedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited); - QSignalSpy gatewayStacksExpandedSpy(m_coreController->m_serversController, &ServersController::gatewayStacksExpanded); - ServerConfig serverConfig = m_coreController->m_serversController->getServerConfig(0); - serverConfig.visit([](auto& arg) { - arg.description = "Edited AWG Server"; - }); - - m_coreController->m_serversController->editServer(0, serverConfig); + amnezia::test::setServerDescription(m_coreController->m_serversRepository, + m_coreController->m_serversController->getServerId(0), + QStringLiteral("Edited AWG Server")); QVERIFY2(serverEditedSpy.count() == 1, "serverEdited signal should be emitted"); - QVERIFY2(serverEditedSpy.at(0).at(0).toInt() == 0, "serverEdited should emit index 0"); + QVERIFY2(serverEditedSpy.at(0).at(0).toString() == m_coreController->m_serversRepository->serverIdAt(0), + "serverEdited should emit edited server id"); - ServerConfig editedServer = m_coreController->m_serversRepository->server(0); - QString editedDesc = editedServer.description(); + const QString editedDesc = amnezia::test::serverDescription(m_coreController->m_serversRepository, + m_coreController->m_serversRepository->serverIdAt(0)); QVERIFY2(editedDesc == "Edited AWG Server", "Server description should be updated"); if (m_coreController->m_serversModel) { @@ -87,20 +85,16 @@ private slots: QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged); - ServerConfig defaultServerConfig = m_coreController->m_serversController->getServerConfig(1); - defaultServerConfig.visit([](auto& arg) { - arg.description = "Edited Default Server"; - }); - m_coreController->m_serversController->editServer(1, defaultServerConfig); + amnezia::test::setServerDescription(m_coreController->m_serversRepository, + m_coreController->m_serversController->getServerId(1), + QStringLiteral("Edited Default Server")); QVERIFY2(defaultServerChangedSpy.count() == 0, "defaultServerChanged should NOT be emitted when editing default server"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Default server index should remain 1"); - ServerConfig nonDefaultServerConfig = m_coreController->m_serversController->getServerConfig(0); - nonDefaultServerConfig.visit([](auto& arg) { - arg.description = "Edited Non-Default Server"; - }); - m_coreController->m_serversController->editServer(0, nonDefaultServerConfig); + amnezia::test::setServerDescription(m_coreController->m_serversRepository, + m_coreController->m_serversController->getServerId(0), + QStringLiteral("Edited Non-Default Server")); QVERIFY2(defaultServerChangedSpy.count() == 0, "defaultServerChanged should NOT be emitted when editing non-default server"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 1, "Default server index should remain 1"); diff --git a/client/tests/testServerRepositoryHelpers.h b/client/tests/testServerRepositoryHelpers.h new file mode 100644 index 000000000..13c63b47c --- /dev/null +++ b/client/tests/testServerRepositoryHelpers.h @@ -0,0 +1,93 @@ +#ifndef TESTSERVERREPOSITORYHELPERS_H +#define TESTSERVERREPOSITORYHELPERS_H + +#include +#include + +#include "core/repositories/secureServersRepository.h" +#include "core/utils/serverConfigUtils.h" + +namespace amnezia::test +{ + +inline QString serverDescription(SecureServersRepository *repo, const QString &serverId) +{ + switch (repo->serverKind(serverId)) { + case serverConfigUtils::ConfigType::SelfHostedAdmin: { + const auto cfg = repo->selfHostedAdminConfig(serverId); + return cfg.has_value() ? cfg->description : QString(); + } + case serverConfigUtils::ConfigType::SelfHostedUser: { + const auto cfg = repo->selfHostedUserConfig(serverId); + return cfg.has_value() ? cfg->description : QString(); + } + case serverConfigUtils::ConfigType::Native: { + const auto cfg = repo->nativeConfig(serverId); + return cfg.has_value() ? cfg->description : QString(); + } + case serverConfigUtils::ConfigType::AmneziaPremiumV2: + case serverConfigUtils::ConfigType::AmneziaFreeV3: + case serverConfigUtils::ConfigType::ExternalPremium: { + const auto cfg = repo->apiV2Config(serverId); + return cfg.has_value() ? cfg->description : QString(); + } + case serverConfigUtils::ConfigType::AmneziaPremiumV1: + case serverConfigUtils::ConfigType::AmneziaFreeV2: { + const auto cfg = repo->legacyApiConfig(serverId); + return cfg.has_value() ? cfg->description : QString(); + } + case serverConfigUtils::ConfigType::Invalid: + default: + return {}; + } +} + +inline void setServerDescription(SecureServersRepository *repo, const QString &serverId, const QString &description) +{ + const serverConfigUtils::ConfigType kind = repo->serverKind(serverId); + switch (kind) { + case serverConfigUtils::ConfigType::SelfHostedAdmin: { + auto cfg = repo->selfHostedAdminConfig(serverId); + if (!cfg.has_value()) return; + cfg->description = description; + cfg->displayName = description; + repo->editServer(serverId, cfg->toJson(), kind); + return; + } + case serverConfigUtils::ConfigType::SelfHostedUser: { + auto cfg = repo->selfHostedUserConfig(serverId); + if (!cfg.has_value()) return; + cfg->description = description; + cfg->displayName = description; + repo->editServer(serverId, cfg->toJson(), kind); + return; + } + case serverConfigUtils::ConfigType::Native: { + auto cfg = repo->nativeConfig(serverId); + if (!cfg.has_value()) return; + cfg->description = description; + cfg->displayName = description; + repo->editServer(serverId, cfg->toJson(), kind); + return; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV2: + case serverConfigUtils::ConfigType::AmneziaFreeV3: + case serverConfigUtils::ConfigType::ExternalPremium: { + auto cfg = repo->apiV2Config(serverId); + if (!cfg.has_value()) return; + cfg->description = description; + cfg->displayName = description; + repo->editServer(serverId, cfg->toJson(), kind); + return; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV1: + case serverConfigUtils::ConfigType::AmneziaFreeV2: + case serverConfigUtils::ConfigType::Invalid: + default: + return; + } +} + +} // namespace amnezia::test + +#endif diff --git a/client/tests/testServersModelSync.cpp b/client/tests/testServersModelSync.cpp index a1e63ccce..12b1288fe 100644 --- a/client/tests/testServersModelSync.cpp +++ b/client/tests/testServersModelSync.cpp @@ -5,7 +5,8 @@ #include #include "core/controllers/coreController.h" -#include "core/models/serverConfig.h" +#include "core/models/serverDescription.h" +#include "tests/testServerRepositoryHelpers.h" #include "ui/models/serversModel.h" #include "vpnConnection.h" #include "secureQSettings.h" @@ -38,7 +39,7 @@ private slots: void init() { m_settings->clearSettings(); if (m_coreController->m_serversModel) { - m_coreController->m_serversModel->updateModel(QVector(), -1, false); + m_coreController->m_serversModel->updateModel(QVector(), -1); } } @@ -58,16 +59,14 @@ private slots: QString modelDesc1 = m_coreController->m_serversModel->data(m_coreController->m_serversModel->index(0, 0), ServersModel::NameRole).toString(); QVERIFY2(modelDesc1 == "AWG Server", "Model should have correct server name"); - ServerConfig serverConfig = m_coreController->m_serversController->getServerConfig(0); - serverConfig.visit([](auto& arg) { - arg.description = "Edited AWG Server"; - }); - m_coreController->m_serversController->editServer(0, serverConfig); + amnezia::test::setServerDescription(m_coreController->m_serversRepository, + m_coreController->m_serversController->getServerId(0), + QStringLiteral("Edited AWG Server")); QString modelDesc2 = m_coreController->m_serversModel->data(m_coreController->m_serversModel->index(0, 0), ServersModel::NameRole).toString(); QVERIFY2(modelDesc2 == "Edited AWG Server", "Model should be updated after edit"); - m_coreController->m_serversController->removeServer(0); + m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(0)); QVERIFY2(m_coreController->m_serversModel->rowCount() == 0, "Model should have 0 rows after removal"); } @@ -98,7 +97,7 @@ private slots: QVERIFY2(!isDefault1, "Server 1 should not be default"); QVERIFY2(isDefault2, "Server 2 should be default"); - m_coreController->m_serversController->setDefaultServerIndex(0); + m_coreController->m_serversController->setDefaultServer(m_coreController->m_serversController->getServerId(0)); isDefault0 = m_coreController->m_serversModel->data(m_coreController->m_serversModel->index(0, 0), ServersModel::IsDefaultRole).toBool(); isDefault2 = m_coreController->m_serversModel->data(m_coreController->m_serversModel->index(2, 0), ServersModel::IsDefaultRole).toBool(); diff --git a/client/tests/testSettingsSignals.cpp b/client/tests/testSettingsSignals.cpp index e0308a56c..22c5c0677 100644 --- a/client/tests/testSettingsSignals.cpp +++ b/client/tests/testSettingsSignals.cpp @@ -6,7 +6,6 @@ #include #include "core/controllers/coreController.h" -#include "core/models/serverConfig.h" #include "ui/controllers/settingsUiController.h" #include "ui/controllers/languageUiController.h" #include "ui/models/allowedDnsModel.h" diff --git a/client/tests/testSignalOrder.cpp b/client/tests/testSignalOrder.cpp index 97b7cb29e..8c6cf0d65 100644 --- a/client/tests/testSignalOrder.cpp +++ b/client/tests/testSignalOrder.cpp @@ -5,7 +5,7 @@ #include #include "core/controllers/coreController.h" -#include "core/models/serverConfig.h" +#include "core/models/serverDescription.h" #include "vpnConnection.h" #include "secureQSettings.h" @@ -38,7 +38,7 @@ private slots: m_settings->clearSettings(); m_coreController->m_serversRepository->invalidateCache(); if (m_coreController->m_serversModel) { - m_coreController->m_serversModel->updateModel(QVector(), -1, false); + m_coreController->m_serversModel->updateModel(QVector(), -1); } } @@ -73,11 +73,12 @@ private slots: QSignalSpy serverRemovedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved); QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged); - m_coreController->m_serversController->removeServer(1); + m_coreController->m_serversController->removeServer(m_coreController->m_serversController->getServerId(1)); QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted"); QVERIFY2(defaultServerChangedSpy.count() == 1, "defaultServerChanged signal should be emitted when removing default server"); - QVERIFY2(defaultServerChangedSpy.at(0).at(0).toInt() == 0, "defaultServerChanged should emit new default index 0"); + QVERIFY2(defaultServerChangedSpy.at(0).at(0).toString() == m_coreController->m_serversRepository->defaultServerId(), + "defaultServerChanged should emit new default server id"); QVERIFY2(m_coreController->m_serversRepository->defaultServerIndex() == 0, "Default server index should be 0"); } }; diff --git a/client/tests/testUiServersModelAndController.cpp b/client/tests/testUiServersModelAndController.cpp index 16039e988..ca67ab612 100644 --- a/client/tests/testUiServersModelAndController.cpp +++ b/client/tests/testUiServersModelAndController.cpp @@ -8,7 +8,7 @@ #include #include "core/controllers/coreController.h" -#include "core/models/serverConfig.h" +#include "core/models/serverDescription.h" #include "core/controllers/selfhosted/importController.h" #include "ui/models/serversModel.h" #include "ui/models/containersModel.h" @@ -23,6 +23,18 @@ using namespace amnezia; using namespace amnezia; +namespace { +int defaultServerRow(const QVector &descriptions, const QString &defaultServerId) +{ + for (int i = 0; i < descriptions.size(); ++i) { + if (descriptions.at(i).serverId == defaultServerId) { + return i; + } + } + return -1; +} +} // namespace + class TestUiServersModelAndController : public QObject { Q_OBJECT @@ -119,7 +131,7 @@ private slots: void init() { m_settings->clearSettings(); if (m_coreController->m_serversModel) { - m_coreController->m_serversModel->updateModel(QVector(), -1, false); + m_coreController->m_serversModel->updateModel(QVector(), -1); } } @@ -166,11 +178,9 @@ private slots: } if (m_coreController->m_serversUiController) { - m_coreController->m_serversUiController->setProcessedServerIndex(serverIndex); - - ServerConfig serverConfig = m_coreController->m_serversRepository->server(serverIndex); - QString actualServerName = serverConfig.description(); - QString containerName = ContainerUtils::containerHumanNames().value(DockerContainer::Awg); + m_coreController->m_serversUiController->setProcessedServerId( + m_coreController->m_serversUiController->getServerId(0)); + QString hostName = "test.example.com"; QString collapsedDescription = m_coreController->m_serversUiController->getDefaultServerDescriptionCollapsed(); @@ -261,27 +271,29 @@ private slots: m_coreController->m_importCoreController->importConfig(configNoDns); QVERIFY2(importFinishedSpy.count() == 1, "importFinished should be emitted"); m_coreController->m_appSettingsRepository->setUseAmneziaDns(false); - m_coreController->m_serversModel->updateModel( - m_coreController->m_serversRepository->servers(), - m_coreController->m_serversRepository->defaultServerIndex(), + QVector descriptionsNoDns = m_coreController->m_serversController->buildServerDescriptions( m_coreController->m_appSettingsRepository->useAmneziaDns()); + const QString defIdNoDns = m_coreController->m_serversRepository->defaultServerId(); + m_coreController->m_serversModel->updateModel(descriptionsNoDns, defaultServerRow(descriptionsNoDns, defIdNoDns)); QString descNoDns = m_coreController->m_serversModel->data( m_coreController->m_serversModel->index(0, 0), ServersModel::ServerDescriptionRole).toString(); QVERIFY2(descNoDns == "test.example.com", QString("Without Amnezia DNS expected 'test.example.com', got '%1'").arg(descNoDns).toUtf8().constData()); - m_coreController->m_serversRepository->setServersArray(QJsonArray()); - m_coreController->m_serversRepository->setDefaultServer(0); + m_coreController->m_serversRepository->clearServers(); + if (m_coreController->m_serversRepository->serversCount() > 0) { + m_coreController->m_serversRepository->setDefaultServer(m_coreController->m_serversRepository->serverIdAt(0)); + } QJsonObject configWithDns = createServerDescriptionTestConfig(true); m_coreController->m_importCoreController->importConfig(configWithDns); QVERIFY2(m_coreController->m_serversRepository->serversCount() == 1, "Server should be imported"); m_coreController->m_appSettingsRepository->setUseAmneziaDns(true); - m_coreController->m_serversModel->updateModel( - m_coreController->m_serversRepository->servers(), - m_coreController->m_serversRepository->defaultServerIndex(), + QVector descriptionsWithDns = m_coreController->m_serversController->buildServerDescriptions( m_coreController->m_appSettingsRepository->useAmneziaDns()); + const QString defIdWithDns = m_coreController->m_serversRepository->defaultServerId(); + m_coreController->m_serversModel->updateModel(descriptionsWithDns, defaultServerRow(descriptionsWithDns, defIdWithDns)); QString descWithDns = m_coreController->m_serversModel->data( m_coreController->m_serversModel->index(0, 0), ServersModel::ServerDescriptionRole).toString(); diff --git a/client/ui/controllers/api/subscriptionUiController.cpp b/client/ui/controllers/api/subscriptionUiController.cpp index 75d96553e..5dee205dc 100644 --- a/client/ui/controllers/api/subscriptionUiController.cpp +++ b/client/ui/controllers/api/subscriptionUiController.cpp @@ -2,14 +2,13 @@ #include "amneziaApplication.h" #include "core/configurators/wireguardConfigurator.h" -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" #include "core/utils/api/apiUtils.h" #include "core/utils/qrCodeUtils.h" #include "ui/controllers/systemController.h" #include "version.h" -#include "core/models/serverConfig.h" #include #include #include @@ -67,7 +66,17 @@ SubscriptionUiController::SubscriptionUiController(ServersController* serversCon ApiDevicesModel* apiDevicesModel, SettingsController* settingsController, QObject *parent) - : QObject(parent), m_serversController(serversController), m_apiServicesModel(apiServicesModel), m_servicesCatalogController(servicesCatalogController), m_subscriptionController(subscriptionController), m_apiSubscriptionPlansModel(apiSubscriptionPlansModel), m_apiBenefitsModel(apiBenefitsModel), m_apiAccountInfoModel(apiAccountInfoModel), m_apiCountryModel(apiCountryModel), m_apiDevicesModel(apiDevicesModel), m_settingsController(settingsController) + : QObject(parent), + m_serversController(serversController), + m_apiServicesModel(apiServicesModel), + m_servicesCatalogController(servicesCatalogController), + m_subscriptionController(subscriptionController), + m_apiSubscriptionPlansModel(apiSubscriptionPlansModel), + m_apiBenefitsModel(apiBenefitsModel), + m_apiAccountInfoModel(apiAccountInfoModel), + m_apiCountryModel(apiCountryModel), + m_apiDevicesModel(apiDevicesModel), + m_settingsController(settingsController) { connect(m_apiServicesModel, &ApiServicesModel::serviceSelectionChanged, this, [this]() { ApiServicesModel::ApiServicesData selectedServiceData = m_apiServicesModel->selectedServiceData(); @@ -76,14 +85,14 @@ SubscriptionUiController::SubscriptionUiController(ServersController* serversCon }); } -bool SubscriptionUiController::exportVpnKey(int serverIndex, const QString &fileName) +bool SubscriptionUiController::exportVpnKey(const QString &serverId, const QString &fileName) { if (fileName.isEmpty()) { emit errorOccurred(ErrorCode::PermissionsError); return false; } - prepareVpnKeyExport(serverIndex); + prepareVpnKeyExport(serverId); if (m_vpnKey.isEmpty()) { emit errorOccurred(ErrorCode::ApiConfigEmptyError); return false; @@ -93,7 +102,8 @@ bool SubscriptionUiController::exportVpnKey(int serverIndex, const QString &file return true; } -bool SubscriptionUiController::exportNativeConfig(int serverIndex, const QString &serverCountryCode, const QString &fileName) + +bool SubscriptionUiController::exportNativeConfig(const QString &serverId, const QString &serverCountryCode, const QString &fileName) { if (fileName.isEmpty()) { emit errorOccurred(ErrorCode::PermissionsError); @@ -101,7 +111,7 @@ bool SubscriptionUiController::exportNativeConfig(int serverIndex, const QString } QString nativeConfig; - ErrorCode errorCode = m_subscriptionController->exportNativeConfig(serverIndex, serverCountryCode, nativeConfig); + ErrorCode errorCode = m_subscriptionController->exportNativeConfig(serverId, serverCountryCode, nativeConfig); if (errorCode != ErrorCode::NoError) { emit errorOccurred(errorCode); return false; @@ -111,9 +121,10 @@ bool SubscriptionUiController::exportNativeConfig(int serverIndex, const QString return true; } -bool SubscriptionUiController::revokeNativeConfig(int serverIndex, const QString &serverCountryCode) + +bool SubscriptionUiController::revokeNativeConfig(const QString &serverId, const QString &serverCountryCode) { - ErrorCode errorCode = m_subscriptionController->revokeNativeConfig(serverIndex, serverCountryCode); + ErrorCode errorCode = m_subscriptionController->revokeNativeConfig(serverId, serverCountryCode); if (errorCode != ErrorCode::NoError) { emit errorOccurred(errorCode); return false; @@ -121,10 +132,11 @@ bool SubscriptionUiController::revokeNativeConfig(int serverIndex, const QString return true; } -void SubscriptionUiController::prepareVpnKeyExport(int serverIndex) + +void SubscriptionUiController::prepareVpnKeyExport(const QString &serverId) { QString vpnKey; - ErrorCode errorCode = m_subscriptionController->prepareVpnKeyExport(serverIndex, vpnKey); + ErrorCode errorCode = m_subscriptionController->prepareVpnKeyExport(serverId, vpnKey); if (errorCode != ErrorCode::NoError) { emit errorOccurred(errorCode); return; @@ -140,6 +152,7 @@ void SubscriptionUiController::prepareVpnKeyExport(int serverIndex) emit vpnKeyExportReady(); } + void SubscriptionUiController::copyVpnKeyToClipboard() { auto clipboard = amnApp->getClipboard(); @@ -170,14 +183,12 @@ bool SubscriptionUiController::importPremiumFromAppStore(const QString &storePro productId = QStringLiteral("amnezia_premium_6_month"); } - ServerConfig serverConfig; int duplicateServerIndex = -1; ErrorCode errorCode = m_subscriptionController->processAppStorePurchase( m_apiServicesModel->getCountryCode(), m_apiServicesModel->getSelectedServiceType(), m_apiServicesModel->getSelectedServiceProtocol(), productId, - serverConfig, &duplicateServerIndex); if (errorCode != ErrorCode::NoError) { @@ -260,11 +271,8 @@ bool SubscriptionUiController::importFreeFromGateway() } SubscriptionController::ProtocolData protocolData = m_subscriptionController->generateProtocolData(serviceProtocol); - - ServerConfig serverConfig; ErrorCode errorCode = m_subscriptionController->importServiceFromGateway(userCountryCode, serviceType, - serviceProtocol, protocolData, - serverConfig); + serviceProtocol, protocolData); if (errorCode == ErrorCode::NoError) { emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName())); @@ -278,12 +286,10 @@ bool SubscriptionUiController::importFreeFromGateway() bool SubscriptionUiController::importTrialFromGateway(const QString &email) { emit trialEmailError(QString()); - ServerConfig serverConfig; ErrorCode errorCode = m_subscriptionController->importTrialFromGateway(m_apiServicesModel->getCountryCode(), m_apiServicesModel->getSelectedServiceType(), m_apiServicesModel->getSelectedServiceProtocol(), - email, - serverConfig); + email); if (errorCode != ErrorCode::NoError) { if (errorCode == ErrorCode::ApiTrialAlreadyUsedError) { emit trialEmailError( @@ -298,21 +304,17 @@ bool SubscriptionUiController::importTrialFromGateway(const QString &email) return true; } -bool SubscriptionUiController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName, +bool SubscriptionUiController::updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, const QString &newCountryName, bool reloadServiceConfig) { bool isConnectEvent = newCountryCode.isEmpty() && newCountryName.isEmpty() && !reloadServiceConfig; bool wasSubscriptionExpired = false; - ServerConfig oldServerConfig = m_serversController->getServerConfig(serverIndex); - if (oldServerConfig.isApiV2()) { - const ApiV2ServerConfig *oldApiV2 = oldServerConfig.as(); - if (oldApiV2) { - wasSubscriptionExpired = oldApiV2->apiConfig.subscriptionExpiredByServer - || oldApiV2->apiConfig.isSubscriptionExpired(); - } + if (const auto oldApiV2 = m_serversController->apiV2Config(serverId)) { + wasSubscriptionExpired = oldApiV2->apiConfig.subscriptionExpiredByServer + || oldApiV2->apiConfig.isSubscriptionExpired(); } - ErrorCode errorCode = m_subscriptionController->updateServiceFromGateway(serverIndex, newCountryCode, isConnectEvent); + ErrorCode errorCode = m_subscriptionController->updateServiceFromGateway(serverId, newCountryCode, isConnectEvent); if (errorCode == ErrorCode::NoError) { if (wasSubscriptionExpired) { @@ -336,27 +338,10 @@ bool SubscriptionUiController::updateServiceFromGateway(const int serverIndex, c } } -bool SubscriptionUiController::updateServiceFromTelegram(const int serverIndex) + +bool SubscriptionUiController::deactivateDevice(const QString &serverId) { -#ifdef Q_OS_IOS - IosController::Instance()->requestInetAccess(); - QThread::msleep(10); -#endif - - ErrorCode errorCode = m_subscriptionController->updateServiceFromTelegram(serverIndex); - - if (errorCode == ErrorCode::NoError) { - emit updateServerFromApiFinished(); - return true; - } else { - emit errorOccurred(errorCode); - return false; - } -} - -bool SubscriptionUiController::deactivateDevice(int serverIndex) -{ - ErrorCode errorCode = m_subscriptionController->deactivateDevice(serverIndex); + ErrorCode errorCode = m_subscriptionController->deactivateDevice(serverId); if (errorCode != ErrorCode::NoError) { emit errorOccurred(errorCode); return false; @@ -365,9 +350,10 @@ bool SubscriptionUiController::deactivateDevice(int serverIndex) return true; } -bool SubscriptionUiController::deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode) + +bool SubscriptionUiController::deactivateExternalDevice(const QString &serverId, const QString &uuid, const QString &serverCountryCode) { - ErrorCode errorCode = m_subscriptionController->deactivateExternalDevice(serverIndex, uuid, serverCountryCode); + ErrorCode errorCode = m_subscriptionController->deactivateExternalDevice(serverId, uuid, serverCountryCode); if (errorCode != ErrorCode::NoError) { emit errorOccurred(errorCode); return false; @@ -376,12 +362,19 @@ bool SubscriptionUiController::deactivateExternalDevice(int serverIndex, const Q return true; } + void SubscriptionUiController::validateConfig() { - int serverIndex = m_serversController->getDefaultServerIndex(); - bool hasInstalledContainers = m_serversController->hasInstalledContainers(serverIndex); + const QString serverId = m_serversController->getDefaultServerId(); + if (!serverId.isEmpty() && m_serversController->isLegacyApiV1Server(serverId)) { + emit unsupportedConnectDrawerRequested(); + emit configValidated(false); + return; + } - ErrorCode errorCode = m_subscriptionController->validateAndUpdateConfig(serverIndex, hasInstalledContainers); + bool hasInstalledContainers = m_serversController->hasInstalledContainers(serverId); + + ErrorCode errorCode = m_subscriptionController->validateAndUpdateConfig(serverId, hasInstalledContainers); if (errorCode != ErrorCode::NoError) { if (errorCode == ErrorCode::ApiSubscriptionExpiredError) { @@ -395,22 +388,25 @@ void SubscriptionUiController::validateConfig() emit configValidated(true); } -void SubscriptionUiController::setCurrentProtocol(int serverIndex, const QString &protocolName) +void SubscriptionUiController::setCurrentProtocol(const QString &serverId, const QString &protocolName) { - m_subscriptionController->setCurrentProtocol(serverIndex, protocolName); + m_subscriptionController->setCurrentProtocol(serverId, protocolName); } -bool SubscriptionUiController::isVlessProtocol(int serverIndex) + +bool SubscriptionUiController::isVlessProtocol(const QString &serverId) { - return m_subscriptionController->isVlessProtocol(serverIndex); + return m_subscriptionController->isVlessProtocol(serverId); } -void SubscriptionUiController::removeApiConfig(int serverIndex) + +void SubscriptionUiController::removeApiConfig(const QString &serverId) { - m_subscriptionController->removeApiConfig(serverIndex); + m_subscriptionController->removeApiConfig(serverId); emit apiConfigRemoved(tr("Api config removed")); } + QList SubscriptionUiController::getQrCodes() { return m_qrCodes; @@ -426,7 +422,7 @@ QString SubscriptionUiController::getVpnKey() return m_vpnKey; } -bool SubscriptionUiController::getAccountInfo(int serverIndex, bool reload) +bool SubscriptionUiController::getAccountInfo(const QString &serverId, bool reload) { if (reload) { QEventLoop wait; @@ -434,15 +430,18 @@ bool SubscriptionUiController::getAccountInfo(int serverIndex, bool reload) wait.exec(QEventLoop::ExcludeUserInputEvents); } QJsonObject accountInfo; - ErrorCode errorCode = m_subscriptionController->getAccountInfo(serverIndex, accountInfo); + ErrorCode errorCode = m_subscriptionController->getAccountInfo(serverId, accountInfo); if (errorCode != ErrorCode::NoError) { emit errorOccurred(errorCode); return false; } - ServerConfig serverConfig = m_serversController->getServerConfig(serverIndex); - QJsonObject serverConfigJson = serverConfig.toJson(); - m_apiAccountInfoModel->updateModel(accountInfo, serverConfigJson); + const auto apiV2 = m_serversController->apiV2Config(serverId); + if (!apiV2.has_value()) { + emit errorOccurred(ErrorCode::InternalError); + return false; + } + m_apiAccountInfoModel->updateModel(accountInfo, apiV2->toJson()); if (reload) { updateApiCountryModel(); @@ -463,9 +462,9 @@ void SubscriptionUiController::updateApiDevicesModel() m_apiDevicesModel->updateModel(m_apiAccountInfoModel->getIssuedConfigsInfo(), m_settingsController->getInstallationUuid(false)); } -void SubscriptionUiController::getRenewalLink(int serverIndex) +void SubscriptionUiController::getRenewalLink(const QString &serverId) { - if (serverIndex < 0) { + if (serverId.isEmpty()) { emit errorOccurred(ErrorCode::InternalError); return; } @@ -483,6 +482,6 @@ void SubscriptionUiController::getRenewalLink(int serverIndex) } emit renewalLinkReceived(url); }); - watcher->setFuture(m_subscriptionController->getRenewalLink(serverIndex)); + watcher->setFuture(m_subscriptionController->getRenewalLink(serverId)); } diff --git a/client/ui/controllers/api/subscriptionUiController.h b/client/ui/controllers/api/subscriptionUiController.h index d64a2eb2d..e4be939a3 100644 --- a/client/ui/controllers/api/subscriptionUiController.h +++ b/client/ui/controllers/api/subscriptionUiController.h @@ -13,6 +13,7 @@ #include "ui/models/api/apiAccountInfoModel.h" #include "ui/models/api/apiCountryModel.h" #include "ui/models/api/apiDevicesModel.h" + class SubscriptionUiController : public QObject { Q_OBJECT @@ -34,10 +35,10 @@ public: Q_PROPERTY(QString vpnKey READ getVpnKey NOTIFY vpnKeyExportReady) public slots: - bool exportNativeConfig(int serverIndex, const QString &serverCountryCode, const QString &fileName); - bool revokeNativeConfig(int serverIndex, const QString &serverCountryCode); - bool exportVpnKey(int serverIndex, const QString &fileName); - void prepareVpnKeyExport(int serverIndex); + bool exportNativeConfig(const QString &serverId, const QString &serverCountryCode, const QString &fileName); + bool revokeNativeConfig(const QString &serverId, const QString &serverCountryCode); + bool exportVpnKey(const QString &serverId, const QString &fileName); + void prepareVpnKeyExport(const QString &serverId); void copyVpnKeyToClipboard(); bool fillAvailableServices(); @@ -45,21 +46,21 @@ public slots: bool importFreeFromGateway(); bool restoreServiceFromAppStore(); bool importTrialFromGateway(const QString &email); - bool updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName, + bool updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, const QString &newCountryName, bool reloadServiceConfig = false); - bool updateServiceFromTelegram(const int serverIndex); - bool deactivateDevice(int serverIndex); - bool deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode); + bool deactivateDevice(const QString &serverId); + bool deactivateExternalDevice(const QString &serverId, const QString &uuid, const QString &serverCountryCode); void validateConfig(); - void setCurrentProtocol(int serverIndex, const QString &protocolName); - bool isVlessProtocol(int serverIndex); + void setCurrentProtocol(const QString &serverId, const QString &protocolName); + bool isVlessProtocol(const QString &serverId); - void removeApiConfig(int serverIndex); + void removeApiConfig(const QString &serverId); + + bool getAccountInfo(const QString &serverId, bool reload); + void getRenewalLink(const QString &serverId); - bool getAccountInfo(int serverIndex, bool reload); - void getRenewalLink(int serverIndex); void updateApiCountryModel(); void updateApiDevicesModel(); @@ -80,6 +81,8 @@ signals: void vpnKeyExportReady(); + void unsupportedConnectDrawerRequested(); + private: QList getQrCodes(); int getQrCodesCount(); diff --git a/client/ui/controllers/connectionUiController.cpp b/client/ui/controllers/connectionUiController.cpp index a69c0bf3b..488d71804 100644 --- a/client/ui/controllers/connectionUiController.cpp +++ b/client/ui/controllers/connectionUiController.cpp @@ -25,9 +25,12 @@ ConnectionUiController::ConnectionUiController(ConnectionController* connectionC void ConnectionUiController::openConnection() { - int serverIndex = m_serversController->getDefaultServerIndex(); + const QString serverId = m_serversController->getDefaultServerId(); + if (serverId.isEmpty()) { + return; + } - ErrorCode errorCode = m_connectionController->openConnection(serverIndex); + ErrorCode errorCode = m_connectionController->openConnection(serverId); if (errorCode != ErrorCode::NoError) { emit connectionErrorOccurred(errorCode); @@ -100,16 +103,6 @@ void ConnectionUiController::onConnectionStateChanged(Vpn::ConnectionState state emit connectionStateChanged(); } -void ConnectionUiController::onCurrentContainerUpdated() -{ - if (m_isConnected || m_isConnectionInProgress) { - emit reconnectWithUpdatedContainer(tr("Settings updated successfully, reconnection...")); - openConnection(); - } else { - emit reconnectWithUpdatedContainer(tr("Settings updated successfully")); - } -} - void ConnectionUiController::onTranslationsUpdated() { onConnectionStateChanged(getCurrentConnectionState()); diff --git a/client/ui/controllers/connectionUiController.h b/client/ui/controllers/connectionUiController.h index e09f2977e..14f3927ca 100644 --- a/client/ui/controllers/connectionUiController.h +++ b/client/ui/controllers/connectionUiController.h @@ -38,8 +38,6 @@ public slots: ErrorCode getLastConnectionError(); void onConnectionStateChanged(Vpn::ConnectionState state); - void onCurrentContainerUpdated(); - void onTranslationsUpdated(); signals: diff --git a/client/ui/controllers/qml/pageController.cpp b/client/ui/controllers/qml/pageController.cpp index 67b9b64c1..b51ece70b 100644 --- a/client/ui/controllers/qml/pageController.cpp +++ b/client/ui/controllers/qml/pageController.cpp @@ -19,8 +19,7 @@ #include "ui/utils/macosUtil.h" #endif -PageController::PageController(ServersController* serversController, - SettingsController* settingsController, +PageController::PageController(ServersController* serversController, SettingsController* settingsController, QObject *parent) : QObject(parent), m_serversController(serversController), m_settingsController(settingsController) { @@ -57,14 +56,7 @@ PageController::PageController(ServersController* serversController, bool PageController::isStartPageVisible() { - if (m_serversController->getServersCount()) { - if (m_serversController->getDefaultServerIndex() < 0) { - m_serversController->setDefaultServerIndex(0); - } - return false; - } else { - return true; - } + return m_serversController->getServersCount() == 0; } QString PageController::getPagePath(PageLoader::PageEnum page) diff --git a/client/ui/controllers/qml/pageController.h b/client/ui/controllers/qml/pageController.h index 603e8a8f4..f98c1c130 100644 --- a/client/ui/controllers/qml/pageController.h +++ b/client/ui/controllers/qml/pageController.h @@ -94,8 +94,7 @@ class PageController : public QObject { Q_OBJECT public: - explicit PageController(ServersController* serversController, - SettingsController* settingsController, + explicit PageController(ServersController* serversController, SettingsController* settingsController, QObject *parent = nullptr); Q_PROPERTY(int safeAreaTopMargin READ getSafeAreaTopMargin NOTIFY safeAreaTopMarginChanged) @@ -164,6 +163,8 @@ signals: void showPassphraseRequestDrawer(); void passphraseRequestDrawerClosed(QString passphrase); + void unsupportedConnectDrawerRequested(); + void escapePressed(); void closeTopDrawer(); diff --git a/client/ui/controllers/selfhosted/exportUiController.cpp b/client/ui/controllers/selfhosted/exportUiController.cpp index 4a9f8bfc4..2c76fccb6 100644 --- a/client/ui/controllers/selfhosted/exportUiController.cpp +++ b/client/ui/controllers/selfhosted/exportUiController.cpp @@ -8,46 +8,46 @@ ExportUiController::ExportUiController(ExportController* exportController, QObje { } -void ExportUiController::generateFullAccessConfig(int serverIndex) +void ExportUiController::generateFullAccessConfig(const QString &serverId) { clearPreviousConfig(); - auto result = m_exportController->generateFullAccessConfig(serverIndex); + auto result = m_exportController->generateFullAccessConfig(serverId); applyExportResult(result); } -void ExportUiController::generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName) +void ExportUiController::generateConnectionConfig(const QString &serverId, int containerIndex, const QString &clientName) { clearPreviousConfig(); - auto result = m_exportController->generateConnectionConfig(serverIndex, containerIndex, clientName); + auto result = m_exportController->generateConnectionConfig(serverId, containerIndex, clientName); applyExportResult(result); } -void ExportUiController::generateOpenVpnConfig(int serverIndex, const QString &clientName) +void ExportUiController::generateOpenVpnConfig(const QString &serverId, const QString &clientName) { clearPreviousConfig(); - auto result = m_exportController->generateOpenVpnConfig(serverIndex, clientName); + auto result = m_exportController->generateOpenVpnConfig(serverId, clientName); applyExportResult(result); } -void ExportUiController::generateWireGuardConfig(int serverIndex, const QString &clientName) +void ExportUiController::generateWireGuardConfig(const QString &serverId, const QString &clientName) { clearPreviousConfig(); - auto result = m_exportController->generateWireGuardConfig(serverIndex, clientName); + auto result = m_exportController->generateWireGuardConfig(serverId, clientName); applyExportResult(result); } -void ExportUiController::generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName) +void ExportUiController::generateAwgConfig(const QString &serverId, int containerIndex, const QString &clientName) { clearPreviousConfig(); - auto result = m_exportController->generateAwgConfig(serverIndex, containerIndex, clientName); + auto result = m_exportController->generateAwgConfig(serverId, containerIndex, clientName); applyExportResult(result); } -void ExportUiController::generateXrayConfig(int serverIndex, const QString &clientName) +void ExportUiController::generateXrayConfig(const QString &serverId, const QString &clientName) { clearPreviousConfig(); - auto result = m_exportController->generateXrayConfig(serverIndex, clientName); + auto result = m_exportController->generateXrayConfig(serverId, clientName); applyExportResult(result); } @@ -71,20 +71,20 @@ void ExportUiController::exportConfig(const QString &fileName) SystemController::saveFile(fileName, m_config); } -void ExportUiController::updateClientManagementModel(int serverIndex, int containerIndex) +void ExportUiController::updateClientManagementModel(const QString &serverId, int containerIndex) { - m_exportController->updateClientManagementModel(serverIndex, containerIndex); + m_exportController->updateClientManagementModel(serverId, containerIndex); } -void ExportUiController::revokeConfig(int row, int serverIndex, int containerIndex) +void ExportUiController::revokeConfig(int row, const QString &serverId, int containerIndex) { - m_exportController->revokeConfig(row, serverIndex, containerIndex); + m_exportController->revokeConfig(row, serverId, containerIndex); emit revokeConfigFinished(); } -void ExportUiController::renameClient(int row, const QString &clientName, int serverIndex, int containerIndex) +void ExportUiController::renameClient(int row, const QString &clientName, const QString &serverId, int containerIndex) { - m_exportController->renameClient(row, clientName, serverIndex, containerIndex); + m_exportController->renameClient(row, clientName, serverId, containerIndex); } int ExportUiController::getQrCodesCount() diff --git a/client/ui/controllers/selfhosted/exportUiController.h b/client/ui/controllers/selfhosted/exportUiController.h index bfc000b0e..5bcac90bd 100644 --- a/client/ui/controllers/selfhosted/exportUiController.h +++ b/client/ui/controllers/selfhosted/exportUiController.h @@ -4,6 +4,7 @@ #include #include "core/controllers/selfhosted/exportController.h" +#include "core/utils/errorCodes.h" class ExportUiController : public QObject { @@ -17,12 +18,13 @@ public: Q_PROPERTY(QString nativeConfigString READ getNativeConfigString NOTIFY exportConfigChanged) public slots: - void generateFullAccessConfig(int serverIndex); - void generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName); - void generateOpenVpnConfig(int serverIndex, const QString &clientName); - void generateWireGuardConfig(int serverIndex, const QString &clientName); - void generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName); - void generateXrayConfig(int serverIndex, const QString &clientName); + void generateFullAccessConfig(const QString &serverId); + + void generateConnectionConfig(const QString &serverId, int containerIndex, const QString &clientName); + void generateOpenVpnConfig(const QString &serverId, const QString &clientName); + void generateWireGuardConfig(const QString &serverId, const QString &clientName); + void generateAwgConfig(const QString &serverId, int containerIndex, const QString &clientName); + void generateXrayConfig(const QString &serverId, const QString &clientName); QString getConfig(); QString getNativeConfigString(); @@ -30,9 +32,11 @@ public slots: void exportConfig(const QString &fileName); - void updateClientManagementModel(int serverIndex, int containerIndex); - void revokeConfig(int row, int serverIndex, int containerIndex); - void renameClient(int row, const QString &clientName, int serverIndex, int containerIndex); + void updateClientManagementModel(const QString &serverId, int containerIndex); + + void revokeConfig(int row, const QString &serverId, int containerIndex); + + void renameClient(int row, const QString &clientName, const QString &serverId, int containerIndex); signals: void generateConfig(int type); diff --git a/client/ui/controllers/selfhosted/installUiController.cpp b/client/ui/controllers/selfhosted/installUiController.cpp index b867a0f5d..32178e7a6 100755 --- a/client/ui/controllers/selfhosted/installUiController.cpp +++ b/client/ui/controllers/selfhosted/installUiController.cpp @@ -11,7 +11,6 @@ #include "core/controllers/selfhosted/installController.h" #include "core/utils/selfhosted/sshSession.h" #include "core/utils/networkUtilities.h" -#include "logger.h" #include "core/utils/protocolEnum.h" #include "core/protocols/protocolUtils.h" #include "core/utils/constants/configKeys.h" @@ -27,33 +26,12 @@ #include "ui/models/services/socks5ProxyConfigModel.h" #include "ui/models/services/torConfigModel.h" #include "core/utils/utilities.h" -#include "core/models/serverConfig.h" #include "core/models/containerConfig.h" #include "core/models/protocols/awgProtocolConfig.h" #include "core/models/protocols/wireGuardProtocolConfig.h" #include "core/models/protocols/openVpnProtocolConfig.h" #include "core/models/protocols/xrayProtocolConfig.h" -namespace -{ - Logger logger("InstallUiController"); - - namespace configKey - { - constexpr char serviceInfo[] = "service_info"; - constexpr char serviceType[] = "service_type"; - constexpr char serviceProtocol[] = "service_protocol"; - constexpr char userCountryCode[] = "user_country_code"; - - constexpr char serverCountryCode[] = "server_country_code"; - constexpr char serverCountryName[] = "server_country_name"; - constexpr char availableCountries[] = "available_countries"; - - constexpr char apiConfig[] = "api_config"; - constexpr char authData[] = "auth_data"; - } -} - InstallUiController::InstallUiController(InstallController *installController, ServersController *serversController, SettingsController *settingsController, @@ -101,19 +79,18 @@ InstallUiController::~InstallUiController() { } -void InstallUiController::install(DockerContainer container, int port, TransportProto transportProto, int serverIndex) +void InstallUiController::install(DockerContainer container, int port, TransportProto transportProto, const QString &serverId) { - const bool isNewServer = serverIndex < 0; + const bool isNewServer = serverId.isEmpty(); ServerCredentials serverCredentials; if (isNewServer) { serverCredentials = m_processedServerCredentials; } else { - serverCredentials = m_serversController->getServerCredentials(serverIndex); + serverCredentials = m_serversController->getServerCredentials(serverId); m_processedServerCredentials = ServerCredentials(); } - QMap preparedContainers; QString finishMessage; ErrorCode errorCode; @@ -131,9 +108,13 @@ void InstallUiController::install(DockerContainer container, int port, Transport return; } - int serverIndex = m_serversController->getServersCount() - 1; - ServerConfig serverConfig = m_serversController->getServerConfig(serverIndex); - QMap containers = serverConfig.containers(); + const QString newServerId = m_serversController->getServerId(m_serversController->getServersCount() - 1); + const auto admin = m_serversController->selfHostedAdminConfig(newServerId); + if (!admin.has_value()) { + emit installationErrorOccurred(ErrorCode::InternalError); + return; + } + QMap containers = admin->containers; int containersCount = containers.size(); if (wasContainerInstalled) { @@ -148,20 +129,28 @@ void InstallUiController::install(DockerContainer container, int port, Transport emit installServerFinished(finishMessage); } else { - ServerConfig serverConfig = m_serversController->getServerConfig(serverIndex); - QMap containers = serverConfig.containers(); + const auto adminBefore = m_serversController->selfHostedAdminConfig(serverId); + if (!adminBefore.has_value()) { + emit installationErrorOccurred(ErrorCode::InternalError); + return; + } + QMap containers = adminBefore->containers; int containersCount = containers.size(); bool wasContainerInstalled = false; - errorCode = m_installController->installContainer(serverIndex, container, port, transportProto, + errorCode = m_installController->installContainer(serverId, container, port, transportProto, wasContainerInstalled); if (errorCode) { emit installationErrorOccurred(errorCode); return; } - ServerConfig newServerConfig = m_serversController->getServerConfig(serverIndex); - QMap newContainers = newServerConfig.containers(); + const auto adminAfter = m_serversController->selfHostedAdminConfig(serverId); + if (!adminAfter.has_value()) { + emit installationErrorOccurred(ErrorCode::InternalError); + return; + } + QMap newContainers = adminAfter->containers; int newContainersCount = newContainers.size(); bool hasNewContainers = (newContainersCount - containersCount) > (wasContainerInstalled ? 1 : 0); @@ -181,17 +170,25 @@ void InstallUiController::install(DockerContainer container, int port, Transport } } -void InstallUiController::scanServerForInstalledContainers(int serverIndex) +void InstallUiController::scanServerForInstalledContainers(const QString &serverId) { - ServerConfig serverBefore = m_serversController->getServerConfig(serverIndex); - QMap containersBefore = serverBefore.containers(); + const auto serverBefore = m_serversController->selfHostedAdminConfig(serverId); + if (!serverBefore.has_value()) { + emit installationErrorOccurred(ErrorCode::InternalError); + return; + } + QMap containersBefore = serverBefore->containers; int containersCountBefore = containersBefore.size(); - ErrorCode errorCode = m_installController->scanServerForInstalledContainers(serverIndex); + ErrorCode errorCode = m_installController->scanServerForInstalledContainers(serverId); if (errorCode == ErrorCode::NoError) { - ServerConfig serverAfter = m_serversController->getServerConfig(serverIndex); - QMap containersAfter = serverAfter.containers(); + const auto serverAfter = m_serversController->selfHostedAdminConfig(serverId); + if (!serverAfter.has_value()) { + emit installationErrorOccurred(ErrorCode::InternalError); + return; + } + QMap containersAfter = serverAfter->containers; int containersCountAfter = containersAfter.size(); bool isInstalledContainerAdded = containersCountAfter > containersCountBefore; @@ -202,7 +199,7 @@ void InstallUiController::scanServerForInstalledContainers(int serverIndex) emit installationErrorOccurred(errorCode); } -void InstallUiController::updateContainer(int serverIndex, int containerIndex, int protocolIndex) +void InstallUiController::updateContainer(const QString &serverId, int containerIndex, int protocolIndex) { DockerContainer container = static_cast(containerIndex); @@ -250,32 +247,26 @@ void InstallUiController::updateContainer(int serverIndex, int containerIndex, i default: return; } - ContainerConfig oldContainerConfig = m_serversController->getContainerConfig(serverIndex, container); + ContainerConfig oldContainerConfig = m_serversController->getContainerConfig(serverId, container); - ErrorCode errorCode = m_installController->updateContainer(serverIndex, container, oldContainerConfig, containerConfig); + ErrorCode errorCode = m_installController->updateContainer(serverId, container, oldContainerConfig, containerConfig); if (errorCode == ErrorCode::NoError) { - ContainerConfig updatedConfig = m_serversController->getContainerConfig(serverIndex, container); + ContainerConfig updatedConfig = m_serversController->getContainerConfig(serverId, container); m_protocolModel->updateModel(updatedConfig); - auto defaultContainer = m_serversController->getServerConfig(serverIndex).defaultContainer(); - if ((serverIndex == m_serversController->getDefaultServerIndex()) && (container == defaultContainer)) { - emit currentContainerUpdated(); - } else { - emit updateContainerFinished(tr("Settings updated successfully")); - } - + emit updateContainerFinished(tr("Settings updated successfully")); return; } emit installationErrorOccurred(errorCode); } -void InstallUiController::rebootServer(int serverIndex) +void InstallUiController::rebootServer(const QString &serverId) { - QString serverName = m_serversController->getServerConfig(serverIndex).displayName(); + const QString serverName = m_serversController->notificationDisplayName(serverId); - const auto errorCode = m_installController->rebootServer(serverIndex); + const auto errorCode = m_installController->rebootServer(serverId); if (errorCode == ErrorCode::NoError) { emit rebootServerFinished(tr("Server '%1' was rebooted").arg(serverName)); } else { @@ -283,19 +274,22 @@ void InstallUiController::rebootServer(int serverIndex) } } -void InstallUiController::removeServer(int serverIndex) +void InstallUiController::removeServer(const QString &serverId) { - QString serverName = m_serversController->getServerConfig(serverIndex).displayName(); + if (serverId.isEmpty()) { + return; + } + const QString serverName = m_serversController->notificationDisplayName(serverId); - m_serversController->removeServer(serverIndex); + m_serversController->removeServer(serverId); emit removeServerFinished(tr("Server '%1' was removed").arg(serverName)); } -void InstallUiController::removeAllContainers(int serverIndex) +void InstallUiController::removeAllContainers(const QString &serverId) { - QString serverName = m_serversController->getServerConfig(serverIndex).displayName(); + const QString serverName = m_serversController->notificationDisplayName(serverId); - ErrorCode errorCode = m_installController->removeAllContainers(serverIndex); + ErrorCode errorCode = m_installController->removeAllContainers(serverId); if (errorCode == ErrorCode::NoError) { emit removeAllContainersFinished(tr("All containers from server '%1' have been removed").arg(serverName)); return; @@ -303,14 +297,14 @@ void InstallUiController::removeAllContainers(int serverIndex) emit installationErrorOccurred(errorCode); } -void InstallUiController::removeContainer(int serverIndex, int containerIndex) +void InstallUiController::removeContainer(const QString &serverId, int containerIndex) { - QString serverName = m_serversController->getServerConfig(serverIndex).displayName(); + const QString serverName = m_serversController->notificationDisplayName(serverId); DockerContainer container = static_cast(containerIndex); QString containerName = ContainerUtils::containerHumanNames().value(container); - ErrorCode errorCode = m_installController->removeContainer(serverIndex, container); + ErrorCode errorCode = m_installController->removeContainer(serverId, container); if (errorCode == ErrorCode::NoError) { emit removeContainerFinished(tr("%1 has been removed from the server '%2'").arg(containerName, serverName)); @@ -319,17 +313,17 @@ void InstallUiController::removeContainer(int serverIndex, int containerIndex) emit installationErrorOccurred(errorCode); } -void InstallUiController::clearCachedProfile(int serverIndex, int containerIndex) +void InstallUiController::clearCachedProfile(const QString &serverId, int containerIndex) { DockerContainer container = static_cast(containerIndex); if (ContainerUtils::containerService(container) == ServiceType::Other) { return; } - m_installController->clearCachedProfile(serverIndex, container); + m_installController->clearCachedProfile(serverId, container); emit cachedProfileCleared(tr("%1 cached profile cleared").arg(ContainerUtils::containerHumanNames().value(container))); - ContainerConfig updatedConfig = m_serversController->getContainerConfig(serverIndex, container); + ContainerConfig updatedConfig = m_serversController->getContainerConfig(serverId, container); m_protocolModel->updateModel(updatedConfig); } @@ -354,9 +348,9 @@ void InstallUiController::setProcessedServerCredentials(const QString &hostName, m_processedServerCredentials.secretData = secretData; } -void InstallUiController::mountSftpDrive(int serverIndex, const QString &port, const QString &password, const QString &username) +void InstallUiController::mountSftpDrive(const QString &serverId, const QString &port, const QString &password, const QString &username) { - ServerCredentials serverCredentials = m_serversController->getServerCredentials(serverIndex); + ServerCredentials serverCredentials = m_serversController->getServerCredentials(serverId); ErrorCode errorCode = m_installController->mountSftpDrive(serverCredentials, port, password, username); if (errorCode != ErrorCode::NoError) { emit installationErrorOccurred(errorCode); @@ -399,40 +393,35 @@ void InstallUiController::setEncryptedPassphrase(QString passphrase) void InstallUiController::addEmptyServer() { - SelfHostedServerConfig serverConfig; - serverConfig.hostName = m_processedServerCredentials.hostName; - serverConfig.userName = m_processedServerCredentials.userName; - serverConfig.password = m_processedServerCredentials.secretData; - serverConfig.port = m_processedServerCredentials.port; - serverConfig.description = m_settingsController->nextAvailableServerName(); - serverConfig.defaultContainer = DockerContainer::None; - - m_serversController->addServer(ServerConfig(serverConfig)); + m_installController->addEmptyServer(m_processedServerCredentials); emit installServerFinished(tr("Server added successfully")); } void InstallUiController::validateConfig() { - int serverIndex = m_serversController->getDefaultServerIndex(); - m_installController->validateConfig(serverIndex); + const QString serverId = m_serversController->getDefaultServerId(); + if (serverId.isEmpty()) { + return; + } + m_installController->validateConfig(serverId); } -void InstallUiController::updateProtocols(int serverIndex, int containerIndex) +void InstallUiController::updateProtocols(const QString &serverId, int containerIndex) { DockerContainer container = static_cast(containerIndex); - ContainerConfig containerConfig = m_serversController->getContainerConfig(serverIndex, container); + ContainerConfig containerConfig = m_serversController->getContainerConfig(serverId, container); containerConfig.container = container; m_protocolModel->updateModel(containerConfig); } -void InstallUiController::openServerSettings(int serverIndex, int containerIndex, int protocolIndex) +void InstallUiController::openServerSettings(const QString &serverId, int containerIndex, int protocolIndex) { - updateProtocolConfigModel(serverIndex, containerIndex, protocolIndex); + updateProtocolConfigModel(serverId, containerIndex, protocolIndex); } -void InstallUiController::openClientSettings(int serverIndex, int containerIndex, int protocolIndex) +void InstallUiController::openClientSettings(const QString &serverId, int containerIndex, int protocolIndex) { - updateProtocolConfigModel(serverIndex, containerIndex, protocolIndex); + updateProtocolConfigModel(serverId, containerIndex, protocolIndex); } int InstallUiController::defaultPort(int protocolIndex) @@ -465,10 +454,10 @@ bool InstallUiController::defaultTransportProtoChangeable(int protocolIndex) return ProtocolUtils::defaultTransportProtoChangeable(proto); } -void InstallUiController::updateProtocolConfigModel(int serverIndex, int containerIndex, int protocolIndex) +void InstallUiController::updateProtocolConfigModel(const QString &serverId, int containerIndex, int protocolIndex) { DockerContainer container = static_cast(containerIndex); - ContainerConfig containerConfig = m_serversController->getContainerConfig(serverIndex, container); + ContainerConfig containerConfig = m_serversController->getContainerConfig(serverId, container); containerConfig.container = container; Proto protocolType = static_cast(protocolIndex); @@ -490,4 +479,3 @@ void InstallUiController::updateProtocolConfigModel(int serverIndex, int contain default: break; } } - diff --git a/client/ui/controllers/selfhosted/installUiController.h b/client/ui/controllers/selfhosted/installUiController.h index 89e0291cf..beec0a3b8 100644 --- a/client/ui/controllers/selfhosted/installUiController.h +++ b/client/ui/controllers/selfhosted/installUiController.h @@ -52,24 +52,24 @@ public: ~InstallUiController(); public slots: - void install(DockerContainer container, int port, TransportProto transportProto, int serverIndex); + void install(DockerContainer container, int port, TransportProto transportProto, const QString &serverId); void setProcessedServerCredentials(const QString &hostName, const QString &userName, const QString &secretData); void clearProcessedServerCredentials(); - void scanServerForInstalledContainers(int serverIndex); + void scanServerForInstalledContainers(const QString &serverId); - void updateContainer(int serverIndex, int containerIndex, int protocolIndex); + void updateContainer(const QString &serverId, int containerIndex, int protocolIndex); - void removeServer(int serverIndex); - void rebootServer(int serverIndex); - void removeAllContainers(int serverIndex); - void removeContainer(int serverIndex, int containerIndex); + void removeServer(const QString &serverId); + void rebootServer(const QString &serverId); + void removeAllContainers(const QString &serverId); + void removeContainer(const QString &serverId, int containerIndex); - void clearCachedProfile(int serverIndex, int containerIndex); + void clearCachedProfile(const QString &serverId, int containerIndex); QRegularExpression ipAddressRegExp(); - void mountSftpDrive(int serverIndex, const QString &port, const QString &password, const QString &username); + void mountSftpDrive(const QString &serverId, const QString &port, const QString &password, const QString &username); bool checkSshConnection(); @@ -78,12 +78,12 @@ public slots: void addEmptyServer(); void validateConfig(); - - Q_INVOKABLE void updateProtocols(int serverIndex, int containerIndex); - - void openServerSettings(int serverIndex, int containerIndex, int protocolIndex); - void openClientSettings(int serverIndex, int containerIndex, int protocolIndex); - + + Q_INVOKABLE void updateProtocols(const QString &serverId, int containerIndex); + + void openServerSettings(const QString &serverId, int containerIndex, int protocolIndex); + void openClientSettings(const QString &serverId, int containerIndex, int protocolIndex); + int defaultPort(int protocolIndex); int getPortForInstall(int protocolIndex); int defaultTransportProto(int protocolIndex); @@ -114,8 +114,6 @@ signals: void serverIsBusy(const bool isBusy); void cancelInstallation(); - void currentContainerUpdated(); - void cachedProfileCleared(const QString &message); void apiConfigRemoved(const QString &message); @@ -145,7 +143,7 @@ private: QString m_privateKeyPassphrase; - void updateProtocolConfigModel(int serverIndex, int containerIndex, int protocolIndex); + void updateProtocolConfigModel(const QString &serverId, int containerIndex, int protocolIndex); }; #endif // INSTALLUICONTROLLER_H diff --git a/client/ui/controllers/serversUiController.cpp b/client/ui/controllers/serversUiController.cpp index ccafe0f4b..b9c2a9bf9 100644 --- a/client/ui/controllers/serversUiController.cpp +++ b/client/ui/controllers/serversUiController.cpp @@ -1,37 +1,37 @@ #include "serversUiController.h" -#include "core/utils/api/apiEnums.h" -#include "core/utils/constants/apiKeys.h" -#include "core/utils/constants/apiConstants.h" -#include "core/utils/api/apiUtils.h" #include "core/utils/containerEnum.h" #include "core/utils/containers/containerUtils.h" #include "core/utils/protocolEnum.h" -#include "core/utils/protocolEnum.h" -#include "core/protocols/protocolUtils.h" -#include "core/utils/constants/configKeys.h" -#include "core/utils/constants/protocolConstants.h" -#include -#include -#include "core/models/serverConfig.h" #include "core/models/protocolConfig.h" #include "core/models/containerConfig.h" -#include "core/models/protocols/awgProtocolConfig.h" using namespace amnezia; -namespace +namespace { +int rowForServerId(const QVector &list, const QString &serverId) { - namespace configKey - { - constexpr char apiConfig[] = "api_config"; - constexpr char serverCountryCode[] = "server_country_code"; - constexpr char serverCountryName[] = "server_country_name"; - constexpr char userCountryCode[] = "user_country_code"; - constexpr char serviceType[] = "service_type"; + if (serverId.isEmpty()) { + return -1; } + for (int i = 0; i < list.size(); ++i) { + if (list.at(i).serverId == serverId) { + return i; + } + } + return -1; } +bool descriptionsHaveGatewayServers(const QVector &list) +{ + for (const auto &d : list) { + if (d.isServerFromGatewayApi) { + return true; + } + } + return false; +} +} // namespace ServersUiController::ServersUiController(ServersController* serversController, SettingsController* settingsController, ServersModel* serversModel, @@ -47,48 +47,70 @@ ServersUiController::ServersUiController(ServersController* serversController, { } -void ServersUiController::removeServer(int index) +void ServersUiController::removeServer(const QString &serverId) { - m_serversController->removeServer(index); - updateModel(); -} - -void ServersUiController::editServerName(int index, const QString &name) -{ - ServerConfig serverConfig = m_serversController->getServerConfig(index); - - if (serverConfig.isApiV1()) { - ApiV1ServerConfig* apiV1 = serverConfig.as(); - if (apiV1) { - apiV1->name = name; - } - } else if (serverConfig.isApiV2()) { - ApiV2ServerConfig* apiV2 = serverConfig.as(); - if (apiV2) { - apiV2->name = name; - apiV2->nameOverriddenByUser = true; - } - } else { - serverConfig.visit([&name](auto& arg) { - arg.description = name; - }); + if (serverId.isEmpty()) { + return; } - - m_serversController->editServer(index, serverConfig); + m_serversController->removeServer(serverId); updateModel(); } -void ServersUiController::setDefaultServerIndex(int index) +void ServersUiController::removeServerAtIndex(int index) { - m_serversController->setDefaultServerIndex(index); - updateModel(); - emit defaultServerIndexChanged(index); + const QString serverId = getServerId(index); + if (!serverId.isEmpty()) { + removeServer(serverId); + } } -void ServersUiController::setDefaultContainer(int serverIndex, int containerIndex) +void ServersUiController::setDefaultServerAtIndex(int index) { + const QString serverId = getServerId(index); + if (!serverId.isEmpty()) { + setDefaultServer(serverId); + } +} + +void ServersUiController::setDefaultContainerAtIndex(int index, int containerIndex) +{ + const QString serverId = getServerId(index); + if (!serverId.isEmpty()) { + setDefaultContainer(serverId, containerIndex); + } +} + +void ServersUiController::editServerName(const QString &serverId, const QString &name) +{ + if (serverId.isEmpty()) { + return; + } + + if (!m_serversController->renameServer(serverId, name)) { + emit errorOccurred(tr("Legacy API v1 configs are no longer supported. Remove this server to continue.")); + emit finished(tr("Use the remove action to delete this legacy config.")); + return; + } + updateModel(); +} + +void ServersUiController::setDefaultServer(const QString &serverId) +{ + if (serverId.isEmpty()) { + return; + } + m_serversController->setDefaultServer(serverId); + updateModel(); + emit defaultServerIdChanged(serverId); +} + +void ServersUiController::setDefaultContainer(const QString &serverId, int containerIndex) +{ + if (serverId.isEmpty()) { + return; + } auto container = static_cast(containerIndex); - m_serversController->setDefaultContainer(serverIndex, container); + m_serversController->setDefaultContainer(serverId, container); updateModel(); } @@ -98,129 +120,123 @@ void ServersUiController::toggleAmneziaDns(bool enabled) updateModel(); } -void ServersUiController::onDefaultServerChanged(int index) +void ServersUiController::onDefaultServerChanged(const QString &/*defaultServerId*/) { - setProcessedServerIndex(index); updateModel(); + setProcessedServerId(m_serversController->getDefaultServerId()); updateDefaultServerContainersModel(); - emit defaultServerIndexChanged(index); + emit defaultServerIdChanged(m_serversController->getDefaultServerId()); } void ServersUiController::updateModel() { - int defaultIndex = m_serversController->getDefaultServerIndex(); - bool wasEmpty = !hasServersFromGatewayApi(); - int serversCount = m_serversController->getServersCount(); + QVector descriptions = + m_serversController->buildServerDescriptions(m_settingsController->isAmneziaDnsEnabled()); - if (m_processedServerIndex >= serversCount) { - setProcessedServerIndex(defaultIndex); - } else if (m_processedServerIndex >= 0) { - setProcessedServerIndex(m_processedServerIndex); + const QString defaultServerId = m_serversController->getDefaultServerId(); + const bool hadServersFromGatewayBefore = descriptionsHaveGatewayServers(m_orderedServerDescriptions); + const bool hasServersFromGatewayNow = descriptionsHaveGatewayServers(descriptions); + const int listCount = descriptions.size(); + const int defaultRowInDescriptions = rowForServerId(descriptions, defaultServerId); + + m_orderedServerDescriptions = descriptions; + + if (listCount == 0) { + setProcessedServerId(QString()); + } else if (m_processedServerIndex >= listCount) { + setProcessedServerId(defaultServerId); + } else if (!m_processedServerId.isEmpty()) { + const int row = rowForServerId(m_orderedServerDescriptions, m_processedServerId); + if (row < 0) { + setProcessedServerId(defaultServerId); + } else { + setProcessedServerId(m_processedServerId); + } + } else if (defaultRowInDescriptions >= 0) { + setProcessedServerId(defaultServerId); } - - m_serversModel->updateModel(m_serversController->getServers(), defaultIndex, m_settingsController->isAmneziaDnsEnabled()); - - + + m_serversModel->updateModel(m_orderedServerDescriptions, defaultRowInDescriptions); + updateContainersModel(); updateDefaultServerContainersModel(); - - bool isEmpty = !hasServersFromGatewayApi(); - if (wasEmpty != isEmpty) { + + if (hadServersFromGatewayBefore != hasServersFromGatewayNow) { emit hasServersFromGatewayApiChanged(); } - - emit defaultServerIndexChanged(defaultIndex); + + emit defaultServerIdChanged(defaultServerId); + emit defaultServerIndexChanged(defaultServerIndex()); } -int ServersUiController::getDefaultServerIndex() const +QString ServersUiController::getDefaultServerId() const { - return m_serversController->getDefaultServerIndex(); + return m_serversController->getDefaultServerId(); } QString ServersUiController::getDefaultServerName() const { - int defaultIndex = getDefaultServerIndex(); - return m_serversController->getServerConfig(defaultIndex).displayName(); + const QString defaultServerId = m_serversController->getDefaultServerId(); + for (const auto &description : m_orderedServerDescriptions) { + if (description.serverId == defaultServerId) { + return description.serverName; + } + } + return QString(); } QString ServersUiController::getDefaultServerDefaultContainerName() const { - int defaultIndex = getDefaultServerIndex(); - const ServerConfig server = m_serversController->getServerConfig(defaultIndex); - return ContainerUtils::containerHumanNames().value(server.defaultContainer()); + const QString defaultServerId = m_serversController->getDefaultServerId(); + for (const auto &description : m_orderedServerDescriptions) { + if (description.serverId == defaultServerId) { + return ContainerUtils::containerHumanNames().value(description.defaultContainer); + } + } + return QString(); } QString ServersUiController::getDefaultServerDescriptionCollapsed() const { - int defaultIndex = getDefaultServerIndex(); - const ServerConfig server = m_serversController->getServerConfig(defaultIndex); - QString description = getDefaultServerDescription(server, defaultIndex); - - if (server.isApiConfig()) { - return description; - } - - DockerContainer container = server.defaultContainer(); - QString containerName = ContainerUtils::containerHumanNames().value(container); - QString protocolVersion; - QString hostName = server.hostName(); - - if (ContainerUtils::isAwgContainer(container)) { - ContainerConfig containerConfig = server.containerConfig(container); - if (auto* awgProtocolConfig = containerConfig.getAwgProtocolConfig()) { - QString version = awgProtocolConfig->serverConfig.protocolVersion; - if (version == protocols::awg::awgV2) { - protocolVersion = QObject::tr(" (version 2)"); - } else if (version == protocols::awg::awgV1_5) { - protocolVersion = QObject::tr(" (version 1.5)"); - } - - if (container == DockerContainer::Awg && !awgProtocolConfig->serverConfig.isThirdPartyConfig) { - containerName = "AmneziaWG Legacy"; - } + const QString defaultServerId = m_serversController->getDefaultServerId(); + for (const auto &description : m_orderedServerDescriptions) { + if (description.serverId == defaultServerId) { + return description.collapsedServerDescription; } } - - return description + containerName + protocolVersion + " | " + hostName; + return QString(); } QString ServersUiController::getDefaultServerImagePathCollapsed() const { - int defaultIndex = getDefaultServerIndex(); - const ServerConfig server = m_serversController->getServerConfig(defaultIndex); - - if (server.isApiV2()) { - const ApiV2ServerConfig* apiV2 = server.as(); - if (!apiV2) return QString(); - const QString countryCode = apiV2->apiConfig.serverCountryCode; - if (countryCode.isEmpty()) { - return ""; + const QString defaultServerId = m_serversController->getDefaultServerId(); + for (const auto &description : m_orderedServerDescriptions) { + if (description.serverId == defaultServerId) { + if (!description.isApiV2 || description.apiServerCountryCode.isEmpty()) { + return ""; + } + return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(description.apiServerCountryCode.toUpper()); } - return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode.toUpper()); } return ""; } QString ServersUiController::getDefaultServerDescriptionExpanded() const { - int defaultIndex = getDefaultServerIndex(); - const ServerConfig server = m_serversController->getServerConfig(defaultIndex); - QString description = getDefaultServerDescription(server, defaultIndex); - - if (server.isApiConfig()) { - return description; + const QString defaultServerId = m_serversController->getDefaultServerId(); + for (const auto &description : m_orderedServerDescriptions) { + if (description.serverId == defaultServerId) { + return description.expandedServerDescription; + } } - - return description + server.hostName(); + return QString(); } bool ServersUiController::isDefaultServerDefaultContainerHasSplitTunneling() const { - int defaultIndex = getDefaultServerIndex(); - const ServerConfig server = m_serversController->getServerConfig(defaultIndex); - DockerContainer defaultContainer = server.defaultContainer(); - - ContainerConfig containerConfig = server.containerConfig(defaultContainer); + const QString defaultServerId = m_serversController->getDefaultServerId(); + const DockerContainer defaultContainer = m_serversController->getDefaultContainer(defaultServerId); + const ContainerConfig containerConfig = m_serversController->getContainerConfig(defaultServerId, defaultContainer); if (defaultContainer == DockerContainer::Awg || defaultContainer == DockerContainer::WireGuard) { auto hasSplitTunnelingFromAllowedIps = [](const QStringList& allowedIps, const QString& nativeConfig) -> bool { @@ -265,16 +281,13 @@ bool ServersUiController::isDefaultServerDefaultContainerHasSplitTunneling() con bool ServersUiController::isDefaultServerFromApi() const { - int defaultIndex = getDefaultServerIndex(); - const ServerConfig server = m_serversController->getServerConfig(defaultIndex); - const int configVersion = server.configVersion(); - return configVersion == apiDefs::ConfigSource::Telegram - || configVersion == apiDefs::ConfigSource::AmneziaGateway; -} - -int ServersUiController::getProcessedServerIndex() const -{ - return m_processedServerIndex; + const QString defaultServerId = m_serversController->getDefaultServerId(); + for (const auto &description : m_orderedServerDescriptions) { + if (description.serverId == defaultServerId) { + return description.isApiV2; + } + } + return false; } int ServersUiController::getProcessedContainerIndex() const @@ -291,186 +304,196 @@ void ServersUiController::setProcessedContainerIndex(int index) } } -void ServersUiController::setProcessedServerIndex(int index) +QString ServersUiController::getProcessedServerId() const { - if (index >= m_serversController->getServersCount()) { + return m_processedServerId; +} + +void ServersUiController::setProcessedServerId(const QString &serverId) +{ + const int index = serverId.isEmpty() ? -1 : serverIndexForId(serverId); + if (!serverId.isEmpty() && index < 0) { return; } - if (m_processedServerIndex != index) { + if (m_processedServerIndex != index || m_processedServerId != serverId) { m_processedServerIndex = index; + m_processedServerId = serverId; m_serversModel->setProcessedServerIndex(index); if (index >= 0) { updateContainersModel(); - - ServerConfig server = m_serversController->getServerConfig(index); - setProcessedContainerIndex(static_cast(server.defaultContainer())); - - if (server.isApiV2()) { - const ApiV2ServerConfig* apiV2 = server.as(); - if (apiV2 && !apiV2->apiConfig.availableCountries.isEmpty()) { - emit updateApiCountryModel(); + for (const auto &description : m_orderedServerDescriptions) { + if (description.serverId == serverId) { + setProcessedContainerIndex(static_cast(description.defaultContainer)); + break; } - emit updateApiServicesModel(); + } + + for (const auto &description : m_orderedServerDescriptions) { + if (description.serverId != serverId) { + continue; + } + if (description.isApiV2) { + if (description.isCountrySelectionAvailable && !description.apiAvailableCountries.isEmpty()) { + emit updateApiCountryModel(); + } + emit updateApiServicesModel(); + } + break; } } + emit processedServerIdChanged(m_processedServerId); emit processedServerIndexChanged(m_processedServerIndex); } } +int ServersUiController::getProcessedServerIndex() const +{ + return m_processedServerIndex; +} + +void ServersUiController::setProcessedServerIndex(int index) +{ + if (index < 0) { + setProcessedServerId(QString()); + return; + } + const QString id = getServerId(index); + if (!id.isEmpty()) { + setProcessedServerId(id); + } +} + +int ServersUiController::defaultServerIndex() const +{ + return rowForServerId(m_orderedServerDescriptions, getDefaultServerId()); +} + bool ServersUiController::processedServerIsPremium() const { - ServerConfig server = m_serversController->getServerConfig(m_processedServerIndex); - if (server.isApiV1()) { - const ApiV1ServerConfig* apiV1 = server.as(); - return apiV1 ? apiV1->isPremium() : false; - } else if (server.isApiV2()) { - const ApiV2ServerConfig* apiV2 = server.as(); - return apiV2 ? (apiV2->isPremium() || apiV2->isExternalPremium()) : false; + for (const auto &description : m_orderedServerDescriptions) { + if (description.serverId == m_processedServerId) { + return description.isPremium; + } } return false; } const ServerCredentials ServersUiController::getProcessedServerCredentials() const { - return m_serversController->getServerCredentials(m_processedServerIndex); + return m_serversController->getServerCredentials(m_processedServerId); } bool ServersUiController::isDefaultServerCurrentlyProcessed() const { - return m_serversController->getDefaultServerIndex() == m_processedServerIndex; + return m_serversController->getDefaultServerId() == m_processedServerId; } bool ServersUiController::isProcessedServerHasWriteAccess() const { - ServerCredentials credentials = m_serversController->getServerCredentials(m_processedServerIndex); + ServerCredentials credentials = m_serversController->getServerCredentials(m_processedServerId); return (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty()); } - -QString ServersUiController::getDefaultServerDescription(const ServerConfig& server, int index) const +QString ServersUiController::getDefaultServerDescription(const QString &serverId) const { - QString description; - - if (server.isApiV2()) { - const ApiV2ServerConfig* apiV2 = server.as(); - if (!apiV2) return QString(); - if (!apiV2->apiConfig.serverCountryCode.isEmpty()) { - return apiV2->apiConfig.serverCountryName; + for (const auto &description : m_orderedServerDescriptions) { + if (description.serverId == serverId) { + return description.baseDescription; } - return apiV2->description; - } else if (server.isApiV1()) { - const ApiV1ServerConfig* apiV1 = server.as(); - return apiV1 ? apiV1->description : QString(); - } else { - ServerCredentials credentials = m_serversController->getServerCredentials(index); - if (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty()) { - bool isAmneziaDnsEnabled = m_settingsController->isAmneziaDnsEnabled(); - if (isAmneziaDnsEnabled && isAmneziaDnsContainerInstalled(index)) { - description += "Amnezia DNS | "; - } - } else { - if (server.dns1() == protocols::dns::amneziaDnsIp) { - description += "Amnezia DNS | "; - } - } - return description; } -} - -bool ServersUiController::isAmneziaDnsContainerInstalled(int serverIndex) const -{ - const ServerConfig server = m_serversController->getServerConfig(serverIndex); - QMap containers = server.containers(); - - return containers.contains(DockerContainer::Dns); + return QString(); } bool ServersUiController::hasServersFromGatewayApi() const { - QVector servers = m_serversController->getServers(); - for (const ServerConfig &server : servers) { - if (server.isApiV2()) { - return true; - } - } - return false; + return listHasServersFromGatewayApi(); } bool ServersUiController::isAdVisible() const { - int defaultIndex = getDefaultServerIndex(); - if (defaultIndex < 0) { + const QString defaultServerId = m_serversController->getDefaultServerId(); + if (defaultServerId.isEmpty()) { return false; } - ServerConfig server = m_serversController->getServerConfig(defaultIndex); - if (server.isApiV2()) { - const ApiV2ServerConfig* apiV2 = server.as(); - if (!apiV2) return false; - return apiV2->apiConfig.serviceInfo.isAdVisible; + for (const auto &description : m_orderedServerDescriptions) { + if (description.serverId == defaultServerId) { + return description.isAdVisible; + } } return false; } QString ServersUiController::adHeader() const { - int defaultIndex = getDefaultServerIndex(); - if (defaultIndex < 0) { + const QString defaultServerId = m_serversController->getDefaultServerId(); + if (defaultServerId.isEmpty()) { return QString(); } - ServerConfig server = m_serversController->getServerConfig(defaultIndex); - if (server.isApiV2()) { - const ApiV2ServerConfig* apiV2 = server.as(); - if (!apiV2) return QString(); - return apiV2->apiConfig.serviceInfo.adHeader; + for (const auto &description : m_orderedServerDescriptions) { + if (description.serverId == defaultServerId) { + return description.adHeader; + } } return QString(); } QString ServersUiController::adDescription() const { - int defaultIndex = getDefaultServerIndex(); - if (defaultIndex < 0) { + const QString defaultServerId = m_serversController->getDefaultServerId(); + if (defaultServerId.isEmpty()) { return QString(); } - ServerConfig server = m_serversController->getServerConfig(defaultIndex); - if (server.isApiV2()) { - const ApiV2ServerConfig* apiV2 = server.as(); - if (!apiV2) return QString(); - return apiV2->apiConfig.serviceInfo.adDescription; + for (const auto &description : m_orderedServerDescriptions) { + if (description.serverId == defaultServerId) { + return description.adDescription; + } } return QString(); } +QString ServersUiController::getServerId(int index) const +{ + if (index < 0 || index >= m_orderedServerDescriptions.size()) { + return QString(); + } + return m_orderedServerDescriptions.at(index).serverId; +} + +int ServersUiController::getServerIndexById(const QString &serverId) const +{ + return rowForServerId(m_orderedServerDescriptions, serverId); +} + void ServersUiController::updateContainersModel() { - if (m_processedServerIndex < 0 || m_processedServerIndex >= m_serversController->getServersCount()) { + if (m_processedServerId.isEmpty()) { return; } - ServerConfig server = m_serversController->getServerConfig(m_processedServerIndex); - QMap containers = server.containers(); + const QMap containers = + m_serversController->getServerContainersMap(m_processedServerId); m_containersModel->updateModel(containers); } void ServersUiController::updateDefaultServerContainersModel() { - int defaultIndex = m_serversController->getDefaultServerIndex(); - if (defaultIndex < 0 || defaultIndex >= m_serversController->getServersCount()) { + const QString defaultServerId = m_serversController->getDefaultServerId(); + if (defaultServerId.isEmpty()) { return; } - ServerConfig server = m_serversController->getServerConfig(defaultIndex); - QMap containers = server.containers(); + const QMap containers = + m_serversController->getServerContainersMap(defaultServerId); m_defaultServerContainersModel->updateModel(containers); } QStringList ServersUiController::getAllInstalledServicesName(int serverIndex) const { QStringList servicesName; - ServerConfig server = m_serversController->getServerConfig(serverIndex); - QMap containers = server.containers(); - + const QString serverId = getServerId(serverIndex); + const QMap containers = m_serversController->getServerContainersMap(serverId); + for (auto it = containers.begin(); it != containers.end(); ++it) { DockerContainer container = it.key(); if (ContainerUtils::containerService(container) == ServiceType::Other) { @@ -489,3 +512,13 @@ QStringList ServersUiController::getAllInstalledServicesName(int serverIndex) co return servicesName; } +int ServersUiController::serverIndexForId(const QString &serverId) const +{ + return rowForServerId(m_orderedServerDescriptions, serverId); +} + +bool ServersUiController::listHasServersFromGatewayApi() const +{ + return descriptionsHaveGatewayServers(m_orderedServerDescriptions); +} + diff --git a/client/ui/controllers/serversUiController.h b/client/ui/controllers/serversUiController.h index 7d16362af..6342faf22 100644 --- a/client/ui/controllers/serversUiController.h +++ b/client/ui/controllers/serversUiController.h @@ -6,35 +6,39 @@ #include #include #include +#include #include "core/controllers/serversController.h" +#include "core/models/serverDescription.h" #include "core/controllers/settingsController.h" #include "ui/models/serversModel.h" #include "ui/models/containersModel.h" -#include "core/models/serverConfig.h" class ServersUiController : public QObject { Q_OBJECT - Q_PROPERTY(int defaultIndex READ getDefaultServerIndex NOTIFY defaultServerIndexChanged) - Q_PROPERTY(QString defaultServerName READ getDefaultServerName NOTIFY defaultServerIndexChanged) - Q_PROPERTY(QString defaultServerDefaultContainerName READ getDefaultServerDefaultContainerName NOTIFY defaultServerIndexChanged) - Q_PROPERTY(QString defaultServerDescriptionCollapsed READ getDefaultServerDescriptionCollapsed NOTIFY defaultServerIndexChanged) - Q_PROPERTY(QString defaultServerImagePathCollapsed READ getDefaultServerImagePathCollapsed NOTIFY defaultServerIndexChanged) - Q_PROPERTY(QString defaultServerDescriptionExpanded READ getDefaultServerDescriptionExpanded NOTIFY defaultServerIndexChanged) - Q_PROPERTY(bool isDefaultServerDefaultContainerHasSplitTunneling READ isDefaultServerDefaultContainerHasSplitTunneling NOTIFY defaultServerIndexChanged) - Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIndexChanged) + Q_PROPERTY(QString defaultServerId READ getDefaultServerId NOTIFY defaultServerIdChanged) + Q_PROPERTY(int defaultServerIndex READ defaultServerIndex NOTIFY defaultServerIndexChanged) + + Q_PROPERTY(QString defaultServerName READ getDefaultServerName NOTIFY defaultServerIdChanged) + Q_PROPERTY(QString defaultServerDefaultContainerName READ getDefaultServerDefaultContainerName NOTIFY defaultServerIdChanged) + Q_PROPERTY(QString defaultServerDescriptionCollapsed READ getDefaultServerDescriptionCollapsed NOTIFY defaultServerIdChanged) + Q_PROPERTY(QString defaultServerImagePathCollapsed READ getDefaultServerImagePathCollapsed NOTIFY defaultServerIdChanged) + Q_PROPERTY(QString defaultServerDescriptionExpanded READ getDefaultServerDescriptionExpanded NOTIFY defaultServerIdChanged) + Q_PROPERTY(bool isDefaultServerDefaultContainerHasSplitTunneling READ isDefaultServerDefaultContainerHasSplitTunneling NOTIFY defaultServerIdChanged) + Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIdChanged) - Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged) + Q_PROPERTY(QString processedServerId READ getProcessedServerId WRITE setProcessedServerId NOTIFY processedServerIdChanged) + Q_PROPERTY(int processedServerIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged) Q_PROPERTY(int processedContainerIndex READ getProcessedContainerIndex WRITE setProcessedContainerIndex NOTIFY processedContainerIndexChanged) Q_PROPERTY(bool processedServerIsPremium READ processedServerIsPremium NOTIFY processedServerIndexChanged) Q_PROPERTY(bool hasServersFromGatewayApi READ hasServersFromGatewayApi NOTIFY hasServersFromGatewayApiChanged) - Q_PROPERTY(bool isAdVisible READ isAdVisible NOTIFY defaultServerIndexChanged) - Q_PROPERTY(QString adHeader READ adHeader NOTIFY defaultServerIndexChanged) - Q_PROPERTY(QString adDescription READ adDescription NOTIFY defaultServerIndexChanged) + Q_PROPERTY(bool isAdVisible READ isAdVisible NOTIFY defaultServerIdChanged) + Q_PROPERTY(QString adHeader READ adHeader NOTIFY defaultServerIdChanged) + Q_PROPERTY(QString adDescription READ adDescription NOTIFY defaultServerIdChanged) public: explicit ServersUiController(ServersController* serversController, @@ -45,15 +49,22 @@ public: QObject *parent = nullptr); public slots: - void removeServer(int index); - void editServerName(int index, const QString &name); - void setDefaultServerIndex(int index); - void setDefaultContainer(int serverIndex, int containerIndex); + void removeServer(const QString &serverId); + void removeServerAtIndex(int index); + + void editServerName(const QString &serverId, const QString &name); + + void setDefaultServer(const QString &serverId); + void setDefaultServerAtIndex(int index); + + void setDefaultContainer(const QString &serverId, int containerIndex); + void setDefaultContainerAtIndex(int index, int containerIndex); + void toggleAmneziaDns(bool enabled); - void onDefaultServerChanged(int index); + void onDefaultServerChanged(const QString &defaultServerId); // Getters for properties - int getDefaultServerIndex() const; + QString getDefaultServerId() const; QString getDefaultServerName() const; QString getDefaultServerDefaultContainerName() const; QString getDefaultServerDescriptionCollapsed() const; @@ -62,8 +73,14 @@ public slots: bool isDefaultServerDefaultContainerHasSplitTunneling() const; bool isDefaultServerFromApi() const; + QString getProcessedServerId() const; + void setProcessedServerId(const QString &serverId); + int getProcessedServerIndex() const; void setProcessedServerIndex(int index); + + int defaultServerIndex() const; + int getProcessedContainerIndex() const; void setProcessedContainerIndex(int index); bool processedServerIsPremium() const; @@ -78,12 +95,16 @@ public slots: QString adHeader() const; QString adDescription() const; + QString getServerId(int index) const; + int getServerIndexById(const QString &serverId) const; QStringList getAllInstalledServicesName(int serverIndex) const; signals: void errorOccurred(const QString &errorMessage); void finished(const QString &message); + void defaultServerIdChanged(const QString &serverId); void defaultServerIndexChanged(int index); + void processedServerIdChanged(const QString &serverId); void processedServerIndexChanged(int index); void processedContainerIndexChanged(int index); void hasServersFromGatewayApiChanged(); @@ -94,20 +115,23 @@ public: void updateModel(); private: - QString getDefaultServerDescription(const ServerConfig& server, int index) const; - bool isAmneziaDnsContainerInstalled(int serverIndex) const; + QString getDefaultServerDescription(const QString &serverId) const; + int serverIndexForId(const QString &serverId) const; + bool listHasServersFromGatewayApi() const; void updateContainersModel(); void updateDefaultServerContainersModel(); - void updateApiModelsForProcessedServer(); - + ServersController* m_serversController; SettingsController* m_settingsController; ServersModel* m_serversModel; ContainersModel* m_containersModel; ContainersModel* m_defaultServerContainersModel; + + QVector m_orderedServerDescriptions; int m_processedServerIndex = -1; + QString m_processedServerId; int m_processedContainerIndex = -1; }; diff --git a/client/ui/models/api/apiAccountInfoModel.cpp b/client/ui/models/api/apiAccountInfoModel.cpp index b9380ed51..85b1226bb 100644 --- a/client/ui/models/api/apiAccountInfoModel.cpp +++ b/client/ui/models/api/apiAccountInfoModel.cpp @@ -4,6 +4,7 @@ #include #include "core/utils/api/apiUtils.h" +#include "core/utils/serverConfigUtils.h" #include "logger.h" namespace @@ -28,7 +29,7 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const switch (role) { case SubscriptionStatusRole: { - if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) { + if (m_accountInfoData.configType == serverConfigUtils::ConfigType::AmneziaFreeV3) { return tr("Active"); } @@ -37,14 +38,14 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const : QStringLiteral("

%1").arg(tr("Active")); } case EndDateRole: { - if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) { + if (m_accountInfoData.configType == serverConfigUtils::ConfigType::AmneziaFreeV3) { return ""; } return QDateTime::fromString(m_accountInfoData.subscriptionEndDate, Qt::ISODate).toLocalTime().toString("d MMM yyyy"); } case ConnectedDevicesRole: { - if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) { + if (m_accountInfoData.configType == serverConfigUtils::ConfigType::AmneziaFreeV3) { return ""; } return tr("%1 out of %2").arg(m_accountInfoData.activeDeviceCount).arg(m_accountInfoData.maxDeviceCount); @@ -53,9 +54,8 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const return m_accountInfoData.subscriptionDescription; } case IsComponentVisibleRole: { - return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2 - || m_accountInfoData.configType == apiDefs::ConfigType::ExternalPremium - || m_accountInfoData.configType == apiDefs::ConfigType::ExternalTrial; + return m_accountInfoData.configType == serverConfigUtils::ConfigType::AmneziaPremiumV2 + || m_accountInfoData.configType == serverConfigUtils::ConfigType::ExternalPremium; } case IsSubscriptionRenewalAvailableRole: { return m_accountInfoData.isRenewalAvailable; @@ -80,7 +80,7 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const return false; } case IsSubscriptionExpiredRole: { - if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) { + if (m_accountInfoData.configType == serverConfigUtils::ConfigType::AmneziaFreeV3) { return false; } if (m_accountInfoData.isInAppPurchase) { @@ -92,7 +92,7 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate); } case IsSubscriptionExpiringSoonRole: { - if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) { + if (m_accountInfoData.configType == serverConfigUtils::ConfigType::AmneziaFreeV3) { return false; } if (m_accountInfoData.isInAppPurchase) { @@ -124,7 +124,7 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons accountInfoData.maxDeviceCount = accountInfoObject.value(apiDefs::key::maxDeviceCount).toInt(); accountInfoData.subscriptionEndDate = accountInfoObject.value(apiDefs::key::subscriptionEndDate).toString(); - accountInfoData.configType = apiUtils::getConfigType(serverConfig); + accountInfoData.configType = serverConfigUtils::configTypeFromJson(serverConfig); const QJsonObject apiConfig = serverConfig.value(apiDefs::key::apiConfig).toObject(); accountInfoData.isInAppPurchase = apiConfig.value(apiDefs::key::isInAppPurchase).toBool(false); diff --git a/client/ui/models/api/apiAccountInfoModel.h b/client/ui/models/api/apiAccountInfoModel.h index 379575dcb..b0c559a4e 100644 --- a/client/ui/models/api/apiAccountInfoModel.h +++ b/client/ui/models/api/apiAccountInfoModel.h @@ -5,7 +5,7 @@ #include #include -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" @@ -56,7 +56,7 @@ private: int activeDeviceCount; int maxDeviceCount; - apiDefs::ConfigType configType; + serverConfigUtils::ConfigType configType; QStringList supportedProtocols; diff --git a/client/ui/models/api/apiCountryModel.cpp b/client/ui/models/api/apiCountryModel.cpp index 4aec43366..b0315f346 100644 --- a/client/ui/models/api/apiCountryModel.cpp +++ b/client/ui/models/api/apiCountryModel.cpp @@ -2,7 +2,7 @@ #include -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" #include "logger.h" diff --git a/client/ui/models/api/apiDevicesModel.cpp b/client/ui/models/api/apiDevicesModel.cpp index 9d69c7b0b..8e205ccf2 100644 --- a/client/ui/models/api/apiDevicesModel.cpp +++ b/client/ui/models/api/apiDevicesModel.cpp @@ -2,7 +2,7 @@ #include -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" #include "logger.h" diff --git a/client/ui/models/serversModel.cpp b/client/ui/models/serversModel.cpp index 131d241f8..018146f99 100644 --- a/client/ui/models/serversModel.cpp +++ b/client/ui/models/serversModel.cpp @@ -1,11 +1,12 @@ #include "serversModel.h" +#include "core/models/serverDescription.h" + #include #include #include -#include "core/models/serverConfig.h" -#include "core/utils/api/apiEnums.h" +#include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" #include "core/utils/selfhosted/sshSession.h" @@ -19,42 +20,15 @@ using namespace amnezia; -namespace -{ - namespace configKey - { - constexpr char apiConfig[] = "api_config"; - constexpr char serviceInfo[] = "service_info"; - constexpr char availableCountries[] = "available_countries"; - constexpr char serverCountryCode[] = "server_country_code"; - constexpr char serverCountryName[] = "server_country_name"; - constexpr char userCountryCode[] = "user_country_code"; - constexpr char serviceType[] = "service_type"; - constexpr char serviceProtocol[] = "service_protocol"; - - constexpr char publicKeyInfo[] = "public_key"; - constexpr char expiresAt[] = "expires_at"; - } - - QString normalizeVpnKey(const QString &vpnKey) - { - QString normalized = vpnKey.trimmed(); - if (normalized.startsWith(QStringLiteral("vpn://"), Qt::CaseInsensitive)) { - normalized = normalized.mid(QStringLiteral("vpn://").size()); - } - return normalized; - } -} - ServersModel::ServersModel(QObject *parent) : QAbstractListModel(parent) { connect(this, &ServersModel::defaultServerIndexChanged, this, &ServersModel::defaultServerNameChanged); connect(this, &ServersModel::defaultServerIndexChanged, this, [this](const int serverIndex) { - if (serverIndex < 0 || serverIndex >= m_servers.size()) { + if (serverIndex < 0 || serverIndex >= m_descriptions.size()) { return; } - auto defaultContainer = m_servers.at(serverIndex).defaultContainer(); + auto defaultContainer = m_descriptions.at(serverIndex).defaultContainer; emit ServersModel::defaultServerDefaultContainerChanged(defaultContainer); emit ServersModel::defaultServerNameChanged(); }); @@ -65,148 +39,73 @@ ServersModel::ServersModel(QObject *parent) : QAbstractListModel(parent) int ServersModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); - return static_cast(m_servers.size()); + return static_cast(m_descriptions.size()); } QVariant ServersModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_servers.size())) { + if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_descriptions.size())) { return QVariant(); } - const ServerConfig &server = m_servers.at(index.row()); - const int configVersion = server.configVersion(); - + const ServerDescription &row = m_descriptions.at(index.row()); + const int configVersion = row.configVersion; + switch (role) { - case NameRole: { - if (configVersion) { - if (server.isApiV1()) { - return server.as()->name; - } else if (server.isApiV2()) { - return server.as()->name; - } - } - QString name = server.description(); - if (name.isEmpty()) { - return server.hostName(); - } - return name; - } - case ServerDescriptionRole: { - auto description = getServerDescription(server, index.row()); - return configVersion ? description : description + server.hostName(); - } - case HostNameRole: return server.hostName(); - case CredentialsRole: return QVariant::fromValue(serverCredentials(index.row())); - case CredentialsLoginRole: return serverCredentials(index.row()).userName; - case IsDefaultRole: return index.row() == m_defaultServerIndex; - case IsCurrentlyProcessedRole: return index.row() == m_processedServerIndex; - case HasWriteAccessRole: { - auto credentials = serverCredentials(index.row()); - return (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty()); - } - case ContainsAmneziaDnsRole: { - QString primaryDns = server.dns1(); - return primaryDns == protocols::dns::amneziaDnsIp; - } - case DefaultContainerRole: { - return server.defaultContainer(); - } - case HasInstalledContainers: { - return serverHasInstalledContainers(index.row()); - } - case IsServerFromTelegramApiRole: { - return configVersion == apiDefs::ConfigSource::Telegram; - } - case IsServerFromGatewayApiRole: { - return configVersion == apiDefs::ConfigSource::AmneziaGateway; - } - case ApiConfigRole: { + case NameRole: + return row.serverName; + case ServerDescriptionRole: + return configVersion ? row.baseDescription : (row.baseDescription + row.hostName); + case CollapsedServerDescriptionRole: + return row.collapsedServerDescription; + case ExpandedServerDescriptionRole: + return row.expandedServerDescription; + case HostNameRole: + return row.hostName; + case CredentialsRole: + return QVariant::fromValue(serverCredentials(index.row())); + case CredentialsLoginRole: + return serverCredentials(index.row()).userName; + case IsDefaultRole: + return index.row() == m_defaultServerIndex; + case IsCurrentlyProcessedRole: + return index.row() == m_processedServerIndex; + case HasWriteAccessRole: + return row.hasWriteAccess; + case ContainsAmneziaDnsRole: + return row.primaryDnsIsAmnezia; + case DefaultContainerRole: + return QVariant::fromValue(row.defaultContainer); + case HasInstalledContainers: + return row.hasInstalledVpnContainers; + case IsServerFromTelegramApiRole: + return false; + case IsServerFromGatewayApiRole: + return row.isServerFromGatewayApi; + case ApiConfigRole: return QVariant(); - } - case IsCountrySelectionAvailableRole: { - if (server.isApiV2()) { - return !server.as()->apiConfig.availableCountries.isEmpty(); - } - return false; - } - case ApiAvailableCountriesRole: { - if (server.isApiV2()) { - return server.as()->apiConfig.availableCountries; - } - return QJsonArray(); - } - case ApiServerCountryCodeRole: { - if (server.isApiV2()) { - return server.as()->apiConfig.serverCountryCode; - } - return QString(); - } - case HasAmneziaDns: { - QString primaryDns = server.dns1(); - return primaryDns == protocols::dns::amneziaDnsIp; - } - case IsAdVisibleRole: { - if (server.isApiV2()) { - return server.as()->apiConfig.serviceInfo.isAdVisible; - } - return false; - } - case AdHeaderRole: { - if (server.isApiV2()) { - return server.as()->apiConfig.serviceInfo.adHeader; - } - return QString(); - } - case AdDescriptionRole: { - if (server.isApiV2()) { - return server.as()->apiConfig.serviceInfo.adDescription; - } - return QString(); - } - case AdEndpointRole: { - if (server.isApiV2()) { - return server.as()->apiConfig.serviceInfo.adEndpoint; - } - return QString(); - } - case IsRenewalAvailableRole: { - if (server.isApiV2()) { - return server.as()->apiConfig.serviceInfo.isRenewalAvailable; - } - return false; - } - case IsSubscriptionExpiredRole: { - if (!server.isApiV2()) { - return false; - } - - const ApiConfig &apiConfig = server.as()->apiConfig; - if (apiConfig.isInAppPurchase) { - return false; - } - if (apiConfig.subscriptionExpiredByServer) { - return true; - } - if (apiConfig.subscription.endDate.isEmpty()) { - return false; - } - return apiUtils::isSubscriptionExpired(apiConfig.subscription.endDate); - } - case IsSubscriptionExpiringSoonRole: { - if (!server.isApiV2()) { - return false; - } - - const ApiConfig &apiConfig = server.as()->apiConfig; - if (apiConfig.isInAppPurchase) { - return false; - } - if (apiConfig.subscription.endDate.isEmpty()) { - return false; - } - return apiUtils::isSubscriptionExpiringSoon(apiConfig.subscription.endDate); - } + case IsCountrySelectionAvailableRole: + return row.isCountrySelectionAvailable; + case ApiAvailableCountriesRole: + return row.apiAvailableCountries; + case ApiServerCountryCodeRole: + return row.apiServerCountryCode; + case HasAmneziaDns: + return row.primaryDnsIsAmnezia; + case IsAdVisibleRole: + return row.isAdVisible; + case AdHeaderRole: + return row.adHeader; + case AdDescriptionRole: + return row.adDescription; + case AdEndpointRole: + return row.adEndpoint; + case IsRenewalAvailableRole: + return row.isRenewalAvailable; + case IsSubscriptionExpiredRole: + return row.isSubscriptionExpired; + case IsSubscriptionExpiringSoonRole: + return row.isSubscriptionExpiringSoon; } return QVariant(); @@ -218,12 +117,11 @@ QVariant ServersModel::data(const int index, int role) const return data(modelIndex, role); } -void ServersModel::updateModel(const QVector &servers, int defaultServerIndex, bool isAmneziaDnsEnabled) +void ServersModel::updateModel(const QVector &descriptions, int defaultServerIndex) { beginResetModel(); - m_servers = servers; + m_descriptions = descriptions; m_defaultServerIndex = defaultServerIndex; - m_isAmneziaDnsEnabled = isAmneziaDnsEnabled; endResetModel(); emit defaultServerIndexChanged(m_defaultServerIndex); emit processedServerChanged(); @@ -234,43 +132,15 @@ const int ServersModel::getDefaultServerIndex() return m_defaultServerIndex; } -QString ServersModel::getServerDescription(const ServerConfig &server, const int index) const -{ - const int configVersion = server.configVersion(); - QString description; - - if (server.isApiV2()) { - const ApiV2ServerConfig *apiV2 = server.as(); - if (apiV2 && !apiV2->apiConfig.serverCountryCode.isEmpty()) { - return apiV2->apiConfig.serverCountryName; - } - return apiV2 ? apiV2->description : server.description(); - } else if (server.isApiV1()) { - const ApiV1ServerConfig *apiV1 = server.as(); - return apiV1 ? apiV1->description : server.description(); - } else if (data(index, HasWriteAccessRole).toBool()) { - QMap containers = server.containers(); - bool isDnsInstalled = containers.contains(DockerContainer::Dns); - if (m_isAmneziaDnsEnabled && isDnsInstalled) { - description += "Amnezia DNS | "; - } - } else { - if (data(index, HasAmneziaDns).toBool()) { - description += "Amnezia DNS | "; - } - } - return description; -} - const int ServersModel::getServersCount() { - return m_servers.size(); + return m_descriptions.size(); } bool ServersModel::hasServerWithWriteAccess() { for (size_t i = 0; i < getServersCount(); i++) { - if (qvariant_cast(data(i, HasWriteAccessRole))) { + if (qvariant_cast(data(static_cast(i), HasWriteAccessRole))) { return true; } } @@ -350,29 +220,17 @@ QHash ServersModel::roleNames() const roles[IsSubscriptionExpiredRole] = "isSubscriptionExpired"; roles[IsSubscriptionExpiringSoonRole] = "isSubscriptionExpiringSoon"; + roles[HasAmneziaDns] = "hasAmneziaDns"; + return roles; } ServerCredentials ServersModel::serverCredentials(int index) const { - if (index < 0 || index >= m_servers.size()) { + if (index < 0 || index >= m_descriptions.size()) { return ServerCredentials(); } - const ServerConfig &server = m_servers.at(index); - - if (server.isSelfHosted()) { - const SelfHostedServerConfig *selfHosted = server.as(); - if (selfHosted) { - ServerCredentials credentials; - credentials.hostName = selfHosted->hostName; - credentials.userName = selfHosted->userName.value_or(""); - credentials.secretData = selfHosted->password.value_or(""); - credentials.port = selfHosted->port.value_or(22); - return credentials; - } - } - - return ServerCredentials(); + return m_descriptions.at(index).selfHostedSshCredentials; } bool ServersModel::isServerFromApi(const int serverIndex) @@ -405,21 +263,10 @@ QVariant ServersModel::getProcessedServerData(const QString &roleString) return {}; } - bool ServersModel::serverHasInstalledContainers(const int serverIndex) const { - const ServerConfig &server = m_servers.at(serverIndex); - QMap containers = server.containers(); - - for (auto it = containers.begin(); it != containers.end(); ++it) { - DockerContainer container = it.key(); - if (ContainerUtils::containerService(container) == ServiceType::Vpn) { - return true; - } - if (container == DockerContainer::SSXray) { - return true; - } + if (serverIndex < 0 || serverIndex >= m_descriptions.size()) { + return false; } - return false; + return m_descriptions.at(serverIndex).hasInstalledVpnContainers; } - diff --git a/client/ui/models/serversModel.h b/client/ui/models/serversModel.h index 9bff2eadb..90637e18b 100644 --- a/client/ui/models/serversModel.h +++ b/client/ui/models/serversModel.h @@ -5,7 +5,7 @@ #include #include "core/utils/selfhosted/sshSession.h" -#include "core/models/serverConfig.h" +#include "core/models/serverDescription.h" class ServersModel : public QAbstractListModel { @@ -75,14 +75,13 @@ public slots: bool isServerFromApi(const int serverIndex); - void updateModel(const QVector &servers, int defaultServerIndex, bool isAmneziaDnsEnabled = false); - + void updateModel(const QVector &descriptions, int defaultServerIndex); + protected: QHash roleNames() const override; signals: void processedServerIndexChanged(const int index); - // emitted when the processed server index or processed server data is changed void processedServerChanged(); void defaultServerIndexChanged(const int index); @@ -97,16 +96,12 @@ signals: private: ServerCredentials serverCredentials(int index) const; - QString getServerDescription(const ServerConfig &server, const int index) const; - bool serverHasInstalledContainers(const int serverIndex) const; - QVector m_servers; + QVector m_descriptions; - int m_defaultServerIndex; - int m_processedServerIndex; - - bool m_isAmneziaDnsEnabled = false; + int m_defaultServerIndex = -1; + int m_processedServerIndex = -1; }; #endif // SERVERSMODEL_H diff --git a/client/ui/qml/Components/ConnectButton.qml b/client/ui/qml/Components/ConnectButton.qml index 153aef122..2f17f4b60 100644 --- a/client/ui/qml/Components/ConnectButton.qml +++ b/client/ui/qml/Components/ConnectButton.qml @@ -182,7 +182,7 @@ Button { } onClicked: { - ServersUiController.setProcessedServerIndex(ServersUiController.defaultIndex) + ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex) ConnectionController.connectButtonClicked() } diff --git a/client/ui/qml/Components/GamepadLoader.qml b/client/ui/qml/Components/GamepadLoader.qml index d7853582c..5dc39a96c 100644 --- a/client/ui/qml/Components/GamepadLoader.qml +++ b/client/ui/qml/Components/GamepadLoader.qml @@ -13,7 +13,7 @@ Item { onButtonStartChanged: { if (buttonStart) { - ServersUiController.setProcessedServerIndex(ServersUiController.defaultIndex) + ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex) ConnectionController.connectButtonClicked() } } diff --git a/client/ui/qml/Components/HomeContainersListView.qml b/client/ui/qml/Components/HomeContainersListView.qml index bd24f9f3b..c1e8ac489 100644 --- a/client/ui/qml/Components/HomeContainersListView.qml +++ b/client/ui/qml/Components/HomeContainersListView.qml @@ -58,7 +58,7 @@ ListViewType { if (checked) { containersDropDown.closeTriggered() - ServersUiController.setDefaultContainer(ServersUiController.defaultIndex, proxyDefaultServerContainersModel.mapToSource(index)) + ServersUiController.setDefaultContainer(ServersUiController.getServerId(ServersUiController.defaultServerIndex), proxyDefaultServerContainersModel.mapToSource(index)) } else { ServersUiController.processedContainerIndex = proxyDefaultServerContainersModel.mapToSource(index) PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings) diff --git a/client/ui/qml/Components/RenameServerDrawer.qml b/client/ui/qml/Components/RenameServerDrawer.qml index a067835e5..328c8f537 100644 --- a/client/ui/qml/Components/RenameServerDrawer.qml +++ b/client/ui/qml/Components/RenameServerDrawer.qml @@ -46,7 +46,7 @@ DrawerType2 { } if (serverName.textField.text !== root.serverNameText) { - ServersUiController.editServerName(ServersUiController.processedIndex, serverName.textField.text); + ServersUiController.editServerName(ServersUiController.getServerId(ServersUiController.processedServerIndex), serverName.textField.text); } root.closeTriggered() } diff --git a/client/ui/qml/Components/ServersListView.qml b/client/ui/qml/Components/ServersListView.qml index b96dfb1d2..6e92a1fd1 100644 --- a/client/ui/qml/Components/ServersListView.qml +++ b/client/ui/qml/Components/ServersListView.qml @@ -17,7 +17,7 @@ import "../Config" ListViewType { id: root - property int selectedIndex: ServersUiController.defaultIndex + property int selectedIndex: ServersUiController.defaultServerIndex anchors.top: serversMenuHeader.bottom anchors.right: parent.right @@ -29,8 +29,8 @@ ListViewType { Connections { target: ServersUiController - function onDefaultServerIndexChanged(serverIndex) { - root.selectedIndex = serverIndex + function onDefaultServerIndexChanged() { + root.selectedIndex = ServersUiController.defaultServerIndex } } @@ -86,7 +86,7 @@ ListViewType { root.selectedIndex = index - ServersUiController.setDefaultServerIndex(index) + ServersUiController.setDefaultServerAtIndex(index) } Keys.onEnterPressed: serverRadioButton.clicked() @@ -106,14 +106,14 @@ ListViewType { z: 1 onClicked: function() { - ServersUiController.processedIndex = index + ServersUiController.processedServerIndex = index if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) { if (ServersModel.getProcessedServerData("isCountrySelectionAvailable")) { PageController.goToPage(PageEnum.PageSettingsApiAvailableCountries) } else { PageController.showBusyIndicator(true) - let result = SubscriptionUiController.getAccountInfo(ServersUiController.getProcessedServerIndex(), false) + let result = SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), false) PageController.showBusyIndicator(false) if (!result) { return diff --git a/client/ui/qml/Components/SettingsContainersListView.qml b/client/ui/qml/Components/SettingsContainersListView.qml index c5dd130e1..209c56438 100644 --- a/client/ui/qml/Components/SettingsContainersListView.qml +++ b/client/ui/qml/Components/SettingsContainersListView.qml @@ -34,19 +34,19 @@ ListViewType { if (isVpnContainer) { // var isThirdPartyConfig = root.model.data(index, ContainersModel.IsThirdPartyConfigRole) if (isThirdPartyConfig) { - InstallController.updateProtocols(ServersUiController.processedIndex, containerIndex) + InstallController.updateProtocols(ServersUiController.getServerId(ServersUiController.processedServerIndex), containerIndex) PageController.goToPage(PageEnum.PageProtocolRaw) return } } if (isIpsec) { - InstallController.updateProtocols(ServersUiController.processedIndex, containerIndex) + InstallController.updateProtocols(ServersUiController.getServerId(ServersUiController.processedServerIndex), containerIndex) PageController.goToPage(PageEnum.PageProtocolRaw) } else if (isDns) { PageController.goToPage(PageEnum.PageServiceDnsSettings) } else { - InstallController.updateProtocols(ServersUiController.processedIndex, containerIndex) + InstallController.updateProtocols(ServersUiController.getServerId(ServersUiController.processedServerIndex), containerIndex) PageController.goToPage(PageEnum.PageSettingsServerProtocol) } diff --git a/client/ui/qml/Components/SubscriptionExpiredDrawer.qml b/client/ui/qml/Components/SubscriptionExpiredDrawer.qml index 24fe66087..b1b27f16b 100644 --- a/client/ui/qml/Components/SubscriptionExpiredDrawer.qml +++ b/client/ui/qml/Components/SubscriptionExpiredDrawer.qml @@ -76,7 +76,7 @@ DrawerType2 { textColor: AmneziaStyle.color.midnightBlack clickedFunc: function() { - SubscriptionUiController.getRenewalLink(ServersUiController.defaultIndex) + SubscriptionUiController.getRenewalLink(ServersUiController.getServerId(ServersUiController.defaultServerIndex)) } } @@ -96,7 +96,7 @@ DrawerType2 { clickedFunc: function() { PageController.showBusyIndicator(true) - let result = SubscriptionUiController.getAccountInfo(ServersUiController.defaultIndex, false) + let result = SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.defaultServerIndex), false) PageController.showBusyIndicator(false) if (result) { root.closeTriggered() diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index 25794211c..12b811652 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -344,14 +344,14 @@ PageType { Keys.onReturnPressed: this.clicked() onClicked: { - ServersUiController.processedIndex = ServersUiController.defaultIndex + ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex) if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) { if (ServersModel.getProcessedServerData("isCountrySelectionAvailable")) { PageController.goToPage(PageEnum.PageSettingsApiAvailableCountries) } else { PageController.showBusyIndicator(true) - let result = SubscriptionUiController.getAccountInfo(ServersUiController.getProcessedServerIndex(), false) + let result = SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), false) PageController.showBusyIndicator(false) if (!result) { return diff --git a/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml index e5ae84aae..12b08fc20 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml @@ -441,7 +441,7 @@ PageType { } PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Awg) + InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.Awg) } var noButtonFunction = function() {} diff --git a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml index c8c8ab0ed..74f998ae0 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml @@ -561,7 +561,7 @@ PageType { } PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Awg) + InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.Awg) } var noButtonFunction = function() {} diff --git a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml index 1e00ac8fb..1e216e2fd 100644 --- a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -434,7 +434,7 @@ PageType { } PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.OpenVpn) + InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.OpenVpn) } var noButtonFunction = function() { if (!GC.isMobile()) { diff --git a/client/ui/qml/Pages2/PageProtocolRaw.qml b/client/ui/qml/Pages2/PageProtocolRaw.qml index 57dc7d9fc..881c20bac 100644 --- a/client/ui/qml/Pages2/PageProtocolRaw.qml +++ b/client/ui/qml/Pages2/PageProtocolRaw.qml @@ -184,7 +184,7 @@ PageType { var yesButtonFunction = function() { PageController.goToPage(PageEnum.PageDeinstalling) - InstallController.removeContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex) + InstallController.removeContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex) } var noButtonFunction = function() {} diff --git a/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml b/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml index f572d9c5c..730750f33 100644 --- a/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml @@ -129,7 +129,7 @@ PageType { } PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.WireGuard) + InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.WireGuard) } var noButtonFunction = function() {} showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) diff --git a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml index 899def535..72257cb2f 100644 --- a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml @@ -129,7 +129,7 @@ PageType { } PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.WireGuard) + InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.WireGuard) } var noButtonFunction = function() { if (!GC.isMobile()) { diff --git a/client/ui/qml/Pages2/PageProtocolXraySettings.qml b/client/ui/qml/Pages2/PageProtocolXraySettings.qml index 552ec3794..43c57caff 100644 --- a/client/ui/qml/Pages2/PageProtocolXraySettings.qml +++ b/client/ui/qml/Pages2/PageProtocolXraySettings.qml @@ -139,7 +139,7 @@ PageType { } PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray) + InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.Xray) } var noButtonFunction = function() { if (!GC.isMobile()) { diff --git a/client/ui/qml/Pages2/PageServiceDnsSettings.qml b/client/ui/qml/Pages2/PageServiceDnsSettings.qml index 6a2f6a07d..5dac3959e 100644 --- a/client/ui/qml/Pages2/PageServiceDnsSettings.qml +++ b/client/ui/qml/Pages2/PageServiceDnsSettings.qml @@ -79,7 +79,7 @@ PageType { PageController.showNotificationMessage(qsTr("Cannot remove AmneziaDNS from running server")) } else { PageController.goToPage(PageEnum.PageDeinstalling) - InstallController.removeContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex) + InstallController.removeContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex) } } var noButtonFunction = function() {} diff --git a/client/ui/qml/Pages2/PageServiceSftpSettings.qml b/client/ui/qml/Pages2/PageServiceSftpSettings.qml index c47f84672..2f6bbbb30 100644 --- a/client/ui/qml/Pages2/PageServiceSftpSettings.qml +++ b/client/ui/qml/Pages2/PageServiceSftpSettings.qml @@ -173,7 +173,7 @@ PageType { clickedFunc: function() { PageController.showBusyIndicator(true) - InstallController.mountSftpDrive(ServersUiController.processedIndex, port, password, username) + InstallController.mountSftpDrive(ServersUiController.getServerId(ServersUiController.processedServerIndex), port, password, username) PageController.showBusyIndicator(false) } } diff --git a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml index b52d0b69e..0e86ddc6a 100644 --- a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml +++ b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml @@ -285,7 +285,7 @@ PageType { } PageController.goToPage(PageEnum.PageSetupWizardInstalling) - InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Socks5Proxy) + InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.Socks5Proxy) tempPort = portTextField.textField.text tempUsername = usernameTextField.textField.text tempPassword = passwordTextField.textField.text diff --git a/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml b/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml index e54508096..4e5d62a41 100644 --- a/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml +++ b/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml @@ -108,7 +108,7 @@ PageType { actionButtonFunction: function() { PageController.showBusyIndicator(true) - let result = SubscriptionUiController.getAccountInfo(ServersUiController.getProcessedServerIndex(), false) + let result = SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), false) PageController.showBusyIndicator(false) if (!result) { return @@ -148,7 +148,7 @@ PageType { text: qsTr("Renew subscription") clickedFunc: function() { - SubscriptionUiController.getRenewalLink(ServersUiController.getProcessedServerIndex()) + SubscriptionUiController.getRenewalLink(ServersUiController.getServerId(ServersUiController.processedServerIndex)) } } @@ -200,7 +200,7 @@ PageType { PageController.showBusyIndicator(true) var prevIndex = ApiCountryModel.currentIndex ApiCountryModel.currentIndex = index - if (!SubscriptionUiController.updateServiceFromGateway(ServersUiController.getProcessedServerIndex(), countryCode, countryName)) { + if (!SubscriptionUiController.updateServiceFromGateway(ServersUiController.getServerId(ServersUiController.processedServerIndex), countryCode, countryName)) { ApiCountryModel.currentIndex = prevIndex } PageController.showBusyIndicator(false) diff --git a/client/ui/qml/Pages2/PageSettingsApiDevices.qml b/client/ui/qml/Pages2/PageSettingsApiDevices.qml index 6dd7b59d7..6aecdc634 100644 --- a/client/ui/qml/Pages2/PageSettingsApiDevices.qml +++ b/client/ui/qml/Pages2/PageSettingsApiDevices.qml @@ -82,8 +82,8 @@ PageType { var noButtonText = qsTr("Cancel") var yesButtonFunction = function() { - var serverIndex = ServersUiController.getProcessedServerIndex() - Qt.callLater(deactivateExternalDevice, serverIndex, supportTag, countryCode) + var serverId = ServersUiController.getServerId(ServersUiController.processedServerIndex) + Qt.callLater(deactivateExternalDevice, serverId, supportTag, countryCode) } var noButtonFunction = function() { } @@ -96,10 +96,10 @@ PageType { } } - function deactivateExternalDevice(serverIndex, supportTag, countryCode) { + function deactivateExternalDevice(serverId, supportTag, countryCode) { PageController.showBusyIndicator(true) - if (SubscriptionUiController.deactivateExternalDevice(serverIndex, supportTag, countryCode)) { - SubscriptionUiController.getAccountInfo(serverIndex, true) + if (SubscriptionUiController.deactivateExternalDevice(serverId, supportTag, countryCode)) { + SubscriptionUiController.getAccountInfo(serverId, true) } PageController.showBusyIndicator(false) } diff --git a/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml b/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml index b55e9f46c..4c09122f3 100644 --- a/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml +++ b/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml @@ -1,243 +1,243 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import QtQuick.Dialogs - -import QtCore - -import SortFilterProxyModel 0.2 - -import PageEnum 1.0 -import Style 1.0 - -import "./" -import "../Controls2" -import "../Controls2/TextTypes" -import "../Config" -import "../Components" - -PageType { - id: root - - property string configExtension: ".conf" - property string configCaption: qsTr("Save AmneziaVPN config") - - BackButtonType { - id: backButton - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.topMargin: 20 + PageController.safeAreaTopMargin - - onActiveFocusChanged: { - if(backButton.enabled && backButton.activeFocus) { - listView.positionViewAtBeginning() - } - } - } - - ListViewType { - id: listView - - anchors.top: backButton.bottom - anchors.bottom: parent.bottom - anchors.right: parent.right - anchors.left: parent.left - - model: ApiCountryModel - - header: ColumnLayout { - width: listView.width - - BaseHeaderType { - id: header - - Layout.fillWidth: true - Layout.rightMargin: 16 - Layout.leftMargin: 16 - - headerText: qsTr("Configuration Files") - descriptionText: qsTr("For router setup or the AmneziaWG app") - } - } - - delegate: ColumnLayout { - width: listView.width - - LabelWithButtonType { - Layout.fillWidth: true - Layout.topMargin: 6 - - text: countryName - descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : "" - hideDescription: isWorkerExpired ? false : true - descriptionColor: AmneziaStyle.color.vibrantRed - - leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg" - rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg" - - clickedFunction: function() { - if (isIssued) { - moreOptionsDrawer.countryName = countryName - moreOptionsDrawer.countryCode = countryCode - moreOptionsDrawer.openTriggered() - } else { - issueConfig(countryCode) - } - } - } - - DividerType {} - } - } - - DrawerType2 { - id: moreOptionsDrawer - - property string countryName - property string countryCode - - anchors.fill: parent - expandedHeight: parent.height * 0.4375 - - expandedStateContent: Item { - implicitHeight: moreOptionsDrawer.expandedHeight - - BackButtonType { - id: moreOptionsDrawerBackButton - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.topMargin: 16 - - backButtonFunction: function() { - moreOptionsDrawer.closeTriggered() - } - } - - ListViewType { - id: drawerListView - - anchors.top: moreOptionsDrawerBackButton.bottom - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - - header: ColumnLayout { - width: drawerListView.width - - Header2Type { - Layout.fillWidth: true - Layout.margins: 16 - - headerText: moreOptionsDrawer.countryName + qsTr(" configuration file") - } - } - - model: 1 // fake model to force the ListView to be created without a model - - delegate: ColumnLayout { - width: drawerListView.width - - LabelWithButtonType { - Layout.fillWidth: true - Layout.leftMargin: 16 - Layout.rightMargin: 16 - - text: qsTr("Generate a new configuration file") - descriptionText: qsTr("The previously created one will stop working") - - clickedFunction: function() { - showQuestion(true, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName) - } - } - - DividerType {} - } - - footer: ColumnLayout { - width: drawerListView.width - - LabelWithButtonType { - Layout.fillWidth: true - Layout.leftMargin: 16 - Layout.rightMargin: 16 - - text: qsTr("Revoke the current configuration file") - - clickedFunction: function() { - showQuestion(false, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName) - } - } - - DividerType {} - } - } - } - } - - function issueConfig(countryCode) { - var fileName = "" - if (GC.isMobile()) { - fileName = countryCode + configExtension - } else { - fileName = SystemController.getFileName(configCaption, - qsTr("Config files (*" + configExtension + ")"), - StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + countryCode, - true, - configExtension) - } - if (fileName !== "") { - PageController.showBusyIndicator(true) - let result = SubscriptionUiController.exportNativeConfig(ServersUiController.getProcessedServerIndex(), countryCode, fileName) - if (result) { - SubscriptionUiController.getAccountInfo(ServersUiController.getProcessedServerIndex(), true) - } - - PageController.showBusyIndicator(false) - if (result) { - PageController.showNotificationMessage(qsTr("Config file saved")) - } - } - } - - function revokeConfig(countryCode) { - PageController.showBusyIndicator(true) - let result = SubscriptionUiController.revokeNativeConfig(ServersUiController.getProcessedServerIndex(), countryCode) - if (result) { - SubscriptionUiController.getAccountInfo(ServersUiController.getProcessedServerIndex(), true) - } - PageController.showBusyIndicator(false) - - if (result) { - PageController.showNotificationMessage(qsTr("The config has been revoked")) - } - } - - function showQuestion(isConfigIssue, countryCode, countryName) { - var headerText - if (isConfigIssue) { - headerText = qsTr("Generate a new %1 configuration file?").arg(countryName) - } else { - headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName) - } - - var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it") - var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue") - var noButtonText = qsTr("Cancel") - - var yesButtonFunction = function() { - if (isConfigIssue) { - issueConfig(countryCode) - } else { - revokeConfig(countryCode) - } - moreOptionsDrawer.closeTriggered() - } - var noButtonFunction = function() {} - - showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) - } -} +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs + +import QtCore + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 +import Style 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + property string configExtension: ".conf" + property string configCaption: qsTr("Save AmneziaVPN config") + + BackButtonType { + id: backButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 20 + PageController.safeAreaTopMargin + + onActiveFocusChanged: { + if(backButton.enabled && backButton.activeFocus) { + listView.positionViewAtBeginning() + } + } + } + + ListViewType { + id: listView + + anchors.top: backButton.bottom + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.left: parent.left + + model: ApiCountryModel + + header: ColumnLayout { + width: listView.width + + BaseHeaderType { + id: header + + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + + headerText: qsTr("Configuration Files") + descriptionText: qsTr("For router setup or the AmneziaWG app") + } + } + + delegate: ColumnLayout { + width: listView.width + + LabelWithButtonType { + Layout.fillWidth: true + Layout.topMargin: 6 + + text: countryName + descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : "" + hideDescription: isWorkerExpired ? false : true + descriptionColor: AmneziaStyle.color.vibrantRed + + leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg" + rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg" + + clickedFunction: function() { + if (isIssued) { + moreOptionsDrawer.countryName = countryName + moreOptionsDrawer.countryCode = countryCode + moreOptionsDrawer.openTriggered() + } else { + issueConfig(countryCode) + } + } + } + + DividerType {} + } + } + + DrawerType2 { + id: moreOptionsDrawer + + property string countryName + property string countryCode + + anchors.fill: parent + expandedHeight: parent.height * 0.4375 + + expandedStateContent: Item { + implicitHeight: moreOptionsDrawer.expandedHeight + + BackButtonType { + id: moreOptionsDrawerBackButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 16 + + backButtonFunction: function() { + moreOptionsDrawer.closeTriggered() + } + } + + ListViewType { + id: drawerListView + + anchors.top: moreOptionsDrawerBackButton.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + + header: ColumnLayout { + width: drawerListView.width + + Header2Type { + Layout.fillWidth: true + Layout.margins: 16 + + headerText: moreOptionsDrawer.countryName + qsTr(" configuration file") + } + } + + model: 1 // fake model to force the ListView to be created without a model + + delegate: ColumnLayout { + width: drawerListView.width + + LabelWithButtonType { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + text: qsTr("Generate a new configuration file") + descriptionText: qsTr("The previously created one will stop working") + + clickedFunction: function() { + showQuestion(true, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName) + } + } + + DividerType {} + } + + footer: ColumnLayout { + width: drawerListView.width + + LabelWithButtonType { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + text: qsTr("Revoke the current configuration file") + + clickedFunction: function() { + showQuestion(false, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName) + } + } + + DividerType {} + } + } + } + } + + function issueConfig(countryCode) { + var fileName = "" + if (GC.isMobile()) { + fileName = countryCode + configExtension + } else { + fileName = SystemController.getFileName(configCaption, + qsTr("Config files (*" + configExtension + ")"), + StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + countryCode, + true, + configExtension) + } + if (fileName !== "") { + PageController.showBusyIndicator(true) + let result = SubscriptionUiController.exportNativeConfig(ServersUiController.getServerId(ServersUiController.processedServerIndex), countryCode, fileName) + if (result) { + SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), true) + } + + PageController.showBusyIndicator(false) + if (result) { + PageController.showNotificationMessage(qsTr("Config file saved")) + } + } + } + + function revokeConfig(countryCode) { + PageController.showBusyIndicator(true) + let result = SubscriptionUiController.revokeNativeConfig(ServersUiController.getServerId(ServersUiController.processedServerIndex), countryCode) + if (result) { + SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), true) + } + PageController.showBusyIndicator(false) + + if (result) { + PageController.showNotificationMessage(qsTr("The config has been revoked")) + } + } + + function showQuestion(isConfigIssue, countryCode, countryName) { + var headerText + if (isConfigIssue) { + headerText = qsTr("Generate a new %1 configuration file?").arg(countryName) + } else { + headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName) + } + + var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it") + var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue") + var noButtonText = qsTr("Cancel") + + var yesButtonFunction = function() { + if (isConfigIssue) { + issueConfig(countryCode) + } else { + revokeConfig(countryCode) + } + moreOptionsDrawer.closeTriggered() + } + var noButtonFunction = function() {} + + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + } +} diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index 7eb119244..07fb3089d 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -186,7 +186,7 @@ PageType { textColor: AmneziaStyle.color.midnightBlack clickedFunc: function() { - SubscriptionUiController.getRenewalLink(ServersUiController.getProcessedServerIndex()) + SubscriptionUiController.getRenewalLink(ServersUiController.getServerId(ServersUiController.processedServerIndex)) } } } @@ -246,7 +246,7 @@ PageType { text: qsTr("Renew subscription") clickedFunc: function() { - SubscriptionUiController.getRenewalLink(ServersUiController.getProcessedServerIndex()) + SubscriptionUiController.getRenewalLink(ServersUiController.getServerId(ServersUiController.processedServerIndex)) } } @@ -258,7 +258,7 @@ PageType { SwitcherType { id: switcher - readonly property bool isVlessProtocol: SubscriptionUiController.isVlessProtocol(ServersUiController.getProcessedServerIndex()) + readonly property bool isVlessProtocol: SubscriptionUiController.isVlessProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex)) readonly property bool isProtocolSwitchBlocked: ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected Layout.fillWidth: true @@ -276,8 +276,8 @@ PageType { PageController.showNotificationMessage(qsTr("Cannot change protocol during active connection")) } else { PageController.showBusyIndicator(true) - SubscriptionUiController.setCurrentProtocol(ServersUiController.getProcessedServerIndex(), switcher.isVlessProtocol ? "awg" : "vless") - SubscriptionUiController.updateServiceFromGateway(ServersUiController.processedIndex, "", "", true) + SubscriptionUiController.setCurrentProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex), switcher.isVlessProtocol ? "awg" : "vless") + SubscriptionUiController.updateServiceFromGateway(ServersUiController.getServerId(ServersUiController.processedServerIndex), "", "", true) PageController.showBusyIndicator(false) } } @@ -325,7 +325,7 @@ PageType { PageController.goToPage(PageEnum.PageSettingsApiSubscriptionKey) PageController.showBusyIndicator(true) - SubscriptionUiController.prepareVpnKeyExport(ServersUiController.getProcessedServerIndex()) + SubscriptionUiController.prepareVpnKeyExport(ServersUiController.getServerId(ServersUiController.processedServerIndex)) PageController.showBusyIndicator(false) } @@ -431,7 +431,7 @@ PageType { PageController.showNotificationMessage(qsTr("Cannot reload API config during active connection")) } else { PageController.showBusyIndicator(true) - SubscriptionUiController.updateServiceFromGateway(ServersUiController.processedIndex, "", "", true) + SubscriptionUiController.updateServiceFromGateway(ServersUiController.getServerId(ServersUiController.processedServerIndex), "", "", true) PageController.showBusyIndicator(false) } } @@ -469,8 +469,8 @@ PageType { PageController.showNotificationMessage(qsTr("Cannot unlink device during active connection")) } else { PageController.showBusyIndicator(true) - if (SubscriptionUiController.deactivateDevice(ServersUiController.getProcessedServerIndex())) { - SubscriptionUiController.getAccountInfo(ServersUiController.getProcessedServerIndex(), true) + if (SubscriptionUiController.deactivateDevice(ServersUiController.getServerId(ServersUiController.processedServerIndex))) { + SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), true) } PageController.showBusyIndicator(false) } @@ -506,7 +506,7 @@ PageType { PageController.showNotificationMessage(qsTr("Cannot remove server during active connection")) } else { PageController.showBusyIndicator(true) - InstallController.removeServer(ServersUiController.getProcessedServerIndex()) + InstallController.removeServer(ServersUiController.getServerId(ServersUiController.processedServerIndex)) PageController.showBusyIndicator(false) } } diff --git a/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml b/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml index ac0c9f71e..442bf2ba2 100644 --- a/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml +++ b/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml @@ -48,7 +48,7 @@ PageType { Component.onCompleted: { PageController.showBusyIndicator(true) - SubscriptionUiController.prepareVpnKeyExport(ServersUiController.getProcessedServerIndex()) + SubscriptionUiController.prepareVpnKeyExport(ServersUiController.getServerId(ServersUiController.processedServerIndex)) PageController.showBusyIndicator(false) } @@ -119,7 +119,7 @@ PageType { if (fileName !== "") { PageController.showBusyIndicator(true) - SubscriptionUiController.exportVpnKey(ServersUiController.getProcessedServerIndex(), fileName) + SubscriptionUiController.exportVpnKey(ServersUiController.getServerId(ServersUiController.processedServerIndex), fileName) PageController.showBusyIndicator(false) } } @@ -141,7 +141,7 @@ PageType { clickedFunc: function() { PageController.showBusyIndicator(true) - SubscriptionUiController.prepareVpnKeyExport(ServersUiController.getProcessedServerIndex()) + SubscriptionUiController.prepareVpnKeyExport(ServersUiController.getServerId(ServersUiController.processedServerIndex)) PageController.showBusyIndicator(false) vpnKeyDrawer.openTriggered() } diff --git a/client/ui/qml/Pages2/PageSettingsDns.qml b/client/ui/qml/Pages2/PageSettingsDns.qml index a96d45a1b..ba68c20c5 100644 --- a/client/ui/qml/Pages2/PageSettingsDns.qml +++ b/client/ui/qml/Pages2/PageSettingsDns.qml @@ -37,7 +37,7 @@ PageType { anchors.right: parent.right anchors.left: parent.left - property var isServerFromApi: ServersModel.isServerFromApi(ServersUiController.defaultIndex) + property var isServerFromApi: ServersModel.isServerFromApi(ServersUiController.defaultServerIndex) enabled: !isServerFromApi diff --git a/client/ui/qml/Pages2/PageSettingsServerData.qml b/client/ui/qml/Pages2/PageSettingsServerData.qml index 8ae736ae0..f34d2d522 100644 --- a/client/ui/qml/Pages2/PageSettingsServerData.qml +++ b/client/ui/qml/Pages2/PageSettingsServerData.qml @@ -111,7 +111,7 @@ PageType { readonly property var tColor: AmneziaStyle.color.paleGray readonly property var clickedHandler: function() { PageController.showBusyIndicator(true) - InstallController.scanServerForInstalledContainers(ServersUiController.processedIndex) + InstallController.scanServerForInstalledContainers(ServersUiController.getServerId(ServersUiController.processedServerIndex)) PageController.showBusyIndicator(false) } } @@ -134,7 +134,7 @@ PageType { PageController.showNotificationMessage(qsTr("Cannot reboot server during active connection")) } else { PageController.showBusyIndicator(true) - InstallController.rebootServer(ServersUiController.processedIndex) + InstallController.rebootServer(ServersUiController.getServerId(ServersUiController.processedServerIndex)) PageController.showBusyIndicator(false) } } @@ -164,7 +164,7 @@ PageType { PageController.showNotificationMessage(qsTr("Cannot remove server during active connection")) } else { PageController.showBusyIndicator(true) - InstallController.removeServer(ServersUiController.processedIndex) + InstallController.removeServer(ServersUiController.getServerId(ServersUiController.processedServerIndex)) PageController.showBusyIndicator(false) } } @@ -194,7 +194,7 @@ PageType { PageController.showNotificationMessage(qsTr("Cannot clear server from Amnezia software during active connection")) } else { PageController.goToPage(PageEnum.PageDeinstalling) - InstallController.removeAllContainers(ServersUiController.processedIndex) + InstallController.removeAllContainers(ServersUiController.getServerId(ServersUiController.processedServerIndex)) } } var noButtonFunction = function() { @@ -223,7 +223,7 @@ PageType { PageController.showNotificationMessage(qsTr("Cannot reset API config during active connection")) } else { PageController.showBusyIndicator(true) - SubscriptionUiController.removeApiConfig(ServersUiController.processedIndex) + SubscriptionUiController.removeApiConfig(ServersUiController.getServerId(ServersUiController.processedServerIndex)) PageController.showBusyIndicator(false) } } diff --git a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml index 13d77aa5b..e7ffe90db 100644 --- a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml +++ b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml @@ -76,7 +76,7 @@ PageType { clickedFunction: function() { if (isClientProtocolExists) { - InstallController.openClientSettings(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, protocolIndex) + InstallController.openClientSettings(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, protocolIndex) PageController.goToPage(clientProtocolPage); } else { PageController.showNotificationMessage(qsTr("Click the \"connect\" button to create a connection configuration")) @@ -104,7 +104,7 @@ PageType { visible: delegateContent.isServerSettingsVisible clickedFunction: function() { - InstallController.openServerSettings(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, protocolIndex) + InstallController.openServerSettings(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, protocolIndex) PageController.goToPage(serverProtocolPage); } @@ -147,7 +147,7 @@ PageType { } PageController.showBusyIndicator(true) - InstallController.clearCachedProfile(ServersUiController.processedIndex, ServersUiController.processedContainerIndex) + InstallController.clearCachedProfile(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex) PageController.showBusyIndicator(false) } @@ -191,7 +191,7 @@ PageType { } else { PageController.goToPage(PageEnum.PageDeinstalling) - InstallController.removeContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex) + InstallController.removeContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex) } } var noButtonFunction = function() { diff --git a/client/ui/qml/Pages2/PageSettingsServersList.qml b/client/ui/qml/Pages2/PageSettingsServersList.qml index c163b1eb9..478900d4e 100644 --- a/client/ui/qml/Pages2/PageSettingsServersList.qml +++ b/client/ui/qml/Pages2/PageSettingsServersList.qml @@ -86,11 +86,11 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() { - ServersUiController.processedIndex = index + ServersUiController.setProcessedServerIndex(index) if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) { PageController.showBusyIndicator(true) - let result = SubscriptionUiController.getAccountInfo(ServersUiController.getProcessedServerIndex(), false) + let result = SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), false) PageController.showBusyIndicator(false) if (!result) { return diff --git a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml index 216d8aa32..24436cc38 100644 --- a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml +++ b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml @@ -121,7 +121,7 @@ PageType { var _secretData = listView.itemAtIndex(vars.secretDataIndex).children[0].textField.text InstallController.setProcessedServerCredentials(_hostname, _username, _secretData) - ServersUiController.processedIndex = -1 + ServersUiController.setProcessedServerIndex(-1) PageController.showBusyIndicator(true) var isConnectionOpened = InstallController.checkSshConnection() diff --git a/client/ui/qml/Pages2/PageSetupWizardEasy.qml b/client/ui/qml/Pages2/PageSetupWizardEasy.qml index 27d955812..faefd1e25 100644 --- a/client/ui/qml/Pages2/PageSetupWizardEasy.qml +++ b/client/ui/qml/Pages2/PageSetupWizardEasy.qml @@ -163,9 +163,9 @@ PageType { ServersUiController.processedContainerIndex = listView.dockerContainer PageController.goToPage(PageEnum.PageSetupWizardInstalling) InstallController.install(listView.dockerContainer, - listView.containerDefaultPort, - listView.containerDefaultTransportProto, - ServersUiController.processedIndex) + listView.containerDefaultPort, + listView.containerDefaultTransportProto, + ServersUiController.getServerId(ServersUiController.processedServerIndex)) } else { PageController.goToPage(PageEnum.PageSetupWizardProtocols) } diff --git a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml index 4759eb3d7..2d7841496 100644 --- a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml +++ b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml @@ -28,7 +28,7 @@ PageType { function onInstallContainerFinished(finishedMessage, isServiceInstall) { var containerIndex = ServersUiController.processedContainerIndex if (!ConnectionController.isConnected && !ContainersModel.isServiceContainer(containerIndex)) { - ServersUiController.setDefaultContainer(ServersUiController.processedIndex, containerIndex) + ServersUiController.setDefaultContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), containerIndex) } PageController.closePage() // close installing page @@ -47,8 +47,8 @@ PageType { function onInstallServerFinished(finishedMessage) { if (!ConnectionController.isConnected) { - ServersUiController.setDefaultServerIndex(ServersModel.getServersCount() - 1); - ServersUiController.processedIndex = ServersUiController.defaultIndex + ServersUiController.setDefaultServerAtIndex(ServersModel.getServersCount() - 1); + ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex) } PageController.goToPageHome() @@ -57,7 +57,7 @@ PageType { function onServerAlreadyExists(serverIndex) { PageController.goToStartPage() - ServersUiController.processedIndex = serverIndex + ServersUiController.setProcessedServerIndex(serverIndex) PageController.goToPage(PageEnum.PageSettingsServerInfo, false) PageController.showErrorMessage(qsTr("The server has already been added to the application")) diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml index 7d7526592..bd51a3ced 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml @@ -243,7 +243,7 @@ PageType { } PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.install(dockerContainer, port.textField.text, transportProtoSelector.currentIndex, ServersUiController.processedIndex) + InstallController.install(dockerContainer, port.textField.text, transportProtoSelector.currentIndex, ServersUiController.getServerId(ServersUiController.processedServerIndex)) } } diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 6c2c81e4e..9dab7a5fa 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -42,40 +42,40 @@ PageType { var configExtension var configFileName - var serverIndex = ServersUiController.processedIndex var containerIndex = ServersUiController.processedContainerIndex + var serverId = ServersUiController.getServerId(ServersUiController.processedServerIndex) switch (type) { case PageShare.ConfigType.AmneziaConnection: { - ExportController.generateConnectionConfig(serverIndex, containerIndex, clientNameTextField.textField.text); + ExportController.generateConnectionConfig(serverId, containerIndex, clientNameTextField.textField.text); configCaption = qsTr("Save AmneziaVPN config") configExtension = ".vpn" configFileName = "amnezia_config" break; } case PageShare.ConfigType.OpenVpn: { - ExportController.generateOpenVpnConfig(serverIndex, clientNameTextField.textField.text) + ExportController.generateOpenVpnConfig(serverId, clientNameTextField.textField.text) configCaption = qsTr("Save OpenVPN config") configExtension = ".ovpn" configFileName = "amnezia_for_openvpn" break } case PageShare.ConfigType.WireGuard: { - ExportController.generateWireGuardConfig(serverIndex, clientNameTextField.textField.text) + ExportController.generateWireGuardConfig(serverId, clientNameTextField.textField.text) configCaption = qsTr("Save WireGuard config") configExtension = ".conf" configFileName = "amnezia_for_wireguard" break } case PageShare.ConfigType.Awg: { - ExportController.generateAwgConfig(serverIndex, containerIndex, clientNameTextField.textField.text) + ExportController.generateAwgConfig(serverId, containerIndex, clientNameTextField.textField.text) configCaption = qsTr("Save AmneziaWG config") configExtension = ".conf" configFileName = "amnezia_for_awg" break } case PageShare.ConfigType.Xray: { - ExportController.generateXrayConfig(serverIndex, clientNameTextField.textField.text) + ExportController.generateXrayConfig(serverId, clientNameTextField.textField.text) configCaption = qsTr("Save XRay config") configExtension = ".json" configFileName = "amnezia_for_xray" @@ -249,7 +249,7 @@ PageType { onClicked: { accessTypeSelector.currentIndex = 1 PageController.showBusyIndicator(true) - ExportController.updateClientManagementModel(ServersUiController.processedIndex, + ExportController.updateClientManagementModel(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex) PageController.showBusyIndicator(false) } @@ -333,7 +333,7 @@ PageType { Component.onCompleted: { if (ServersModel.isDefaultServerHasWriteAccess() && ServersModel.getDefaultServerData("hasInstalledContainers")) { - serverSelectorListView.selectedIndex = proxyServersModel.mapFromSource(ServersUiController.defaultIndex) + serverSelectorListView.selectedIndex = proxyServersModel.mapFromSource(ServersUiController.defaultServerIndex) } else { serverSelectorListView.selectedIndex = 0 } @@ -344,7 +344,7 @@ PageType { function handler() { serverSelector.text = selectedText - ServersUiController.processedIndex = proxyServersModel.mapToSource(selectedIndex) + ServersUiController.setProcessedServerIndex(proxyServersModel.mapToSource(selectedIndex)) } } } @@ -417,7 +417,7 @@ PageType { if (accessTypeSelector.currentIndex === 1) { PageController.showBusyIndicator(true) - ExportController.updateClientManagementModel(ServersUiController.processedIndex, + ExportController.updateClientManagementModel(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex) PageController.showBusyIndicator(false) } @@ -792,9 +792,9 @@ PageType { clientsListView.freezeFilter = true PageController.showBusyIndicator(true) ExportController.renameClient(proxyClientManagementModel.mapToSource(index), - clientNameEditor.textField.text, - ServersUiController.processedIndex, - ServersUiController.processedContainerIndex) + clientNameEditor.textField.text, + ServersUiController.getServerId(ServersUiController.processedServerIndex), + ServersUiController.processedContainerIndex) PageController.showBusyIndicator(false) Qt.callLater(function(){ clientsListView.freezeFilter = false }) clientNameEditDrawer.closeTriggered() @@ -829,8 +829,8 @@ PageType { clientInfoDrawer.closeTriggered() PageController.showBusyIndicator(true) ExportController.revokeConfig(proxyClientManagementModel.mapToSource(index), - ServersUiController.processedIndex, - ServersUiController.processedContainerIndex) + ServersUiController.getServerId(ServersUiController.processedServerIndex), + ServersUiController.processedContainerIndex) } var noButtonFunction = function() { } diff --git a/client/ui/qml/Pages2/PageShareFullAccess.qml b/client/ui/qml/Pages2/PageShareFullAccess.qml index 8b162899c..7fb0dd61d 100644 --- a/client/ui/qml/Pages2/PageShareFullAccess.qml +++ b/client/ui/qml/Pages2/PageShareFullAccess.qml @@ -119,13 +119,13 @@ PageType { Component.onCompleted: { serverSelectorListView.currentIndex = ServersModel.isDefaultServerHasWriteAccess() ? - proxyServersModel.mapFromSource(ServersUiController.defaultIndex) : 0 + proxyServersModel.mapFromSource(ServersUiController.defaultServerIndex) : 0 serverSelectorListView.triggerCurrentItem() } function handler() { serverSelector.text = selectedText - ServersUiController.processedIndex = proxyServersModel.mapToSource(selectedIndex) + ServersUiController.setProcessedServerIndex(proxyServersModel.mapToSource(selectedIndex)) } } } @@ -155,7 +155,7 @@ PageType { ExportController.exportErrorOccurred(qsTr("Access error!")) return } else { - ExportController.generateFullAccessConfig(ServersUiController.processedIndex) + ExportController.generateFullAccessConfig(ServersUiController.getServerId(ServersUiController.processedServerIndex)) } PageController.showBusyIndicator(false) diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index d39baa9f8..00411b5e1 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -154,7 +154,7 @@ PageType { function onNoInstalledContainers() { PageController.setTriggeredByConnectButton(true) - ServersUiController.processedIndex = ServersUiController.defaultIndex + ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex) PageController.goToPage(PageEnum.PageSetupWizardEasy) } } @@ -227,11 +227,11 @@ PageType { function onInstallServerFromApiFinished(message, preferredDefaultIndex) { if (!ConnectionController.isConnected) { if (preferredDefaultIndex !== undefined && preferredDefaultIndex >= 0) { - ServersUiController.setDefaultServerIndex(preferredDefaultIndex) + ServersUiController.setDefaultServerAtIndex(preferredDefaultIndex) } else { - ServersUiController.setDefaultServerIndex(ServersModel.getServersCount() - 1); + ServersUiController.setDefaultServerAtIndex(ServersModel.getServersCount() - 1); } - ServersUiController.processedIndex = ServersUiController.defaultIndex + ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex) } PageController.goToPageHome() @@ -274,7 +274,7 @@ PageType { } else { tabBar.visible = true pagePath = PageController.getPagePath(PageEnum.PageHome) - ServersUiController.processedIndex = ServersUiController.defaultIndex + ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex) } tabBarStackView.push(pagePath, { "objectName" : pagePath }) @@ -348,7 +348,7 @@ PageType { image: "qrc:/images/controls/home.svg" clickedFunc: function () { tabBarStackView.goToTabBarPage(PageEnum.PageHome) - ServersUiController.processedIndex = ServersUiController.defaultIndex + ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex) tabBar.currentIndex = 0 } } diff --git a/client/ui/qml/main2.qml b/client/ui/qml/main2.qml index bf3ab0518..18c59b264 100644 --- a/client/ui/qml/main2.qml +++ b/client/ui/qml/main2.qml @@ -304,6 +304,14 @@ Window { } } + Connections { + target: PageController + + function onUnsupportedConnectDrawerRequested() { + root.showUnsupportedConnectDrawer() + } + } + Connections { target: SubscriptionUiController @@ -332,6 +340,28 @@ Window { } } + function showUnsupportedConnectDrawer() { + let headerText = qsTr("This subscription format is no longer supported") + let descriptionText = qsTr("This legacy Amnezia subscription type can no longer be used to connect in this application version.\nRemove the server from the app to continue.") + let yesButtonText = qsTr("Continue") + let noButtonText = qsTr("Cancel") + + let yesButtonFunction = function() { + if (ConnectionController.isConnected) { + PageController.showNotificationMessage(qsTr("Cannot remove server during active connection")) + return + } + + PageController.showBusyIndicator(true) + InstallController.removeServer(ServersUiController.defaultServerId) + PageController.showBusyIndicator(false) + } + let noButtonFunction = function() { + } + + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + } + function showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) { questionDrawer.headerText = headerText questionDrawer.descriptionText = descriptionText diff --git a/client/vpnConnection.cpp b/client/vpnConnection.cpp index ad8ce917d..5329f2dc5 100644 --- a/client/vpnConnection.cpp +++ b/client/vpnConnection.cpp @@ -30,6 +30,7 @@ #endif #include "core/utils/networkUtilities.h" +#include "core/utils/serverConfigUtils.h" #include "vpnConnection.h" using namespace ProtocolUtils; @@ -74,8 +75,46 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) return; } - ServerConfig defaultServer = m_serversRepository->server(m_serversRepository->defaultServerIndex()); - DockerContainer container = defaultServer.defaultContainer(); + const QString defaultServerId = m_serversRepository->defaultServerId(); + DockerContainer container = DockerContainer::None; + switch (m_serversRepository->serverKind(defaultServerId)) { + case serverConfigUtils::ConfigType::SelfHostedAdmin: { + const auto cfg = m_serversRepository->selfHostedAdminConfig(defaultServerId); + if (cfg.has_value()) { + container = cfg->defaultContainer; + } + break; + } + case serverConfigUtils::ConfigType::SelfHostedUser: { + const auto cfg = m_serversRepository->selfHostedUserConfig(defaultServerId); + if (cfg.has_value()) { + container = cfg->defaultContainer; + } + break; + } + case serverConfigUtils::ConfigType::Native: { + const auto cfg = m_serversRepository->nativeConfig(defaultServerId); + if (cfg.has_value()) { + container = cfg->defaultContainer; + } + break; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV2: + case serverConfigUtils::ConfigType::AmneziaFreeV3: + case serverConfigUtils::ConfigType::ExternalPremium: { + const auto cfg = m_serversRepository->apiV2Config(defaultServerId); + if (cfg.has_value()) { + container = cfg->defaultContainer; + } + break; + } + case serverConfigUtils::ConfigType::AmneziaPremiumV1: + case serverConfigUtils::ConfigType::AmneziaFreeV2: + break; + case serverConfigUtils::ConfigType::Invalid: + default: + break; + } IpcClient::withInterface([&](QSharedPointer iface) { switch (state) { @@ -247,7 +286,7 @@ Vpn::ConnectionState VpnConnection::connectionState() const return m_connectionState; } -void VpnConnection::connectToVpn(int serverIndex, DockerContainer container, const QJsonObject &vpnConfiguration) +void VpnConnection::connectToVpn(const QString &serverId, DockerContainer container, const QJsonObject &vpnConfiguration) { if (!m_appSettingsRepository || !m_serversRepository) { qCritical() << "VpnConnection::connectToVpn: repositories not initialized"; @@ -255,8 +294,8 @@ void VpnConnection::connectToVpn(int serverIndex, DockerContainer container, con return; } - qDebug() << QString("Trying to connect to VPN, server index is %1, container is %2, route mode is") - .arg(serverIndex) + qDebug() << QString("Trying to connect to VPN, server id is %1, container is %2, route mode is") + .arg(serverId) .arg(ContainerUtils::containerToString(container)) << m_appSettingsRepository->routeMode(); diff --git a/client/vpnConnection.h b/client/vpnConnection.h index 0a6857447..9e5a9aa55 100644 --- a/client/vpnConnection.h +++ b/client/vpnConnection.h @@ -49,7 +49,7 @@ public: public slots: void setRepositories(SecureServersRepository* serversRepository, SecureAppSettingsRepository* appSettingsRepository); - void connectToVpn(int serverIndex, DockerContainer container, const QJsonObject &vpnConfiguration); + void connectToVpn(const QString &serverId, DockerContainer container, const QJsonObject &vpnConfiguration); void reconnectToVpn(); void disconnectFromVpn(); From fd0c773918eb66fe2db3bc2c30a07c6d41d941bb Mon Sep 17 00:00:00 2001 From: Yaroslav Gurov <31506978+ygurov@users.noreply.github.com> Date: Fri, 15 May 2026 06:36:38 +0200 Subject: [PATCH 2/9] fix: change artifact names (#2589) --- client/client_scripts/clientScripts.qrc | 1 - client/client_scripts/linux_installer.sh | 29 -------------- client/core/controllers/updateController.cpp | 40 +++---------------- .../core/utils/selfhosted/scriptsRegistry.cpp | 1 - .../core/utils/selfhosted/scriptsRegistry.h | 1 - 5 files changed, 6 insertions(+), 66 deletions(-) delete mode 100644 client/client_scripts/linux_installer.sh diff --git a/client/client_scripts/clientScripts.qrc b/client/client_scripts/clientScripts.qrc index 1c0ba9909..5e561a011 100644 --- a/client/client_scripts/clientScripts.qrc +++ b/client/client_scripts/clientScripts.qrc @@ -1,6 +1,5 @@ - linux_installer.sh mac_installer.sh diff --git a/client/client_scripts/linux_installer.sh b/client/client_scripts/linux_installer.sh deleted file mode 100644 index f2232bc4c..000000000 --- a/client/client_scripts/linux_installer.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -EXTRACT_DIR="$1" -INSTALLER_PATH="$2" - -# Create and clean extract directory -rm -rf "$EXTRACT_DIR" -mkdir -p "$EXTRACT_DIR" - -# Extract TAR archive -tar -xf "$INSTALLER_PATH" -C "$EXTRACT_DIR" -if [ $? -ne 0 ]; then - echo 'Failed to extract TAR archive' - exit 1 -fi - -# Find and run installer -INSTALLER=$(find "$EXTRACT_DIR" -type f -executable) -if [ -z "$INSTALLER" ]; then - echo 'Installer not found' - exit 1 -fi - -"$INSTALLER" -EXIT_CODE=$? - -# Cleanup -rm -rf "$EXTRACT_DIR" -exit $EXIT_CODE \ No newline at end of file diff --git a/client/core/controllers/updateController.cpp b/client/core/controllers/updateController.cpp index 24009705e..de7106c00 100644 --- a/client/core/controllers/updateController.cpp +++ b/client/core/controllers/updateController.cpp @@ -21,14 +21,14 @@ namespace Logger logger("UpdateController"); #if defined(Q_OS_WINDOWS) - const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN_%1_x64.exe"); + const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN-%1-win64.exe"); const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN_installer.exe"; #elif defined(Q_OS_MACOS) - const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN_%1_macos.pkg"); + const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN-%1-Darwin.pkg"); const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN.pkg"; #elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) - const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN_%1_linux_x64.tar"); - const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN.tar"; + const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN-%1-Linux.run"); + const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN.run"; #endif } @@ -346,36 +346,10 @@ int UpdateController::runMacInstaller(const QString &installerPath) #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) int UpdateController::runLinuxInstaller(const QString &installerPath) { - // Create temporary directory for extraction - QTemporaryDir extractDir; - extractDir.setAutoRemove(false); - if (!extractDir.isValid()) { - logger.error() << "Failed to create temporary directory"; - return -1; - } - logger.info() << "Temporary directory created:" << extractDir.path(); + QFile::setPermissions(installerPath, QFile::permissions(installerPath) | QFile::ExeUser); - // Create script file in the temporary directory - QString scriptPath = extractDir.path() + "/installer.sh"; - QFile scriptFile(scriptPath); - if (!scriptFile.open(QIODevice::WriteOnly)) { - logger.error() << "Failed to create script file"; - return -1; - } - - // Get script content from registry - QString scriptContent = amnezia::scriptData(amnezia::ClientScriptType::linux_installer); - scriptFile.write(scriptContent.toUtf8()); - scriptFile.close(); - logger.info() << "Script file created:" << scriptPath; - - // Make script executable - QFile::setPermissions(scriptPath, QFile::permissions(scriptPath) | QFile::ExeUser); - - // Start detached process qint64 pid; - bool success = - QProcess::startDetached("/bin/bash", QStringList() << scriptPath << extractDir.path() << installerPath, extractDir.path(), &pid); + bool success = QProcess::startDetached(installerPath, QStringList(), QString(), &pid); if (success) { logger.info() << "Installation process started with PID:" << pid; @@ -387,5 +361,3 @@ int UpdateController::runLinuxInstaller(const QString &installerPath) return 0; } #endif - - diff --git a/client/core/utils/selfhosted/scriptsRegistry.cpp b/client/core/utils/selfhosted/scriptsRegistry.cpp index 3ff409499..26bf73de6 100644 --- a/client/core/utils/selfhosted/scriptsRegistry.cpp +++ b/client/core/utils/selfhosted/scriptsRegistry.cpp @@ -76,7 +76,6 @@ QString amnezia::scriptName(ProtocolScriptType type) QString amnezia::scriptName(ClientScriptType type) { switch (type) { - case ClientScriptType::linux_installer: return QLatin1String("linux_installer.sh"); case ClientScriptType::mac_installer: return QLatin1String("mac_installer.sh"); default: return QString(); } diff --git a/client/core/utils/selfhosted/scriptsRegistry.h b/client/core/utils/selfhosted/scriptsRegistry.h index 26bb2f0e9..b9d320455 100644 --- a/client/core/utils/selfhosted/scriptsRegistry.h +++ b/client/core/utils/selfhosted/scriptsRegistry.h @@ -43,7 +43,6 @@ enum ProtocolScriptType { enum ClientScriptType { // Client-side scripts - linux_installer, mac_installer }; From d0a1af03815f3dfca8ac9edd109d378fb3f88348 Mon Sep 17 00:00:00 2001 From: yp Date: Fri, 15 May 2026 09:56:09 +0300 Subject: [PATCH 3/9] refactor: deactivate api config before remove (#2569) Co-authored-by: vkamn --- .../api/subscriptionController.cpp | 21 +++++++++++++++++++ .../controllers/api/subscriptionController.h | 2 ++ .../api/subscriptionUiController.cpp | 9 ++++++++ .../api/subscriptionUiController.h | 3 +++ .../qml/Pages2/PageSettingsApiServerInfo.qml | 2 +- client/ui/qml/Pages2/PageStart.qml | 10 +++++++++ 6 files changed, 46 insertions(+), 1 deletion(-) diff --git a/client/core/controllers/api/subscriptionController.cpp b/client/core/controllers/api/subscriptionController.cpp index d7f149f43..8efc14558 100644 --- a/client/core/controllers/api/subscriptionController.cpp +++ b/client/core/controllers/api/subscriptionController.cpp @@ -679,6 +679,27 @@ void SubscriptionController::removeApiConfig(const QString &serverId) serverConfigUtils::configTypeFromJson(apiV2->toJson())); } +bool SubscriptionController::removeServer(const QString &serverId) +{ + if (serverId.isEmpty()) { + return false; + } + + if (!m_serversRepository->apiV2Config(serverId).has_value()) { + qWarning().noquote() << "SubscriptionController::removeServer: not an Api V2 server, id" << serverId; + return false; + } + + const ErrorCode revokeError = deactivateDevice(serverId); + if (revokeError != ErrorCode::NoError && revokeError != ErrorCode::ApiNotFoundError) { + qWarning().noquote() << "SubscriptionController::removeServer: deactivateDevice failed (error" + << static_cast(revokeError) << "); removing locally anyway."; + } + + m_serversRepository->removeServer(serverId); + return true; +} + bool SubscriptionController::isApiKeyExpired(const QString &serverId) const { auto apiV2 = m_serversRepository->apiV2Config(serverId); diff --git a/client/core/controllers/api/subscriptionController.h b/client/core/controllers/api/subscriptionController.h index 5b0f65da6..a0ac5d24b 100644 --- a/client/core/controllers/api/subscriptionController.h +++ b/client/core/controllers/api/subscriptionController.h @@ -74,6 +74,8 @@ public: void removeApiConfig(const QString &serverId); + bool removeServer(const QString &serverId); + void setCurrentProtocol(const QString &serverId, const QString &protocolName); bool isVlessProtocol(const QString &serverId) const; diff --git a/client/ui/controllers/api/subscriptionUiController.cpp b/client/ui/controllers/api/subscriptionUiController.cpp index 5dee205dc..550334c3a 100644 --- a/client/ui/controllers/api/subscriptionUiController.cpp +++ b/client/ui/controllers/api/subscriptionUiController.cpp @@ -406,6 +406,15 @@ void SubscriptionUiController::removeApiConfig(const QString &serverId) emit apiConfigRemoved(tr("Api config removed")); } +void SubscriptionUiController::removeServer(const QString &serverId) +{ + const QString serverName = m_serversController->notificationDisplayName(serverId); + if (!m_subscriptionController->removeServer(serverId)) { + return; + } + emit apiServerRemoved(tr("Server '%1' was removed").arg(serverName)); +} + QList SubscriptionUiController::getQrCodes() { diff --git a/client/ui/controllers/api/subscriptionUiController.h b/client/ui/controllers/api/subscriptionUiController.h index e4be939a3..8d28b52c9 100644 --- a/client/ui/controllers/api/subscriptionUiController.h +++ b/client/ui/controllers/api/subscriptionUiController.h @@ -58,6 +58,8 @@ public slots: void removeApiConfig(const QString &serverId); + void removeServer(const QString &serverId); + bool getAccountInfo(const QString &serverId, bool reload); void getRenewalLink(const QString &serverId); @@ -78,6 +80,7 @@ signals: void subscriptionRefreshNeeded(); void apiConfigRemoved(const QString &message); + void apiServerRemoved(const QString &message); void vpnKeyExportReady(); diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index 07fb3089d..e1b8c7664 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -506,7 +506,7 @@ PageType { PageController.showNotificationMessage(qsTr("Cannot remove server during active connection")) } else { PageController.showBusyIndicator(true) - InstallController.removeServer(ServersUiController.getServerId(ServersUiController.processedServerIndex)) + SubscriptionUiController.removeServer(ServersUiController.getServerId(ServersUiController.processedServerIndex)) PageController.showBusyIndicator(false) } } diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index 00411b5e1..950a5a7d8 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -224,6 +224,16 @@ PageType { PageController.showNotificationMessage(message) } + function onApiServerRemoved(message) { + if (!ServersModel.getServersCount()) { + PageController.goToPageHome() + } else { + PageController.goToStartPage() + PageController.goToPage(PageEnum.PageSettingsServersList) + } + PageController.showNotificationMessage(message) + } + function onInstallServerFromApiFinished(message, preferredDefaultIndex) { if (!ConnectionController.isConnected) { if (preferredDefaultIndex !== undefined && preferredDefaultIndex >= 0) { From cb48667b9104ca3d4b2cf3cbdaf52c089705f972 Mon Sep 17 00:00:00 2001 From: yp Date: Fri, 15 May 2026 09:57:44 +0300 Subject: [PATCH 4/9] fix: bug when saving after canceling the save action (#2568) Co-authored-by: vkamn --- client/platforms/ios/ios_controller.mm | 4 ++- .../ui/controllers/allowedDnsUiController.cpp | 6 +++- .../api/subscriptionUiController.cpp | 8 +++--- .../ipSplitTunnelingUiController.cpp | 7 ++++- .../selfhosted/exportUiController.cpp | 6 +++- .../ui/controllers/settingsUiController.cpp | 12 ++++++-- client/ui/controllers/systemController.cpp | 28 ++++++++++++++----- client/ui/controllers/systemController.h | 4 ++- .../Pages2/PageSettingsApiNativeConfigs.qml | 3 -- .../Pages2/PageSettingsApiSubscriptionKey.qml | 5 +++- 10 files changed, 60 insertions(+), 23 deletions(-) diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index 73aa02484..c93dac460 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -977,7 +977,9 @@ bool IosController::shareText(const QStringList& filesToSend) { } #if !MACOS_NE UIViewController *qtController = getViewController(); - if (!qtController) return; + if (!qtController) { + return false; + } UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil]; #endif diff --git a/client/ui/controllers/allowedDnsUiController.cpp b/client/ui/controllers/allowedDnsUiController.cpp index f9d62a5a6..e71b8dd5c 100644 --- a/client/ui/controllers/allowedDnsUiController.cpp +++ b/client/ui/controllers/allowedDnsUiController.cpp @@ -1,5 +1,6 @@ #include "allowedDnsUiController.h" +#include #include #include #include @@ -98,7 +99,10 @@ void AllowedDnsUiController::exportDns(const QString &fileName) QJsonDocument jsonDocument(jsonArray); QByteArray jsonData = jsonDocument.toJson(); - SystemController::saveFile(fileName, jsonData); + if (!SystemController::saveFile(fileName, jsonData)) { + qInfo() << "AllowedDnsUiController::exportDns: save or share was cancelled or failed"; + return; + } emit finished(tr("Export completed")); } diff --git a/client/ui/controllers/api/subscriptionUiController.cpp b/client/ui/controllers/api/subscriptionUiController.cpp index 550334c3a..05369d34d 100644 --- a/client/ui/controllers/api/subscriptionUiController.cpp +++ b/client/ui/controllers/api/subscriptionUiController.cpp @@ -98,8 +98,7 @@ bool SubscriptionUiController::exportVpnKey(const QString &serverId, const QStri return false; } - SystemController::saveFile(fileName, m_vpnKey); - return true; + return SystemController::saveFile(fileName, m_vpnKey); } @@ -117,8 +116,9 @@ bool SubscriptionUiController::exportNativeConfig(const QString &serverId, const return false; } - SystemController::saveFile(fileName, nativeConfig); - return true; + const bool saved = SystemController::saveFile(fileName, nativeConfig); + getAccountInfo(serverIndex, true); + return saved; } diff --git a/client/ui/controllers/ipSplitTunnelingUiController.cpp b/client/ui/controllers/ipSplitTunnelingUiController.cpp index c9081bd83..449a7ccc7 100644 --- a/client/ui/controllers/ipSplitTunnelingUiController.cpp +++ b/client/ui/controllers/ipSplitTunnelingUiController.cpp @@ -1,5 +1,7 @@ #include "ipSplitTunnelingUiController.h" +#include + #include "systemController.h" #include "core/utils/errorCodes.h" #include "core/utils/routeModes.h" @@ -55,7 +57,10 @@ void IpSplitTunnelingUiController::importSites(const QString &fileName, bool rep void IpSplitTunnelingUiController::exportSites(const QString &fileName) { QByteArray jsonData = m_ipSplitTunnelingController->exportSitesToJson(); - SystemController::saveFile(fileName, QString::fromUtf8(jsonData)); + if (!SystemController::saveFile(fileName, jsonData)) { + qInfo() << "IpSplitTunnelingUiController::exportSites: save or share was cancelled or failed"; + return; + } emit finished(tr("Export completed")); } diff --git a/client/ui/controllers/selfhosted/exportUiController.cpp b/client/ui/controllers/selfhosted/exportUiController.cpp index 2c76fccb6..3e7984668 100644 --- a/client/ui/controllers/selfhosted/exportUiController.cpp +++ b/client/ui/controllers/selfhosted/exportUiController.cpp @@ -1,5 +1,7 @@ #include "exportUiController.h" +#include + #include "../systemController.h" ExportUiController::ExportUiController(ExportController* exportController, QObject *parent) @@ -68,7 +70,9 @@ QList ExportUiController::getQrCodes() void ExportUiController::exportConfig(const QString &fileName) { - SystemController::saveFile(fileName, m_config); + if (!SystemController::saveFile(fileName, m_config)) { + qInfo() << "ExportUiController::exportConfig: save or share was cancelled or failed"; + } } void ExportUiController::updateClientManagementModel(const QString &serverId, int containerIndex) diff --git a/client/ui/controllers/settingsUiController.cpp b/client/ui/controllers/settingsUiController.cpp index a577d3cbc..6037aed76 100644 --- a/client/ui/controllers/settingsUiController.cpp +++ b/client/ui/controllers/settingsUiController.cpp @@ -107,7 +107,9 @@ void SettingsUiController::exportLogsFile(const QString &fileName) #ifdef Q_OS_ANDROID AndroidController::instance()->exportLogsFile(fileName); #else - SystemController::saveFile(fileName, Logger::getLogFile()); + if (!SystemController::saveFile(fileName, Logger::getLogFile())) { + qInfo() << "SettingsUiController::exportLogsFile: save or share was cancelled or failed"; + } #endif } @@ -116,7 +118,9 @@ void SettingsUiController::exportServiceLogsFile(const QString &fileName) #ifdef Q_OS_ANDROID AndroidController::instance()->exportLogsFile(fileName); #else - SystemController::saveFile(fileName, Logger::getServiceLogFile()); + if (!SystemController::saveFile(fileName, Logger::getServiceLogFile())) { + qInfo() << "SettingsUiController::exportServiceLogsFile: save or share was cancelled or failed"; + } #endif } @@ -132,7 +136,9 @@ void SettingsUiController::clearLogs() void SettingsUiController::backupAppConfig(const QString &fileName) { QByteArray data = m_settingsController->backupAppConfig(); - SystemController::saveFile(fileName, data); + if (!SystemController::saveFile(fileName, data)) { + qInfo() << "SettingsUiController::backupAppConfig: save or share was cancelled or failed"; + } } void SettingsUiController::restoreAppConfig(const QString &fileName) diff --git a/client/ui/controllers/systemController.cpp b/client/ui/controllers/systemController.cpp index 236643315..57409dfeb 100644 --- a/client/ui/controllers/systemController.cpp +++ b/client/ui/controllers/systemController.cpp @@ -1,5 +1,6 @@ #include "systemController.h" +#include #include #include #include @@ -24,11 +25,20 @@ SystemController::SystemController(QObject *parent) { } -void SystemController::saveFile(const QString &fileName, const QString &data) +bool SystemController::saveFile(const QString &fileName, const QString &data) { #if defined Q_OS_ANDROID AndroidController::instance()->saveFile(fileName, data); - return; + return true; +#endif + return saveFile(fileName, data.toUtf8()); +} + +bool SystemController::saveFile(const QString &fileName, const QByteArray &data) +{ +#if defined Q_OS_ANDROID + AndroidController::instance()->saveFile(fileName, QString::fromUtf8(data)); + return true; #endif #ifdef Q_OS_IOS @@ -39,17 +49,20 @@ void SystemController::saveFile(const QString &fileName, const QString &data) #endif if (!file.open(QIODevice::WriteOnly)) { - return; + qWarning() << "SystemController::saveFile: cannot open" << fileName; + return false; + } + if (file.write(data) != data.size()) { + qWarning() << "SystemController::saveFile: write failed" << fileName; + file.close(); + return false; } - file.write(data.toUtf8()); file.close(); #ifdef Q_OS_IOS QStringList filesToSend; filesToSend.append(fileUrl.toString()); - // todo check if save successful - IosController::Instance()->shareText(filesToSend); - return; + return IosController::Instance()->shareText(filesToSend); #else QFileInfo fi(fileName); @@ -62,6 +75,7 @@ void SystemController::saveFile(const QString &fileName, const QString &data) #ifndef MACOS_NE QDesktopServices::openUrl(url); #endif + return true; #endif } diff --git a/client/ui/controllers/systemController.h b/client/ui/controllers/systemController.h index 8f62ef6ee..83f81fbb5 100644 --- a/client/ui/controllers/systemController.h +++ b/client/ui/controllers/systemController.h @@ -1,6 +1,7 @@ #ifndef SYSTEMCONTROLLER_H #define SYSTEMCONTROLLER_H +#include #include class SystemController : public QObject @@ -9,7 +10,8 @@ class SystemController : public QObject public: explicit SystemController(QObject *parent = nullptr); - static void saveFile(const QString &fileName, const QString &data); + static bool saveFile(const QString &fileName, const QString &data); + static bool saveFile(const QString &fileName, const QByteArray &data); static bool readFile(const QString &fileName, QByteArray &data); static bool readFile(const QString &fileName, QString &data); diff --git a/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml b/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml index 4c09122f3..51b12bd2d 100644 --- a/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml +++ b/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml @@ -192,9 +192,6 @@ PageType { if (fileName !== "") { PageController.showBusyIndicator(true) let result = SubscriptionUiController.exportNativeConfig(ServersUiController.getServerId(ServersUiController.processedServerIndex), countryCode, fileName) - if (result) { - SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), true) - } PageController.showBusyIndicator(false) if (result) { diff --git a/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml b/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml index 442bf2ba2..848884475 100644 --- a/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml +++ b/client/ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml @@ -119,8 +119,11 @@ PageType { if (fileName !== "") { PageController.showBusyIndicator(true) - SubscriptionUiController.exportVpnKey(ServersUiController.getServerId(ServersUiController.processedServerIndex), fileName) + let ok = SubscriptionUiController.exportVpnKey(ServersUiController.getServerId(ServersUiController.processedServerIndex), fileName) PageController.showBusyIndicator(false) + if (ok) { + PageController.showNotificationMessage(qsTr("Config file saved")) + } } } } From 0433e03bdcd7481cd9d725729f4388ca3f2bf506 Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Fri, 15 May 2026 09:58:11 +0300 Subject: [PATCH 5/9] fix: amnezia free card button hovers when card enabled (#2602) --- client/ui/qml/Controls2/CardWithIconsType.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/qml/Controls2/CardWithIconsType.qml b/client/ui/qml/Controls2/CardWithIconsType.qml index 827a3950c..95f462302 100644 --- a/client/ui/qml/Controls2/CardWithIconsType.qml +++ b/client/ui/qml/Controls2/CardWithIconsType.qml @@ -188,7 +188,7 @@ Button { anchors.fill: parent radius: 12 - color: root.pressed ? rightImage.pressedColor : root.hovered ? rightImage.hoveredColor : rightImage.defaultColor + color: root.pressed ? rightImage.pressedColor : root.hovered && root.enabled ? rightImage.hoveredColor : rightImage.defaultColor Behavior on color { PropertyAnimation { duration: 200 } From 98771027b77951d9ab1243b6b71a83f524ad4ee3 Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Fri, 15 May 2026 09:58:23 +0300 Subject: [PATCH 6/9] fix: vless switch between dividers (#2600) --- client/ui/qml/Pages2/PageSettingsApiServerInfo.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index e1b8c7664..2dd6439c0 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -265,6 +265,7 @@ PageType { Layout.topMargin: 24 Layout.rightMargin: 16 Layout.leftMargin: 16 + Layout.bottomMargin: 24 visible: ApiAccountInfoModel.data("isProtocolSelectionSupported") enabled: !switcher.isProtocolSwitchBlocked From 2a3e3126ac2262cb5fab20d113c6d76b4af60092 Mon Sep 17 00:00:00 2001 From: yp Date: Fri, 15 May 2026 10:44:58 +0300 Subject: [PATCH 7/9] feat: regional country codes (#2567) Co-authored-by: vkamn --- client/core/utils/api/apiUtils.cpp | 16 ++++++++++++++++ client/core/utils/api/apiUtils.h | 3 +++ .../controllers/api/subscriptionUiController.cpp | 2 +- client/ui/controllers/serversUiController.cpp | 7 ++++++- client/ui/models/api/apiCountryModel.cpp | 3 ++- 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/client/core/utils/api/apiUtils.cpp b/client/core/utils/api/apiUtils.cpp index 60b78d565..4555d80dc 100644 --- a/client/core/utils/api/apiUtils.cpp +++ b/client/core/utils/api/apiUtils.cpp @@ -2,6 +2,7 @@ #include "core/utils/serverConfigUtils.h" #include "core/utils/constants/configKeys.h" +#include #include #include #include @@ -232,3 +233,18 @@ QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject) return vpnKeyText; } + +QString apiUtils::countryCodeBaseForFlag(const QString &fullCountryCode) +{ + const QString trimmed = fullCountryCode.trimmed(); + if (trimmed.isEmpty()) { + return QString(); + } + const int dashIdx = trimmed.indexOf(QLatin1Char('-')); + const QString base = dashIdx < 0 ? trimmed : trimmed.left(dashIdx); + const QString normalized = base.trimmed(); + if (normalized.isEmpty()) { + return QString(); + } + return normalized.toUpper(); +} diff --git a/client/core/utils/api/apiUtils.h b/client/core/utils/api/apiUtils.h index e1ada61ae..c601ec895 100644 --- a/client/core/utils/api/apiUtils.h +++ b/client/core/utils/api/apiUtils.h @@ -25,6 +25,9 @@ namespace apiUtils QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject); QString getPremiumV2VpnKey(const QJsonObject &serverConfigObject); + + // ISO2-style segment for flagKit assets (e.g. US-WEST -> US). Do not use in API request bodies. + QString countryCodeBaseForFlag(const QString &fullCountryCode); } #endif // APIUTILS_H diff --git a/client/ui/controllers/api/subscriptionUiController.cpp b/client/ui/controllers/api/subscriptionUiController.cpp index 05369d34d..581a10a2d 100644 --- a/client/ui/controllers/api/subscriptionUiController.cpp +++ b/client/ui/controllers/api/subscriptionUiController.cpp @@ -117,7 +117,7 @@ bool SubscriptionUiController::exportNativeConfig(const QString &serverId, const } const bool saved = SystemController::saveFile(fileName, nativeConfig); - getAccountInfo(serverIndex, true); + getAccountInfo(serverId, true); return saved; } diff --git a/client/ui/controllers/serversUiController.cpp b/client/ui/controllers/serversUiController.cpp index b9c2a9bf9..a5c0741f5 100644 --- a/client/ui/controllers/serversUiController.cpp +++ b/client/ui/controllers/serversUiController.cpp @@ -1,5 +1,6 @@ #include "serversUiController.h" +#include "core/utils/api/apiUtils.h" #include "core/utils/containerEnum.h" #include "core/utils/containers/containerUtils.h" #include "core/utils/protocolEnum.h" @@ -215,7 +216,11 @@ QString ServersUiController::getDefaultServerImagePathCollapsed() const if (!description.isApiV2 || description.apiServerCountryCode.isEmpty()) { return ""; } - return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(description.apiServerCountryCode.toUpper()); + const QString imageCode = apiUtils::countryCodeBaseForFlag(description.apiServerCountryCode.toUpper()); + if (imageCode.isEmpty()) { + return QString(); + } + return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(imageCode); } } return ""; diff --git a/client/ui/models/api/apiCountryModel.cpp b/client/ui/models/api/apiCountryModel.cpp index b0315f346..b1154e434 100644 --- a/client/ui/models/api/apiCountryModel.cpp +++ b/client/ui/models/api/apiCountryModel.cpp @@ -5,6 +5,7 @@ #include "core/utils/serverConfigUtils.h" #include "core/utils/constants/apiKeys.h" #include "core/utils/constants/apiConstants.h" +#include "core/utils/api/apiUtils.h" #include "logger.h" namespace @@ -41,7 +42,7 @@ QVariant ApiCountryModel::data(const QModelIndex &index, int role) const return countryInfo.countryName; } case CountryImageCodeRole: { - return countryInfo.countryCode.toUpper(); + return apiUtils::countryCodeBaseForFlag(countryInfo.countryCode); } case IsIssuedRole: { return isIssued; From c9ed0baf3b0b98d2884337ee2c40d1a4fe0eb0b3 Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Fri, 15 May 2026 16:01:39 +0300 Subject: [PATCH 8/9] fix: app freezes when revoke awg/wg client during active connection (#2211) * block configs revoke during connection * update: check that current config is active * update: notification text --- client/ui/qml/Pages2/PageShare.qml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 9dab7a5fa..0ea626ef0 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -835,7 +835,15 @@ PageType { var noButtonFunction = function() { } - showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + var isActiveConfigForCurrentClient = ServersModel.isDefaultServerCurrentlyProcessed() + && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex() + + if ((ConnectionController.isConnectionInProgress || ConnectionController.isConnected) + && isActiveConfigForCurrentClient) { + PageController.showNotificationMessage("Unable to revoke current config during active connection") + } else { + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + } } } } From c7b1c2809fb1c555587190dacff225b9fb538e48 Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Fri, 15 May 2026 16:02:09 +0300 Subject: [PATCH 9/9] fix: app buttons clicked instead of buttons in context menu (#2200) * fix: app buttons clicked instead of buttons in context menu * update: using MouseArea instead of changing popupType * fix(cursor): fixed cursor type at opened context menu --------- Co-authored-by: Mitternacht822 --- client/ui/qml/Controls2/ContextMenuType.qml | 10 ++++++++++ client/ui/qml/Controls2/TextAreaType.qml | 3 ++- client/ui/qml/Controls2/TextAreaWithFooterType.qml | 3 ++- client/ui/qml/Controls2/TextFieldWithHeaderType.qml | 3 ++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/client/ui/qml/Controls2/ContextMenuType.qml b/client/ui/qml/Controls2/ContextMenuType.qml index cb32e3118..6b3c3ae1c 100644 --- a/client/ui/qml/Controls2/ContextMenuType.qml +++ b/client/ui/qml/Controls2/ContextMenuType.qml @@ -6,6 +6,9 @@ Menu { popupType: Popup.Native + onAboutToShow: blocker.enabled = true + onClosed: blocker.enabled = false + MenuItem { text: qsTr("C&ut") enabled: textObj.selectedText @@ -28,4 +31,11 @@ Menu { enabled: textObj.length > 0 onTriggered: textObj.selectAll() } + + MouseArea { + id: blocker + z: 2 + enabled: false + preventStealing: true + } } diff --git a/client/ui/qml/Controls2/TextAreaType.qml b/client/ui/qml/Controls2/TextAreaType.qml index 3200e1f35..92d104f4e 100644 --- a/client/ui/qml/Controls2/TextAreaType.qml +++ b/client/ui/qml/Controls2/TextAreaType.qml @@ -24,7 +24,7 @@ Rectangle { MouseArea { id: parentMouse anchors.fill: parent - cursorShape: Qt.IBeamCursor + cursorShape: contextMenu.opened ? Qt.ArrowCursor : Qt.IBeamCursor onClicked: textArea.forceActiveFocus() hoverEnabled: true @@ -94,6 +94,7 @@ Rectangle { wrapMode: Text.Wrap ContextMenu.menu: ContextMenuType { + id: contextMenu textObj: textArea } diff --git a/client/ui/qml/Controls2/TextAreaWithFooterType.qml b/client/ui/qml/Controls2/TextAreaWithFooterType.qml index f54a0688e..a46e3f117 100644 --- a/client/ui/qml/Controls2/TextAreaWithFooterType.qml +++ b/client/ui/qml/Controls2/TextAreaWithFooterType.qml @@ -34,7 +34,7 @@ Rectangle { MouseArea { id: parentMouse anchors.fill: parent - cursorShape: Qt.IBeamCursor + cursorShape: contextMenu.opened ? Qt.ArrowCursor : Qt.IBeamCursor onClicked: textArea.forceActiveFocus() hoverEnabled: true @@ -80,6 +80,7 @@ Rectangle { wrapMode: Text.Wrap ContextMenu.menu: ContextMenuType { + id: contextMenu textObj: textArea } diff --git a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml index 897584303..e192535ac 100644 --- a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml +++ b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml @@ -135,6 +135,7 @@ Item { } ContextMenu.menu: ContextMenuType { + id: contextMenu textObj: textField } @@ -159,7 +160,7 @@ Item { MouseArea { anchors.fill: root - cursorShape: Qt.IBeamCursor + cursorShape: contextMenu.opened ? Qt.ArrowCursor : Qt.IBeamCursor hoverEnabled: true