parent
f6acec53c0
commit
ba4237f1dd
64 changed files with 1933 additions and 336 deletions
|
|
@ -337,6 +337,38 @@ void ExportController::generateCloakConfig()
|
|||
emit exportConfigChanged();
|
||||
}
|
||||
|
||||
void ExportController::generateXrayConfig()
|
||||
{
|
||||
clearPreviousConfig();
|
||||
|
||||
int serverIndex = m_serversModel->getProcessedServerIndex();
|
||||
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
|
||||
|
||||
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
|
||||
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
|
||||
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
|
||||
|
||||
ErrorCode errorCode = ErrorCode::NoError;
|
||||
|
||||
QString clientId;
|
||||
QString config =
|
||||
m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, Proto::Xray, clientId, &errorCode);
|
||||
|
||||
if (errorCode) {
|
||||
emit exportErrorOccurred(errorString(errorCode));
|
||||
return;
|
||||
}
|
||||
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::Xray, config);
|
||||
QJsonObject configJson = QJsonDocument::fromJson(config.toUtf8()).object();
|
||||
|
||||
QStringList lines = QString(QJsonDocument(configJson).toJson()).replace("\r", "").split("\n");
|
||||
for (const QString &line : lines) {
|
||||
m_config.append(line + "\n");
|
||||
}
|
||||
|
||||
emit exportConfigChanged();
|
||||
}
|
||||
|
||||
QString ExportController::getConfig()
|
||||
{
|
||||
return m_config;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ public slots:
|
|||
void generateAwgConfig(const QString &clientName);
|
||||
void generateShadowSocksConfig();
|
||||
void generateCloakConfig();
|
||||
void generateXrayConfig();
|
||||
|
||||
QString getConfig();
|
||||
QString getNativeConfigString();
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ namespace
|
|||
Amnezia,
|
||||
OpenVpn,
|
||||
WireGuard,
|
||||
Xray,
|
||||
Backup,
|
||||
Invalid
|
||||
};
|
||||
|
|
@ -34,6 +35,9 @@ namespace
|
|||
const QString wireguardConfigPatternSectionInterface = "[Interface]";
|
||||
const QString wireguardConfigPatternSectionPeer = "[Peer]";
|
||||
|
||||
const QString xrayConfigPatternInbound = "inbounds";
|
||||
const QString xrayConfigPatternOutbound = "outbounds";
|
||||
|
||||
const QString amneziaConfigPattern = "containers";
|
||||
const QString amneziaFreeConfigPattern = "api_key";
|
||||
const QString backupPattern = "Servers/serversList";
|
||||
|
|
@ -49,6 +53,8 @@ namespace
|
|||
} else if (config.contains(wireguardConfigPatternSectionInterface)
|
||||
&& config.contains(wireguardConfigPatternSectionPeer)) {
|
||||
return ConfigTypes::WireGuard;
|
||||
} else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) {
|
||||
return ConfigTypes::Xray;
|
||||
}
|
||||
return ConfigTypes::Invalid;
|
||||
}
|
||||
|
|
@ -109,6 +115,10 @@ bool ImportController::extractConfigFromData(QString data)
|
|||
m_config = extractWireGuardConfig(config);
|
||||
return m_config.empty() ? false : true;
|
||||
}
|
||||
case ConfigTypes::Xray: {
|
||||
m_config = extractXrayConfig(config);
|
||||
return m_config.empty() ? false : true;
|
||||
}
|
||||
case ConfigTypes::Amnezia: {
|
||||
m_config = QJsonDocument::fromJson(config.toUtf8()).object();
|
||||
return m_config.empty() ? false : true;
|
||||
|
|
@ -349,6 +359,42 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
|
|||
return config;
|
||||
}
|
||||
|
||||
QJsonObject ImportController::extractXrayConfig(const QString &data)
|
||||
{
|
||||
QJsonParseError parserErr;
|
||||
QJsonDocument jsonConf = QJsonDocument::fromJson(data.toLocal8Bit(), &parserErr);
|
||||
|
||||
QJsonObject xrayVpnConfig;
|
||||
xrayVpnConfig[config_key::config] = jsonConf.toJson().constData();
|
||||
QJsonObject lastConfig;
|
||||
lastConfig[config_key::last_config] = jsonConf.toJson().constData();
|
||||
lastConfig[config_key::isThirdPartyConfig] = true;
|
||||
|
||||
QJsonObject containers;
|
||||
containers.insert(config_key::container, QJsonValue("amnezia-xray"));
|
||||
containers.insert(config_key::xray, QJsonValue(lastConfig));
|
||||
|
||||
QJsonArray arr;
|
||||
arr.push_back(containers);
|
||||
|
||||
QString hostName;
|
||||
|
||||
const static QRegularExpression hostNameRegExp("\"address\":\\s*\"([^\"]+)");
|
||||
QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data);
|
||||
if (hostNameMatch.hasMatch()) {
|
||||
hostName = hostNameMatch.captured(1);
|
||||
}
|
||||
|
||||
QJsonObject config;
|
||||
config[config_key::containers] = arr;
|
||||
config[config_key::defaultContainer] = "amnezia-xray";
|
||||
config[config_key::description] = m_settings->nextAvailableServerName();
|
||||
|
||||
config[config_key::hostName] = hostName;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
static QMutex qrDecodeMutex;
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ signals:
|
|||
private:
|
||||
QJsonObject extractOpenVpnConfig(const QString &data);
|
||||
QJsonObject extractWireGuardConfig(const QString &data);
|
||||
QJsonObject extractXrayConfig(const QString &data);
|
||||
|
||||
#if defined Q_OS_ANDROID || defined Q_OS_IOS
|
||||
void stopDecodingQr();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "core/errorstrings.h"
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/networkUtilities.h"
|
||||
#include "utilities.h"
|
||||
#include "ui/models/protocols/awgConfigModel.h"
|
||||
#include "ui/models/protocols/wireguardConfigModel.h"
|
||||
|
|
@ -352,12 +353,12 @@ void InstallController::removeCurrentlyProcessedContainer()
|
|||
|
||||
QRegularExpression InstallController::ipAddressPortRegExp()
|
||||
{
|
||||
return Utils::ipAddressPortRegExp();
|
||||
return NetworkUtilities::ipAddressPortRegExp();
|
||||
}
|
||||
|
||||
QRegularExpression InstallController::ipAddressRegExp()
|
||||
{
|
||||
return Utils::ipAddressRegExp();
|
||||
return NetworkUtilities::ipAddressRegExp();
|
||||
}
|
||||
|
||||
void InstallController::setCurrentlyInstalledServerCredentials(const QString &hostName, const QString &userName,
|
||||
|
|
@ -450,7 +451,6 @@ void InstallController::mountSftpDrive(const QString &port, const QString &passw
|
|||
process->write((password + "\n").toUtf8());
|
||||
}
|
||||
|
||||
// qDebug().noquote() << "onPushButtonSftpMountDriveClicked" << args;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ namespace PageLoader
|
|||
PageProtocolOpenVpnSettings,
|
||||
PageProtocolShadowSocksSettings,
|
||||
PageProtocolCloakSettings,
|
||||
PageProtocolXraySettings,
|
||||
PageProtocolWireGuardSettings,
|
||||
PageProtocolAwgSettings,
|
||||
PageProtocolIKev2Settings,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include <QStandardPaths>
|
||||
|
||||
#include "systemController.h"
|
||||
#include "utilities.h"
|
||||
#include "core/networkUtilities.h"
|
||||
|
||||
SitesController::SitesController(const std::shared_ptr<Settings> &settings,
|
||||
const QSharedPointer<VpnConnection> &vpnConnection,
|
||||
|
|
@ -25,7 +25,7 @@ void SitesController::addSite(QString hostname)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!Utils::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
|
||||
if (!NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
|
||||
// get domain name if it present
|
||||
hostname.replace("https://", "");
|
||||
hostname.replace("http://", "");
|
||||
|
|
@ -40,7 +40,7 @@ void SitesController::addSite(QString hostname)
|
|||
if (!ip.isEmpty()) {
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, QStringList() << ip));
|
||||
} else if (Utils::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
|
||||
} else if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, QStringList() << hostname));
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ void SitesController::addSite(QString hostname)
|
|||
}
|
||||
};
|
||||
|
||||
if (Utils::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
|
||||
if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
|
||||
processSite(hostname, "");
|
||||
} else {
|
||||
processSite(hostname, "");
|
||||
|
|
@ -110,7 +110,7 @@ void SitesController::importSites(const QString &fileName, bool replaceExisting)
|
|||
auto hostname = jsonObject.value("hostname").toString("");
|
||||
auto ip = jsonObject.value("ip").toString("");
|
||||
|
||||
if (!hostname.contains(".") && !Utils::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
|
||||
if (!hostname.contains(".") && !NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
|
||||
qDebug() << hostname << " not look like ip adress or domain name";
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
69
client/ui/models/protocols/xrayConfigModel.cpp
Normal file
69
client/ui/models/protocols/xrayConfigModel.cpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#include "xrayConfigModel.h"
|
||||
|
||||
#include "protocols/protocols_defs.h"
|
||||
|
||||
XrayConfigModel::XrayConfigModel(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
int XrayConfigModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool XrayConfigModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case Roles::SiteRole: m_protocolConfig.insert(config_key::site, value.toString()); break;
|
||||
}
|
||||
|
||||
emit dataChanged(index, index, QList { role });
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant XrayConfigModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case Roles::SiteRole: return m_protocolConfig.value(config_key::site).toString(protocols::xray::defaultSite);
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void XrayConfigModel::updateModel(const QJsonObject &config)
|
||||
{
|
||||
beginResetModel();
|
||||
m_container = ContainerProps::containerFromString(config.value(config_key::container).toString());
|
||||
|
||||
m_fullConfig = config;
|
||||
QJsonObject protocolConfig = config.value(config_key::xray).toObject();
|
||||
|
||||
m_protocolConfig.insert(config_key::site,
|
||||
protocolConfig.value(config_key::site).toString(protocols::xray::defaultSite));
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QJsonObject XrayConfigModel::getConfig()
|
||||
{
|
||||
m_fullConfig.insert(config_key::xray, m_protocolConfig);
|
||||
return m_fullConfig;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> XrayConfigModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
|
||||
roles[SiteRole] = "site";
|
||||
|
||||
return roles;
|
||||
}
|
||||
38
client/ui/models/protocols/xrayConfigModel.h
Normal file
38
client/ui/models/protocols/xrayConfigModel.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef XRAYCONFIGMODEL_H
|
||||
#define XRAYCONFIGMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
|
||||
class XrayConfigModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
SiteRole
|
||||
};
|
||||
|
||||
explicit XrayConfigModel(QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
public slots:
|
||||
void updateModel(const QJsonObject &config);
|
||||
QJsonObject getConfig();
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
DockerContainer m_container;
|
||||
QJsonObject m_protocolConfig;
|
||||
QJsonObject m_fullConfig;
|
||||
};
|
||||
|
||||
#endif // XRAYCONFIGMODEL_H
|
||||
|
|
@ -79,6 +79,8 @@ PageLoader::PageEnum ProtocolsModel::protocolPage(Proto protocol) const
|
|||
case Proto::WireGuard: return PageLoader::PageEnum::PageProtocolWireGuardSettings;
|
||||
case Proto::Ikev2: return PageLoader::PageEnum::PageProtocolIKev2Settings;
|
||||
case Proto::L2tp: return PageLoader::PageEnum::PageProtocolIKev2Settings;
|
||||
case Proto::Xray: return PageLoader::PageEnum::PageProtocolXraySettings;
|
||||
|
||||
// non-vpn
|
||||
case Proto::TorWebSite: return PageLoader::PageEnum::PageServiceTorWebsiteSettings;
|
||||
case Proto::Dns: return PageLoader::PageEnum::PageServiceDnsSettings;
|
||||
|
|
|
|||
|
|
@ -57,6 +57,12 @@ ListView {
|
|||
PageController.goToPage(PageEnum.PageProtocolOpenVpnSettings)
|
||||
break
|
||||
}
|
||||
case ContainerEnum.Xray: {
|
||||
XrayConfigModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageProtocolXraySettings)
|
||||
break
|
||||
}
|
||||
|
||||
case ContainerEnum.WireGuard: {
|
||||
WireGuardConfigModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageProtocolWireGuardSettings)
|
||||
|
|
|
|||
156
client/ui/qml/Pages2/PageProtocolXraySettings.qml
Normal file
156
client/ui/qml/Pages2/PageProtocolXraySettings.qml
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import PageEnum 1.0
|
||||
import ContainerEnum 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
|
||||
Column {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
enabled: ServersModel.isCurrentlyProcessedServerHasWriteAccess()
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
|
||||
model: XrayConfigModel
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
spacing: 0
|
||||
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("XRay settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
|
||||
headerText: qsTr("Disguised as traffic from")
|
||||
textFieldText: site
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textFieldText !== site) {
|
||||
var tmpText = textFieldText
|
||||
tmpText = tmpText.toLocaleLowerCase()
|
||||
|
||||
var indexHttps = tmpText.indexOf("https://")
|
||||
if (indexHttps === 0) {
|
||||
tmpText = textFieldText.substring(8)
|
||||
} else {
|
||||
site = textFieldText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
Layout.topMargin: 24
|
||||
Layout.leftMargin: -8
|
||||
implicitHeight: 32
|
||||
|
||||
visible: ContainersModel.getCurrentlyProcessedContainerIndex() === ContainerEnum.Xray
|
||||
|
||||
defaultColor: "transparent"
|
||||
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
|
||||
pressedColor: Qt.rgba(1, 1, 1, 0.12)
|
||||
textColor: "#EB5757"
|
||||
|
||||
text: qsTr("Remove XRay")
|
||||
|
||||
onClicked: {
|
||||
questionDrawer.headerText = qsTr("Remove XRay from server?")
|
||||
questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
|
||||
questionDrawer.yesButtonText = qsTr("Continue")
|
||||
questionDrawer.noButtonText = qsTr("Cancel")
|
||||
|
||||
questionDrawer.yesButtonFunction = function() {
|
||||
questionDrawer.visible = false
|
||||
PageController.goToPage(PageEnum.PageDeinstalling)
|
||||
InstallController.removeCurrentlyProcessedContainer()
|
||||
}
|
||||
questionDrawer.noButtonFunction = function() {
|
||||
questionDrawer.visible = false
|
||||
}
|
||||
questionDrawer.visible = true
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
text: qsTr("Save and Restart Amnezia")
|
||||
|
||||
onClicked: {
|
||||
forceActiveFocus()
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(XrayConfigModel.getConfig())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QuestionDrawer {
|
||||
id: questionDrawer
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -84,6 +84,7 @@ PageType {
|
|||
case ProtocolEnum.OpenVpn: OpenVpnConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.ShadowSocks: ShadowSocksConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Cloak: CloakConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Xray: XrayConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,8 +69,8 @@ PageType {
|
|||
leftImageSource: "qrc:/images/controls/folder-open.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.backup)" :
|
||||
"Config files (*.vpn *.ovpn *.conf)"
|
||||
var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.json *.backup)" :
|
||||
"Config files (*.vpn *.ovpn *.conf *.json)"
|
||||
var fileName = SystemController.getFileName(qsTr("Open config file"), nameFilter)
|
||||
if (fileName !== "") {
|
||||
if (ImportController.extractConfigFromFile(fileName)) {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ PageType {
|
|||
WireGuard,
|
||||
Awg,
|
||||
ShadowSocks,
|
||||
Cloak
|
||||
Cloak,
|
||||
Xray
|
||||
}
|
||||
|
||||
signal revokeConfig(int index)
|
||||
|
|
@ -88,6 +89,13 @@ PageType {
|
|||
shareConnectionDrawer.configFileName = "amnezia_for_cloak"
|
||||
break
|
||||
}
|
||||
case PageShare.ConfigType.Xray: {
|
||||
ExportController.generateXrayConfig()
|
||||
shareConnectionDrawer.configCaption = qsTr("Save XRay config")
|
||||
shareConnectionDrawer.configExtension = ".json"
|
||||
shareConnectionDrawer.configFileName = "amnezia_for_xray"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
PageController.showBusyIndicator(false)
|
||||
|
|
@ -136,6 +144,11 @@ PageType {
|
|||
property string name: qsTr("Cloak native format")
|
||||
property var type: PageShare.ConfigType.Cloak
|
||||
}
|
||||
QtObject {
|
||||
id: xrayConnectionFormat
|
||||
property string name: qsTr("XRay native format")
|
||||
property var type: PageShare.ConfigType.Xray
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
anchors.top: parent.top
|
||||
|
|
@ -435,6 +448,8 @@ PageType {
|
|||
root.connectionTypesModel.push(openVpnConnectionFormat)
|
||||
root.connectionTypesModel.push(shadowSocksConnectionFormat)
|
||||
root.connectionTypesModel.push(cloakConnectionFormat)
|
||||
} else if (index === ContainerProps.containerFromString("amnezia-xray")) {
|
||||
root.connectionTypesModel.push(xrayConnectionFormat)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue