mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-24 02:00:24 +07:00
feat: add vless string on sharing screen (#1999)
* feat: add vless config string and serialization * feat: add vless config string and serialization
This commit is contained in:
@@ -21,6 +21,7 @@ namespace amnezia::serialization
|
|||||||
namespace vless
|
namespace vless
|
||||||
{
|
{
|
||||||
QJsonObject Deserialize(const QString &vless, QString *alias, QString *errMessage);
|
QJsonObject Deserialize(const QString &vless, QString *alias, QString *errMessage);
|
||||||
|
const QString Serialize(const VlessServerObject &server, const QString &alias);
|
||||||
} // namespace vless
|
} // namespace vless
|
||||||
|
|
||||||
namespace ss
|
namespace ss
|
||||||
|
|||||||
@@ -42,6 +42,25 @@ struct VMessServerObject
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct VlessServerObject
|
||||||
|
{
|
||||||
|
QString address;
|
||||||
|
QString id; // UUID
|
||||||
|
int port;
|
||||||
|
QString flow = "xtls-rprx-vision";
|
||||||
|
QString encryption = "none";
|
||||||
|
QString network = "tcp";
|
||||||
|
QString security = "reality";
|
||||||
|
QString serverName; // SNI
|
||||||
|
QString publicKey;
|
||||||
|
QString shortId;
|
||||||
|
QString fingerprint = "chrome";
|
||||||
|
QString spiderX = "";
|
||||||
|
JSONSTRUCT_COMPARE(VlessServerObject, address, id, port, flow, encryption)
|
||||||
|
JSONSTRUCT_REGISTER(VlessServerObject, F(address, id, port, flow, encryption, network, security, serverName, publicKey, shortId, fingerprint, spiderX))
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
namespace transfer
|
namespace transfer
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -252,5 +252,65 @@ QJsonObject Deserialize(const QString &str, QString *alias, QString *errMessage)
|
|||||||
root["inbounds"] = QJsonArray { inbound };
|
root["inbounds"] = QJsonArray { inbound };
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
} // namespace amnezia::serialization::vless
|
|
||||||
|
const QString Serialize(const VlessServerObject &server, const QString &alias)
|
||||||
|
{
|
||||||
|
|
||||||
|
QUrl url;
|
||||||
|
|
||||||
|
// Set basic URL components
|
||||||
|
url.setScheme("vless");
|
||||||
|
url.setUserInfo(server.id);
|
||||||
|
url.setHost(server.address);
|
||||||
|
url.setPort(server.port);
|
||||||
|
|
||||||
|
QUrlQuery query;
|
||||||
|
|
||||||
|
if (!server.network.isEmpty() && server.network != "tcp") {
|
||||||
|
query.addQueryItem("type", server.network);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server.encryption.isEmpty()) {
|
||||||
|
query.addQueryItem("encryption", server.encryption);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server.security.isEmpty() && server.security != "none") {
|
||||||
|
query.addQueryItem("security", server.security);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server.flow.isEmpty() && (server.security == "xtls" || server.security == "reality")) {
|
||||||
|
query.addQueryItem("flow", server.flow);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server.serverName.isEmpty()) {
|
||||||
|
query.addQueryItem("sni", server.serverName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server.security == "reality") {
|
||||||
|
if (!server.fingerprint.isEmpty()) {
|
||||||
|
query.addQueryItem("fp", server.fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server.publicKey.isEmpty()) {
|
||||||
|
query.addQueryItem("pbk", server.publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server.shortId.isEmpty()) {
|
||||||
|
query.addQueryItem("sid", server.shortId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server.spiderX.isEmpty()) {
|
||||||
|
query.addQueryItem("spiderX", server.spiderX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
url.setQuery(query);
|
||||||
|
|
||||||
|
if (!alias.isEmpty()) {
|
||||||
|
url.setFragment(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.toString(QUrl::ComponentFormattingOption::FullyEncoded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,10 @@
|
|||||||
|
|
||||||
#include "core/controllers/vpnConfigurationController.h"
|
#include "core/controllers/vpnConfigurationController.h"
|
||||||
#include "core/qrCodeUtils.h"
|
#include "core/qrCodeUtils.h"
|
||||||
|
#include "core/serialization/serialization.h"
|
||||||
|
#include "core/serialization/transfer.h"
|
||||||
#include "systemController.h"
|
#include "systemController.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
ExportController::ExportController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
|
ExportController::ExportController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
|
||||||
const QSharedPointer<ClientManagementModel> &clientManagementModel,
|
const QSharedPointer<ClientManagementModel> &clientManagementModel,
|
||||||
@@ -249,6 +252,7 @@ void ExportController::generateCloakConfig()
|
|||||||
|
|
||||||
void ExportController::generateXrayConfig(const QString &clientName)
|
void ExportController::generateXrayConfig(const QString &clientName)
|
||||||
{
|
{
|
||||||
|
//Xray data
|
||||||
QJsonObject nativeConfig;
|
QJsonObject nativeConfig;
|
||||||
ErrorCode errorCode = generateNativeConfig(DockerContainer::Xray, clientName, Proto::Xray, nativeConfig);
|
ErrorCode errorCode = generateNativeConfig(DockerContainer::Xray, clientName, Proto::Xray, nativeConfig);
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
@@ -258,8 +262,72 @@ void ExportController::generateXrayConfig(const QString &clientName)
|
|||||||
|
|
||||||
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
||||||
for (const QString &line : std::as_const(lines)) {
|
for (const QString &line : std::as_const(lines)) {
|
||||||
m_config.append(line + "\n");
|
m_config.append(line+ "\n");
|
||||||
}
|
}
|
||||||
|
//Xray data
|
||||||
|
|
||||||
|
// Parse the Xray data to extract VLESS parameters and generate string
|
||||||
|
QString configString = QString(QJsonDocument(nativeConfig).toJson(QJsonDocument::Compact));
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(configString.toUtf8());
|
||||||
|
if (doc.isNull() || !doc.isObject()) {
|
||||||
|
qDebug() << "ERROR: Failed to parse config JSON";
|
||||||
|
emit exportErrorOccurred(ErrorCode::InternalError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject xrayConfig = doc.object();
|
||||||
|
QJsonArray outbounds = xrayConfig.value("outbounds").toArray();
|
||||||
|
|
||||||
|
if (outbounds.isEmpty()) {
|
||||||
|
qDebug() << "ERROR: Outbounds array is empty";
|
||||||
|
emit exportErrorOccurred(ErrorCode::InternalError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject outbound = outbounds[0].toObject();
|
||||||
|
QJsonObject settings = outbound.value("settings").toObject();
|
||||||
|
QJsonObject streamSettings = outbound.value("streamSettings").toObject();
|
||||||
|
|
||||||
|
QJsonArray vnext = settings.value("vnext").toArray();
|
||||||
|
if (vnext.isEmpty()) {
|
||||||
|
qDebug() << "ERROR: vnext array is empty";
|
||||||
|
emit exportErrorOccurred(ErrorCode::InternalError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject server = vnext[0].toObject();
|
||||||
|
QJsonArray users = server.value("users").toArray();
|
||||||
|
if (users.isEmpty()) {
|
||||||
|
qDebug() << "ERROR: users array is empty";
|
||||||
|
emit exportErrorOccurred(ErrorCode::InternalError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject user = users[0].toObject();
|
||||||
|
|
||||||
|
amnezia::serialization::VlessServerObject vlessServer;
|
||||||
|
vlessServer.address = server.value("address").toString();
|
||||||
|
vlessServer.port = server.value("port").toInt();
|
||||||
|
vlessServer.id = user.value("id").toString();
|
||||||
|
vlessServer.flow = user.value("flow").toString("xtls-rprx-vision");
|
||||||
|
vlessServer.encryption = user.value("encryption").toString("none");
|
||||||
|
|
||||||
|
vlessServer.network = streamSettings.value("network").toString("tcp");
|
||||||
|
vlessServer.security = streamSettings.value("security").toString("reality");
|
||||||
|
|
||||||
|
|
||||||
|
if (vlessServer.security == "reality") {
|
||||||
|
QJsonObject realitySettings = streamSettings.value("realitySettings").toObject();
|
||||||
|
vlessServer.serverName = realitySettings.value("serverName").toString();
|
||||||
|
vlessServer.publicKey = realitySettings.value("publicKey").toString();
|
||||||
|
vlessServer.shortId = realitySettings.value("shortId").toString();
|
||||||
|
vlessServer.fingerprint = realitySettings.value("fingerprint").toString("chrome");
|
||||||
|
vlessServer.spiderX = realitySettings.value("spiderX").toString("");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_nativeConfigString = amnezia::serialization::vless::Serialize(vlessServer, "AmneziaVPN");
|
||||||
|
|
||||||
|
|
||||||
emit exportConfigChanged();
|
emit exportConfigChanged();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user