mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
rebased and rewrited for mvvm2 refactoring
This commit is contained in:
@@ -11,6 +11,9 @@
|
||||
#include <QRegularExpressionMatch>
|
||||
#include <QRegularExpressionMatchIterator>
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
#include <algorithm>
|
||||
|
||||
#include "core/utils/containerEnum.h"
|
||||
@@ -372,6 +375,224 @@ int ImportController::qrChunksTotal() const
|
||||
return m_totalQrCodeChunksCount;
|
||||
}
|
||||
|
||||
ImportController::ImportResult ImportController::importLink(const QUrl &url)
|
||||
{
|
||||
ImportResult result;
|
||||
|
||||
if (!url.isValid()) {
|
||||
qWarning() << "Invalid URL:" << url;
|
||||
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||
return result;
|
||||
}
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
|
||||
QNetworkReply *reply = manager->get(request);
|
||||
|
||||
QEventLoop loop;
|
||||
QTimer timer;
|
||||
timer.setSingleShot(true);
|
||||
bool timedOut = false;
|
||||
|
||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
connect(&timer, &QTimer::timeout, &loop, [&]() {
|
||||
timedOut = true;
|
||||
loop.quit();
|
||||
});
|
||||
|
||||
timer.start(10000); // 10 sec
|
||||
loop.exec();
|
||||
|
||||
if (timedOut) {
|
||||
qWarning() << "Request timed out";
|
||||
reply->abort();
|
||||
reply->deleteLater();
|
||||
|
||||
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "Network error:" << reply->errorString();
|
||||
reply->deleteLater();
|
||||
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||
return result;
|
||||
}
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
reply->deleteLater();
|
||||
|
||||
if (data.isEmpty()) {
|
||||
qWarning() << "Empty response";
|
||||
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||
return result;
|
||||
}
|
||||
|
||||
QByteArray decoded;
|
||||
QString text;
|
||||
|
||||
if (isValidBase64(data)) {
|
||||
decoded = QByteArray::fromBase64(data);
|
||||
text = QString::fromUtf8(decoded).trimmed();
|
||||
} else {
|
||||
data.replace('\r', "");
|
||||
text = QString::fromUtf8(data).trimmed();
|
||||
}
|
||||
|
||||
if (text.isEmpty()) {
|
||||
qWarning() << "Decoded text is empty";
|
||||
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList configs = text.split('\n', Qt::SkipEmptyParts);
|
||||
|
||||
QJsonArray configStrings;
|
||||
QJsonArray configNames;
|
||||
|
||||
for (const QString &cfg : configs) {
|
||||
|
||||
if (!(cfg.startsWith("vless://") || cfg.startsWith("vmess://") || cfg.startsWith("trojan://")
|
||||
|| cfg.startsWith("ss://") || cfg.startsWith("ssd://"))) {
|
||||
|
||||
qWarning() << "Unknown protocol:" << cfg.left(20);
|
||||
continue;
|
||||
}
|
||||
|
||||
QUrl url(cfg);
|
||||
QUrlQuery query(url);
|
||||
|
||||
QString security = query.queryItemValue("security").isEmpty() ? "None" : query.queryItemValue("security");
|
||||
QString name = QUrl::fromPercentEncoding(url.fragment().toUtf8());
|
||||
|
||||
if (name.isEmpty())
|
||||
name = "Unnamed";
|
||||
|
||||
configStrings.append(cfg);
|
||||
configNames.append(name + " (" + security + ")");
|
||||
}
|
||||
|
||||
if (configStrings.isEmpty()) {
|
||||
qWarning() << "No valid configs found";
|
||||
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||
return result;
|
||||
}
|
||||
|
||||
QString firstConfig = configStrings.first().toString();
|
||||
result = extractConfigFromData(firstConfig);
|
||||
|
||||
QJsonObject xraySubConfig;
|
||||
QJsonObject serverConfig;
|
||||
|
||||
xraySubConfig["config_string"] = configStrings;
|
||||
xraySubConfig["config_name"] = configNames;
|
||||
|
||||
for (auto it = result.config.begin(); it != result.config.end(); ++it) {
|
||||
serverConfig.insert(it.key(), it.value());
|
||||
}
|
||||
|
||||
serverConfig.insert(configKey::description, m_appSettingsRepository->nextAvailableServerName());
|
||||
serverConfig["xray_subscription_config"] = xraySubConfig;
|
||||
serverConfig["xray_subscription_config_current"] = 0;
|
||||
|
||||
result.config = serverConfig;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ImportController::ImportResult ImportController::editServerConfigWithData(QString data, int serverIndex)
|
||||
{
|
||||
ImportResult result = extractConfigFromData(data);
|
||||
|
||||
ServerConfig serverCurrentConfig = m_serversRepository->server(serverIndex);
|
||||
ServerConfig serverConfig = ServerConfig::fromJson(result.config);
|
||||
|
||||
serverConfig.visit([&](auto &cfg) {
|
||||
using T = std::decay_t<decltype(cfg)>;
|
||||
|
||||
if (auto current = serverCurrentConfig.as<T>()) {
|
||||
cfg.description = current->description;
|
||||
|
||||
if constexpr (std::is_same_v<T, SelfHostedServerConfig>) {
|
||||
cfg.xraySubscriptionConfigs->configString = current->xraySubscriptionConfigs->configString;
|
||||
cfg.currentConfig = m_serversRepository->getCurrentConfigIndex();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
m_serversRepository->editServer(serverIndex, serverConfig);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ImportController::isValidBase64(const QByteArray &input)
|
||||
{
|
||||
QByteArray data = input;
|
||||
data = data.trimmed();
|
||||
|
||||
if (data.isEmpty())
|
||||
return false;
|
||||
|
||||
static QRegularExpression base64Regex("^[A-Za-z0-9+/=_\\r\\n-]+$");
|
||||
|
||||
if (!base64Regex.match(QString::fromLatin1(data)).hasMatch())
|
||||
return false;
|
||||
|
||||
data.replace("\r", "");
|
||||
data.replace("\n", "");
|
||||
|
||||
if (data.size() % 4 != 0)
|
||||
return false;
|
||||
|
||||
QByteArray decoded = QByteArray::fromBase64(data, QByteArray::Base64UrlEncoding);
|
||||
|
||||
if (decoded.isEmpty())
|
||||
decoded = QByteArray::fromBase64(data);
|
||||
|
||||
return !decoded.isEmpty();
|
||||
}
|
||||
|
||||
QByteArray ImportController::base64Decode(const QByteArray &input)
|
||||
{
|
||||
std::string clean(input.constData(), input.length());
|
||||
|
||||
for (auto &c : clean) {
|
||||
if (c == '-')
|
||||
c = '+';
|
||||
if (c == '_')
|
||||
c = '/';
|
||||
}
|
||||
|
||||
while (clean.size() % 4 != 0)
|
||||
clean += '=';
|
||||
|
||||
static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
std::string output;
|
||||
std::vector<int> T(256, -1);
|
||||
for (int i = 0; i < 64; i++)
|
||||
T[base64_chars[i]] = i;
|
||||
|
||||
int val = 0, valb = -8;
|
||||
for (unsigned char c : clean) {
|
||||
if (T[c] == -1)
|
||||
break;
|
||||
val = (val << 6) + T[c];
|
||||
valb += 6;
|
||||
if (valb >= 0) {
|
||||
output.push_back(char((val >> valb) & 0xFF));
|
||||
valb -= 8;
|
||||
}
|
||||
}
|
||||
QByteArray output_qa(output.c_str(), output.length());
|
||||
return output_qa;
|
||||
}
|
||||
|
||||
void ImportController::importConfig(const QJsonObject &config)
|
||||
{
|
||||
ServerCredentials credentials;
|
||||
@@ -380,8 +601,20 @@ void ImportController::importConfig(const QJsonObject &config)
|
||||
credentials.userName = config.value(configKey::userName).toString();
|
||||
credentials.secretData = config.value(configKey::password).toString();
|
||||
|
||||
qDebug() << "credentials";
|
||||
qDebug() << "hostName: " << credentials.hostName;
|
||||
qDebug() << "port: " << credentials.port;
|
||||
qDebug() << "userName: " << credentials.userName;
|
||||
qDebug() << "secretData: " << credentials.secretData << '\n';
|
||||
|
||||
qDebug() << "creds valid? -> " << credentials.isValid();
|
||||
qDebug() << "config contains containers? -> " << config.contains(configKey::containers);
|
||||
|
||||
if (credentials.isValid() || config.contains(configKey::containers)) {
|
||||
ServerConfig serverConfig = ServerConfig::fromJson(config);
|
||||
qDebug() << "server config is SH? -> " << serverConfig.isSelfHosted();
|
||||
qDebug() << "server config is XRay? -> " << serverConfig.isXRayConfig();
|
||||
qDebug() << "adding server";
|
||||
m_serversRepository->addServer(serverConfig);
|
||||
emit importFinished();
|
||||
} else if (config.contains(configKey::configVersion)) {
|
||||
|
||||
@@ -63,11 +63,17 @@ public:
|
||||
int qrChunksReceived() const;
|
||||
int qrChunksTotal() const;
|
||||
|
||||
ImportResult importLink(const QUrl &url);
|
||||
ImportResult editServerConfigWithData(QString data, int serverIndex);
|
||||
bool isValidBase64(const QByteArray &input);
|
||||
QByteArray base64Decode(const QByteArray &input);
|
||||
|
||||
void importConfig(const QJsonObject &config);
|
||||
QJsonObject processNativeWireGuardConfig(const QJsonObject &config);
|
||||
|
||||
signals:
|
||||
void importFinished();
|
||||
void linkImportFinished(const ImportResult &result);
|
||||
void importErrorOccurred(ErrorCode errorCode, bool goToPageHome);
|
||||
void restoreAppConfig(const QByteArray &data);
|
||||
|
||||
|
||||
@@ -58,6 +58,31 @@ void ServersController::clearCachedProfile(int serverIndex, DockerContainer cont
|
||||
m_serversRepository->clearLastConnectionConfig(serverIndex, container);
|
||||
}
|
||||
|
||||
void ServersController::setCurrentConfigIndex(const int index)
|
||||
{
|
||||
m_serversRepository->setCurrentConfigIndex(index);
|
||||
}
|
||||
|
||||
int ServersController::getCurrentConfigIndex() const
|
||||
{
|
||||
return m_serversRepository->getCurrentConfigIndex();
|
||||
}
|
||||
|
||||
QString ServersController::getConfigString(const int index) const
|
||||
{
|
||||
return m_serversRepository->getConfigString(index);
|
||||
}
|
||||
|
||||
QString ServersController::getConfigName(const int index) const
|
||||
{
|
||||
return m_serversRepository->getConfigName(index);
|
||||
}
|
||||
|
||||
QJsonArray ServersController::getConfigNames() const
|
||||
{
|
||||
return m_serversRepository->getConfigNames();
|
||||
}
|
||||
|
||||
QJsonArray ServersController::getServersArray() const
|
||||
{
|
||||
QJsonArray result;
|
||||
|
||||
@@ -64,6 +64,13 @@ public:
|
||||
// Cache management
|
||||
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;
|
||||
|
||||
// Getters
|
||||
QJsonArray getServersArray() const;
|
||||
QVector<ServerConfig> getServers() const;
|
||||
|
||||
@@ -15,6 +15,31 @@
|
||||
namespace amnezia
|
||||
{
|
||||
|
||||
QJsonObject SelfHostedServerConfig::XRaySubscriptionConfigs::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
|
||||
if (!configString.isEmpty()) {
|
||||
obj[QLatin1String("config_string")] = configString;
|
||||
}
|
||||
|
||||
if (!configName.isEmpty()) {
|
||||
obj[QLatin1String("config_name")] = configName;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
SelfHostedServerConfig::XRaySubscriptionConfigs SelfHostedServerConfig::XRaySubscriptionConfigs::fromJson(const QJsonObject &json)
|
||||
{
|
||||
QJsonObject xrayConfigObj = json.value(QLatin1String("xray_subscription_config")).toObject();
|
||||
|
||||
XRaySubscriptionConfigs xraySubscriptionConfigs;
|
||||
xraySubscriptionConfigs.configString = xrayConfigObj.value(QLatin1String("config_string")).toArray();
|
||||
xraySubscriptionConfigs.configName = xrayConfigObj.value(QLatin1String("config_name")).toArray();
|
||||
return xraySubscriptionConfigs;
|
||||
}
|
||||
|
||||
using namespace ContainerEnumNS;
|
||||
|
||||
bool SelfHostedServerConfig::hasCredentials() const
|
||||
@@ -95,6 +120,15 @@ QJsonObject SelfHostedServerConfig::toJson() const
|
||||
if (port.has_value()) {
|
||||
obj[configKey::port] = port.value();
|
||||
}
|
||||
|
||||
QJsonObject xraySubscriptionConfigsObj = xraySubscriptionConfigs->toJson();
|
||||
if (!xraySubscriptionConfigsObj.isEmpty()) {
|
||||
obj[QLatin1String("xray_subscription_configs")] = xraySubscriptionConfigsObj;
|
||||
}
|
||||
|
||||
if (currentConfig) {
|
||||
obj[QLatin1String("xray_subscription_config_current")] = currentConfig.value();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define SELFHOSTEDSERVERCONFIG_H
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QMap>
|
||||
#include <optional>
|
||||
|
||||
@@ -29,6 +30,17 @@ struct SelfHostedServerConfig {
|
||||
std::optional<QString> userName;
|
||||
std::optional<QString> password;
|
||||
std::optional<int> port;
|
||||
|
||||
struct XRaySubscriptionConfigs
|
||||
{
|
||||
QJsonArray configString;
|
||||
QJsonArray configName;
|
||||
|
||||
QJsonObject toJson() const;
|
||||
static XRaySubscriptionConfigs fromJson(const QJsonObject &json);
|
||||
};
|
||||
std::optional<XRaySubscriptionConfigs> xraySubscriptionConfigs;
|
||||
std::optional<int> currentConfig;
|
||||
|
||||
bool hasCredentials() const;
|
||||
bool isReadOnly() const;
|
||||
|
||||
@@ -120,6 +120,10 @@ bool ServerConfig::isApiConfig() const
|
||||
return isApiV1() || isApiV2();
|
||||
}
|
||||
|
||||
bool ServerConfig::isXRayConfig() const {
|
||||
return isSelfHosted() && std::get<SelfHostedServerConfig>(data).currentConfig.has_value();
|
||||
}
|
||||
|
||||
QJsonObject ServerConfig::toJson() const
|
||||
{
|
||||
return std::visit([](const auto& v) { return v.toJson(); }, data);
|
||||
|
||||
@@ -57,6 +57,7 @@ struct ServerConfig {
|
||||
bool isApiV1() const;
|
||||
bool isApiV2() const;
|
||||
bool isApiConfig() const;
|
||||
bool isXRayConfig() const;
|
||||
|
||||
template<typename T>
|
||||
T* as() {
|
||||
|
||||
@@ -176,6 +176,55 @@ void SecureServersRepository::clearLastConnectionConfig(int serverIndex, DockerC
|
||||
setContainerConfig(serverIndex, container, containerCfg);
|
||||
}
|
||||
|
||||
void SecureServersRepository::setCurrentConfigIndex(const int index)
|
||||
{
|
||||
ServerConfig config = server(m_defaultServerIndex);
|
||||
|
||||
const SelfHostedServerConfig *xrayConfigs = config.as<SelfHostedServerConfig>();
|
||||
if (auto *cfg = std::get_if<SelfHostedServerConfig>(&config.data))
|
||||
cfg->currentConfig = index;
|
||||
}
|
||||
|
||||
int SecureServersRepository::getCurrentConfigIndex() const
|
||||
{
|
||||
const ServerConfig config = server(m_defaultServerIndex);
|
||||
if (!config.isXRayConfig())
|
||||
return int();
|
||||
|
||||
const SelfHostedServerConfig *xrayConfigs = config.as<SelfHostedServerConfig>();
|
||||
return xrayConfigs->currentConfig.value();
|
||||
}
|
||||
|
||||
QString SecureServersRepository::getConfigString(const int index) const
|
||||
{
|
||||
const ServerConfig config = server(m_defaultServerIndex);
|
||||
if (!config.isXRayConfig())
|
||||
return QString();
|
||||
|
||||
const SelfHostedServerConfig *xrayConfigs = config.as<SelfHostedServerConfig>();
|
||||
return xrayConfigs->xraySubscriptionConfigs->configString.at(index).toString();
|
||||
}
|
||||
|
||||
QString SecureServersRepository::getConfigName(const int index) const
|
||||
{
|
||||
const ServerConfig config = server(m_defaultServerIndex);
|
||||
if (!config.isXRayConfig())
|
||||
return QString();
|
||||
|
||||
const SelfHostedServerConfig *xrayConfigs = config.as<SelfHostedServerConfig>();
|
||||
return xrayConfigs->xraySubscriptionConfigs->configName.at(index).toString();
|
||||
}
|
||||
|
||||
QJsonArray SecureServersRepository::getConfigNames() const
|
||||
{
|
||||
const ServerConfig config = server(m_defaultServerIndex);
|
||||
if (!config.isXRayConfig())
|
||||
return QJsonArray();
|
||||
|
||||
const SelfHostedServerConfig *xrayConfigs = config.as<SelfHostedServerConfig>();
|
||||
return xrayConfigs->xraySubscriptionConfigs->configName;
|
||||
}
|
||||
|
||||
ServerCredentials SecureServersRepository::serverCredentials(int index) const
|
||||
{
|
||||
ServerConfig config = server(index);
|
||||
|
||||
@@ -35,6 +35,12 @@ 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;
|
||||
|
||||
ServerCredentials serverCredentials(int index) const;
|
||||
bool hasServerWithVpnKey(const QString &vpnKey) const;
|
||||
bool hasServerWithCrc(quint16 crc) const;
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
<file>controls/amnezia.svg</file>
|
||||
<file>controls/app.svg</file>
|
||||
<file>controls/archive-restore.svg</file>
|
||||
<file>controls/arrow-down.svg</file>
|
||||
<file>controls/arrow-left.svg</file>
|
||||
<file>controls/arrow-right.svg</file>
|
||||
<file>controls/arrow-up.svg</file>
|
||||
<file>controls/bug.svg</file>
|
||||
<file>controls/check.svg</file>
|
||||
<file>controls/chevron-down.svg</file>
|
||||
|
||||
@@ -31,6 +31,41 @@ ImportUiController::ImportUiController(ImportController* importController, QObje
|
||||
connect(m_importController, &ImportController::restoreAppConfig, this, &ImportUiController::restoreAppConfig);
|
||||
}
|
||||
|
||||
bool ImportUiController::importLink(const QUrl &url)
|
||||
{
|
||||
auto result = m_importController->importLink(url);
|
||||
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
emit importErrorOccurred(result.errorCode, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_config = result.config;
|
||||
m_configFileName = result.configFileName;
|
||||
m_maliciousWarningText = result.maliciousWarningText;
|
||||
m_isNativeWireGuardConfig = result.isNativeWireGuardConfig;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImportUiController::editServerConfigWithData(QString data, int serverIndex)
|
||||
{
|
||||
auto result = m_importController->editServerConfigWithData(data, serverIndex);
|
||||
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
emit importErrorOccurred(result.errorCode, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_config = result.config;
|
||||
m_configFileName = result.configFileName;
|
||||
m_maliciousWarningText = result.maliciousWarningText;
|
||||
m_isNativeWireGuardConfig = result.isNativeWireGuardConfig;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ImportUiController::extractConfigFromFile(const QString &fileName)
|
||||
{
|
||||
QString data;
|
||||
|
||||
@@ -20,6 +20,8 @@ public:
|
||||
public slots:
|
||||
void importConfig();
|
||||
void clearConfigFileName();
|
||||
bool importLink(const QUrl &url);
|
||||
bool editServerConfigWithData(QString data, int serverIndex);
|
||||
bool extractConfigFromFile(const QString &fileName);
|
||||
bool extractConfigFromData(QString data);
|
||||
bool extractConfigFromQr(const QByteArray &data);
|
||||
|
||||
@@ -272,6 +272,13 @@ bool ServersUiController::isDefaultServerFromApi() const
|
||||
|| configVersion == apiDefs::ConfigSource::AmneziaGateway;
|
||||
}
|
||||
|
||||
bool ServersUiController::isDefaultServerContainXRayConfigs() const
|
||||
{
|
||||
int defaultIndex = getDefaultServerIndex();
|
||||
const ServerConfig server = m_serversController->getServerConfig(defaultIndex);
|
||||
return server.isXRayConfig();
|
||||
}
|
||||
|
||||
int ServersUiController::getProcessedServerIndex() const
|
||||
{
|
||||
return m_processedServerIndex;
|
||||
@@ -444,6 +451,26 @@ QString ServersUiController::adDescription() const
|
||||
return QString();
|
||||
}
|
||||
|
||||
void ServersUiController::setCurrentConfigIndex(const int index)
|
||||
{
|
||||
m_serversController->setCurrentConfigIndex(index);
|
||||
}
|
||||
|
||||
int ServersUiController::getCurrentConfigIndex() const
|
||||
{
|
||||
return m_serversController->getCurrentConfigIndex();
|
||||
}
|
||||
|
||||
QString ServersUiController::getConfigName(const int index) const
|
||||
{
|
||||
return m_serversController->getConfigName(index);
|
||||
}
|
||||
|
||||
QJsonArray ServersUiController::getConfigNames() const
|
||||
{
|
||||
return m_serversController->getConfigNames();
|
||||
}
|
||||
|
||||
void ServersUiController::updateContainersModel()
|
||||
{
|
||||
if (m_processedServerIndex < 0 || m_processedServerIndex >= m_serversController->getServersCount()) {
|
||||
|
||||
@@ -26,6 +26,8 @@ class ServersUiController : public QObject
|
||||
Q_PROPERTY(bool isDefaultServerDefaultContainerHasSplitTunneling READ isDefaultServerDefaultContainerHasSplitTunneling NOTIFY defaultServerIndexChanged)
|
||||
Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIndexChanged)
|
||||
|
||||
Q_PROPERTY(bool isDefaultServerContainXRayConfigs READ isDefaultServerContainXRayConfigs NOTIFY defaultServerIndexChanged)
|
||||
|
||||
Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged)
|
||||
Q_PROPERTY(int processedContainerIndex READ getProcessedContainerIndex WRITE setProcessedContainerIndex NOTIFY processedContainerIndexChanged)
|
||||
Q_PROPERTY(bool processedServerIsPremium READ processedServerIsPremium NOTIFY processedServerIndexChanged)
|
||||
@@ -61,6 +63,7 @@ public slots:
|
||||
QString getDefaultServerDescriptionExpanded() const;
|
||||
bool isDefaultServerDefaultContainerHasSplitTunneling() const;
|
||||
bool isDefaultServerFromApi() const;
|
||||
bool isDefaultServerContainXRayConfigs() const;
|
||||
|
||||
int getProcessedServerIndex() const;
|
||||
void setProcessedServerIndex(int index);
|
||||
@@ -77,6 +80,11 @@ public slots:
|
||||
bool isAdVisible() const;
|
||||
QString adHeader() const;
|
||||
QString adDescription() const;
|
||||
|
||||
void setCurrentConfigIndex(int index);
|
||||
int getCurrentConfigIndex() const;
|
||||
QString getConfigName(const int index) const;
|
||||
QJsonArray getConfigNames() const;
|
||||
|
||||
QStringList getAllInstalledServicesName(int serverIndex) const;
|
||||
|
||||
|
||||
@@ -34,6 +34,11 @@ namespace
|
||||
|
||||
constexpr char publicKeyInfo[] = "public_key";
|
||||
constexpr char expiresAt[] = "expires_at";
|
||||
|
||||
constexpr char xraySubscriptionConfig[] = "xray_subscription_configs";
|
||||
constexpr char xraySubscriptionConfigString[] = "config_string";
|
||||
constexpr char xraySubscriptionConfigName[] = "config_name";
|
||||
constexpr char xraySubscriptionConfigCurrent[] = "xray_subscription_config_current";
|
||||
}
|
||||
|
||||
QString normalizeVpnKey(const QString &vpnKey)
|
||||
@@ -207,6 +212,11 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
return apiUtils::isSubscriptionExpiringSoon(apiConfig.subscription.endDate);
|
||||
}
|
||||
case IsXRayConfigSelectionAvailableRole: {
|
||||
if (server.isSelfHosted()) {
|
||||
return server.as<SelfHostedServerConfig>()->xraySubscriptionConfigs.has_value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
@@ -301,6 +311,11 @@ bool ServersModel::isDefaultServerFromApi()
|
||||
|| data(m_defaultServerIndex, IsServerFromGatewayApiRole).toBool();
|
||||
}
|
||||
|
||||
bool ServersModel::isDefaultServerContainXRayConfigs()
|
||||
{
|
||||
return data(m_defaultServerIndex, IsXRayConfigSelectionAvailableRole).toBool();
|
||||
}
|
||||
|
||||
bool ServersModel::isProcessedServerHasWriteAccess()
|
||||
{
|
||||
return qvariant_cast<bool>(data(m_processedServerIndex, HasWriteAccessRole));
|
||||
@@ -350,6 +365,8 @@ QHash<int, QByteArray> ServersModel::roleNames() const
|
||||
roles[IsSubscriptionExpiredRole] = "isSubscriptionExpired";
|
||||
roles[IsSubscriptionExpiringSoonRole] = "isSubscriptionExpiringSoon";
|
||||
|
||||
roles[IsXRayConfigSelectionAvailableRole] = "isXRayConfigSelectionAvailable";
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ public:
|
||||
IsSubscriptionExpiredRole,
|
||||
IsSubscriptionExpiringSoonRole,
|
||||
|
||||
IsXRayConfigSelectionAvailableRole,
|
||||
|
||||
HasAmneziaDns
|
||||
};
|
||||
|
||||
@@ -60,6 +62,8 @@ public slots:
|
||||
bool isDefaultServerCurrentlyProcessed();
|
||||
bool isDefaultServerFromApi();
|
||||
|
||||
bool isDefaultServerContainXRayConfigs();
|
||||
|
||||
bool isProcessedServerHasWriteAccess();
|
||||
bool isDefaultServerHasWriteAccess();
|
||||
bool hasServerWithWriteAccess();
|
||||
|
||||
@@ -311,11 +311,11 @@ PageType {
|
||||
objectName: "rowLayoutLabel"
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
Layout.topMargin: 8
|
||||
Layout.bottomMargin: drawer.isCollapsedStateActive ? 44 : ServersUiController.isDefaultServerFromApi ? 61 : 16
|
||||
Layout.bottomMargin: drawer.isCollapsedStateActive ? 44 : (ServersUiController.isDefaultServerFromApi || ServersUiController.isDefaultServerContainXRayConfigs) ? 61 : 16
|
||||
spacing: 0
|
||||
|
||||
BasicButtonType {
|
||||
enabled: (ServersUiController.defaultServerImagePathCollapsed !== "") && drawer.isCollapsedStateActive
|
||||
enabled: (ServersUiController.defaultServerImagePathCollapsed !== "" || ServersUiController.isDefaultServerContainXRayConfigs) && drawer.isCollapsedStateActive
|
||||
hoverEnabled: enabled
|
||||
|
||||
implicitHeight: 36
|
||||
@@ -381,7 +381,7 @@ PageType {
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
spacing: 8
|
||||
|
||||
visible: !ServersUiController.isDefaultServerFromApi
|
||||
visible: !ServersUiController.isDefaultServerFromApi && !ServersUiController.isDefaultServerContainXRayConfigs
|
||||
|
||||
DropDownType {
|
||||
id: containersDropDown
|
||||
|
||||
@@ -107,7 +107,7 @@ PageType {
|
||||
|
||||
imageSource: "qrc:/images/controls/download.svg"
|
||||
|
||||
checked: index === ServersModel.getCurrentConfigIndex()
|
||||
checked: index === ServersUiController.getCurrentConfigIndex()
|
||||
checkable: !ConnectionController.isConnected
|
||||
|
||||
onClicked: {
|
||||
@@ -120,10 +120,10 @@ PageType {
|
||||
return
|
||||
}
|
||||
|
||||
if (index !== ServersModel.getCurrentConfigIndex()) {
|
||||
if (index !== ServersUiController.getCurrentConfigIndex()) {
|
||||
PageController.showBusyIndicator(true)
|
||||
ServersModel.setCurrentConfigIndex(index)
|
||||
ImportController.editServerConfigWithData(ServersModel.getConfigString(index), ServersModel.getProcessedServerIndex())
|
||||
ServersUiController.setCurrentConfigIndex(index)
|
||||
ImportController.editServerConfigWithData(ServersUiController.getConfigString(index), ServersUiController.getProcessedServerIndex())
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
@@ -154,7 +154,7 @@ PageType {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
const names = ServersModel.getConfigNames()
|
||||
const names = ServersUiController.getConfigNames()
|
||||
xrayConfigs.clear()
|
||||
|
||||
for (let i = 0; i < names.length; ++i) {
|
||||
|
||||
@@ -197,8 +197,7 @@ PageType {
|
||||
}
|
||||
|
||||
clickedFunc: function() {
|
||||
if (isValidUrl(textKey.textField.text)) {
|
||||
ImportController.importLink(textKey.textField.text)
|
||||
if (isValidUrl(textKey.textField.text) && ImportController.importLink(textKey.textField.text)) {
|
||||
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
|
||||
}else if (ImportController.extractConfigFromData(textKey.textField.text)) {
|
||||
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
|
||||
|
||||
@@ -99,6 +99,8 @@
|
||||
<file>Pages2/PageSettingsServerServices.qml</file>
|
||||
<file>Pages2/PageSettingsServersList.qml</file>
|
||||
<file>Pages2/PageSettingsSplitTunneling.qml</file>
|
||||
<file>Pages2/PageSettingsXRayAvailableConfigs.qml</file>
|
||||
<file>Pages2/PageSettingsXRayServerInfo.qml</file>
|
||||
<file>Pages2/PageSettingsNewsNotifications.qml</file>
|
||||
<file>Pages2/PageSettingsNewsDetail.qml</file>
|
||||
<file>Pages2/PageProtocolAwgClientSettings.qml</file>
|
||||
|
||||
Reference in New Issue
Block a user