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/cmake/sources.cmake b/client/cmake/sources.cmake
index be96468d4..8afd4a5fd 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
@@ -140,6 +139,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..8efc14558 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,42 @@ 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::removeServer(const QString &serverId)
{
- ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
-
- if (!serverConfigModel.isApiV2()) {
+ if (serverId.isEmpty()) {
return false;
}
- const ApiV2ServerConfig* apiV2 = serverConfigModel.as();
- if (!apiV2) {
+ 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);
+ if (!apiV2.has_value()) {
return false;
}
const QString expiresAt = apiV2->apiConfig.publicKey.expiresAt;
@@ -833,31 +720,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 +771,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 +835,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 +869,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 +902,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..a0ac5d24b 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,40 @@ 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);
+ bool removeServer(const QString &serverId);
- void setCurrentProtocol(int serverIndex, const QString &protocolName);
- bool isVlessProtocol(int serverIndex) const;
+ void setCurrentProtocol(const QString &serverId, const QString &protocolName);
+ bool isVlessProtocol(const QString &serverId) 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 +93,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 +100,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 33920196e..3240f6b5c 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"
@@ -148,7 +147,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);
@@ -168,7 +167,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,
@@ -269,9 +268,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)
@@ -329,11 +331,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 8100379e5..64645ab02 100644
--- a/client/core/controllers/coreController.h
+++ b/client/core/controllers/coreController.h
@@ -87,7 +87,6 @@ class TestDefaultServerChange;
class TestServerEdgeCases;
class TestSignalOrder;
class TestServersModelSync;
-class TestGatewayStacks;
class TestComplexOperations;
class TestSettingsSignals;
class TestUiServersModelAndController;
@@ -104,7 +103,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 a34e55709..393c7e567 100644
--- a/client/core/controllers/selfhosted/installController.cpp
+++ b/client/core/controllers/selfhosted/installController.cpp
@@ -34,7 +34,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/mtProxyProtocolConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
@@ -146,20 +145,32 @@ 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)) {
+ auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
+ if (!adminConfig.has_value()) {
+ return ErrorCode::InternalError;
+ }
if (container == DockerContainer::MtProxy) {
- ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
+ ServerCredentials credentials = adminConfig->credentials();
SshSession sshSession(this);
MtProxyInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
}
- m_serversRepository->setContainerConfig(serverIndex, container, newConfig);
+ 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);
@@ -179,42 +190,51 @@ ErrorCode InstallController::updateContainer(int serverIndex, DockerContainer co
if (container == DockerContainer::MtProxy) {
MtProxyInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
}
- 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) {
@@ -223,20 +243,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);
@@ -255,6 +276,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)) {
@@ -282,7 +318,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
@@ -291,13 +327,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);
@@ -305,7 +341,7 @@ ErrorCode InstallController::processContainerForAdmin(DockerContainer container,
return errorCode;
}
}
- adminAppendRequested(serverIndex, container, containerConfig, clientName);
+ adminAppendRequested(serverId, container, containerConfig, clientName);
return ErrorCode::NoError;
}
@@ -752,9 +788,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");
@@ -773,27 +816,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,
@@ -801,11 +855,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;
@@ -813,12 +866,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;
@@ -887,9 +938,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;
@@ -898,8 +956,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());
@@ -907,29 +964,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;
@@ -971,7 +1024,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;
@@ -984,21 +1037,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;
@@ -1021,15 +1082,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);
}
}
@@ -1065,7 +1128,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..aeedd39f3 100644
--- a/client/core/controllers/serversController.cpp
+++ b/client/core/controllers/serversController.cpp
@@ -1,81 +1,282 @@
#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);
+}
+
+void ServersController::updateContainerConfig(const QString &serverId, DockerContainer container, const ContainerConfig &config)
+{
+ const serverConfigUtils::ConfigType kind = m_serversRepository->serverKind(serverId);
+ if (kind != serverConfigUtils::ConfigType::SelfHostedAdmin) {
+ return;
+ }
+ auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
+ if (!cfg.has_value()) {
+ return;
+ }
+ cfg->updateContainerConfig(container, config);
+ m_serversRepository->editServer(serverId, cfg->toJson(), kind);
}
int ServersController::getDefaultServerIndex() const
@@ -83,114 +284,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 +421,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..abb0974e9 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,39 @@ 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;
+ void updateContainerConfig(const QString &serverId, DockerContainer container, const ContainerConfig &config);
// 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/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/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..4555d80dc 100644
--- a/client/core/utils/api/apiUtils.cpp
+++ b/client/core/utils/api/apiUtils.cpp
@@ -1,6 +1,8 @@
#include "apiUtils.h"
+#include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/configKeys.h"
+#include
#include
#include
#include
@@ -75,63 +77,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 +142,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 +187,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 {};
}
@@ -289,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 be770defa..c601ec895 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,23 +13,21 @@
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);
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/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 f47e3eb95..71d63674a 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");
@@ -122,6 +123,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/selfhosted/scriptsRegistry.cpp b/client/core/utils/selfhosted/scriptsRegistry.cpp
index a5a54c205..e0e49c26b 100644
--- a/client/core/utils/selfhosted/scriptsRegistry.cpp
+++ b/client/core/utils/selfhosted/scriptsRegistry.cpp
@@ -77,7 +77,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 5789a8c20..cf3c39394 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
};
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/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/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