feat: fix AWG auto-switch — isAutoMode guard, V1/ExternalPremium coverage, reconnect on container update

This commit is contained in:
NickVs2015
2026-05-26 14:02:37 +03:00
parent 586e1f0b71
commit cff1e2962c
9 changed files with 127 additions and 135 deletions
@@ -418,7 +418,9 @@ ErrorCode SubscriptionController::updateServiceFromGateway(const QString &server
}
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
QString serviceProtocol = apiV2->serviceProtocol();
ProtocolData protocolData = generateProtocolData(serviceProtocol);
// Auto mode (empty) defaults to AWG — gateway requires public_key for all requests
const QString effectiveProtocol = serviceProtocol.isEmpty() ? configKey::awg : serviceProtocol;
ProtocolData protocolData = generateProtocolData(effectiveProtocol);
QJsonObject authDataJson = apiV2->authData.toJson();
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
@@ -432,7 +434,7 @@ ErrorCode SubscriptionController::updateServiceFromGateway(const QString &server
authDataJson };
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
appendProtocolDataToApiPayload(effectiveProtocol, protocolData, apiPayload);
if (isConnectEvent) {
apiPayload[apiDefs::key::isConnectEvent] = true;
@@ -451,7 +453,7 @@ ErrorCode SubscriptionController::updateServiceFromGateway(const QString &server
}
QJsonObject serverConfigJson;
errorCode = extractServerConfigJsonFromResponse(responseBody, serviceProtocol, protocolData, serverConfigJson);
errorCode = extractServerConfigJsonFromResponse(responseBody, effectiveProtocol, protocolData, serverConfigJson);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
+7 -6
View File
@@ -226,17 +226,18 @@ void CoreController::initControllers()
setQmlContextProperty("SubscriptionUiController", m_subscriptionUiController);
connect(m_connectionUiController, &ConnectionUiController::requestSetCurrentProtocol,
this, [this](const QString &protocol) {
const QString serverId = m_serversController->getDefaultServerId();
if (!serverId.isEmpty()) {
m_subscriptionUiController->setCurrentProtocol(serverId, protocol);
}
}, Qt::QueuedConnection);
m_subscriptionUiController, &SubscriptionUiController::setCurrentProtocol, Qt::QueuedConnection);
connect(m_connectionUiController, &ConnectionUiController::requestUpdateServiceFromGateway,
m_subscriptionUiController, &SubscriptionUiController::updateServiceFromGateway, Qt::QueuedConnection);
connect(m_subscriptionUiController, &SubscriptionUiController::updateServiceFromGatewayCompleted,
m_connectionUiController, &ConnectionUiController::onUpdateServiceFromGatewayCompleted,
Qt::QueuedConnection);
connect(m_connectionUiController, &ConnectionUiController::requestSetProcessedServer,
this, [this](const QString &serverId) {
m_serversUiController->setProcessedServerIndex(m_serversController->indexOfServerId(serverId));
}, Qt::QueuedConnection);
connect(m_installUiController, &InstallUiController::currentContainerUpdated,
m_connectionUiController, &ConnectionUiController::onCurrentContainerUpdated);
m_apiNewsUiController = new ApiNewsUiController(m_newsModel, m_newsController, this);
setQmlContextProperty("ApiNewsController", m_apiNewsUiController);
+22
View File
@@ -82,11 +82,33 @@
#endif
class CoreSignalHandlers;
class TestMultipleImports;
class TestAdminSelfHostedExport;
class TestServerEdit;
class TestDefaultServerChange;
class TestServerEdgeCases;
class TestSignalOrder;
class TestServersModelSync;
class TestComplexOperations;
class TestSettingsSignals;
class TestUiServersModelAndController;
class TestSelfHostedServerSetup;
class CoreController : public QObject
{
Q_OBJECT
friend class CoreSignalHandlers;
friend class TestMultipleImports;
friend class TestAdminSelfHostedExport;
friend class TestServerEdit;
friend class TestDefaultServerChange;
friend class TestServerEdgeCases;
friend class TestSignalOrder;
friend class TestServersModelSync;
friend class TestComplexOperations;
friend class TestSettingsSignals;
friend class TestUiServersModelAndController;
friend class TestSelfHostedServerSetup;
public:
explicit CoreController(const QSharedPointer<VpnConnection> &vpnConnection, SecureQSettings* settings,
+20 -3
View File
@@ -161,7 +161,12 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
return encRequestData.errorCode;
}
QNetworkReply *reply = amnApp->networkManager()->post(encRequestData.request, encRequestData.requestBody);
QNetworkAccessManager *nam = amnApp ? amnApp->networkManager() : nullptr;
if (!nam) {
return ErrorCode::InternalError;
}
QNetworkReply *reply = nam->post(encRequestData.request, encRequestData.requestBody);
QEventLoop wait;
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
@@ -236,7 +241,14 @@ QFuture<QPair<ErrorCode, QByteArray>> GatewayController::postAsync(const QString
return promise->future();
}
QNetworkReply *reply = amnApp->networkManager()->post(encRequestData.request, encRequestData.requestBody);
QNetworkAccessManager *nam = amnApp ? amnApp->networkManager() : nullptr;
if (!nam) {
promise->addResult(qMakePair(ErrorCode::InternalError, QByteArray()));
promise->finish();
return promise->future();
}
QNetworkReply *reply = nam->post(encRequestData.request, encRequestData.requestBody);
auto sslErrors = QSharedPointer<QList<QSslError>>::create();
@@ -378,9 +390,14 @@ QStringList GatewayController::getProxyUrls(const QString &serviceType, const QS
return {};
}
QNetworkAccessManager *nam = amnApp ? amnApp->networkManager() : nullptr;
if (!nam) {
return {};
}
for (const auto &proxyStorageUrl : proxyStorageUrls) {
request.setUrl(proxyStorageUrl);
reply = amnApp->networkManager()->get(request);
reply = nam->get(request);
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
@@ -170,6 +170,16 @@ bool ConnectionUiController::isConnected() const
return m_isConnected;
}
void ConnectionUiController::onCurrentContainerUpdated()
{
if (m_isConnected || m_isConnectionInProgress) {
emit reconnectWithUpdatedContainer(tr("Settings updated successfully, reconnecting..."));
openConnection();
} else {
emit reconnectWithUpdatedContainer(tr("Settings updated successfully"));
}
}
void ConnectionUiController::checkAndStartAwgStateTimer()
{
const QString serverId = m_serversController->getDefaultServerId();
@@ -184,9 +194,18 @@ void ConnectionUiController::checkAndStartAwgStateTimer()
const Proto proto = ContainerUtils::defaultProtocol(container);
if (proto == Proto::Awg) {
const auto v2Config = m_serversController->apiV2Config(serverId);
if (v2Config.has_value() && v2Config->isPremium()) {
if (v2Config.has_value() && (v2Config->isPremium() || v2Config->isExternalPremium())) {
const bool isAutoMode = v2Config->serviceProtocol().isEmpty();
if (isAutoMode) {
if (!m_awgStateTimer.isActive()) {
m_awgStateTimer.start(10000);
m_awgStateTimer.start(kAwgSwitchTimeoutMs);
}
return;
}
}
else if (m_serversController->isLegacyApiV1Server(serverId)) {
if (!m_awgStateTimer.isActive()) {
m_awgStateTimer.start(kAwgSwitchTimeoutMs);
}
return;
}
@@ -226,7 +245,8 @@ void ConnectionUiController::onAwgStateTimeout()
m_apiSwitched = false;
m_waitingForApiUpdate = true;
emit requestSetCurrentProtocol(QStringLiteral("vless"));
emit requestSetProcessedServer(serverId);
emit requestSetCurrentProtocol(serverId, QStringLiteral("vless"));
emit requestUpdateServiceFromGateway(serverId, QString(), QString(), true);
});
}
@@ -245,6 +265,7 @@ void ConnectionUiController::onUpdateServiceFromGatewayCompleted(bool success, c
if (containersMap.contains(DockerContainer::Xray)) {
qDebug().noquote() << "AWG connect timeout (10s), switching default container to Xray and reconnecting";
m_serversController->setDefaultContainer(serverId, DockerContainer::Xray);
emit requestSetCurrentProtocol(serverId, QStringLiteral("vless"));
m_pendingApiServerId.clear();
if (!m_isConnected && !m_isConnectionInProgress) {
@@ -44,6 +44,7 @@ public slots:
public slots:
void checkAndStartAwgStateTimer();
void onUpdateServiceFromGatewayCompleted(bool success, const QString &serverId);
void onCurrentContainerUpdated();
private slots:
void onAwgStateTimeout();
@@ -57,13 +58,17 @@ signals:
void preparingConfig();
void prepareConfig();
void requestSetCurrentProtocol(const QString &protocol);
// serverId + protocol — both carried so the receiver doesn't need to re-read default server
void requestSetCurrentProtocol(const QString &serverId, const QString &protocol);
void requestUpdateServiceFromGateway(const QString &serverId, const QString &newCountryCode,
const QString &newCountryName, bool reloadServiceConfig);
void requestSetProcessedServer(const QString &serverId);
private:
Vpn::ConnectionState getCurrentConnectionState();
static constexpr int kAwgSwitchTimeoutMs = 10000;
QTimer m_awgStateTimer;
ConnectionController* m_connectionController;
ServersController* m_serversController;
@@ -263,47 +263,16 @@ PageType {
&& root.isSubscriptionRenewalAvailable && !root.isInAppPurchase
}
SwitcherType {
id: switcher
readonly property bool isVlessProtocol: SubscriptionUiController.isVlessProtocol(ServersUiController.processedServerId)
readonly property bool isProtocolSwitchBlocked: ServersUiController.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
Layout.fillWidth: true
Layout.topMargin: 24
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 24
visible: ApiAccountInfoModel.data("isProtocolSelectionSupported")
enabled: !switcher.isProtocolSwitchBlocked
text: qsTr("Use VLESS protocol")
checked: switcher.isVlessProtocol
onToggled: function() {
if (ServersUiController.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot change protocol during active connection"))
} else {
PageController.showBusyIndicator(true)
SubscriptionUiController.setCurrentProtocol(ServersUiController.processedServerId, switcher.isVlessProtocol ? "awg" : "vless")
SubscriptionUiController.updateServiceFromGateway(ServersUiController.processedServerId, "", "", true)
PageController.showBusyIndicator(false)
}
}
}
DividerType {
visible: footer.isVisibleForAmneziaFree
}
WarningType {
id: warning
Layout.topMargin: 24
Layout.topMargin: visible ? 24 : 0
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.fillWidth: true
Layout.preferredHeight: visible ? implicitHeight : 0
backGroundColor: AmneziaStyle.color.translucentRichBrown
@@ -324,7 +293,7 @@ PageType {
id: connectionSwitcher
Layout.fillWidth: true
Layout.topMargin: warning.visible ? 16 : 32
Layout.topMargin: warning.visible ? 16 : 0
text: qsTr("Connection")
descriptionText: qsTr("Protocol selection and local proxy setup")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
@@ -12,56 +12,18 @@ import "../Config"
PageType {
id: root
// Protocol to re-assert after updateServiceFromGateway completes (empty = auto)
property string pendingProtocol: ""
property bool waitingForGatewayUpdate: false
Timer {
id: updateProtocolTimer
interval: 100
repeat: false
property string savedProtocol: ""
onTriggered: {
if (!root || !root.visible) {
PageController.showBusyIndicator(false)
return
}
SubscriptionUiController.updateServiceFromGateway(ServersUiController.getServerId(ServersUiController.processedServerIndex), "", "", true)
// After update, restore the protocol we set (because updateServiceFromGateway may overwrite it)
if (savedProtocol !== "") {
Qt.callLater(function() {
try {
if (!root || !root.visible) {
PageController.showBusyIndicator(false)
return
}
var protocolToSet = savedProtocol === "auto" ? "" : savedProtocol
SubscriptionUiController.setCurrentProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex), protocolToSet)
PageController.showBusyIndicator(false)
Qt.callLater(function() {
try {
if (root && root.visible && typeof root.getCurrentProtocol === "function") {
root.currentProtocol = root.getCurrentProtocol()
}
} catch (e) {
console.log("Error updating protocol display:", e)
}
})
} catch (e) {
console.log("Error in updateProtocolTimer:", e)
PageController.showBusyIndicator(false)
}
})
} else {
PageController.showBusyIndicator(false)
Qt.callLater(function() {
try {
if (root && root.visible && typeof root.getCurrentProtocol === "function") {
root.currentProtocol = root.getCurrentProtocol()
}
} catch (e) {
console.log("Error updating protocol display:", e)
}
})
}
var serverId = ServersUiController.getServerId(ServersUiController.processedServerIndex)
root.waitingForGatewayUpdate = true
SubscriptionUiController.updateServiceFromGateway(serverId, "", "", true)
}
}
@@ -106,19 +68,21 @@ PageType {
Connections {
target: SubscriptionUiController
function onUpdateServerFromApiFinished() {
if (!root || !root.visible) {
if (!root.waitingForGatewayUpdate) {
return
}
Qt.callLater(function() {
try {
if (root && root.visible && typeof root.getCurrentProtocol === "function") {
root.waitingForGatewayUpdate = false
// Re-assert the protocol the user chose (gateway reload may have reset it)
if (root.pendingProtocol !== "") {
var protocolToSet = root.pendingProtocol === "auto" ? "" : root.pendingProtocol
SubscriptionUiController.setCurrentProtocol(
ServersUiController.getServerId(ServersUiController.processedServerIndex),
protocolToSet)
}
root.currentProtocol = root.getCurrentProtocol()
}
} catch (e) {
console.log("Error in ApiConfigsController.onUpdateServerFromApiFinished:", e)
}
})
}
}
Component.onCompleted: {
@@ -203,9 +167,8 @@ PageType {
return
}
PageController.showBusyIndicator(true)
SubscriptionUiController.setCurrentProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex), "")
updateProtocolTimer.savedProtocol = "auto"
root.pendingProtocol = "auto"
updateProtocolTimer.start()
}
@@ -234,11 +197,10 @@ PageType {
return
}
PageController.showBusyIndicator(true)
SubscriptionUiController.setCurrentProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex), "awg")
updateProtocolTimer.savedProtocol = "awg"
updateProtocolTimer.start()
root.currentProtocol = "awg"
root.pendingProtocol = "awg"
SubscriptionUiController.setCurrentProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex), "awg")
updateProtocolTimer.start()
}
Keys.onEnterPressed: this.clicked()
@@ -266,11 +228,10 @@ PageType {
return
}
PageController.showBusyIndicator(true)
SubscriptionUiController.setCurrentProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex), "vless")
updateProtocolTimer.savedProtocol = "vless"
updateProtocolTimer.start()
root.currentProtocol = "vless"
root.pendingProtocol = "vless"
SubscriptionUiController.setCurrentProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex), "vless")
updateProtocolTimer.start()
}
Keys.onEnterPressed: this.clicked()
@@ -68,25 +68,19 @@ PageType {
}
}
DividerType {}
LabelWithButtonType {
id: localProxyButton
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Local proxy")
descriptionText: qsTr("Running: 127.0.0.1:1080")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
// Placeholder - no action
}
}
DividerType {}
// TODO: Local proxy
// DividerType {}
// LabelWithButtonType {
// id: localProxyButton
// Layout.fillWidth: true
// Layout.leftMargin: 16
// Layout.rightMargin: 16
// text: qsTr("Local proxy")
// descriptionText: qsTr("Running: 127.0.0.1:1080")
// rightImageSource: "qrc:/images/controls/chevron-right.svg"
// clickedFunction: function() {}
// }
// DividerType {}
}
}
}