diff --git a/client/core/configurators/xrayConfigurator.cpp b/client/core/configurators/xrayConfigurator.cpp index d16e8ab4b..bf85d6e37 100644 --- a/client/core/configurators/xrayConfigurator.cpp +++ b/client/core/configurators/xrayConfigurator.cpp @@ -464,6 +464,7 @@ ProtocolConfig XrayConfigurator::createConfig(const ServerCredentials &credentia XrayClientConfig clientConfig; clientConfig.nativeConfig = config; + qDebug() << "config:" << config; clientConfig.localPort = QString(amnezia::protocols::xray::defaultLocalProxyPort); clientConfig.id = xrayClientId; diff --git a/client/ui/controllers/importUiController.cpp b/client/ui/controllers/importUiController.cpp index ce9b952c1..db81c608c 100644 --- a/client/ui/controllers/importUiController.cpp +++ b/client/ui/controllers/importUiController.cpp @@ -201,3 +201,12 @@ bool ImportUiController::decodeQrCode(const QString &code) return mInstance->parseQrCodeChunk(code); } #endif + +QString ImportUiController::readTextFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return {}; + } + return QString::fromUtf8(file.readAll()); +} diff --git a/client/ui/controllers/importUiController.h b/client/ui/controllers/importUiController.h index 853539d05..c527e93c2 100644 --- a/client/ui/controllers/importUiController.h +++ b/client/ui/controllers/importUiController.h @@ -28,6 +28,7 @@ public slots: QString getMaliciousWarningText(); bool isNativeWireGuardConfig(); void processNativeWireGuardConfig(); + QString readTextFile(const QString &fileName); #if defined Q_OS_ANDROID || defined Q_OS_IOS void startDecodingQr(); diff --git a/client/ui/controllers/selfhosted/exportUiController.cpp b/client/ui/controllers/selfhosted/exportUiController.cpp index 4a9f8bfc4..6f49cadc0 100644 --- a/client/ui/controllers/selfhosted/exportUiController.cpp +++ b/client/ui/controllers/selfhosted/exportUiController.cpp @@ -114,3 +114,13 @@ void ExportUiController::applyExportResult(const ExportController::ExportResult emit exportConfigChanged(); } + +void ExportUiController::setConfigFromString(const QString &config, const QString &fileName) +{ + clearPreviousConfig(); + m_config = config; + emit exportConfigChanged(); + if (!fileName.isEmpty()) { + SystemController::saveFile(fileName, m_config); + } +} diff --git a/client/ui/controllers/selfhosted/exportUiController.h b/client/ui/controllers/selfhosted/exportUiController.h index bfc000b0e..8f777da4f 100644 --- a/client/ui/controllers/selfhosted/exportUiController.h +++ b/client/ui/controllers/selfhosted/exportUiController.h @@ -29,6 +29,7 @@ public slots: QList getQrCodes(); void exportConfig(const QString &fileName); + void setConfigFromString(const QString &config, const QString &fileName); void updateClientManagementModel(int serverIndex, int containerIndex); void revokeConfig(int row, int serverIndex, int containerIndex); diff --git a/client/ui/models/protocols/xrayConfigsModel.cpp b/client/ui/models/protocols/xrayConfigsModel.cpp index 4629cb9c4..cd4a95d34 100644 --- a/client/ui/models/protocols/xrayConfigsModel.cpp +++ b/client/ui/models/protocols/xrayConfigsModel.cpp @@ -193,3 +193,23 @@ QString XrayConfigsModel::buildDisplayName(const amnezia::XrayServerConfig &cfg) return QString("%1 %2").arg(transport, security).trimmed(); } + +void XrayConfigsModel::createFromXrayModel(XrayConfigModel *model) +{ + if (!model) { + return; + } + createFromCurrent(model->getProtocolConfig().serverConfig); +} + +void XrayConfigsModel::applyConfigToXrayModel(int index, XrayConfigModel *model) +{ + if (!model) { + return; + } + amnezia::XrayServerConfig cfg = applyConfig(index); + if (cfg.port.isEmpty()) { + return; // guard against invalid index + } + model->applyServerConfig(cfg); +} diff --git a/client/ui/models/protocols/xrayConfigsModel.h b/client/ui/models/protocols/xrayConfigsModel.h index f5eee9c90..a17f416c1 100644 --- a/client/ui/models/protocols/xrayConfigsModel.h +++ b/client/ui/models/protocols/xrayConfigsModel.h @@ -9,6 +9,7 @@ #include #include "core/models/protocols/xrayProtocolConfig.h" +#include "ui/models/protocols/xrayConfigModel.h" class SecureAppSettingsRepository; @@ -49,6 +50,10 @@ public slots: Q_INVOKABLE QString exportToJson(int index) const; Q_INVOKABLE bool importFromJson(const QString &jsonString); + // Convenience: create snapshot from live model, apply snapshot back to model + Q_INVOKABLE void createFromXrayModel(XrayConfigModel *model); + Q_INVOKABLE void applyConfigToXrayModel(int index, XrayConfigModel *model); + signals: void configApplied(int index); void configRemoved(int index); diff --git a/client/ui/qml/Pages2/PageProtocolXrayConfigsSettings.qml b/client/ui/qml/Pages2/PageProtocolXrayConfigsSettings.qml index ef7e92e39..514d699b9 100644 --- a/client/ui/qml/Pages2/PageProtocolXrayConfigsSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolXrayConfigsSettings.qml @@ -58,7 +58,7 @@ PageType { textMaximumLineCount: 2 rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() { - XrayConfigsModel.createFromCurrent(XrayConfigModel.getProtocolConfig().serverConfig) + XrayConfigsModel.createFromXrayModel(XrayConfigModel) } } @@ -72,11 +72,11 @@ PageType { clickedFunction: function() { if (root.selectedConfigIndex >= 0) { var json = XrayConfigsModel.exportToJson(root.selectedConfigIndex) - ExportController.shareText(json, "xray_config.json") + ExportController.setConfigFromString(json, "xray_config.json") } else if (XrayConfigsModel.rowCount() > 0) { // Export the first one if none selected var json = XrayConfigsModel.exportToJson(0) - ExportController.shareText(json, "xray_config.json") + ExportController.setConfigFromString(json, "xray_config.json") } } } @@ -90,7 +90,13 @@ PageType { descriptionText: qsTr("In JSON format") rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() { - ImportController.importConfig() + var filePath = SystemController.getFileName(qsTr("Open XRay config"), qsTr("JSON files (*.json)")) + if (filePath !== "") { + var jsonContent = ImportController.readTextFile(filePath) + if (jsonContent !== "") { + XrayConfigsModel.importFromJson(jsonContent) + } + } } } @@ -200,8 +206,7 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() { configActionsDrawer.closeTriggered() - var serverConfig = XrayConfigsModel.applyConfig(root.selectedConfigIndex) - XrayConfigModel.applyServerConfig(serverConfig) + XrayConfigsModel.applyConfigToXrayModel(root.selectedConfigIndex, XrayConfigModel) PageController.closePage() } } @@ -216,7 +221,7 @@ PageType { clickedFunction: function() { configActionsDrawer.closeTriggered() var json = XrayConfigsModel.exportToJson(root.selectedConfigIndex) - ExportController.shareText(json, "xray_config.json") + ExportController.setConfigFromString(json, "xray_config.json") } } diff --git a/client/ui/qml/qml.qrc b/client/ui/qml/qml.qrc index d70a531b6..cb84c94ac 100644 --- a/client/ui/qml/qml.qrc +++ b/client/ui/qml/qml.qrc @@ -84,6 +84,7 @@ Pages2/PageProtocolXrayTransportSettings.qml Pages2/PageProtocolXrayXmuxSettings.qml Pages2/PageProtocolXrayXPaddingSettings.qml + Pages2/PageProtocolXrayXPaddingBytesSettings.qml Controls2/MinMaxRowType.qml Pages2/PageServiceDnsSettings.qml