mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
refactor: transferController
This commit is contained in:
@@ -4,6 +4,9 @@
|
||||
#include <QJsonParseError>
|
||||
#include <QDebug>
|
||||
#include <qeventloop.h>
|
||||
#include <QNetworkProxyFactory>
|
||||
#include <QNetworkProxyQuery>
|
||||
#include <QUrl>
|
||||
#include "core/api/apiUtils.h"
|
||||
|
||||
#include "amnezia_application.h"
|
||||
@@ -13,11 +16,27 @@
|
||||
#include "ui/controllers/importController.h"
|
||||
#include "core/api/apiDefs.h"
|
||||
#include "core/controllers/gatewayController.h"
|
||||
#include "core/errorstrings.h"
|
||||
|
||||
static ErrorCode postPlainJson(const QString& url,
|
||||
const QJsonObject& payload,
|
||||
int timeoutMs,
|
||||
QByteArray& responseBody);
|
||||
namespace {
|
||||
void logSystemProxiesForUrl(const QString &urlStr)
|
||||
{
|
||||
const QUrl url(urlStr);
|
||||
const QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery(QNetworkProxyQuery(url));
|
||||
QStringList proxyDesc;
|
||||
proxyDesc.reserve(proxies.size());
|
||||
for (const auto &p : proxies) {
|
||||
proxyDesc << QStringLiteral("%1 %2:%3")
|
||||
.arg(p.type() == QNetworkProxy::NoProxy ? QStringLiteral("NoProxy")
|
||||
: p.type() == QNetworkProxy::HttpProxy ? QStringLiteral("HttpProxy")
|
||||
: p.type() == QNetworkProxy::Socks5Proxy ? QStringLiteral("Socks5Proxy")
|
||||
: QStringLiteral("Proxy"))
|
||||
.arg(p.hostName())
|
||||
.arg(p.port());
|
||||
}
|
||||
qDebug() << "TransferController: system proxies for" << urlStr << ":" << proxyDesc;
|
||||
}
|
||||
}
|
||||
|
||||
TransferController::TransferController(const std::shared_ptr<Settings> &settings,
|
||||
const QSharedPointer<ServersModel> &serversModel,
|
||||
@@ -37,21 +56,20 @@ void TransferController::handleImportControllerDestroyed()
|
||||
TransferController::~TransferController() {
|
||||
}
|
||||
|
||||
QString TransferController::buildQrPayloadJson(const QString &gatewayUrl, const QString &uuid, int version) const
|
||||
QString TransferController::buildQrPayloadJson(const QString &gatewayUrl, const QString &uuid) const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj["gw"] = gatewayUrl;
|
||||
obj["u"] = uuid;
|
||||
obj["v"] = version;
|
||||
obj["uuid"] = uuid;
|
||||
qDebug() << "built QrPayload with GW = " << gatewayUrl
|
||||
<< " uuid = " << uuid
|
||||
<< " version = " << version;
|
||||
<< " uuid = " << uuid;
|
||||
return QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
|
||||
void TransferController::generateNewQrCode()
|
||||
{
|
||||
qDebug() << "TransferController::generateNewQrCode: generating QR code";
|
||||
// Debug mode: keep UUID/payload generation, but disable actual QR rendering (temporary).
|
||||
qDebug() << "TransferController::generateNewQrCode: generating transfer payload (QR rendering disabled)";
|
||||
|
||||
QString gw = m_settings->getGatewayEndpoint();
|
||||
if (!gw.endsWith('/')) {
|
||||
@@ -61,13 +79,20 @@ void TransferController::generateNewQrCode()
|
||||
m_currentUuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||
qDebug() << "uuid:" << m_currentUuid;
|
||||
|
||||
const QString payload = buildQrPayloadJson(gw, m_currentUuid, 1);
|
||||
m_currentPayload = buildQrPayloadJson(gw, m_currentUuid);
|
||||
|
||||
auto qr = qrCodeUtils::generateQrCode(payload.toUtf8());
|
||||
const QString svg = QString::fromStdString(toSvgString(qr, 1));
|
||||
m_qrCodeUrl = qrCodeUtils::svgToBase64(svg);
|
||||
// QR generation disabled for debugging/CLI-style copy-paste flow.
|
||||
// If/when QR is re-enabled, restore this block:
|
||||
//
|
||||
// auto qr = qrCodeUtils::generateQrCode(m_currentPayload.toUtf8());
|
||||
// const QString svg = QString::fromStdString(toSvgString(qr, 1));
|
||||
// m_qrCodeUrl = qrCodeUtils::svgToBase64(svg);
|
||||
// emit qrCodeUpdated();
|
||||
|
||||
m_qrCodeUrl.clear();
|
||||
emit qrCodeUpdated();
|
||||
emit currentUuidChanged();
|
||||
emit currentPayloadChanged();
|
||||
}
|
||||
|
||||
void TransferController::stopScanner()
|
||||
@@ -76,14 +101,7 @@ void TransferController::stopScanner()
|
||||
emit scannerShouldStop();
|
||||
}
|
||||
|
||||
QString TransferController::getPremiumConfigToSend() const
|
||||
{
|
||||
qDebug() << "TransferController:getPremiumConfigToSend() called with apiKey " << apiDefs::key::apiKey;
|
||||
//Q_UNUSED(apiDefs::key::apiKey)
|
||||
return m_exportController ? m_exportController->getConfig() : QString();
|
||||
}
|
||||
|
||||
QString TransferController::getCurrentApiKey() const
|
||||
QString TransferController::getCurrentApiKey(QString *vpnKeyOut) const
|
||||
{
|
||||
const int idx = m_serversModel ? m_serversModel->getProcessedServerIndex() : -1;
|
||||
if (idx < 0 || !m_serversModel) {
|
||||
@@ -95,23 +113,25 @@ QString TransferController::getCurrentApiKey() const
|
||||
qDebug() << "server:" << server;
|
||||
|
||||
const QJsonObject apiConfig = server.value(apiDefs::key::apiConfig).toObject();
|
||||
QJsonObject authData = server.value(QStringLiteral("auth_data")).toObject();
|
||||
//QString key = authData.value(apiDefs::key::apiKey).toString();
|
||||
QString key = authData.value(QStringLiteral("api_key")).toString();
|
||||
const QJsonObject authData = server.value(QStringLiteral("auth_data")).toObject();
|
||||
|
||||
/*if (key.isEmpty()) {
|
||||
const QJsonObject nestedAuth = apiConfig.value(QStringLiteral("auth_data")).toObject();
|
||||
if (!nestedAuth.isEmpty()) {
|
||||
key = nestedAuth.value(apiDefs::key::apiKey).toString();
|
||||
const QString apiKey = authData.value(QStringLiteral("api_key")).toString();
|
||||
|
||||
if (vpnKeyOut) {
|
||||
QString vpnKey = apiConfig.value(apiDefs::key::vpnKey).toString();
|
||||
if (vpnKey.isEmpty()) {
|
||||
// Fallback for older Premium V1 configs where vpn_key may be derived.
|
||||
vpnKey = apiUtils::getPremiumV1VpnKey(server);
|
||||
}
|
||||
}*/
|
||||
*vpnKeyOut = vpnKey;
|
||||
}
|
||||
|
||||
return key;
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
void TransferController::onTransferQrScanned(const QString &code)
|
||||
{
|
||||
qDebug() << "TransferController has scanned the Qr";
|
||||
qDebug() << "TransferController has scanned the Qr";
|
||||
|
||||
QJsonParseError err;
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(code.toUtf8(), &err);
|
||||
@@ -123,7 +143,7 @@ void TransferController::onTransferQrScanned(const QString &code)
|
||||
|
||||
const QJsonObject obj = doc.object();
|
||||
QString gw = obj.value("gw").toString();
|
||||
const QString uuid = obj.value("u").toString();
|
||||
const QString uuid = obj.value("uuid").toString();
|
||||
|
||||
if (gw.isEmpty() || uuid.isEmpty()) {
|
||||
qWarning() << "TransferController::onTransferQrScanned: QR missing gw or uuid";
|
||||
@@ -134,17 +154,15 @@ void TransferController::onTransferQrScanned(const QString &code)
|
||||
gw.append('/');
|
||||
}
|
||||
|
||||
const QString apiKey = getCurrentApiKey();
|
||||
QString vpnKey;
|
||||
const QString apiKey = getCurrentApiKey(&vpnKey);
|
||||
qDebug() << "scanned apiKey:" << apiKey;
|
||||
const QString config = getPremiumConfigToSend();
|
||||
qDebug() << "config:" << config;
|
||||
if (apiKey.isEmpty() || config.isEmpty()) {
|
||||
if (apiKey.isEmpty()) {
|
||||
qWarning() << "TransferController::onTransferQrScanned: no subscription key or config to send";
|
||||
emit postFailed(QStringLiteral("No subscription key or config to send"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow only premium (subscription) configs
|
||||
bool isPremium = m_serversModel && m_serversModel->processedServerIsPremium();
|
||||
qDebug() << "isPremium: " << isPremium;
|
||||
bool isFromGatewayApi = m_serversModel && m_serversModel->getProcessedServerData("isServerFromGatewayApi").toBool();
|
||||
@@ -157,85 +175,52 @@ void TransferController::onTransferQrScanned(const QString &code)
|
||||
|
||||
emit postStarted();
|
||||
|
||||
qDebug() << "entered POST section";
|
||||
qDebug() << "gw:" << gw;
|
||||
qDebug() << "uuid:" << uuid;
|
||||
qDebug() << "apiKey:" << apiKey;
|
||||
qDebug() << "config:" << config;
|
||||
if (vpnKey.isEmpty()) {
|
||||
qWarning() << "TransferController::onTransferQrScanned: missing vpn_key";
|
||||
emit postFailed(QStringLiteral("Missing vpn_key to send"));
|
||||
return;
|
||||
}
|
||||
|
||||
/*GatewayController gatewayController(gw, //m_settings->getGatewayEndpoint(),
|
||||
// sendConfig can take longer on slower networks / debug setups, so use a bigger timeout than generic API calls.
|
||||
const int sendTimeoutMs = 120000;
|
||||
GatewayController gatewayController(gw,
|
||||
m_settings->isDevGatewayEnv(),
|
||||
apiDefs::requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());*/
|
||||
QByteArray responseBody;
|
||||
|
||||
sendTimeoutMs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
QJsonObject payload;
|
||||
payload.insert(QStringLiteral("config"), config);
|
||||
payload.insert(QStringLiteral("uuid"), uuid);
|
||||
payload.insert(QStringLiteral("api_key"), apiKey);
|
||||
payload.insert(QStringLiteral("config"), vpnKey);
|
||||
|
||||
const QString url = gw + "sendConfig";
|
||||
qDebug() << "TransferController::onTransferQrScanned: sending POST to" << url
|
||||
<< "with payload:" << QJsonDocument(payload).toJson(QJsonDocument::Compact);
|
||||
const QString endpoint = QStringLiteral("%1v1/sendConfig");
|
||||
QByteArray responseBody;
|
||||
const QString fullUrl = endpoint.arg(gw);
|
||||
qDebug() << "TransferController::onTransferQrScanned: POST" << fullUrl
|
||||
<< "uuid:" << uuid;
|
||||
logSystemProxiesForUrl(fullUrl);
|
||||
const auto errorCode = gatewayController.post(endpoint, payload, responseBody);
|
||||
qDebug() << "TransferController::onTransferQrScanned: sendConfig finished with code"
|
||||
<< static_cast<int>(errorCode)
|
||||
<< "response size:" << responseBody.size();
|
||||
|
||||
auto ec = postPlainJson(url, payload, apiDefs::requestTimeoutMsecs, responseBody);
|
||||
qDebug() << "TransferController::onTransferQrScanned: POST finished with code"
|
||||
<< static_cast<int>(ec);
|
||||
|
||||
if (ec != ErrorCode::NoError) {
|
||||
qWarning() << "TransferController::onTransferQrScanned: network error during POST"
|
||||
<< "body:" << responseBody;
|
||||
emit postFailed(QStringLiteral("Network error"));
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
qWarning() << "TransferController::onTransferQrScanned: sendConfig failed with code" << static_cast<int>(errorCode);
|
||||
emit postFailed(QStringLiteral("sendConfig failed: %1").arg(errorString(errorCode)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse response and handle success/failure
|
||||
{
|
||||
QJsonParseError parseErr;
|
||||
const QJsonDocument respDoc = QJsonDocument::fromJson(responseBody, &parseErr);
|
||||
if (parseErr.error == QJsonParseError::NoError && respDoc.isObject()) {
|
||||
const QJsonObject respObj = respDoc.object();
|
||||
const QString status = respObj.value(QStringLiteral("status")).toString();
|
||||
if (status == QStringLiteral("success")) {
|
||||
qDebug() << "TransferController::onTransferQrScanned: gateway returned success";
|
||||
emit postSucceeded();
|
||||
stopScanner();
|
||||
return;
|
||||
}
|
||||
}
|
||||
qWarning() << "TransferController::onTransferQrScanned: gateway response error" << responseBody;
|
||||
emit postFailed(QStringLiteral("Gateway response error"));
|
||||
QJsonParseError parseErr;
|
||||
const QJsonDocument respDoc = QJsonDocument::fromJson(responseBody, &parseErr);
|
||||
if (parseErr.error == QJsonParseError::NoError && respDoc.isObject()
|
||||
&& respDoc.object().value(QStringLiteral("status")).toString() == QStringLiteral("success")) {
|
||||
emit postSucceeded();
|
||||
stopScanner();
|
||||
return;
|
||||
}
|
||||
//const QString endpoint = QStringLiteral("%1sendConfig").arg(gw);
|
||||
//const QString endpoint = QStringLiteral("sendConfig");
|
||||
|
||||
/*qDebug() << "TransferController::onTransferQrScanned: sending POST to " << endpoint
|
||||
<< "with payload: " << QJsonDocument(payload).toJson(QJsonDocument::Compact);
|
||||
auto errorCode = gatewayController.post(endpoint, payload, responseBody);
|
||||
qDebug() << "TransferController::onTransferQrScanned: POST finished with code"
|
||||
<< static_cast<int>(errorCode);
|
||||
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
QJsonParseError err;
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(responseBody, &err);
|
||||
if (err.error == QJsonParseError::NoError && doc.isObject()) {
|
||||
const QJsonObject obj = doc.object();
|
||||
if (obj.value(QStringLiteral("status")).toString() == QStringLiteral("success")) {
|
||||
qDebug() << "TransferController::onTransferQrScanned: gateway returned success";
|
||||
emit postSucceeded();
|
||||
stopScanner();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qWarning() << "TransferController::onTransferQrScanned: gateway response error";
|
||||
emit postFailed(QStringLiteral("Gateway response error"));
|
||||
} else {
|
||||
qWarning() << "TransferController::onTransferQrScanned: network error during POST";
|
||||
emit postFailed(QStringLiteral("Network error"));
|
||||
}*/
|
||||
qWarning() << "TransferController::onTransferQrScanned: unexpected gateway response:" << responseBody;
|
||||
emit postFailed(QStringLiteral("Gateway response error"));
|
||||
}
|
||||
|
||||
QString TransferController::qrCodeUrl() const
|
||||
@@ -261,58 +246,33 @@ void TransferController::startWaitForConfig(ImportController *importController)
|
||||
|
||||
m_importController = importController;
|
||||
if (m_importController) {
|
||||
connect(m_importController, &QObject::destroyed,
|
||||
connect(m_importController, &ImportController::destroyed,
|
||||
this,
|
||||
&TransferController::handleImportControllerDestroyed,
|
||||
Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
// Blocking request to /waitConfig with a timeout.
|
||||
const int waitTimeoutMs = 30000;
|
||||
|
||||
/*GatewayController gatewayController(gw, //m_settings->getGatewayEndpoint(),
|
||||
m_settings->isDevGatewayEnv(),
|
||||
waitTimeoutMs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
const QString endpoint = QStringLiteral("%1waitConfig");
|
||||
//const QString endpoint = QStringLiteral("waitConfig");
|
||||
|
||||
qDebug() << "waitConfig endpoint:" << QString(endpoint).arg(gw);*/
|
||||
const int waitTimeoutMs = 300000;
|
||||
|
||||
QJsonObject payload;
|
||||
payload.insert(QStringLiteral("uuid"), uuid);
|
||||
|
||||
GatewayController gatewayController(gw,
|
||||
m_settings->isDevGatewayEnv(),
|
||||
waitTimeoutMs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
const QString endpoint = QStringLiteral("%1v1/waitConfig");
|
||||
QByteArray responseBody;
|
||||
const QString fullUrl = endpoint.arg(gw);
|
||||
qDebug() << "TransferController::startWaitForConfig: POST" << fullUrl
|
||||
<< "uuid:" << uuid;
|
||||
logSystemProxiesForUrl(fullUrl);
|
||||
const auto errorCode = gatewayController.post(endpoint, payload, responseBody);
|
||||
|
||||
const QString url = gw + "waitConfig";
|
||||
qDebug() << "waitConfig endpoint: " << url;
|
||||
auto ec = postPlainJson(url, payload, waitTimeoutMs, responseBody);
|
||||
if (ec != ErrorCode::NoError) {
|
||||
qWarning() << "waitConfig failed, code:" << (int)ec << "body:" << responseBody;
|
||||
emit waitError(QStringLiteral("Network error"));
|
||||
return;
|
||||
}
|
||||
|
||||
/*auto errorCode = gatewayController.post(endpoint, payload, responseBody);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
qWarning() << "TransferController::startWaitForConfig: network error during waitConfig";
|
||||
emit waitError(QStringLiteral("Network error"));
|
||||
return;
|
||||
}*/
|
||||
|
||||
QJsonParseError err;
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(responseBody, &err);
|
||||
if (err.error != QJsonParseError::NoError ||!doc.isObject()) {
|
||||
qWarning() << "TransferController::startWaitForConfig: invalid gateway JSON";
|
||||
emit waitError(QStringLiteral("Gateway Response Error"));
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonObject obj = doc.object();
|
||||
QString cfg = obj.value(QStringLiteral("config")).toString();
|
||||
|
||||
if (cfg.isEmpty() || cfg == QStringLiteral("timeout")) {
|
||||
qWarning() << "TransferController::startWaitForConfig: timeout or empty config";
|
||||
emit waitError(QStringLiteral("Gateway response error (timeout)"));
|
||||
qWarning() << "TransferController::startWaitForConfig: waitConfig failed with code" << static_cast<int>(errorCode);
|
||||
emit waitError(QStringLiteral("waitConfig failed (%1)").arg(static_cast<int>(errorCode)));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -322,40 +282,41 @@ void TransferController::startWaitForConfig(ImportController *importController)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_importController->extractConfigFromData(cfg)) {
|
||||
qWarning() << "TransferController::startWaitForConfig: invalid config received from gateway";
|
||||
emit waitError(QStringLiteral("Invalid config received from gateway"));
|
||||
QJsonParseError parseErr;
|
||||
const QJsonDocument respDoc = QJsonDocument::fromJson(responseBody, &parseErr);
|
||||
if (parseErr.error != QJsonParseError::NoError || !respDoc.isObject()) {
|
||||
qWarning() << "TransferController::startWaitForConfig: invalid JSON response:" << responseBody;
|
||||
emit waitError(QStringLiteral("Invalid gateway response"));
|
||||
return;
|
||||
}
|
||||
const QJsonObject respObj = respDoc.object();
|
||||
const QString status = respObj.value(QStringLiteral("status")).toString();
|
||||
const QString configStr = respObj.value(QStringLiteral("config")).toString();
|
||||
if (status != QStringLiteral("success")) {
|
||||
qWarning() << "TransferController::startWaitForConfig: gateway status not success:" << status;
|
||||
emit waitError(QStringLiteral("Gateway error"));
|
||||
return;
|
||||
}
|
||||
if (configStr.isEmpty()) {
|
||||
emit waitError(QStringLiteral("Empty config"));
|
||||
return;
|
||||
}
|
||||
if (configStr == QStringLiteral("timeout")) {
|
||||
emit waitError(QStringLiteral("Timeout"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_importController->extractConfigFromData(configStr)) {
|
||||
qWarning() << "TransferController::startWaitForConfig: failed to parse config string";
|
||||
emit waitError(QStringLiteral("Invalid config payload"));
|
||||
return;
|
||||
}
|
||||
|
||||
m_importController->importConfig();
|
||||
emit configApplied();
|
||||
}
|
||||
|
||||
void TransferController::stopWaitForConfig()
|
||||
{
|
||||
qDebug() << "TransferController::stopWaitForConfig: stop flag set";
|
||||
}
|
||||
|
||||
static ErrorCode postPlainJson(const QString& url, const QJsonObject& payload, int timeoutMs, QByteArray& responseBody)
|
||||
{
|
||||
QNetworkRequest request;
|
||||
request.setTransferTimeout(timeoutMs);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
request.setUrl(QUrl(url));
|
||||
|
||||
QNetworkReply* reply = amnApp->networkManager()->post(request, QJsonDocument(payload).toJson(QJsonDocument::Compact));
|
||||
|
||||
QEventLoop wait;
|
||||
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||
|
||||
QList<QSslError> sslErrors;
|
||||
QObject::connect(reply, &QNetworkReply::sslErrors, [&sslErrors](const QList<QSslError>& errors){ sslErrors = errors; });
|
||||
|
||||
wait.exec();
|
||||
responseBody = reply->readAll();
|
||||
|
||||
auto ec = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
|
||||
reply->deleteLater();
|
||||
return ec;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QUuid>
|
||||
|
||||
#include "core/qrCodeUtils.h"
|
||||
|
||||
class Settings;
|
||||
class ServersModel;
|
||||
class ExportController;
|
||||
@@ -20,6 +18,9 @@ class TransferController : public QObject
|
||||
|
||||
Q_PROPERTY(QString qrCodeUrl READ qrCodeUrl NOTIFY qrCodeUpdated)
|
||||
Q_PROPERTY(QString pendingQrCode READ pendingQrCode WRITE setPendingQrCode NOTIFY pendingQrCodeChanged)
|
||||
// Debug / manual transfer support (temporary): expose UUID and payload JSON used to generate the QR code.
|
||||
Q_PROPERTY(QString currentUuid READ currentUuid NOTIFY currentUuidChanged)
|
||||
Q_PROPERTY(QString currentPayload READ currentPayload NOTIFY currentPayloadChanged)
|
||||
|
||||
public:
|
||||
explicit TransferController(const std::shared_ptr<Settings> &settings,
|
||||
@@ -41,11 +42,15 @@ public:
|
||||
Q_INVOKABLE void stopWaitForConfig();
|
||||
|
||||
QString qrCodeUrl() const;
|
||||
QString currentUuid() const { return m_currentUuid; }
|
||||
QString currentPayload() const { return m_currentPayload; }
|
||||
|
||||
signals:
|
||||
void qrCodeUpdated();
|
||||
void scannerShouldStop();
|
||||
void pendingQrCodeChanged();
|
||||
void currentUuidChanged();
|
||||
void currentPayloadChanged();
|
||||
|
||||
void waitError(const QString &message);
|
||||
void configApplied();
|
||||
@@ -58,10 +63,10 @@ private slots:
|
||||
void handleImportControllerDestroyed();
|
||||
|
||||
private:
|
||||
QString buildQrPayloadJson(const QString &gatewayUrl, const QString &uuid, int version) const;
|
||||
QString getPremiumConfigToSend() const;
|
||||
QString buildQrPayloadJson(const QString &gatewayUrl, const QString &uuid) const;
|
||||
//QString getPremiumConfigToSend() const;
|
||||
QString m_pendingQrCode;
|
||||
QString getCurrentApiKey() const;
|
||||
QString getCurrentApiKey(QString *vpnKeyOut = nullptr) const;
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
QSharedPointer<ServersModel> m_serversModel;
|
||||
ExportController *m_exportController { nullptr };
|
||||
@@ -69,6 +74,7 @@ private:
|
||||
|
||||
QString m_qrCodeUrl;
|
||||
QString m_currentUuid;
|
||||
QString m_currentPayload;
|
||||
};
|
||||
|
||||
#endif // TRANSFERCONTROLLER_H
|
||||
|
||||
@@ -14,6 +14,9 @@ import "../Config"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
// Debug-friendly: don't auto-close on send; keep the page responsive and show status.
|
||||
property bool isSending: false
|
||||
|
||||
function getAvailableCount() {
|
||||
var max = ApiAccountInfoModel.data("maxDeviceCount")
|
||||
var active = ApiAccountInfoModel.data("activeDeviceCount")
|
||||
@@ -32,7 +35,16 @@ PageType {
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BackButtonType {}
|
||||
BackButtonType {
|
||||
backButtonFunction: function() {
|
||||
if (root.isSending) {
|
||||
// We cannot truly abort GatewayController::post() without changing it,
|
||||
// but we can at least stop waiting on the other side and let the user navigate.
|
||||
TransferController.stopWaitForConfig()
|
||||
}
|
||||
PageController.closePage()
|
||||
}
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
@@ -50,13 +62,13 @@ PageType {
|
||||
Layout.topMargin: 16
|
||||
|
||||
text: qsTr("Yes, share")
|
||||
enabled: root.getAvailableCount() > 0 && TransferController.pendingQrCode !== ""
|
||||
enabled: !root.isSending && root.getAvailableCount() > 0 && TransferController.pendingQrCode !== ""
|
||||
|
||||
clickedFunc: function() {
|
||||
if (TransferController.pendingQrCode !== "") {
|
||||
root.isSending = true
|
||||
TransferController.onTransferQrScanned(TransferController.pendingQrCode)
|
||||
}
|
||||
PageController.closePage()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +86,7 @@ PageType {
|
||||
borderWidth: 1
|
||||
|
||||
text: qsTr("Cancel")
|
||||
enabled: !root.isSending
|
||||
|
||||
clickedFunc: function() {
|
||||
PageController.closePage()
|
||||
@@ -86,14 +99,17 @@ PageType {
|
||||
target: TransferController
|
||||
|
||||
function onPostStarted() {
|
||||
PageController.showInfoMessage(qsTr("Sending configuration..."))
|
||||
PageController.showNotificationMessage(qsTr("Sending configuration..."))
|
||||
}
|
||||
|
||||
function onPostSucceeded() {
|
||||
PageController.showInfoMessage(qsTr("Configuration sent successfully"))
|
||||
root.isSending = false
|
||||
PageController.showNotificationMessage(qsTr("Configuration sent successfully"))
|
||||
PageController.closePage()
|
||||
}
|
||||
|
||||
function onPostFailed(message) {
|
||||
root.isSending = false
|
||||
PageController.showErrorMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import PageEnum 1.0
|
||||
import QRCodeReader 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
@@ -35,7 +34,7 @@ PageType {
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
text: qsTr("Point the camera at the QR code and hold it for a couple of seconds. ") + progressString
|
||||
text: qsTr("Debug mode: paste payload JSON from the other device (or enter UUID). ") + progressString
|
||||
}
|
||||
|
||||
ProgressBarType {
|
||||
@@ -49,7 +48,7 @@ PageType {
|
||||
anchors.rightMargin: 16
|
||||
}
|
||||
|
||||
// Manual UUID input (temporary instead of camera scanning)
|
||||
// Debug: manual payload/UUID input (temporary instead of camera scanning)
|
||||
ColumnLayout {
|
||||
id: manualInput
|
||||
anchors.left: parent.left
|
||||
@@ -63,6 +62,14 @@ PageType {
|
||||
anchors.bottomMargin: 24
|
||||
spacing: 12
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: payloadField
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("Payload JSON")
|
||||
textField.placeholderText: qsTr("Paste JSON like {\"gw\":\"...\",\"uuid\":\"...\"}")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: uuidField
|
||||
Layout.fillWidth: true
|
||||
@@ -72,9 +79,28 @@ PageType {
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: sendButton
|
||||
id: usePayloadButton
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Send")
|
||||
text: qsTr("Next (use payload)")
|
||||
enabled: payloadField.textField.text.length > 0
|
||||
|
||||
clickedFunc: function() {
|
||||
TransferController.setPendingQrCode(payloadField.textField.text)
|
||||
PageController.goToPage(PageEnum.PageSettingsApiAddDeviceConfirm)
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: buildFromUuidButton
|
||||
Layout.fillWidth: true
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
text: qsTr("Next (build from UUID)")
|
||||
enabled: uuidField.textField.text.length > 0
|
||||
|
||||
clickedFunc: function() {
|
||||
@@ -94,7 +120,7 @@ PageType {
|
||||
gw = gw + "/"
|
||||
}
|
||||
|
||||
var payload = JSON.stringify({ gw: gw, u: uuidField.textField.text, v: 1 })
|
||||
var payload = JSON.stringify({ gw: gw, uuid: uuidField.textField.text })
|
||||
TransferController.setPendingQrCode(payload)
|
||||
PageController.goToPage(PageEnum.PageSettingsApiAddDeviceConfirm)
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ PageType {
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 16
|
||||
|
||||
visible: GC.isMobile()
|
||||
//visible: GC.isMobile()
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
|
||||
@@ -7,7 +7,6 @@ import PageEnum 1.0
|
||||
import Style 1.0
|
||||
import "../Controls2"
|
||||
import "../Components"
|
||||
import QRCodeReader 1.0
|
||||
import "../Controls2/TextTypes"
|
||||
|
||||
PageType {
|
||||
@@ -58,31 +57,56 @@ PageType {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
text: qsTr("Scan the QR code with the phone where Amnezia Premium is active")
|
||||
text: qsTr("Debug mode: copy the payload to the other device and send it there")
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: qrContainer
|
||||
ColumnLayout {
|
||||
id: debugPayload
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: qrHeader.bottom
|
||||
anchors.topMargin: 8
|
||||
// anchors.horizontalCenter: parent.horizontalCenter // УБРАТЬ
|
||||
anchors.topMargin: 12
|
||||
anchors.bottom: bottomHint.top
|
||||
anchors.bottomMargin: 8
|
||||
anchors.bottomMargin: 12
|
||||
spacing: 8
|
||||
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.color: AmneziaStyle.color.paleGray
|
||||
border.width: 1
|
||||
TextArea {
|
||||
id: payloadArea
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
readOnly: true
|
||||
wrapMode: TextArea.WrapAnywhere
|
||||
selectByMouse: true
|
||||
text: TransferController.currentPayload
|
||||
placeholderText: qsTr("Payload will appear here after generating")
|
||||
}
|
||||
|
||||
Image {
|
||||
id: qrImage
|
||||
anchors.centerIn: parent
|
||||
width: Math.min(parent.width, parent.height)
|
||||
height: width
|
||||
source: TransferController.qrCodeUrl
|
||||
fillMode: Image.Stretch
|
||||
smooth: false
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
|
||||
BasicButtonType {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Regenerate")
|
||||
clickedFunc: function() {
|
||||
TransferController.generateNewQrCode()
|
||||
TransferController.startWaitForConfig(ImportController)
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
Layout.fillWidth: true
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
text: qsTr("Restart wait")
|
||||
clickedFunc: function() {
|
||||
TransferController.startWaitForConfig(ImportController)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +116,7 @@ PageType {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 8
|
||||
text: qsTr("In the Amnezia app on your phone, tap «+» at the bottom → «QR code»")
|
||||
text: qsTr("Copy the JSON payload above, paste it on the other device, and confirm sending there")
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
@@ -100,7 +124,6 @@ PageType {
|
||||
|
||||
Connections {
|
||||
target: TransferController
|
||||
function onQrCodeUpdated() { qrImage.source = TransferController.qrCodeUrl }
|
||||
function onConfigApplied() {
|
||||
PageController.showNotificationMessage(qsTr("Configuration received and applied"))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user