diff --git a/client/core/controllers/selfhosted/importController.cpp b/client/core/controllers/selfhosted/importController.cpp index 57b14146c..27ea157d2 100644 --- a/client/core/controllers/selfhosted/importController.cpp +++ b/client/core/controllers/selfhosted/importController.cpp @@ -16,8 +16,6 @@ #include #include -#include - #include "core/utils/containerEnum.h" #include "core/utils/containers/containerUtils.h" #include "core/utils/protocolEnum.h" @@ -500,9 +498,9 @@ ImportController::ImportResult ImportController::importLink(const QUrl &url) } serverConfig.insert(configKey::description, m_appSettingsRepository->nextAvailableServerName()); - serverConfig["xray_subscription_config"] = configStrings; - serverConfig["xray_subscription_config_name"] = configNames; - serverConfig["xray_subscription_config_current"] = 0; + serverConfig[configKey::xraySubscriptionConfig] = configStrings; + serverConfig[configKey::xraySubscriptionConfigName] = configNames; + serverConfig[configKey::xraySubscriptionConfigCurrent] = 0; result.config = serverConfig; @@ -516,10 +514,8 @@ ImportController::ImportResult ImportController::editServerConfigWithData(QStrin if (result.errorCode != ErrorCode::NoError) return result; - const ServerConfig currentServerConfig = m_serversRepository->server(serverIndex); - + const QJsonObject currentConfig = m_serversRepository->server(serverIndex).toJson(); QJsonObject editedConfig = result.config; - const QJsonObject currentConfig = currentServerConfig.toJson(); for (auto it = uiConfig.begin(); it != uiConfig.end(); ++it) { editedConfig.insert(it.key(), it.value()); @@ -529,16 +525,16 @@ ImportController::ImportResult ImportController::editServerConfigWithData(QStrin editedConfig.insert(configKey::description, currentConfig.value(configKey::description)); } - if (currentConfig.contains("xray_subscription_config")) { - editedConfig.insert("xray_subscription_config", currentConfig.value("xray_subscription_config")); + if (currentConfig.contains(configKey::xraySubscriptionConfig)) { + editedConfig.insert(configKey::xraySubscriptionConfig, currentConfig.value(configKey::xraySubscriptionConfig)); } - if (currentConfig.contains("xray_subscription_config_name")) { - editedConfig.insert("xray_subscription_config_name", currentConfig.value("xray_subscription_config_name")); + if (currentConfig.contains(configKey::xraySubscriptionConfigName)) { + editedConfig.insert(configKey::xraySubscriptionConfigName, currentConfig.value(configKey::xraySubscriptionConfigName)); } - if (currentConfig.contains("xray_subscription_config_current")) { - editedConfig.insert("xray_subscription_config_current", currentConfig.value("xray_subscription_config_current")); + if (currentConfig.contains(configKey::xraySubscriptionConfigCurrent)) { + editedConfig.insert(configKey::xraySubscriptionConfigCurrent, currentConfig.value(configKey::xraySubscriptionConfigCurrent)); } const ServerConfig finalServerConfig = ServerConfig::fromJson(editedConfig); diff --git a/client/core/controllers/serversController.cpp b/client/core/controllers/serversController.cpp index 5bdbcaad3..1fbd58168 100644 --- a/client/core/controllers/serversController.cpp +++ b/client/core/controllers/serversController.cpp @@ -58,29 +58,29 @@ void ServersController::clearCachedProfile(int serverIndex, DockerContainer cont m_serversRepository->clearLastConnectionConfig(serverIndex, container); } -void ServersController::setCurrentConfigIndex(const int index) +void ServersController::setCurrentConfigIndex(const int serverIndex, const int index) { - m_serversRepository->setCurrentConfigIndex(index); + m_serversRepository->setCurrentConfigIndex(serverIndex, index); } -int ServersController::getCurrentConfigIndex() const +int ServersController::getCurrentConfigIndex(const int serverIndex) const { - return m_serversRepository->getCurrentConfigIndex(); + return m_serversRepository->getCurrentConfigIndex(serverIndex); } -QString ServersController::getConfigString(const int index) const +QString ServersController::getConfigString(const int serverIndex, const int index) const { - return m_serversRepository->getConfigString(index); + return m_serversRepository->getConfigString(serverIndex, index); } -QString ServersController::getConfigName(const int index) const +QString ServersController::getConfigName(const int serverIndex, const int index) const { - return m_serversRepository->getConfigName(index); + return m_serversRepository->getConfigName(serverIndex, index); } -QJsonArray ServersController::getConfigNames() const +QJsonArray ServersController::getConfigNames(const int serverIndex) const { - return m_serversRepository->getConfigNames(); + return m_serversRepository->getConfigNames(serverIndex); } QJsonArray ServersController::getServersArray() const diff --git a/client/core/controllers/serversController.h b/client/core/controllers/serversController.h index 61c97de09..089638efc 100644 --- a/client/core/controllers/serversController.h +++ b/client/core/controllers/serversController.h @@ -65,11 +65,11 @@ public: void clearCachedProfile(int serverIndex, DockerContainer container); // XRay subscription config getters/setters - void setCurrentConfigIndex(int index); - int getCurrentConfigIndex() const; - QString getConfigString(const int index) const; - QString getConfigName(const int index) const; - QJsonArray getConfigNames() const; + void setCurrentConfigIndex(const int serverIndex, int index); + int getCurrentConfigIndex(const int serverIndex) const; + QString getConfigString(const int serverIndex, const int index) const; + QString getConfigName(const int serverIndex, const int index) const; + QJsonArray getConfigNames(const int serverIndex) const; // Getters QJsonArray getServersArray() const; diff --git a/client/core/models/selfhosted/nativeServerConfig.cpp b/client/core/models/selfhosted/nativeServerConfig.cpp index 95642238b..187e5fd7f 100644 --- a/client/core/models/selfhosted/nativeServerConfig.cpp +++ b/client/core/models/selfhosted/nativeServerConfig.cpp @@ -5,7 +5,6 @@ #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" @@ -58,18 +57,6 @@ QJsonObject NativeServerConfig::toJson() const if (!dns2.isEmpty()) { obj[configKey::dns2] = dns2; } - - if (configString) { - obj[QLatin1String("xray_subscription_config")] = configString.value(); - } - - if (configName) { - obj[QLatin1String("xray_subscription_config_name")] = configName.value(); - } - - if (currentConfig) { - obj[QLatin1String("xray_subscription_config_current")] = currentConfig.value(); - } return obj; } @@ -98,15 +85,6 @@ NativeServerConfig NativeServerConfig::fromJson(const QJsonObject& json) config.dns1 = json.value(configKey::dns1).toString(); config.dns2 = json.value(configKey::dns2).toString(); - if (json.contains(QLatin1String("xray_subscription_config"))) - config.configString = json.value(QLatin1String("xray_subscription_config")).toArray(); - - if (json.contains(QLatin1String("xray_subscription_config_name"))) - config.configName = json.value(QLatin1String("xray_subscription_config_name")).toArray(); - - if (json.contains(QLatin1String("xray_subscription_config_current"))) - config.currentConfig = json.value(QLatin1String("xray_subscription_config_current")).toInt(); - return config; } diff --git a/client/core/models/selfhosted/nativeServerConfig.h b/client/core/models/selfhosted/nativeServerConfig.h index 3f2c3f045..4df3328f1 100644 --- a/client/core/models/selfhosted/nativeServerConfig.h +++ b/client/core/models/selfhosted/nativeServerConfig.h @@ -23,10 +23,6 @@ struct NativeServerConfig { DockerContainer defaultContainer; QString dns1; QString dns2; - - std::optional configString; - std::optional configName; - std::optional currentConfig; bool hasContainers() const; ContainerConfig containerConfig(DockerContainer container) const; diff --git a/client/core/models/selfhosted/xraySubscriptionConfig.cpp b/client/core/models/selfhosted/xraySubscriptionConfig.cpp new file mode 100644 index 000000000..e797767a9 --- /dev/null +++ b/client/core/models/selfhosted/xraySubscriptionConfig.cpp @@ -0,0 +1,105 @@ +#include "xraySubscriptionConfig.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" + +namespace amnezia +{ + + using namespace ContainerEnumNS; + + bool XRaySubscriptionConfig::hasContainers() const + { + return !containers.isEmpty(); + } + + ContainerConfig XRaySubscriptionConfig::containerConfig(DockerContainer container) const + { + if (!containers.contains(container)) { + return ContainerConfig {}; + } + return containers.value(container); + } + + QJsonObject XRaySubscriptionConfig::toJson() const + { + QJsonObject obj; + + if (!description.isEmpty()) { + obj[configKey::description] = this->description; + } + 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 (!configString.isEmpty()) { + obj[configKey::xraySubscriptionConfig] = configString; + } + if (!configName.isEmpty()) { + obj[configKey::xraySubscriptionConfigName] = configName; + } + if (currentConfig > -1) { + obj[configKey::xraySubscriptionConfigCurrent] = currentConfig; + } + + return obj; + } + + XRaySubscriptionConfig XRaySubscriptionConfig::fromJson(const QJsonObject &json) + { + XRaySubscriptionConfig config; + + config.description = json.value(configKey::description).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 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.configString = json.value(configKey::xraySubscriptionConfig).toArray(); + config.configName = json.value(configKey::xraySubscriptionConfigName).toArray(); + config.currentConfig = json.value(configKey::xraySubscriptionConfigCurrent).toInt(); + + return config; + } + +} // namespace amnezia diff --git a/client/core/models/selfhosted/xraySubscriptionConfig.h b/client/core/models/selfhosted/xraySubscriptionConfig.h new file mode 100644 index 000000000..25522e037 --- /dev/null +++ b/client/core/models/selfhosted/xraySubscriptionConfig.h @@ -0,0 +1,40 @@ +#ifndef XRAYSUBSCRIPTIONCONFIG_H +#define XRAYSUBSCRIPTIONCONFIG_H + +#include +#include +#include +#include + +#include "core/models/containerConfig.h" +#include "core/utils/containerEnum.h" +#include "core/utils/containers/containerUtils.h" +#include "core/utils/protocolEnum.h" + +namespace amnezia +{ + + using namespace ContainerEnumNS; + + struct XRaySubscriptionConfig + { + QString description; + QString hostName; + QMap containers; + DockerContainer defaultContainer; + QString dns1; + QString dns2; + + QJsonArray configString; + QJsonArray configName; + int currentConfig; + + bool hasContainers() const; + ContainerConfig containerConfig(DockerContainer container) const; + QJsonObject toJson() const; + static XRaySubscriptionConfig fromJson(const QJsonObject &json); + }; + +} // namespace amnezia + +#endif // XRAYSUBSCRIPTIONCONFIG_H diff --git a/client/core/models/serverConfig.cpp b/client/core/models/serverConfig.cpp index a08e539f7..7b9edfa23 100644 --- a/client/core/models/serverConfig.cpp +++ b/client/core/models/serverConfig.cpp @@ -91,7 +91,7 @@ int ServerConfig::configVersion() const } else if constexpr (std::is_same_v) { return apiDefs::ConfigSource::AmneziaGateway; } - return 0; // SelfHostedServerConfig or NativeServerConfig + return 0; // SelfHostedServerConfig or NativeServerConfig or XRaySubscriptionConfig }, data); } @@ -120,8 +120,9 @@ bool ServerConfig::isApiConfig() const return isApiV1() || isApiV2(); } -bool ServerConfig::isXRayConfig() const { - return isNative() && std::get(data).configString.has_value(); +bool ServerConfig::isXRayConfig() const +{ + return std::holds_alternative(data); } QJsonObject ServerConfig::toJson() const @@ -135,6 +136,10 @@ ServerConfig ServerConfig::fromJson(const QJsonObject& json) switch (configType) { case apiDefs::ConfigType::SelfHosted: { + if (json.contains(configKey::xraySubscriptionConfig)) { + return ServerConfig {XRaySubscriptionConfig::fromJson(json)}; + } + bool hasThirdPartyConfig = false; QJsonArray containersArray = json.value(configKey::containers).toArray(); for (const QJsonValue& val : containersArray) { diff --git a/client/core/models/serverConfig.h b/client/core/models/serverConfig.h index 7e396eaf0..97c0ccb40 100644 --- a/client/core/models/serverConfig.h +++ b/client/core/models/serverConfig.h @@ -10,6 +10,7 @@ #include "core/utils/protocolEnum.h" #include "core/models/selfhosted/selfHostedServerConfig.h" #include "core/models/selfhosted/nativeServerConfig.h" +#include "core/models/selfhosted/xraySubscriptionConfig.h" #include "core/models/api/apiV1ServerConfig.h" #include "core/models/api/apiV2ServerConfig.h" #include "core/models/containerConfig.h" @@ -23,6 +24,7 @@ struct ServerConfig { using Variant = std::variant< SelfHostedServerConfig, NativeServerConfig, + XRaySubscriptionConfig, ApiV1ServerConfig, ApiV2ServerConfig >; diff --git a/client/core/repositories/secureServersRepository.cpp b/client/core/repositories/secureServersRepository.cpp index 6e3e83744..0f912fb50 100644 --- a/client/core/repositories/secureServersRepository.cpp +++ b/client/core/repositories/secureServersRepository.cpp @@ -176,71 +176,62 @@ void SecureServersRepository::clearLastConnectionConfig(int serverIndex, DockerC setContainerConfig(serverIndex, container, containerCfg); } -void SecureServersRepository::setCurrentConfigIndex(const int index) +void SecureServersRepository::setCurrentConfigIndex(const int serverIndex, const int index) { - ServerConfig serverConfig = server(m_defaultServerIndex); - NativeServerConfig *xrayConfig = serverConfig.as(); + ServerConfig serverConfig = server(serverIndex); - xrayConfig->currentConfig = index; - editServer(m_defaultServerIndex, serverConfig); + if (serverConfig.isXRayConfig()) { + XRaySubscriptionConfig* xrayConfig = serverConfig.as(); + if (xrayConfig && xrayConfig->currentConfig != index) { + xrayConfig->currentConfig = index; + editServer(serverIndex, serverConfig); + } + } } -int SecureServersRepository::getCurrentConfigIndex() const +int SecureServersRepository::getCurrentConfigIndex(const int serverIndex) const { - const ServerConfig serverConfig = server(m_defaultServerIndex); + ServerConfig serverConfig = server(serverIndex); if (!serverConfig.isXRayConfig()) return int(); - const NativeServerConfig *xrayConfig = serverConfig.as(); - if (!xrayConfig->currentConfig.has_value()) + const XRaySubscriptionConfig* xrayConfig = serverConfig.as(); + if (xrayConfig->currentConfig < 0) return int(); - return xrayConfig->currentConfig.value(); + return xrayConfig->currentConfig; } -QString SecureServersRepository::getConfigString(const int index) const +QString SecureServersRepository::getConfigString(const int serverIndex, const int index) const { - const ServerConfig serverConfig = server(m_defaultServerIndex); + ServerConfig serverConfig = server(serverIndex); if (!serverConfig.isXRayConfig()) return QString(); - const NativeServerConfig *xrayConfig = serverConfig.as(); - if (!xrayConfig->configString.has_value()) + const XRaySubscriptionConfig* xrayConfig = serverConfig.as(); + if (xrayConfig->configString.isEmpty()) return QString(); - if (index < 0 || index >= xrayConfig->configString.value().size()) - return QString(); - - return xrayConfig->configString.value().at(index).toString(); + return xrayConfig->configString.at(index).toString(); } -QString SecureServersRepository::getConfigName(const int index) const +QString SecureServersRepository::getConfigName(const int serverIndex, const int index) const { - const ServerConfig serverConfig = server(m_defaultServerIndex); - if (!serverConfig.isXRayConfig()) - return QString(); - - const NativeServerConfig *xrayConfig = serverConfig.as(); - if (!xrayConfig->configName.has_value()) - return QString(); - - if (index < 0 || index >= xrayConfig->configName.value().size()) - return QString(); - - return xrayConfig->configName.value().at(index).toString(); + QJsonArray names = getConfigNames(serverIndex); + return names.at(index).toString(); } -QJsonArray SecureServersRepository::getConfigNames() const +QJsonArray SecureServersRepository::getConfigNames(const int serverIndex) const { - const ServerConfig serverConfig = server(m_defaultServerIndex); + ServerConfig serverConfig = server(serverIndex); if (!serverConfig.isXRayConfig()) return QJsonArray(); - const NativeServerConfig *xrayConfig = serverConfig.as(); - if (!xrayConfig->configName.has_value()) + const XRaySubscriptionConfig* xrayConfig = serverConfig.as(); + if (xrayConfig->configName.isEmpty()) return QJsonArray(); - return xrayConfig->configName.value(); + return xrayConfig->configName; } ServerCredentials SecureServersRepository::serverCredentials(int index) const diff --git a/client/core/repositories/secureServersRepository.h b/client/core/repositories/secureServersRepository.h index a52c9b776..208c6e05b 100644 --- a/client/core/repositories/secureServersRepository.h +++ b/client/core/repositories/secureServersRepository.h @@ -35,11 +35,11 @@ public: void setContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config); void clearLastConnectionConfig(int serverIndex, DockerContainer container); - void setCurrentConfigIndex(int index); - int getCurrentConfigIndex() const; - QString getConfigString(const int index) const; - QString getConfigName(const int index) const; - QJsonArray getConfigNames() const; + void setCurrentConfigIndex(const int serverIndex, int index); + int getCurrentConfigIndex(const int serverIndex) const; + QString getConfigString(const int serverIndex, const int index) const; + QString getConfigName(const int serverIndex, const int index) const; + QJsonArray getConfigNames(const int serverIndex) const; ServerCredentials serverCredentials(int index) const; bool hasServerWithVpnKey(const QString &vpnKey) const; diff --git a/client/core/utils/constants/configKeys.h b/client/core/utils/constants/configKeys.h index 40bc842b1..99a292438 100644 --- a/client/core/utils/constants/configKeys.h +++ b/client/core/utils/constants/configKeys.h @@ -32,6 +32,10 @@ namespace amnezia constexpr QLatin1String protocol("protocol"); constexpr QLatin1String protocols("protocols"); + constexpr QLatin1String xraySubscriptionConfig("xray_subscription_config"); + constexpr QLatin1String xraySubscriptionConfigName("xray_subscription_config_name"); + constexpr QLatin1String xraySubscriptionConfigCurrent("xray_subscription_config_current"); + constexpr QLatin1String remote("remote"); constexpr QLatin1String transportProto("transport_proto"); constexpr QLatin1String cipher("cipher"); diff --git a/client/ui/controllers/serversUiController.cpp b/client/ui/controllers/serversUiController.cpp index c2a200271..f0e4f32aa 100644 --- a/client/ui/controllers/serversUiController.cpp +++ b/client/ui/controllers/serversUiController.cpp @@ -379,6 +379,9 @@ QString ServersUiController::getDefaultServerDescription(const ServerConfig& ser } else if (server.isApiV1()) { const ApiV1ServerConfig* apiV1 = server.as(); return apiV1 ? apiV1->description : QString(); + } else if (server.isXRayConfig()) { + const XRaySubscriptionConfig *xray = server.as(); + return xray ? xray->configName.at(xray->currentConfig).toString() : server.description(); } else { ServerCredentials credentials = m_serversController->getServerCredentials(index); if (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty()) { @@ -461,27 +464,27 @@ QString ServersUiController::adDescription() const void ServersUiController::setCurrentConfigIndex(const int index) { - m_serversController->setCurrentConfigIndex(index); + m_serversController->setCurrentConfigIndex(m_processedServerIndex, index); } int ServersUiController::getCurrentConfigIndex() const { - return m_serversController->getCurrentConfigIndex(); + return m_serversController->getCurrentConfigIndex(m_processedServerIndex); } QString ServersUiController::getConfigString(const int index) const { - return m_serversController->getConfigString(index); + return m_serversController->getConfigString(m_processedServerIndex, index); } QString ServersUiController::getConfigName(const int index) const { - return m_serversController->getConfigName(index); + return m_serversController->getConfigName(m_processedServerIndex, index); } QJsonArray ServersUiController::getConfigNames() const { - return m_serversController->getConfigNames(); + return m_serversController->getConfigNames(m_processedServerIndex); } void ServersUiController::updateContainersModel() diff --git a/client/ui/models/serversModel.cpp b/client/ui/models/serversModel.cpp index 88bcbda45..0cc70efa5 100644 --- a/client/ui/models/serversModel.cpp +++ b/client/ui/models/serversModel.cpp @@ -98,7 +98,7 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const } case ServerDescriptionRole: { auto description = getServerDescription(server, index.row()); - return configVersion ? description : description + server.hostName(); + return configVersion ? description : server.isXRayConfig() ? description : description + server.hostName(); } case HostNameRole: return server.hostName(); case CredentialsRole: return QVariant::fromValue(serverCredentials(index.row())); @@ -213,7 +213,7 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const } case IsXRayConfigSelectionAvailableRole: { if (server.isXRayConfig()) { - return server.as()->configString.has_value(); + return !server.as()->configString.isEmpty(); } } } @@ -257,6 +257,9 @@ QString ServersModel::getServerDescription(const ServerConfig &server, const int } else if (server.isApiV1()) { const ApiV1ServerConfig *apiV1 = server.as(); return apiV1 ? apiV1->description : server.description(); + } else if (server.isXRayConfig()) { + const XRaySubscriptionConfig *xray = server.as(); + return xray ? xray->configName.at(xray->currentConfig).toString() : server.description(); } else if (data(index, HasWriteAccessRole).toBool()) { QMap containers = server.containers(); bool isDnsInstalled = containers.contains(DockerContainer::Dns); diff --git a/client/ui/qml/Components/ServersListView.qml b/client/ui/qml/Components/ServersListView.qml index 3e3b1f808..ab9d2539f 100644 --- a/client/ui/qml/Components/ServersListView.qml +++ b/client/ui/qml/Components/ServersListView.qml @@ -68,7 +68,7 @@ ListViewType { text: name descriptionText: isServerFromGatewayApi && (isSubscriptionExpired || isSubscriptionExpiringSoon) ? (isSubscriptionExpired ? qsTr("Subscription expired. Please renew") : qsTr("Subscription expiring soon")) - : (isXRayConfigSelectionAvailable ? ServersUiController.getConfigName(ServersUiController.getCurrentConfigIndex()) : serverDescription) + : serverDescription descriptionColor: isServerFromGatewayApi && (isSubscriptionExpired || isSubscriptionExpiringSoon) ? (isSubscriptionExpired ? AmneziaStyle.color.vibrantRed : AmneziaStyle.color.goldenApricot) : AmneziaStyle.color.mutedGray