feat: add auto switch from AWG to Xray (only premium)

This commit is contained in:
NickVs2015
2025-12-10 19:01:35 +03:00
parent a1e28ba9af
commit d7f301ea3b
4 changed files with 99 additions and 31 deletions
@@ -436,6 +436,7 @@ bool SubscriptionUiController::updateServiceFromGateway(const QString &serverId,
} else { } else {
emit changeApiCountryFinished(tr("Successfully changed the country of connection to %1").arg(newCountryName)); emit changeApiCountryFinished(tr("Successfully changed the country of connection to %1").arg(newCountryName));
} }
emit updateServiceFromGatewayCompleted(true, serverId);
return true; return true;
} else { } else {
if (errorCode == ErrorCode::ApiSubscriptionExpiredError) { if (errorCode == ErrorCode::ApiSubscriptionExpiredError) {
@@ -443,6 +444,7 @@ bool SubscriptionUiController::updateServiceFromGateway(const QString &serverId,
} else { } else {
emit errorOccurred(errorCode); emit errorOccurred(errorCode);
} }
emit updateServiceFromGatewayCompleted(false, serverId);
return false; return false;
} }
} }
@@ -83,6 +83,7 @@ signals:
void changeApiCountryFinished(const QString &message); void changeApiCountryFinished(const QString &message);
void reloadServerFromApiFinished(const QString &message); void reloadServerFromApiFinished(const QString &message);
void updateServerFromApiFinished(); void updateServerFromApiFinished();
void updateServiceFromGatewayCompleted(bool success, const QString &serverId);
void subscriptionRefreshNeeded(); void subscriptionRefreshNeeded();
void apiConfigRemoved(const QString &message); void apiConfigRemoved(const QString &message);
@@ -60,7 +60,9 @@ void ConnectionUiController::onConnectionStateChanged(Vpn::ConnectionState state
m_connectionStateText = tr("Connecting..."); m_connectionStateText = tr("Connecting...");
switch (state) { switch (state) {
case Vpn::ConnectionState::Connected: { case Vpn::ConnectionState::Connected: {
m_awgStateTimer.stop(); if (m_awgStateTimer.isActive()) {
m_awgStateTimer.stop();
}
amnApp->networkManager()->clearConnectionCache(); amnApp->networkManager()->clearConnectionCache();
m_isConnectionInProgress = false; m_isConnectionInProgress = false;
@@ -69,54 +71,55 @@ void ConnectionUiController::onConnectionStateChanged(Vpn::ConnectionState state
break; break;
} }
case Vpn::ConnectionState::Connecting: { case Vpn::ConnectionState::Connecting: {
{ checkAndStartAwgStateTimer();
const QString serverId = m_serversController->getDefaultServerId();
if (!serverId.isEmpty()) {
const DockerContainer container = m_serversController->getDefaultContainer(serverId);
const Proto proto = ContainerUtils::defaultProtocol(container);
if (proto == Proto::Awg) {
m_awgStateTimer.start(10000);
} else {
m_awgStateTimer.stop();
}
}
}
m_isConnectionInProgress = true; m_isConnectionInProgress = true;
break; break;
} }
case Vpn::ConnectionState::Reconnecting: { case Vpn::ConnectionState::Reconnecting: {
m_awgStateTimer.stop(); if (m_awgStateTimer.isActive()) {
m_awgStateTimer.stop();
}
m_isConnectionInProgress = true; m_isConnectionInProgress = true;
m_connectionStateText = tr("Reconnecting..."); m_connectionStateText = tr("Reconnecting...");
break; break;
} }
case Vpn::ConnectionState::Disconnected: { case Vpn::ConnectionState::Disconnected: {
m_awgStateTimer.stop(); if (m_awgStateTimer.isActive()) {
m_awgStateTimer.stop();
}
m_isConnectionInProgress = false; m_isConnectionInProgress = false;
m_connectionStateText = tr("Connect"); m_connectionStateText = tr("Connect");
break; break;
} }
case Vpn::ConnectionState::Disconnecting: { case Vpn::ConnectionState::Disconnecting: {
m_awgStateTimer.stop(); if (m_awgStateTimer.isActive()) {
m_awgStateTimer.stop();
}
m_isConnectionInProgress = true; m_isConnectionInProgress = true;
m_connectionStateText = tr("Disconnecting..."); m_connectionStateText = tr("Disconnecting...");
break; break;
} }
case Vpn::ConnectionState::Preparing: { case Vpn::ConnectionState::Preparing: {
m_awgStateTimer.stop(); if (m_awgStateTimer.isActive()) {
m_awgStateTimer.stop();
}
m_isConnectionInProgress = true; m_isConnectionInProgress = true;
m_connectionStateText = tr("Preparing..."); m_connectionStateText = tr("Preparing...");
break; break;
} }
case Vpn::ConnectionState::Error: { case Vpn::ConnectionState::Error: {
m_awgStateTimer.stop(); if (m_awgStateTimer.isActive()) {
m_awgStateTimer.stop();
}
m_isConnectionInProgress = false; m_isConnectionInProgress = false;
m_connectionStateText = tr("Connect"); m_connectionStateText = tr("Connect");
emit connectionErrorOccurred(getLastConnectionError()); emit connectionErrorOccurred(getLastConnectionError());
break; break;
} }
case Vpn::ConnectionState::Unknown: { case Vpn::ConnectionState::Unknown: {
m_awgStateTimer.stop(); if (m_awgStateTimer.isActive()) {
m_awgStateTimer.stop();
}
m_isConnectionInProgress = false; m_isConnectionInProgress = false;
m_connectionStateText = tr("Connect"); m_connectionStateText = tr("Connect");
emit connectionErrorOccurred(getLastConnectionError()); emit connectionErrorOccurred(getLastConnectionError());
@@ -167,6 +170,32 @@ bool ConnectionUiController::isConnected() const
return m_isConnected; return m_isConnected;
} }
void ConnectionUiController::checkAndStartAwgStateTimer()
{
const QString serverId = m_serversController->getDefaultServerId();
if (serverId.isEmpty()) {
if (m_awgStateTimer.isActive()) {
m_awgStateTimer.stop();
}
return;
}
const DockerContainer container = m_serversController->getDefaultContainer(serverId);
const Proto proto = ContainerUtils::defaultProtocol(container);
if (proto == Proto::Awg) {
const auto v2Config = m_serversController->apiV2Config(serverId);
if (v2Config.has_value() && v2Config->isPremium()) {
if (!m_awgStateTimer.isActive()) {
m_awgStateTimer.start(10000);
}
return;
}
}
if (m_awgStateTimer.isActive()) {
m_awgStateTimer.stop();
}
}
void ConnectionUiController::onAwgStateTimeout() void ConnectionUiController::onAwgStateTimeout()
{ {
if (m_state != Vpn::ConnectionState::Connecting) { if (m_state != Vpn::ConnectionState::Connecting) {
@@ -184,22 +213,48 @@ void ConnectionUiController::onAwgStateTimeout()
return; return;
} }
const QMap<DockerContainer, ContainerConfig> containersMap = m_serversController->getServerContainersMap(serverId); closeConnection();
if (!containersMap.contains(DockerContainer::Xray)) {
qDebug().noquote() << "AWG connect timeout: no Xray container available"; QTimer::singleShot(1000, this, [this, serverId]() {
if (m_isConnected || m_isConnectionInProgress) {
return;
}
qDebug().noquote() << "AWG connect timeout: trying to switch API protocol to VLESS and reload config from gateway";
m_pendingApiServerId = serverId;
m_apiSwitched = false;
m_waitingForApiUpdate = true;
emit requestSetCurrentProtocol(QStringLiteral("vless"));
emit requestUpdateServiceFromGateway(serverId, QString(), QString(), true);
});
}
void ConnectionUiController::onUpdateServiceFromGatewayCompleted(bool success, const QString &serverId)
{
if (!m_waitingForApiUpdate || m_pendingApiServerId != serverId) {
return; return;
} }
qDebug().noquote() << "AWG connect timeout (10s), switching default container to Xray and reconnecting"; m_waitingForApiUpdate = false;
m_apiSwitched = success;
m_serversController->setDefaultContainer(serverId, DockerContainer::Xray); if (success) {
emit requestSetCurrentProtocol(QStringLiteral("vless")); const QMap<DockerContainer, ContainerConfig> containersMap = m_serversController->getServerContainersMap(serverId);
if (containersMap.contains(DockerContainer::Xray)) {
qDebug().noquote() << "AWG connect timeout (10s), switching default container to Xray and reconnecting";
m_serversController->setDefaultContainer(serverId, DockerContainer::Xray);
m_pendingApiServerId.clear();
closeConnection(); if (!m_isConnected && !m_isConnectionInProgress) {
emit prepareConfig();
QTimer::singleShot(500, this, [this]() { }
if (!m_isConnected && !m_isConnectionInProgress) { return;
emit prepareConfig();
} }
}); }
qDebug().noquote() << "AWG connect timeout: no Xray available (API switch success ="
<< (m_apiSwitched ? "YES" : "NO") << ")";
m_pendingApiServerId.clear();
} }
@@ -41,6 +41,10 @@ public slots:
void onTranslationsUpdated(); void onTranslationsUpdated();
public slots:
void checkAndStartAwgStateTimer();
void onUpdateServiceFromGatewayCompleted(bool success, const QString &serverId);
private slots: private slots:
void onAwgStateTimeout(); void onAwgStateTimeout();
@@ -54,6 +58,8 @@ signals:
void prepareConfig(); void prepareConfig();
void requestSetCurrentProtocol(const QString &protocol); void requestSetCurrentProtocol(const QString &protocol);
void requestUpdateServiceFromGateway(const QString &serverId, const QString &newCountryCode,
const QString &newCountryName, bool reloadServiceConfig);
private: private:
Vpn::ConnectionState getCurrentConnectionState(); Vpn::ConnectionState getCurrentConnectionState();
@@ -67,6 +73,10 @@ private:
QString m_connectionStateText = tr("Connect"); QString m_connectionStateText = tr("Connect");
Vpn::ConnectionState m_state; Vpn::ConnectionState m_state;
QString m_pendingApiServerId;
bool m_apiSwitched = false;
bool m_waitingForApiUpdate = false;
}; };
#endif #endif