chore: minor ui fixes (#1446)

* chore: minor ui fixes

* chore: update ru translation file

* bugfix: fixed config update by ttl for gateway configs

* bugfix: fixed proxy bypassing

* chore: minor ui fixes

* chore: update ru translation file

* chore: bump version
This commit is contained in:
Nethius 2025-03-04 13:33:35 +07:00 committed by GitHub
parent 728b48044c
commit 678bfffe49
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 963 additions and 401 deletions

View file

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN) set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.8.4.2 project(${PROJECT} VERSION 4.8.4.3
DESCRIPTION "AmneziaVPN" DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/" HOMEPAGE_URL "https://amnezia.org/"
) )
@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}") set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 2079) set(APP_ANDROID_VERSION_CODE 2080)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")

View file

@ -157,12 +157,12 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt, auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt,
this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) { this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) {
encryptedResponseBody = nestedReply->readAll(); encryptedResponseBody = nestedReply->readAll();
if (!sslErrors.isEmpty() || !shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) { reply = nestedReply;
if (!sslErrors.isEmpty() || shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
sslErrors = nestedSslErrors; sslErrors = nestedSslErrors;
reply = nestedReply; return false;
return true;
} }
return false; return true;
}; };
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction); bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
@ -212,45 +212,45 @@ QStringList GatewayController::getProxyUrls()
wait.exec(); wait.exec();
if (reply->error() == QNetworkReply::NetworkError::NoError) { if (reply->error() == QNetworkReply::NetworkError::NoError) {
break; auto encryptedResponseBody = reply->readAll();
} reply->deleteLater();
reply->deleteLater();
}
auto encryptedResponseBody = reply->readAll(); EVP_PKEY *privateKey = nullptr;
reply->deleteLater(); QByteArray responseBody;
try {
if (!m_isDevEnvironment) {
QCryptographicHash hash(QCryptographicHash::Sha512);
hash.addData(key);
QByteArray hashResult = hash.result().toHex();
EVP_PKEY *privateKey = nullptr; QByteArray key = QByteArray::fromHex(hashResult.left(64));
QByteArray responseBody; QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
try {
if (!m_isDevEnvironment) {
QCryptographicHash hash(QCryptographicHash::Sha512);
hash.addData(key);
QByteArray hashResult = hash.result().toHex();
QByteArray key = QByteArray::fromHex(hashResult.left(64)); QByteArray ba = QByteArray::fromBase64(encryptedResponseBody);
QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
QByteArray ba = QByteArray::fromBase64(encryptedResponseBody); QSimpleCrypto::QBlockCipher blockCipher;
responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
} else {
responseBody = encryptedResponseBody;
}
} catch (...) {
Utils::logException();
qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
continue;
}
QSimpleCrypto::QBlockCipher blockCipher; auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
QStringList endpoints;
for (const auto &endpoint : endpointsArray) {
endpoints.push_back(endpoint.toString());
}
return endpoints;
} else { } else {
responseBody = encryptedResponseBody; reply->deleteLater();
} }
} catch (...) {
Utils::logException();
qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
return {};
} }
return {};
auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
QStringList endpoints;
for (const auto &endpoint : endpointsArray) {
endpoints.push_back(endpoint.toString());
}
return endpoints;
} }
bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key, bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key,
@ -262,7 +262,7 @@ bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray
} else if (responseBody.contains("html")) { } else if (responseBody.contains("html")) {
qDebug() << "The response contains an html tag"; qDebug() << "The response contains an html tag";
return true; return true;
} else if (checkEncryption) { } else if (reply->error() == QNetworkReply::NetworkError::NoError && checkEncryption) {
try { try {
QSimpleCrypto::QBlockCipher blockCipher; QSimpleCrypto::QBlockCipher blockCipher;
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt)); static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
@ -296,7 +296,7 @@ void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *repl
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; }); connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec(); wait.exec();
if (!replyProcessingFunction(reply, sslErrors)) { if (replyProcessingFunction(reply, sslErrors)) {
break; break;
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -407,7 +407,7 @@ bool ApiConfigsController::isConfigValid()
return updateServiceFromGateway(serverIndex, "", ""); return updateServiceFromGateway(serverIndex, "", "");
} else if (configSource && m_serversModel->isApiKeyExpired(serverIndex)) { } else if (configSource && m_serversModel->isApiKeyExpired(serverIndex)) {
qDebug() << "attempt to update api config by expires_at event"; qDebug() << "attempt to update api config by expires_at event";
if (configSource == apiDefs::ConfigSource::Telegram) { if (configSource == apiDefs::ConfigSource::AmneziaGateway) {
return updateServiceFromGateway(serverIndex, "", ""); return updateServiceFromGateway(serverIndex, "", "");
} else { } else {
m_serversModel->removeApiConfig(serverIndex); m_serversModel->removeApiConfig(serverIndex);

View file

@ -48,8 +48,8 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
} }
case ServiceDescriptionRole: { case ServiceDescriptionRole: {
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) { if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) {
return tr("Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to 200 " return tr("Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. "
"Mb/s"); "Speeds up to 200 Mbps");
} else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) { } else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
return tr("Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and " return tr("Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and "
"more. YouTube is not included in the free plan."); "more. YouTube is not included in the free plan.");

View file

@ -81,7 +81,7 @@ PageType {
actionButtonImage: "qrc:/images/controls/settings.svg" actionButtonImage: "qrc:/images/controls/settings.svg"
headerText: root.processedServer.name headerText: root.processedServer.name
descriptionText: qsTr("Locations for connection") descriptionText: qsTr("Location for connection")
actionButtonFunction: function() { actionButtonFunction: function() {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)

View file

@ -42,8 +42,8 @@ PageType {
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
headerText: qsTr("Connected devices") headerText: qsTr("Active devices")
descriptionText: qsTr("To manage connected devices") descriptionText: qsTr("Manage currently connected devices")
} }
WarningType { WarningType {
@ -71,8 +71,13 @@ PageType {
rightImageSource: "qrc:/images/controls/trash.svg" rightImageSource: "qrc:/images/controls/trash.svg"
clickedFunction: function() { clickedFunction: function() {
var headerText = qsTr("Deactivate the subscription on selected device") if (isCurrentDevice && ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
var descriptionText = qsTr("The next time the “Connect” button is pressed, the device will be activated again") PageController.showNotificationMessage(qsTr("Cannot unlink device during active connection"))
return
}
var headerText = qsTr("Are you sure you want to unlink this device?")
var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.")
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel") var noButtonText = qsTr("Cancel")

View file

@ -99,7 +99,7 @@ PageType {
Layout.leftMargin: 16 Layout.leftMargin: 16
headerText: qsTr("How to connect on another device") headerText: qsTr("How to connect on another device")
descriptionText: qsTr("Instructions on the Amnezia website") descriptionText: qsTr("Setup guides on the Amnezia website")
} }
} }

View file

@ -46,7 +46,7 @@ PageType {
Layout.leftMargin: 16 Layout.leftMargin: 16
headerText: qsTr("Configuration files") headerText: qsTr("Configuration files")
descriptionText: qsTr("To connect a router or AmneziaWG application") descriptionText: qsTr("For router setup or the AmneziaWG app")
} }
} }
@ -123,13 +123,13 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16
headerText: qsTr("Configuration file ") + moreOptionsDrawer.countryName headerText: moreOptionsDrawer.countryName + qsTr(" configuration file")
} }
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Create a new") text: qsTr("Generate a new configuration file")
descriptionText: qsTr("The previously created one will stop working") descriptionText: qsTr("The previously created one will stop working")
clickedFunction: function() { clickedFunction: function() {
@ -193,9 +193,15 @@ PageType {
} }
function showQuestion(isConfigIssue, countryCode, countryName) { function showQuestion(isConfigIssue, countryCode, countryName) {
var headerText = qsTr("Revoke the actual %1 configuration file?").arg(countryName) var headerText
var descriptionText = qsTr("The previously created file will no longer be valid. It will not be possible to connect using it.") if (isConfigIssue) {
var yesButtonText = qsTr("Continue") headerText = qsTr("Generate a new %1 configuration file?").arg(countryName)
} else {
headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName)
}
var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it")
var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue")
var noButtonText = qsTr("Cancel") var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { var yesButtonFunction = function() {

View file

@ -42,7 +42,7 @@ PageType {
QtObject { QtObject {
id: deviceCountObject id: deviceCountObject
readonly property string title: qsTr("Connected devices") readonly property string title: qsTr("Active connections")
readonly property string contentKey: "connectedDevices" readonly property string contentKey: "connectedDevices"
readonly property string objectImageSource: "qrc:/images/controls/monitor.svg" readonly property string objectImageSource: "qrc:/images/controls/monitor.svg"
} }
@ -215,7 +215,7 @@ PageType {
text: qsTr("Configuration files") text: qsTr("Configuration files")
descriptionText: qsTr("To connect a router or AmneziaWG application") descriptionText: qsTr("Manage configuration files")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
@ -233,9 +233,9 @@ PageType {
visible: footer.isVisibleForAmneziaFree visible: footer.isVisibleForAmneziaFree
text: qsTr("Connected devices") text: qsTr("Active devices")
descriptionText: qsTr("To manage connected devices") descriptionText: qsTr("Manage currently connected devices")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
@ -265,6 +265,8 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
visible: footer.isVisibleForAmneziaFree
text: qsTr("How to connect on another device") text: qsTr("How to connect on another device")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
@ -273,7 +275,9 @@ PageType {
} }
} }
DividerType {} DividerType {
visible: footer.isVisibleForAmneziaFree
}
BasicButtonType { BasicButtonType {
id: resetButton id: resetButton
@ -325,17 +329,17 @@ PageType {
pressedColor: AmneziaStyle.color.sheerWhite pressedColor: AmneziaStyle.color.sheerWhite
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
text: qsTr("Deactivate the subscription on this device") text: qsTr("Unlink this device")
clickedFunc: function() { clickedFunc: function() {
var headerText = qsTr("Deactivate the subscription on this device?") var headerText = qsTr("Are you sure you want to unlink this device?")
var descriptionText = qsTr("The next time the “Connect” button is pressed, the device will be activated again") var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.")
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel") var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) { if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot deactivate subscription during active connection")) PageController.showNotificationMessage(qsTr("Cannot unlink device during active connection"))
} else { } else {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
if (ApiConfigsController.deactivateDevice()) { if (ApiConfigsController.deactivateDevice()) {

View file

@ -27,7 +27,7 @@ PageType {
QtObject { QtObject {
id: techSupport id: techSupport
readonly property string title: qsTr("For technical support") readonly property string title: qsTr("Email Support")
readonly property string description: qsTr("support@amnezia.org") readonly property string description: qsTr("support@amnezia.org")
readonly property string link: "mailto:support@amnezia.org" readonly property string link: "mailto:support@amnezia.org"
} }
@ -35,7 +35,7 @@ PageType {
QtObject { QtObject {
id: paymentSupport id: paymentSupport
readonly property string title: qsTr("For payment issues") readonly property string title: qsTr("Email Billing & Orders")
readonly property string description: qsTr("help@vpnpay.io") readonly property string description: qsTr("help@vpnpay.io")
readonly property string link: "mailto:help@vpnpay.io" readonly property string link: "mailto:help@vpnpay.io"
} }
@ -43,7 +43,7 @@ PageType {
QtObject { QtObject {
id: site id: site
readonly property string title: qsTr("Site") readonly property string title: qsTr("Website")
readonly property string description: qsTr("amnezia.org") readonly property string description: qsTr("amnezia.org")
readonly property string link: LanguageModel.getCurrentSiteUrl() readonly property string link: LanguageModel.getCurrentSiteUrl()
} }
@ -79,7 +79,7 @@ PageType {
Layout.leftMargin: 16 Layout.leftMargin: 16
headerText: qsTr("Support") headerText: qsTr("Support")
descriptionText: qsTr("Our technical support specialists are ready to help you at any time") descriptionText: qsTr("Our technical support specialists are available to assist you at any time")
} }
} }