From 63b5257986baa9dc30af0e73bd3a3bb88fe5d6b2 Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Mon, 12 May 2025 10:31:41 +0300 Subject: [PATCH 1/5] chore: update text translations and text (#1573) --- client/containers/containers_defs.cpp | 153 +++-- client/translations/amneziavpn_ru_RU.ts | 537 +++++++----------- client/ui/models/api/apiServicesModel.cpp | 4 +- .../ui/qml/Pages2/PageSettingsApiDevices.qml | 2 +- .../qml/Pages2/PageSettingsApiServerInfo.qml | 2 +- 5 files changed, 290 insertions(+), 408 deletions(-) diff --git a/client/containers/containers_defs.cpp b/client/containers/containers_defs.cpp index 52b148c0..214e2a51 100644 --- a/client/containers/containers_defs.cpp +++ b/client/containers/containers_defs.cpp @@ -140,98 +140,83 @@ QMap ContainerProps::containerDetailedDescriptions() { return { { DockerContainer::OpenVpn, - QObject::tr( - "OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n" - "It employs its unique security protocol, " - "leveraging the strength of SSL/TLS for encryption and key exchange. " - "Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, " - "catering to a wide range of devices and operating systems. " - "Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, " - "which continually reinforces its security. " - "With a strong balance of performance, security, and compatibility, " - "OpenVPN remains a top choice for privacy-conscious individuals and businesses alike.\n\n" - "* Available in the AmneziaVPN across all platforms\n" - "* Normal power consumption on mobile devices\n" - "* Flexible customisation to suit user needs to work with different operating systems and devices\n" - "* Recognised by DPI systems and therefore susceptible to blocking\n" - "* Can operate over both TCP and UDP network protocols.") }, + QObject::tr("OpenVPN is one of the most popular and reliable VPN protocols. " + "It uses SSL/TLS encryption, supports a wide variety of devices and operating systems, " + "and is continuously improved by the community due to its open-source nature. " + "It provides a good balance between speed and security but is easily recognized by DPI systems, " + "making it susceptible to blocking.\n" + "\nFeatures:\n" + "* Available on all AmneziaVPN platforms\n" + "* Normal battery consumption on mobile devices\n" + "* Flexible customization for various devices and OS\n" + "* Operates over both TCP and UDP protocols") }, { DockerContainer::ShadowSocks, - QObject::tr("Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. " - "Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection." - "However, certain traffic analysis systems might still detect a Shadowsocks connection. " - "Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.\n\n" - "* Available in the AmneziaVPN only on desktop platforms\n" - "* Configurable encryption protocol\n" + QObject::tr("Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. " + "Although designed to be discreet, it doesn't mimic a standard HTTPS connection and can be detected by some DPI systems. " + "Due to limited support in Amnezia, we recommend using the AmneziaWG protocol.\n" + "\nFeatures:\n" + "* Available in AmneziaVPN only on desktop platforms\n" + "* Customizable encryption protocol\n" "* Detectable by some DPI systems\n" - "* Works over TCP network protocol.") }, + "* Operates over TCP protocol\n") }, { DockerContainer::Cloak, - QObject::tr("This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for " - "protecting against detection.\n\n" - "OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client " - "and the server.\n\n" - "Cloak protects OpenVPN from detection. \n\n" - "Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, " - "and also protects the VPN from detection by Active Probing. This makes it very resistant to " - "being detected\n\n" - "Immediately after receiving the first data packet, Cloak authenticates the incoming connection. " - "If authentication fails, the plugin masks the server as a fake website and your VPN becomes " - "invisible to analysis systems.\n\n" - "* Available in the AmneziaVPN across all platforms\n" + QObject::tr("This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking.\n" + "\nOpenVPN securely encrypts all internet traffic between your device and the server.\n" + "\nThe Cloak plugin further protects the connection from DPI detection. " + "It modifies traffic metadata to disguise VPN traffic as regular web traffic and prevents detection through active probing. " + "If an incoming connection fails authentication, Cloak serves a fake website, making your VPN invisible to traffic analysis systems.\n" + "\nIn regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection.\n" + "\nFeatures:\n" + "* Available on all AmneziaVPN platforms\n" "* High power consumption on mobile devices\n" - "* Flexible settings\n" - "* Not recognised by detection systems\n" - "* Works over TCP network protocol, 443 port.\n") }, + "* Flexible configuration options\n" + "* Undetectable by DPI systems\n" + "* Operates over TCP protocol on port 443") }, { DockerContainer::WireGuard, - QObject::tr("A relatively new popular VPN protocol with a simplified architecture.\n" - "WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption " - "settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput.\n" - "WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. " - "Unlike some other VPN protocols that employ obfuscation techniques, " - "the consistent signature patterns of WireGuard packets can be more easily identified and " - "thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools.\n\n" - "* Available in the AmneziaVPN across all platforms\n" - "* Low power consumption\n" - "* Minimum number of settings\n" - "* Easily recognised by DPI analysis systems, susceptible to blocking\n" - "* Works over UDP network protocol.") }, + QObject::tr("WireGuard is a modern, streamlined VPN protocol offering stable connectivity and excellent performance across all devices. " + "It uses fixed encryption settings, delivering lower latency and higher data transfer speeds compared to OpenVPN. " + "However, WireGuard is easily identifiable by DPI systems due to its distinctive packet signatures, making it susceptible to blocking.\n" + "\nFeatures:\n" + "* Available on all AmneziaVPN platforms\n" + "* Low power consumption on mobile devices\n" + "* Minimal configuration required\n" + "* Easily detected by DPI systems (susceptible to blocking)\n" + "* Operates over UDP protocol") }, { DockerContainer::Awg, - QObject::tr("A modern iteration of the popular VPN protocol, " - "AmneziaWG builds upon the foundation set by WireGuard, " - "retaining its simplified architecture and high-performance capabilities across devices.\n" - "While WireGuard is known for its efficiency, " - "it had issues with being easily detected due to its distinct packet signatures. " - "AmneziaWG solves this problem by using better obfuscation methods, " - "making its traffic blend in with regular internet traffic.\n" - "This means that AmneziaWG keeps the fast performance of the original " - "while adding an extra layer of stealth, " - "making it a great choice for those wanting a fast and discreet VPN connection.\n\n" - "* Available in the AmneziaVPN across all platforms\n" - "* Low power consumption\n" - "* Minimum number of settings\n" - "* Not recognised by traffic analysis systems\n" - "* Works over UDP network protocol.") }, + QObject::tr("AmneziaWG is a modern VPN protocol based on WireGuard, " + "combining simplified architecture with high performance across all devices. " + "It addresses WireGuard's main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, " + "making VPN traffic indistinguishable from regular internet traffic.\n" + "\nAmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection.\n" + "\nFeatures:\n" + "* Available on all AmneziaVPN platforms\n" + "* Low battery consumption on mobile devices\n" + "* Minimal settings required\n" + "* Undetectable by traffic analysis systems (DPI)\n" + "* Operates over UDP protocol") }, { DockerContainer::Xray, - QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, " - "is designed to provide the highest level of protection against detection through its innovative approach to security and privacy.\n" - "It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, " - "thus presenting an authentic TLS certificate and data. \n" - "This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, " - "legitimate sites without the need for specific configurations. \n" - "Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, " - "REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security. " - "This makes REALITY a robust solution for maintaining internet freedom.") - }, + QObject::tr("REALITY is an innovative protocol developed by the creators of XRay, designed specifically to combat high levels of internet censorship. " + "REALITY identifies censorship systems during the TLS handshake, " + "redirecting suspicious traffic seamlessly to legitimate websites like google.com while providing genuine TLS certificates. " + "This allows VPN traffic to blend indistinguishably with regular web traffic without special configuration." + "\nUnlike older protocols such as VMess, VLESS, and XTLS-Vision, REALITY incorporates an advanced built-in \"friend-or-foe\" detection mechanism, " + "effectively protecting against DPI and other traffic analysis methods.\n" + "\nFeatures:\n" + "* Resistant to active probing and DPI detection\n" + "* No special configuration required to disguise traffic\n" + "* Highly effective in heavily censored regions\n" + "* Minimal battery consumption on devices\n" + "* Operates over TCP protocol") }, { DockerContainer::Ipsec, - QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n" - "One of its distinguishing features is its ability to swiftly switch between networks and devices, " - "making it particularly adaptive in dynamic network environments. \n" - "While it offers a blend of security, stability, and speed, " - "it's essential to note that IKEv2 can be easily detected and is susceptible to blocking.\n\n" - "* Available in the AmneziaVPN only on Windows\n" - "* Low power consumption, on mobile devices\n" - "* Minimal configuration\n" - "* Recognised by DPI analysis systems\n" - "* Works over UDP network protocol, ports 500 and 4500.") }, + QObject::tr("IKEv2, combined with IPSec encryption, is a modern and reliable VPN protocol. " + "It reconnects quickly when switching networks or devices, making it ideal for dynamic network environments. " + "While it provides good security and speed, it's easily recognized by DPI systems and susceptible to blocking.\n" + "\nFeatures:\n" + "* Available in AmneziaVPN only on Windows\n" + "* Low battery consumption on mobile devices\n" + "* Minimal configuration required\n" + "* Detectable by DPI analysis systems(easily blocked)\n" + "* Operates over UDP protocol(ports 500 and 4500)") }, { DockerContainer::TorWebSite, QObject::tr("Website in Tor network") }, { DockerContainer::Dns, QObject::tr("DNS Service") }, diff --git a/client/translations/amneziavpn_ru_RU.ts b/client/translations/amneziavpn_ru_RU.ts index c740a560..e653b32b 100644 --- a/client/translations/amneziavpn_ru_RU.ts +++ b/client/translations/amneziavpn_ru_RU.ts @@ -100,8 +100,8 @@ - AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan. - AmneziaFree предоставляет бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включен в бесплатный тариф. + Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan. + Amnezia Free позволяет бесплатно и без ограничений пользоваться базовым набором сайтов и приложений, включая Facebook, Instagram, Twitter (X), Discord, Telegram и другие. YouTube не входит в бесплатный тариф. Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions. @@ -127,6 +127,11 @@ %1 days %1 дней + + + + + VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, <a href="%1/free" style="color: #FBB26A;">more details on the website.</a> @@ -1655,8 +1660,12 @@ Already installed containers were found on the server. All installed containers + This will unlink the device from your subscription. You can reconnect it anytime by pressing "Reload API config" in subscription settings on device. + Это отключит устройство от вашей подписки. Вы можете повторно подключить его в любое время, нажав "Перезагрузить конфигурацию API" в настройках подписки на устройстве. + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. - Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку "Подключиться". + Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку "Подключиться". @@ -2007,8 +2016,12 @@ Already installed containers were found on the server. All installed containers + This will unlink the device from your subscription. You can reconnect it anytime by pressing "Reload API config" in subscription settings on device. + Это отключит устройство от вашей подписки. Вы можете повторно подключить его в любое время, нажав "Перезагрузить конфигурацию API" в настройках подписки на устройстве. + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. - Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку Подключиться. + Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку Подключиться. @@ -2618,11 +2631,6 @@ Already installed containers were found on the server. All installed containers No new installed containers found Новые установленные протоколы и сервисы не обнаружены - - - - - @@ -2787,11 +2795,6 @@ Already installed containers were found on the server. All installed containers Clear %1 profile? Очистить профиль %1? - - - - - connection settings @@ -3045,7 +3048,7 @@ It's okay as long as it's from someone you trust. Что у вас есть? - + File with connection settings Файл с настройками подключения @@ -3054,112 +3057,135 @@ It's okay as long as it's from someone you trust. Файл с настройками подключения или резервной копией - + Connection Соединение - + Settings Настройки - + Enable logs Включить запись логов + Export client logs + + + + + Save + Сохранить + + + + Logs files (*.log) + Файлы логов (*.log) + + + + Logs file saved + Файл с логами сохранен + + + Support tag Support tag - + Copied Скопировано - + Insert the key, add a configuration file or scan the QR-code Вставьте ключ, добавьте файл конфигурации или отсканируйте QR-код - + Insert key Вставьте ключ - + Insert Вставить - + Continue Продолжить - + Other connection options Другие варианты подключения - + Site Amnezia Сайт Amnezia - + VPN by Amnezia VPN от Amnezia - + Connect to classic paid and free VPN services from Amnezia Подключайтесь к классическим платным и бесплатным VPN-сервисам от Amnezia - + Self-hosted VPN Self-hosted VPN - + Configure Amnezia VPN on your own server Настроить VPN на собственном сервере - + Restore from backup Восстановить из резервной копии - + + + + - + Open backup file Открыть резервную копию - + Backup files (*.backup) Файлы резервных копий (*.backup) - + Open config file Открыть файл с конфигурацией - + QR code QR-код - + I have nothing У меня ничего нет @@ -4404,95 +4430,56 @@ and will not be shared or disclosed to the Amnezia or any third parties Создайте на сервере файловое хранилище для безопасного хранения и передачи файлов. - This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against blocking. + + AmneziaWG is a modern VPN protocol based on WireGuard, combining simplified architecture with high performance across all devices. It addresses WireGuard's main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, making VPN traffic indistinguishable from regular internet traffic. -OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. +AmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection. -Cloak protects OpenVPN from detection and blocking. +Features: +* Available on all AmneziaVPN platforms +* Low battery consumption on mobile devices +* Minimal settings required +* Undetectable by traffic analysis systems (DPI) +* Operates over UDP protocol + AmneziaWG — современный VPN-протокол на основе WireGuard, сочетающий простую архитектуру и высокую производительность на всех устройствах. Он устраняет основной недостаток WireGuard (лёгкое обнаружение трафика системами DPI) за счёт эффективного маскирования VPN-трафика под обычный интернет-трафик. -Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected +Таким образом, AmneziaWG идеально подойдёт тем, кто ищет быстрое и незаметное VPN-соединение. -Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. - -If there is a extreme level of Internet censorship in your region, we advise you to use only OpenVPN over Cloak from the first connection - -* Available in the AmneziaVPN across all platforms -* High power consumption on mobile devices -* Flexible settings -* Not recognised by DPI analysis systems -* Works over TCP network protocol, 443 port. - - Это связка протокола OpenVPN и плагина Cloak, разработанная специально для защиты от блокировки. - -OpenVPN обеспечивает безопасное VPN-соединение, шифруя весь интернет-трафик между клиентом и сервером. - -Cloak защищает OpenVPN от обнаружения и блокировки. - -Cloak изменяет метаданные пакетов таким образом, что полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью активного зондирования. Это делает его очень защищенным от обнаружения. - -Сразу после получения первого пакета данных Cloak устанавливает подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под фальшивый веб-сайт, и ваш VPN становится невидимым для систем анализа трафика. - -Если в вашем регионе наблюдается жесткая интернет-цензура, мы советуем вам уже при первом подключении использовать только OpenVPN over Cloak. - -* Доступен в AmneziaVPN на всех платформах -* Высокое энергопотребление на мобильных устройствах -* Гибкие настройки -* Не распознается системами DPI-анализа -* Работает по сетевому протоколу TCP, использует порт 443 - - - A relatively new popular VPN protocol with a simplified architecture. -WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. -WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. - -* Available in the AmneziaVPN across all platforms -* Low power consumption -* Minimum number of settings -* Easily recognised by DPI analysis systems, susceptible to blocking -* Works over UDP network protocol. - Относительно новый и популярный VPN-протокол с простой архитектурой. -WireGuard обеспечивает стабильное VPN-соединение и высокую производительность на всех устройствах. Он использует строго заданные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность при передаче данных. -WireGuard очень уязвим для блокировки из-за характерных сигнатур пакетов. В отличие от некоторых других VPN-протоколов, использующих методы обфускации, последовательные сигнатуры пакетов WireGuard легче идентифицируются и, следовательно, могут блокироваться современными Deep Packet Inspection (DPI) системами и другими инструментами для сетевого мониторинга. - -* Доступен в AmneziaVPN на всех платформах +Особенности: +* Доступен во всех версиях AmneziaVPN * Низкое энергопотребление на мобильных устройствах -* Минимальная конфигурация -* Легко распознается системами DPI-анализа, поддается блокировке -* Работает по сетевому протоколу UDP +* Минимум настроек +* Незаметен для систем анализа трафика (DPI) +* Работает по протоколу UDP + - The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. -It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. -This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. -Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. - Протокол REALITY, новаторская разработка создателей XRay, специально спроектирован для противодействия самой строгой цензуре с помощью нового способа обхода блокировок. -Он уникальным образом идентифицирует цензоров на этапе TLS-рукопожатия, беспрепятственно работая в качестве прокси для реальных клиентов и перенаправляя цензоров на реальные сайты, такие как google.com, тем самым предъявляя подлинный TLS-сертификат и данные. -REALITY отличается от аналогичных технологий благодаря способности без специальной настройки маскировать веб-трафик так, как будто он поступает со случайных легитимных сайтов. -В отличие от более старых протоколов, таких как VMess, VLESS и транспорт XTLS-Vision, технология распознавания "друг или враг" на этапе TLS-рукопожатия повышает безопасность и обходит обнаружение сложными системами DPI-анализа, которые используют методы активного зондирования. Это делает REALITY эффективным решением для поддержания свободы интернета в регионах с жесткой цензурой. - - - - IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol. -One of its distinguishing features is its ability to swiftly switch between networks and devices, making it particularly adaptive in dynamic network environments. -While it offers a blend of security, stability, and speed, it's essential to note that IKEv2 can be easily detected and is susceptible to blocking. + + REALITY is an innovative protocol developed by the creators of XRay, designed specifically to combat high levels of internet censorship. REALITY identifies censorship systems during the TLS handshake, redirecting suspicious traffic seamlessly to legitimate websites like google.com while providing genuine TLS certificates. This allows VPN traffic to blend indistinguishably with regular web traffic without special configuration. +Unlike older protocols such as VMess, VLESS, and XTLS-Vision, REALITY incorporates an advanced built-in "friend-or-foe" detection mechanism, effectively protecting against DPI and other traffic analysis methods. -* Available in the AmneziaVPN only on Windows -* Low power consumption, on mobile devices -* Minimal configuration -* Recognised by DPI analysis systems -* Works over UDP network protocol, ports 500 and 4500. - IKEv2 в сочетании с уровнем шифрования IPSec представляет собой современный и стабильный VPN-протокол. -Он может быстро переключаться между сетями и устройствами, что делает его особенно адаптивным в динамичных сетевых средах. -Несмотря на сочетание безопасности, стабильности и скорости, необходимо отметить, что IKEv2 легко обнаруживается и подвержен блокировке. +Features: +* Resistant to active probing and DPI detection +* No special configuration required to disguise traffic +* Highly effective in heavily censored regions +* Minimal battery consumption on devices +* Operates over TCP protocol + REALITY — это инновационный протокол от разработчиков XRay, специально созданный для эффективного противодействия жесткой интернет-цензуре. -* Доступен в AmneziaVPN только для Windows -* Низкое энергопотребление на мобильных устройствах -* Минимальная конфигурация -* Распознается системами DPI-анализа -* Работает по сетевому протоколу UDP, использует порты 500 и 4500 +REALITY распознаёт системы блокировки во время TLS-рукопожатия и незаметно перенаправляет подозрительные запросы на реальные сайты, такие как google.com, предъявляя подлинные TLS-сертификаты. Это позволяет маскировать VPN-трафик под обычный веб-трафик без дополнительных настроек. + +В отличие от протоколов старого поколения (VMess, VLESS и XTLS-Vision), REALITY использует встроенную технологию распознавания «свой-чужой», надёжно защищая от DPI и других методов сетевого анализа. + +Особенности: +* Устойчив к активному зондированию и DPI-системам +* Не требует специальной настройки для маскировки трафика +* Эффективен в регионах с жесткой цензурой +* Минимальное энергопотребление на устройствах +* Работает по протоколу TCP + - + DNS Service Сервис DNS @@ -4503,7 +4490,7 @@ While it offers a blend of security, stability, and speed, it's essential t - + Website in Tor network Веб-сайт в сети Tor @@ -4543,109 +4530,140 @@ While it offers a blend of security, stability, and speed, it's essential t XRay с REALITY маскирует VPN-трафик под веб-трафик. Обладает высокой устойчивостью к обнаружению и обеспечивает высокую скорость соединения. - - OpenVPN stands as one of the most popular and time-tested VPN protocols available. -It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. - -* Available in the AmneziaVPN across all platforms -* Normal power consumption on mobile devices -* Flexible customisation to suit user needs to work with different operating systems and devices -* Recognised by DPI systems and therefore susceptible to blocking -* Can operate over both TCP and UDP network protocols. - OpenVPN является одним из самых популярных и проверенных временем VPN-протоколов. Он использует собственный протокол безопасности, и криптографические протоколы SSL/TLS для шифрования и обмена ключами. Более того, поддержка множества методов аутентификации делает OpenVPN универсальным, адаптируемым и подходящим для широкого спектра устройств и операционных систем. Благодаря своему открытому коду, OpenVPN подвергается тщательной проверке со стороны мирового сообщества, что постоянно укрепляет его безопасность. Имея отличный баланс между производительностью, безопасностью и совместимостью OpenVPN остается лучшим выбором для людей и компаний, заботящихся о конфиденциальности, однако OpenVPN легко распознается современными системами анализа трафика. -Доступен в AmneziaVPN на всех платформах -Нормальное энергопотребление на мобильных устройствах -Гибкая настройка полезная при работе с различными операционными системами и устройствами -Распознается системами DPI и, следовательно, уязвим к блокировкам -Может работать как по TCP, так и по UDP протоколу. + + + - - This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against detection. + + OpenVPN is one of the most popular and reliable VPN protocols. It uses SSL/TLS encryption, supports a wide variety of devices and operating systems, and is continuously improved by the community due to its open-source nature. It provides a good balance between speed and security but is easily recognized by DPI systems, making it susceptible to blocking. -OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. +Features: +* Available on all AmneziaVPN platforms +* Normal battery consumption on mobile devices +* Flexible customization for various devices and OS +* Operates over both TCP and UDP protocols + OpenVPN — один из самых популярных и надежных VPN-протоколов. Он использует шифрование SSL/TLS, совместим со множеством устройств и ОС, а благодаря открытому коду постоянно совершенствуется сообществом. Имеет хороший баланс скорости и безопасности, но легко распознаётся системами DPI, что делает его уязвимым к блокировкам. -Cloak protects OpenVPN from detection. +Особенности: +* Доступен во всех приложениях AmneziaVPN +* Нормальное энергопотребление на мобильных устройствах +* Гибкие настройки под разные устройства и ОС +* Работает по TCP и UDP + + + + Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. Although designed to be discreet, it doesn't mimic a standard HTTPS connection and can be detected by some DPI systems. Due to limited support in Amnezia, we recommend using the AmneziaWG protocol. -Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected - -Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. - -* Available in the AmneziaVPN across all platforms -* High power consumption on mobile devices -* Flexible settings -* Not recognised by detection systems -* Works over TCP network protocol, 443 port. +Features: +* Available in AmneziaVPN only on desktop platforms +* Customizable encryption protocol +* Detectable by some DPI systems +* Operates over TCP protocol - Это связка протокола OpenVPN и плагина Cloak, созданная специально для защиты от обнаружения. + Shadowsocks основан на протоколе SOCKS5 и шифрует соединение алгоритмом AEAD. Он разработан так, чтобы быть малозаметным, однако не идентичен HTTPS, поэтому может распознаваться некоторыми системами DPI. В связи с ограниченной поддержкой в Amnezia, рекомендуем использовать протокол AmneziaWG. -OpenVPN обеспечивает безопасное VPN-соединение, шифруя весь интернет-трафик между клиентом и сервером. +Особенности: +* Доступен только на ПК в AmneziaVPN +* Настраиваемое шифрование +* Может обнаруживаться некоторыми DPI-системами +* Работает по протоколу TCP + + + + This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking. -Плагин Cloak защищает OpenVPN от обнаружения. +OpenVPN securely encrypts all internet traffic between your device and the server. -Cloak может изменять метаданные пакета, чтобы полностью замаскировать VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью метода Active Probing. Это делает его очень устойчивым к обнаружению. +The Cloak plugin further protects the connection from DPI detection. It modifies traffic metadata to disguise VPN traffic as regular web traffic and prevents detection through active probing. If an incoming connection fails authentication, Cloak serves a fake website, making your VPN invisible to traffic analysis systems. -Сразу после получения первого пакета данных Cloak аутентифицирует входящее соединение, если аутентификация не удалась, плагин маскирует сервер под настоящий веб-сайт, и ваш VPN становится невидимым для систем анализа. Имеет низкую скорость работы в сравнении с другими похожими протоколами. +In regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection. -* Доступно в AmneziaVPN на всех платформах. +Features: +* Available on all AmneziaVPN platforms +* High power consumption on mobile devices +* Flexible configuration options +* Undetectable by DPI systems +* Operates over TCP protocol on port 443 + Эта комбинация состоит из протокола OpenVPN и плагина Cloak, специально разработанных для защиты от блокировок. + +OpenVPN надёжно шифрует весь интернет-трафик между вами и сервером. + +Плагин Cloak дополнительно защищает соединение от распознавания системами DPI. Он изменяет метаданные трафика, маскируя VPN-подключение под обычный веб-трафик, и предотвращает обнаружение с помощью активного зондирования. Если попытка подключения не прошла аутентификацию, Cloak выдаёт поддельный веб-сайт, делая VPN невидимым для анализирующих систем. + +Если в вашем регионе сильная интернет-цензура, мы рекомендуем сразу использовать OpenVPN с плагином Cloak. + +Особенности: +* Доступен на всех платформах AmneziaVPN * Высокое энергопотребление на мобильных устройствах * Гибкие настройки -* Не распознается системами обнаружения. -* Работает по сетевому протоколу TCP, порт 443. - +* Незаметен для систем DPI-анализа +* Использует протокол TCP на порту 443 - - A relatively new popular VPN protocol with a simplified architecture. -WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. -WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. + + WireGuard is a modern, streamlined VPN protocol offering stable connectivity and excellent performance across all devices. It uses fixed encryption settings, delivering lower latency and higher data transfer speeds compared to OpenVPN. However, WireGuard is easily identifiable by DPI systems due to its distinctive packet signatures, making it susceptible to blocking. -* Available in the AmneziaVPN across all platforms -* Low power consumption -* Minimum number of settings -* Easily recognised by DPI analysis systems, susceptible to blocking -* Works over UDP network protocol. - Популярный VPN-протокол с упрощенной архитектурой. -WireGuard обеспечивает стабильное VPN-соединение и высокую производительность на всех устройствах. Он использует закодированные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность передачи данных. -WireGuard очень чувствителен к обнаружению и блокировке из-за различных сигнатур пакетов. В отличие от некоторых других VPN протоколов, использующих методы запутывания, последовательные шаблоны сигнатур пакетов WireGuard легко идентифицируются системами анализа трафика. +Features: +* Available on all AmneziaVPN platforms +* Low power consumption on mobile devices +* Minimal configuration required +* Easily detected by DPI systems (susceptible to blocking) +* Operates over UDP protocol + WireGuard — современный и простой VPN-протокол, который обеспечивает стабильное соединение и высокую скорость передачи данных на любых устройствах. Он использует фиксированные настройки шифрования, имеет меньшую задержку и выше пропускную способность по сравнению с OpenVPN. -* Доступно в AmneziaVPN на всех платформах. -* Низкое энергопотребление -* Минимальное количество настроек -* Легко распознается системами анализа DPI, подвержен блокировке. -* Работает по сетевому протоколу UDP. +Однако WireGuard легко распознаётся системами DPI из-за характерных сигнатур трафика, что делает его уязвимым к блокировкам. + +Особенности: +* Доступен на всех платформах AmneziaVPN +* Низкое энергопотребление на мобильных устройствах +* Минимум настроек +* Легко определяется DPI-системами (подвержен блокировкам) +* Работает по протоколу UDP - - A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. -While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. -This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. + + IKEv2, combined with IPSec encryption, is a modern and reliable VPN protocol. It reconnects quickly when switching networks or devices, making it ideal for dynamic network environments. While it provides good security and speed, it's easily recognized by DPI systems and susceptible to blocking. -* Available in the AmneziaVPN across all platforms -* Low power consumption -* Minimum number of settings -* Not recognised by traffic analysis systems -* Works over UDP network protocol. - AmneziaWG — это современная версия популярного VPN протокола, основанная на базе WireGuard, сохранившая упрощенную архитектуру и высокопроизводительные возможности на всех устройствах. -Хотя WireGuard известен своей эффективностью, обнаружить его довольно легко из-за различных сигнатур пакетов. AmneziaWG решает эту проблему, используя более совершенные методы работы, смешивая свой трафик с обычным интернет-трафиком. -Это означает, что AmneziaWG сохраняет высокую производительность оригинала, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение. +Features: +* Available in AmneziaVPN only on Windows +* Low battery consumption on mobile devices +* Minimal configuration required +* Detectable by DPI analysis systems(easily blocked) +* Operates over UDP protocol(ports 500 and 4500) + IKEv2 — современный и стабильный VPN-протокол, работающий совместно с шифрованием IPSec. Он обеспечивает быстрое переподключение при смене сети или устройства, отлично подходит для динамичных сетевых условий. Несмотря на хорошую скорость и безопасность, легко распознаётся системами DPI и подвержен блокировкам. -* Доступно в AmneziaVPN на всех платформах. -* Низкое энергопотребление -* Минимальное количество настроек -* Не распознается системами анализа трафика. -* Работает по сетевому протоколу UDP. +Особенности: +* Доступен в AmneziaVPN только на Windows +* Низкое энергопотребление на мобильных устройствах +* Минимум настроек +* Распознаётся DPI-системами (легко блокируется) +* Работает по UDP (порты 500 и 4500) - - The REALITY protocol, a pioneering development by the creators of XRay, is designed to provide the highest level of protection against detection through its innovative approach to security and privacy. -It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, thus presenting an authentic TLS certificate and data. -This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. -Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom. - Протокол REALITY, современная разработка от создателей XRay. Призван обеспечить высочайший уровень защиты от обнаружения благодаря инновационному подходу к безопасности и конфиденциальности. -Он безошибочно идентифицирует злоумышленников на этапе установления связи TLS, беспрепятственно работая в качестве прокси-сервера для оригинального клиента и перенаправляя злоумышленников на подлинные веб-сайты, предоставляя тем самым подлинный сертификат TLS и данные. -Эта расширенная возможность отличает REALITY от аналогичных технологий тем, что способна маскироваться под случайный веб-трафик без использования специальных настроек. -В отличие от старых протоколов, таких как VMess, VLESS и транспорт XTLS-Vision, REALITY имеет инновационную технологию распознавания «свой-чужой».Это делает REALITY надежным решением для обеспечения доступа к свободному интернету. + + AmneziaWG is a modern VPN protocol based on WireGuard, combining simplified architecture with high performance across all devices. It addresses WireGuard’s main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, making VPN traffic indistinguishable from regular internet traffic. + + AmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection. + + Features: + + * Available on all AmneziaVPN platforms + * Low battery consumption on mobile devices + * Minimal settings required + * Undetectable by traffic analysis systems (DPI) + * Operates over UDP protocol + + AmneziaWG — современный VPN-протокол на основе WireGuard, сочетающий простую архитектуру и высокую производительность на всех устройствах. Он устраняет основной недостаток WireGuard (лёгкое обнаружение трафика системами DPI) за счёт эффективного маскирования VPN-трафика под обычный интернет-трафик. + +Таким образом, AmneziaWG идеально подойдёт тем, кто ищет быстрое и незаметное VPN-соединение. + +Особенности: +* Доступен во всех версиях AmneziaVPN +* Низкое энергопотребление на мобильных устройствах +* Минимум настроек +* Незаметен для систем анализа трафика (DPI) +* Работает по протоколу UDP WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. @@ -4674,50 +4692,7 @@ Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REAL Замените текущий DNS-сервер на свой собственный. Это повысит уровень вашей конфиденциальности. - OpenVPN stands as one of the most popular and time-tested VPN protocols available. -It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. - -* Available in the AmneziaVPN across all platforms -* Normal power consumption on mobile devices -* Flexible customisation to suit user needs to work with different operating systems and devices -* Recognised by DPI analysis systems and therefore susceptible to blocking -* Can operate over both TCP and UDP network protocols. - OpenVPN — один из самых популярных и проверенных временем VPN-протоколов. -В нем используется уникальный протокол безопасности, опирающийся на SSL/TLS для шифрования и обмена ключами. Кроме того, OpenVPN поддерживает множество методов аутентификации, что делает его универсальным и адаптируемым к широкому спектру устройств и операционных систем. Благодаря открытому исходному коду OpenVPN подвергается тщательному анализу со стороны мирового сообщества, что постоянно повышает его безопасность. Оптимальное соотношение производительности, безопасности и совместимости делает OpenVPN лучшим выбором как для частных лиц, так и для компаний, заботящихся о конфиденциальности. - -* Доступен в AmneziaVPN на всех платформах -* Нормальное энергопотребление на мобильных устройствах -* Гибкая настройка под нужды пользователя для работы с различными операционными системами и устройствами -* Распознается системами DPI-анализа и поэтому подвержен блокировке -* Может работать по сетевым протоколам TCP и UDP - - - - Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol. - -* Available in the AmneziaVPN only on desktop platforms -* Configurable encryption protocol -* Detectable by some DPI systems -* Works over TCP network protocol. - Shadowsocks создан на основе протокола SOCKS5, защищает соединение с помощью шифра AEAD. Несмотря на то, что протокол Shadowsocks разработан таким образом, чтобы быть незаметным и сложным для идентификации, он не идентичен стандартному HTTPS-соединению, поэтому некоторые системы анализа трафика всё же могут обнаружить соединение Shadowsocks. В связи с ограниченной поддержкой в Amnezia рекомендуется использовать протокол AmneziaWG. - -* Доступен в AmneziaVPN только для ПК и ноутбуков -* Настраиваемый протокол шифрования -* Распознается некоторыми системами DPI-анализа -* Работает по сетевому протоколу TCP. - - - The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. - It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. - This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. - Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. - Протокол REALITY, новаторская разработка создателей XRay, специально спроектирован для противодействия самой строгой цензуре с помощью нового способа обхода блокировок. - Он уникальным образом идентифицирует цензоров на этапе TLS-рукопожатия, беспрепятственно работая в качестве прокси для реальных клиентов и перенаправляя цензоров на реальные сайты, такие как google.com, тем самым предъявляя подлинный TLS-сертификат и данные. - REALITY отличается от аналогичных технологий благодаря способности без специальной настройки маскировать веб-трафик так, как будто он поступает со случайных легитимных сайтов. - В отличие от более старых протоколов, таких как VMess, VLESS и XTLS-Vision, технология распознавания "друг или враг" на этапе TLS-рукопожатия повышает безопасность и обходит обнаружение сложными системами DPI-анализа, которые используют методы активного зондирования. Это делает REALITY эффективным решением для поддержания свободы интернета в регионах с жесткой цензурой. - - - + After installation, Amnezia will create a file storage on your server. You will be able to access it using @@ -4734,84 +4709,6 @@ For more detailed information, you can Более подробную информацию вы можете найти в разделе поддержки "Создание файлового хранилища SFTP." - - - This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for blocking protection. - -OpenVPN provides a secure VPN connection by encrypting all Internet traffic between the client and the server. - -Cloak protects OpenVPN from detection and blocking. - -Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected - -Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. - -If there is a extreme level of Internet censorship in your region, we advise you to use only OpenVPN over Cloak from the first connection - -* Available in the AmneziaVPN across all platforms -* High power consumption on mobile devices -* Flexible settings -* Not recognised by DPI analysis systems -* Works over TCP network protocol, 443 port. - - OpenVPN over Cloak - это комбинация протокола OpenVPN и плагина Cloak, разработанного специально для защиты от обнаружения и блокировок. - -Протокол OpenVPN обеспечивает безопасное VPN-соединение за счет шифрования всего интернет-трафика между клиентом и сервером. - -Плагин Cloak защищает OpenVPN от обнаружения и блокировок. - -Cloak может изменять метаданные пакетов. Он полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью Active Probing. Это делает его очень устойчивым к обнаружению - -Сразу же после получения первого пакета данных Cloak проверяет подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под поддельный сайт, и ваш VPN становится невидимым для аналитических систем. - -Если в вашем регионе экстремальный уровень цензуры в Интернете, мы советуем вам с первого подключения использовать только OpenVPN over Cloak - -* Доступен в AmneziaVPN для всех платформ -* Высокое энергопотребление на мобильных устройствах -* Гибкие настройки -* Не распознается системами DPI-анализа -* Работает по сетевому протоколу TCP, 443 порт. - - - - A relatively new popular VPN protocol with a simplified architecture. -Provides stable VPN connection, high performance on all devices. Uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. -WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. - -* Available in the AmneziaVPN across all platforms -* Low power consumption -* Minimum number of settings -* Easily recognised by DPI analysis systems, susceptible to blocking -* Works over UDP network protocol. - WireGuard - относительно новый популярный VPN-протокол с упрощенной архитектурой. -Обеспечивает стабильное VPN-соединение, высокую производительность на всех устройствах. Использует жестко заданные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность при передаче данных. -WireGuard очень восприимчив к блокированию из-за особенностей сигнатур пакетов. В отличие от некоторых других VPN-протоколов, использующих методы обфускации, последовательные сигнатуры пакетов WireGuard легче выявляются и, соответственно, блокируются современными системами глубокой проверки пакетов (DPI) и другими средствами сетевого мониторинга. - -* Доступен в AmneziaVPN для всех платформ -* Низкое энергопотребление -* Минимальное количество настроек -* Легко распознается системами DPI-анализа, подвержен блокировке -* Работает по сетевому протоколу UDP. - - - A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. -While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. -This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. - -* Available in the AmneziaVPN across all platforms -* Low power consumption -* Minimum number of settings -* Not recognised by DPI analysis systems, resistant to blocking -* Works over UDP network protocol. - AmneziaWG — усовершенствованная версия популярного VPN-протокола WireGuard. AmneziaWG опирается на фундамент, заложенный WireGuard, сохраняя упрощенную архитектуру и высокую производительность на различных устройствах. -Хотя WireGuard известен своей эффективностью, у него были проблемы с обнаружением из-за характерных сигнатур пакетов. AmneziaWG решает эту проблему за счет использования более совершенных методов обфускации, благодаря чему его трафик сливается с обычным интернет-трафиком. -Таким образом, AmneziaWG сохраняет высокую производительность оригинального протокола, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение. - -* Доступен в AmneziaVPN на всех платформах -* Низкое энергопотребление на мобильных устройствах -* Минимальное количество настроек -* Не распознается системами DPI-анализа, устойчив к блокировке -* Работает по сетевому протоколу UDP AmneziaWG container @@ -4894,7 +4791,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin - + SOCKS5 proxy server Прокси-сервер SOCKS5 @@ -5308,12 +5205,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin Хочу просто повысить уровень приватности - + Automatic Автоматическая - + AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions. Будет установлен протокол AmneziaWG. Он обеспечивает высокую скорость соединения и гарантирует стабильную работу даже в самых сложных условиях. diff --git a/client/ui/models/api/apiServicesModel.cpp b/client/ui/models/api/apiServicesModel.cpp index 65f17758..b893096a 100644 --- a/client/ui/models/api/apiServicesModel.cpp +++ b/client/ui/models/api/apiServicesModel.cpp @@ -69,7 +69,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const "Access all websites and online resources. Speeds up to %1 Mbps.") .arg(speed); } else if (serviceType == serviceType::amneziaFree) { - QString description = tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan."); + QString description = tr("Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan."); if (!isServiceAvailable) { description += tr("

Not available in your region. If you have VPN enabled, disable it, " "return to the previous screen, and try again."); @@ -82,7 +82,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const return tr("Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. " "Access all websites and online resources."); } else { - return tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan."); + return tr("Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan."); } } case IsServiceAvailableRole: { diff --git a/client/ui/qml/Pages2/PageSettingsApiDevices.qml b/client/ui/qml/Pages2/PageSettingsApiDevices.qml index d8e24d42..231b58a0 100644 --- a/client/ui/qml/Pages2/PageSettingsApiDevices.qml +++ b/client/ui/qml/Pages2/PageSettingsApiDevices.qml @@ -77,7 +77,7 @@ PageType { } 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 descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing \"Reload API config\" in subscription settings on device.") var yesButtonText = qsTr("Continue") var noButtonText = qsTr("Cancel") diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index 48080c3a..93118755 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -333,7 +333,7 @@ PageType { clickedFunc: function() { 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 descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing \"Reload API config\" in subscription settings on device.") var yesButtonText = qsTr("Continue") var noButtonText = qsTr("Cancel") From 7e380b6cfb46ae23ab3682e5a676f8585e6d22e3 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Mon, 12 May 2025 05:36:25 -0700 Subject: [PATCH 2/5] OpenVPN with system disabled IPv6 (#1563) * Fix for Win OpenVPN with disabled IPv6 and AllExceptSites Splittunnel mode * Remove unneeded stuff for ipv6 openvpn --- client/configurators/openvpn_configurator.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index eca81afd..6d6603da 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -120,13 +120,6 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPairisSitesSplitTunnelingEnabled()) { config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n"); - -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) - // Prevent ipv6 leak - if (NetworkUtilities::checkIpv6Enabled()) { - config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n"); - } -#endif config.append("block-ipv6\n"); } else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { @@ -135,7 +128,6 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair Date: Mon, 12 May 2025 20:37:35 +0800 Subject: [PATCH 3/5] chore: update link to submodule (#1544) * chore: update link to submodule --- client/3rd-prebuilt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/3rd-prebuilt b/client/3rd-prebuilt index efad1a5b..0f3748ef 160000 --- a/client/3rd-prebuilt +++ b/client/3rd-prebuilt @@ -1 +1 @@ -Subproject commit efad1a5b5cb8e8ab61e49ccdca18c9090a0da8d3 +Subproject commit 0f3748efd7cc04e0c914304b68931f925bed1259 From a28ed6a977c6104004061665b9be9ad99dc6525c Mon Sep 17 00:00:00 2001 From: Mitternacht822 Date: Mon, 12 May 2025 18:14:59 +0400 Subject: [PATCH 4/5] feature: added the ability to change port after installing xray (#1556) * added the ability to change port after installing xray * fixed issue with not updating server config for xray on windows platform * fixed some warning in exportcontroller.cpp --- client/core/controllers/serverController.cpp | 15 ++++++++---- client/ui/controllers/exportController.cpp | 12 +++++----- .../ui/models/protocols/xrayConfigModel.cpp | 3 +++ client/ui/models/protocols/xrayConfigModel.h | 3 ++- .../qml/Pages2/PageProtocolXraySettings.qml | 23 ++++++++++++++++++- 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/client/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index 9ac62759..8ff6b6c8 100644 --- a/client/core/controllers/serverController.cpp +++ b/client/core/controllers/serverController.cpp @@ -138,7 +138,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) { e = runScript(credentials, - replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path), + replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, path), genVarsForScript(credentials, container)), cbReadStd, cbReadStd); @@ -146,7 +146,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, return e; } else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) { e = runScript(credentials, - replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName), + replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, tmpFileName), genVarsForScript(credentials, container)), cbReadStd, cbReadStd); @@ -154,7 +154,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, return e; e = runScript(credentials, - replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path), + replaceVars(QStringLiteral("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName, path), genVarsForScript(credentials, container)), cbReadStd, cbReadStd); @@ -177,7 +177,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container, errorCode = ErrorCode::NoError; - QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"").arg(ContainerProps::containerToString(container)).arg(path); + QString script = QStringLiteral("sudo docker exec -i %1 sh -c \"xxd -p '%2'\"").arg(ContainerProps::containerToString(container), path); QString stdOut; auto cbReadStdOut = [&](const QString &data, libssh::Client &) { @@ -383,6 +383,13 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c return true; } + if (container == DockerContainer::Xray) { + if (oldProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort) + != newProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)) { + return true; + } + } + return false; } diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index b47111ae..c487e3b2 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -145,7 +145,7 @@ void ExportController::generateOpenVpnConfig(const QString &clientName) } QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n"); - for (const QString &line : lines) { + for (const QString &line : std::as_const(lines)) { m_config.append(line + "\n"); } @@ -163,7 +163,7 @@ void ExportController::generateWireGuardConfig(const QString &clientName) } QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n"); - for (const QString &line : lines) { + for (const QString &line : std::as_const(lines)) { m_config.append(line + "\n"); } @@ -183,7 +183,7 @@ void ExportController::generateAwgConfig(const QString &clientName) } QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n"); - for (const QString &line : lines) { + for (const QString &line : std::as_const(lines)) { m_config.append(line + "\n"); } @@ -211,7 +211,7 @@ void ExportController::generateShadowSocksConfig() } QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n"); - for (const QString &line : lines) { + for (const QString &line : std::as_const(lines)) { m_config.append(line + "\n"); } @@ -240,7 +240,7 @@ void ExportController::generateCloakConfig() nativeConfig.insert("ProxyMethod", "shadowsocks"); QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n"); - for (const QString &line : lines) { + for (const QString &line : std::as_const(lines)) { m_config.append(line + "\n"); } @@ -257,7 +257,7 @@ void ExportController::generateXrayConfig(const QString &clientName) } QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n"); - for (const QString &line : lines) { + for (const QString &line : std::as_const(lines)) { m_config.append(line + "\n"); } diff --git a/client/ui/models/protocols/xrayConfigModel.cpp b/client/ui/models/protocols/xrayConfigModel.cpp index 84bbb2f7..3917b544 100644 --- a/client/ui/models/protocols/xrayConfigModel.cpp +++ b/client/ui/models/protocols/xrayConfigModel.cpp @@ -20,6 +20,7 @@ bool XrayConfigModel::setData(const QModelIndex &index, const QVariant &value, i switch (role) { case Roles::SiteRole: m_protocolConfig.insert(config_key::site, value.toString()); break; + case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; } emit dataChanged(index, index, QList { role }); @@ -34,6 +35,7 @@ QVariant XrayConfigModel::data(const QModelIndex &index, int role) const switch (role) { case Roles::SiteRole: return m_protocolConfig.value(config_key::site).toString(protocols::xray::defaultSite); + case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::xray::defaultPort); } return QVariant(); @@ -67,6 +69,7 @@ QHash XrayConfigModel::roleNames() const QHash roles; roles[SiteRole] = "site"; + roles[PortRole] = "port"; return roles; } diff --git a/client/ui/models/protocols/xrayConfigModel.h b/client/ui/models/protocols/xrayConfigModel.h index cb57f858..41aac589 100644 --- a/client/ui/models/protocols/xrayConfigModel.h +++ b/client/ui/models/protocols/xrayConfigModel.h @@ -12,7 +12,8 @@ class XrayConfigModel : public QAbstractListModel public: enum Roles { - SiteRole + SiteRole, + PortRole }; explicit XrayConfigModel(QObject *parent = nullptr); diff --git a/client/ui/qml/Pages2/PageProtocolXraySettings.qml b/client/ui/qml/Pages2/PageProtocolXraySettings.qml index bef84af7..d22e31a2 100644 --- a/client/ui/qml/Pages2/PageProtocolXraySettings.qml +++ b/client/ui/qml/Pages2/PageProtocolXraySettings.qml @@ -103,8 +103,29 @@ PageType { } } + TextFieldWithHeaderType { + id: portTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + enabled: delegateItem.isEnabled + + headerText: qsTr("Port") + textField.text: port + textField.maximumLength: 5 + textField.validator: IntValidator { bottom: 1; top: 65535 } + + textField.onEditingFinished: { + if (textField.text !== port) { + port = textField.text + } + } + + checkEmptyText: true + } + BasicButtonType { - id: basicButton + id: saveButton Layout.fillWidth: true Layout.topMargin: 24 Layout.bottomMargin: 24 From b457ef9a3f7f1c74f4898a3acd9e1955f80110b3 Mon Sep 17 00:00:00 2001 From: Nethius Date: Tue, 13 May 2025 12:29:33 +0800 Subject: [PATCH 5/5] feature/premium v1 migration (#1569) * feature: premium v1 migration * chore: added stage for macos with new qt version * chore: downgrade qif version * chore: minor ui fixes --- .github/workflows/deploy.yml | 82 +++++++- .github/workflows/tag-deploy.yml | 2 + client/CMakeLists.txt | 3 + client/core/api/apiDefs.h | 11 + client/core/api/apiUtils.cpp | 68 +++++- client/core/api/apiUtils.h | 2 + client/core/controllers/coreController.cpp | 28 ++- client/core/controllers/coreController.h | 4 + client/core/defs.h | 1 + client/core/errorstrings.cpp | 1 + client/resources.qrc | 3 + client/settings.cpp | 10 + client/settings.h | 3 + .../api/apiPremV1MigrationController.cpp | 127 ++++++++++++ .../api/apiPremV1MigrationController.h | 50 +++++ client/ui/models/servers_model.cpp | 19 ++ client/ui/models/servers_model.h | 1 + .../Components/ApiPremV1MigrationDrawer.qml | 194 ++++++++++++++++++ .../qml/Components/ApiPremV1SubListDrawer.qml | 89 ++++++++ client/ui/qml/Components/OtpCodeDrawer.qml | 77 +++++++ client/ui/qml/Components/QuestionDrawer.qml | 20 +- client/ui/qml/Pages2/PageHome.qml | 30 +++ .../ui/qml/Pages2/PageSettingsServerData.qml | 18 ++ 23 files changed, 829 insertions(+), 14 deletions(-) create mode 100644 client/ui/controllers/api/apiPremV1MigrationController.cpp create mode 100644 client/ui/controllers/api/apiPremV1MigrationController.h create mode 100644 client/ui/qml/Components/ApiPremV1MigrationDrawer.qml create mode 100644 client/ui/qml/Components/ApiPremV1SubListDrawer.qml create mode 100644 client/ui/qml/Components/OtpCodeDrawer.qml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ae8768a6..86779f33 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -20,6 +20,8 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Install Qt' @@ -90,6 +92,8 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Get sources' @@ -156,6 +160,8 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Setup xcode' @@ -243,7 +249,7 @@ jobs: # ------------------------------------------------------ - Build-MacOS: + Build-MacOS-old: runs-on: macos-latest env: @@ -255,6 +261,78 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} + + steps: + - name: 'Setup xcode' + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.4.0' + + - name: 'Install Qt' + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + host: 'mac' + target: 'desktop' + arch: 'clang_64' + modules: 'qtremoteobjects qt5compat qtshadertools' + dir: ${{ runner.temp }} + setup-python: 'true' + set-env: 'true' + extra: '--external 7z --base ${{ env.QT_MIRROR }}' + + - name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}' + run: | + mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework + wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip + unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/ + + - name: 'Get sources' + uses: actions/checkout@v4 + with: + submodules: 'true' + fetch-depth: 10 + + - name: 'Setup ccache' + uses: hendrikmuhs/ccache-action@v1.2 + + - name: 'Build project' + run: | + export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin" + export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin" + bash deploy/build_macos.sh + + - name: 'Upload installer artifact' + uses: actions/upload-artifact@v4 + with: + name: AmneziaVPN_MacOS_old_installer + path: AmneziaVPN.dmg + retention-days: 7 + + - name: 'Upload unpacked artifact' + uses: actions/upload-artifact@v4 + with: + name: AmneziaVPN_MacOS_old_unpacked + path: deploy/build/client/AmneziaVPN.app + retention-days: 7 + +# ------------------------------------------------------ + + Build-MacOS: + runs-on: macos-latest + + env: + QT_VERSION: 6.8.0 + QIF_VERSION: 4.8.1 + PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} + PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} + DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} + DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} + DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Setup xcode' @@ -324,6 +402,8 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Install desktop Qt' diff --git a/.github/workflows/tag-deploy.yml b/.github/workflows/tag-deploy.yml index 2bcbd8c6..31c334bf 100644 --- a/.github/workflows/tag-deploy.yml +++ b/.github/workflows/tag-deploy.yml @@ -20,6 +20,8 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Install desktop Qt' diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index b3f775a0..a454142d 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -31,6 +31,9 @@ add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}") add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}") add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}") +add_definitions(-DFREE_V2_ENDPOINT="$ENV{FREE_V2_ENDPOINT}") +add_definitions(-DPREM_V1_ENDPOINT="$ENV{PREM_V1_ENDPOINT}") + if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) set(PACKAGES ${PACKAGES} Widgets) endif() diff --git a/client/core/api/apiDefs.h b/client/core/api/apiDefs.h index d1a92d9d..6d1a27fa 100644 --- a/client/core/api/apiDefs.h +++ b/client/core/api/apiDefs.h @@ -22,12 +22,19 @@ namespace apiDefs namespace key { constexpr QLatin1String configVersion("config_version"); + constexpr QLatin1String apiEndpoint("api_endpoint"); + constexpr QLatin1String apiKey("api_key"); + constexpr QLatin1String description("description"); + constexpr QLatin1String name("name"); + constexpr QLatin1String protocol("protocol"); constexpr QLatin1String apiConfig("api_config"); constexpr QLatin1String stackType("stack_type"); constexpr QLatin1String serviceType("service_type"); constexpr QLatin1String vpnKey("vpn_key"); + constexpr QLatin1String config("config"); + constexpr QLatin1String configs("configs"); constexpr QLatin1String installationUuid("installation_uuid"); constexpr QLatin1String workerLastUpdated("worker_last_updated"); @@ -51,6 +58,10 @@ namespace apiDefs constexpr QLatin1String website("website"); constexpr QLatin1String websiteName("website_name"); constexpr QLatin1String telegram("telegram"); + + constexpr QLatin1String id("id"); + constexpr QLatin1String orderId("order_id"); + constexpr QLatin1String migrationCode("migration_code"); } const int requestTimeoutMsecs = 12 * 1000; // 12 secs diff --git a/client/core/api/apiUtils.cpp b/client/core/api/apiUtils.cpp index f5f575c5..f85d2207 100644 --- a/client/core/api/apiUtils.cpp +++ b/client/core/api/apiUtils.cpp @@ -3,6 +3,24 @@ #include #include +namespace +{ + const QByteArray AMNEZIA_CONFIG_SIGNATURE = QByteArray::fromHex("000000ff"); + + QString escapeUnicode(const QString &input) + { + QString output; + for (QChar c : input) { + if (c.unicode() < 0x20 || c.unicode() > 0x7E) { + output += QString("\\u%1").arg(QString::number(c.unicode(), 16).rightJustified(4, '0')); + } else { + output += c; + } + } + return output; + } +} + bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate) { QDateTime now = QDateTime::currentDateTime(); @@ -27,22 +45,28 @@ apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObjec case apiDefs::ConfigSource::Telegram: { }; case apiDefs::ConfigSource::AmneziaGateway: { - constexpr QLatin1String stackPremium("prem"); - constexpr QLatin1String stackFree("free"); - constexpr QLatin1String servicePremium("amnezia-premium"); constexpr QLatin1String serviceFree("amnezia-free"); constexpr QLatin1String serviceExternalPremium("external-premium"); + constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT); + constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT); + auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject(); auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString(); + auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString(); + if (serviceType == servicePremium) { return apiDefs::ConfigType::AmneziaPremiumV2; } else if (serviceType == serviceFree) { return apiDefs::ConfigType::AmneziaFreeV3; } else if (serviceType == serviceExternalPremium) { return apiDefs::ConfigType::ExternalPremium; + } else if (apiEndpoint.contains(premiumV1Endpoint)) { + return apiDefs::ConfigType::AmneziaPremiumV1; + } else if (apiEndpoint.contains(freeV2Endpoint)) { + return apiDefs::ConfigType::AmneziaFreeV2; } } default: { @@ -95,3 +119,41 @@ bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject) apiDefs::ConfigType::ExternalPremium }; return premiumTypes.contains(getConfigType(serverConfigObject)); } + +QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject) +{ + if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) { + return {}; + } + + QList> orderedFields; + orderedFields.append(qMakePair(apiDefs::key::name, serverConfigObject[apiDefs::key::name].toString())); + orderedFields.append(qMakePair(apiDefs::key::description, serverConfigObject[apiDefs::key::description].toString())); + orderedFields.append(qMakePair(apiDefs::key::configVersion, serverConfigObject[apiDefs::key::configVersion].toDouble())); + orderedFields.append(qMakePair(apiDefs::key::protocol, serverConfigObject[apiDefs::key::protocol].toString())); + orderedFields.append(qMakePair(apiDefs::key::apiEndpoint, serverConfigObject[apiDefs::key::apiEndpoint].toString())); + orderedFields.append(qMakePair(apiDefs::key::apiKey, serverConfigObject[apiDefs::key::apiKey].toString())); + + QString vpnKeyStr = "{"; + for (int i = 0; i < orderedFields.size(); ++i) { + const auto &pair = orderedFields[i]; + if (pair.second.typeId() == QMetaType::Type::QString) { + vpnKeyStr += "\"" + pair.first + "\": \"" + pair.second.toString() + "\""; + } else if (pair.second.typeId() == QMetaType::Type::Double || pair.second.typeId() == QMetaType::Type::Int) { + vpnKeyStr += "\"" + pair.first + "\": " + QString::number(pair.second.toDouble(), 'f', 1); + } + + if (i < orderedFields.size() - 1) { + vpnKeyStr += ", "; + } + } + vpnKeyStr += "}"; + + QByteArray vpnKeyCompressed = escapeUnicode(vpnKeyStr).toUtf8(); + vpnKeyCompressed = qCompress(vpnKeyCompressed, 6); + vpnKeyCompressed = vpnKeyCompressed.mid(4); + + QByteArray signedData = AMNEZIA_CONFIG_SIGNATURE + vpnKeyCompressed; + + return QString("vpn://%1").arg(QString(signedData.toBase64(QByteArray::Base64UrlEncoding))); +} diff --git a/client/core/api/apiUtils.h b/client/core/api/apiUtils.h index 47006e80..45eaf2de 100644 --- a/client/core/api/apiUtils.h +++ b/client/core/api/apiUtils.h @@ -19,6 +19,8 @@ namespace apiUtils apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject); amnezia::ErrorCode checkNetworkReplyErrors(const QList &sslErrors, QNetworkReply *reply); + + QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject); } #endif // APIUTILS_H diff --git a/client/core/controllers/coreController.cpp b/client/core/controllers/coreController.cpp index f2c09d45..0e72ef1a 100644 --- a/client/core/controllers/coreController.cpp +++ b/client/core/controllers/coreController.cpp @@ -148,6 +148,9 @@ void CoreController::initControllers() m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings)); m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get()); + + m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this)); + m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get()); } void CoreController::initAndroidController() @@ -220,6 +223,8 @@ void CoreController::initSignalHandlers() initAutoConnectHandler(); initAmneziaDnsToggledHandler(); initPrepareConfigHandler(); + initImportPremiumV2VpnKeyHandler(); + initShowMigrationDrawerHandler(); initStrictKillSwitchHandler(); } @@ -363,10 +368,29 @@ void CoreController::initPrepareConfigHandler() }); } +void CoreController::initImportPremiumV2VpnKeyHandler() +{ + connect(m_apiPremV1MigrationController.get(), &ApiPremV1MigrationController::importPremiumV2VpnKey, this, [this](const QString &vpnKey) { + m_importController->extractConfigFromData(vpnKey); + m_importController->importConfig(); + + emit m_apiPremV1MigrationController->migrationFinished(); + }); +} + +void CoreController::initShowMigrationDrawerHandler() +{ + QTimer::singleShot(1000, this, [this]() { + if (m_apiPremV1MigrationController->isPremV1MigrationReminderActive() && m_apiPremV1MigrationController->hasConfigsToMigration()) { + m_apiPremV1MigrationController->showMigrationDrawer(); + } + }); +} + void CoreController::initStrictKillSwitchHandler() { - connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, - m_vpnConnection.get(), &VpnConnection::onKillSwitchModeChanged); + connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, m_vpnConnection.get(), + &VpnConnection::onKillSwitchModeChanged); } QSharedPointer CoreController::pageController() const diff --git a/client/core/controllers/coreController.h b/client/core/controllers/coreController.h index 6342d738..9ae53562 100644 --- a/client/core/controllers/coreController.h +++ b/client/core/controllers/coreController.h @@ -7,6 +7,7 @@ #include "ui/controllers/api/apiConfigsController.h" #include "ui/controllers/api/apiSettingsController.h" +#include "ui/controllers/api/apiPremV1MigrationController.h" #include "ui/controllers/appSplitTunnelingController.h" #include "ui/controllers/allowedDnsController.h" #include "ui/controllers/connectionController.h" @@ -82,6 +83,8 @@ private: void initAutoConnectHandler(); void initAmneziaDnsToggledHandler(); void initPrepareConfigHandler(); + void initImportPremiumV2VpnKeyHandler(); + void initShowMigrationDrawerHandler(); void initStrictKillSwitchHandler(); QQmlApplicationEngine *m_engine {}; // TODO use parent child system here? @@ -109,6 +112,7 @@ private: QScopedPointer m_apiSettingsController; QScopedPointer m_apiConfigsController; + QScopedPointer m_apiPremV1MigrationController; QSharedPointer m_containersModel; QSharedPointer m_defaultServerContainersModel; diff --git a/client/core/defs.h b/client/core/defs.h index eff3df3b..674d1add 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -117,6 +117,7 @@ namespace amnezia ApiServicesMissingError = 1107, ApiConfigLimitError = 1108, ApiNotFoundError = 1109, + ApiMigrationError = 1110, // QFile errors OpenError = 1200, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index 6abab0e0..f330bc34 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -74,6 +74,7 @@ QString errorString(ErrorCode code) { case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break; case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break; case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break; + case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error occurred. Please contact our technical support"); break; // QFile errors case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; diff --git a/client/resources.qrc b/client/resources.qrc index a36b60d1..72eb15c7 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -236,6 +236,9 @@ ui/qml/Pages2/PageSettingsApiNativeConfigs.qml ui/qml/Pages2/PageSettingsApiDevices.qml images/controls/monitor.svg + ui/qml/Components/ApiPremV1MigrationDrawer.qml + ui/qml/Components/ApiPremV1SubListDrawer.qml + ui/qml/Components/OtpCodeDrawer.qml images/flagKit/ZW.svg diff --git a/client/settings.cpp b/client/settings.cpp index 9a0a32e5..fb9c72c1 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -559,6 +559,16 @@ void Settings::disableHomeAdLabel() setValue("Conf/homeAdLabelVisible", false); } +bool Settings::isPremV1MigrationReminderActive() +{ + return value("Conf/premV1MigrationReminderActive", true).toBool(); +} + +void Settings::disablePremV1MigrationReminder() +{ + setValue("Conf/premV1MigrationReminderActive", false); +} + QStringList Settings::allowedDnsServers() const { return value("Conf/allowedDnsServers").toStringList(); diff --git a/client/settings.h b/client/settings.h index 01155c0c..eec6cc44 100644 --- a/client/settings.h +++ b/client/settings.h @@ -229,6 +229,9 @@ public: bool isHomeAdLabelVisible(); void disableHomeAdLabel(); + bool isPremV1MigrationReminderActive(); + void disablePremV1MigrationReminder(); + QStringList allowedDnsServers() const; void setAllowedDnsServers(const QStringList &servers); diff --git a/client/ui/controllers/api/apiPremV1MigrationController.cpp b/client/ui/controllers/api/apiPremV1MigrationController.cpp new file mode 100644 index 00000000..0a9b6139 --- /dev/null +++ b/client/ui/controllers/api/apiPremV1MigrationController.cpp @@ -0,0 +1,127 @@ +#include "apiPremV1MigrationController.h" + +#include +#include + +#include "core/api/apiDefs.h" +#include "core/api/apiUtils.h" +#include "core/controllers/gatewayController.h" + +ApiPremV1MigrationController::ApiPremV1MigrationController(const QSharedPointer &serversModel, + const std::shared_ptr &settings, QObject *parent) + : QObject(parent), m_serversModel(serversModel), m_settings(settings) +{ +} + +bool ApiPremV1MigrationController::hasConfigsToMigration() +{ + QJsonArray vpnKeys; + + auto serversCount = m_serversModel->getServersCount(); + for (size_t i = 0; i < serversCount; i++) { + auto serverConfigObject = m_serversModel->getServerConfig(i); + + if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) { + continue; + } + + QString vpnKey = apiUtils::getPremiumV1VpnKey(serverConfigObject); + vpnKeys.append(vpnKey); + } + + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + QJsonObject apiPayload; + + apiPayload["configs"] = vpnKeys; + QByteArray responseBody; + ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/is-active-subscription"), apiPayload, responseBody); + + auto migrationsStatus = QJsonDocument::fromJson(responseBody).object(); + for (const auto &migrationStatus : migrationsStatus) { + if (migrationStatus == "not_found") { + return true; + } + } + + return false; +} + +void ApiPremV1MigrationController::getSubscriptionList(const QString &email) +{ + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + QJsonObject apiPayload; + + apiPayload[apiDefs::key::email] = email; + QByteArray responseBody; + ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/subscription-list"), apiPayload, responseBody); + + if (errorCode == ErrorCode::NoError) { + m_email = email; + m_subscriptionsModel = QJsonDocument::fromJson(responseBody).array(); + if (m_subscriptionsModel.isEmpty()) { + emit noSubscriptionToMigrate(); + return; + } + + emit subscriptionsModelChanged(); + } else { + emit errorOccurred(ErrorCode::ApiMigrationError); + } +} + +QJsonArray ApiPremV1MigrationController::getSubscriptionModel() +{ + return m_subscriptionsModel; +} + +void ApiPremV1MigrationController::sendMigrationCode(const int subscriptionIndex) +{ + QEventLoop wait; + QTimer::singleShot(1000, &wait, &QEventLoop::quit); + wait.exec(); + + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + QJsonObject apiPayload; + + apiPayload[apiDefs::key::email] = m_email; + QByteArray responseBody; + ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/migration-code"), apiPayload, responseBody); + + if (errorCode == ErrorCode::NoError) { + m_subscriptionIndex = subscriptionIndex; + emit otpSuccessfullySent(); + } else { + emit errorOccurred(ErrorCode::ApiMigrationError); + } +} + +void ApiPremV1MigrationController::migrate(const QString &migrationCode) +{ + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + QJsonObject apiPayload; + + apiPayload[apiDefs::key::email] = m_email; + apiPayload[apiDefs::key::orderId] = m_subscriptionsModel.at(m_subscriptionIndex).toObject().value(apiDefs::key::id).toString(); + apiPayload[apiDefs::key::migrationCode] = migrationCode; + QByteArray responseBody; + ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/migrate"), apiPayload, responseBody); + + if (errorCode == ErrorCode::NoError) { + auto responseObject = QJsonDocument::fromJson(responseBody).object(); + QString premiumV2VpnKey = responseObject.value(apiDefs::key::config).toString(); + + emit importPremiumV2VpnKey(premiumV2VpnKey); + } else { + emit errorOccurred(ErrorCode::ApiMigrationError); + } +} + +bool ApiPremV1MigrationController::isPremV1MigrationReminderActive() +{ + return m_settings->isPremV1MigrationReminderActive(); +} + +void ApiPremV1MigrationController::disablePremV1MigrationReminder() +{ + m_settings->disablePremV1MigrationReminder(); +} diff --git a/client/ui/controllers/api/apiPremV1MigrationController.h b/client/ui/controllers/api/apiPremV1MigrationController.h new file mode 100644 index 00000000..d7c10460 --- /dev/null +++ b/client/ui/controllers/api/apiPremV1MigrationController.h @@ -0,0 +1,50 @@ +#ifndef APIPREMV1MIGRATIONCONTROLLER_H +#define APIPREMV1MIGRATIONCONTROLLER_H + +#include + +#include "ui/models/servers_model.h" + +class ApiPremV1MigrationController : public QObject +{ + Q_OBJECT +public: + ApiPremV1MigrationController(const QSharedPointer &serversModel, const std::shared_ptr &settings, + QObject *parent = nullptr); + + Q_PROPERTY(QJsonArray subscriptionsModel READ getSubscriptionModel NOTIFY subscriptionsModelChanged) + +public slots: + bool hasConfigsToMigration(); + void getSubscriptionList(const QString &email); + QJsonArray getSubscriptionModel(); + void sendMigrationCode(const int subscriptionIndex); + void migrate(const QString &migrationCode); + + bool isPremV1MigrationReminderActive(); + void disablePremV1MigrationReminder(); + +signals: + void subscriptionsModelChanged(); + + void otpSuccessfullySent(); + + void importPremiumV2VpnKey(const QString &vpnKey); + + void errorOccurred(ErrorCode errorCode); + + void showMigrationDrawer(); + void migrationFinished(); + + void noSubscriptionToMigrate(); + +private: + QSharedPointer m_serversModel; + std::shared_ptr m_settings; + + QJsonArray m_subscriptionsModel; + int m_subscriptionIndex; + QString m_email; +}; + +#endif // APIPREMV1MIGRATIONCONTROLLER_H diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 7cde28b4..0a7b2526 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -348,6 +348,25 @@ void ServersModel::removeServer() endResetModel(); } +void ServersModel::removeServer(const int serverIndex) +{ + beginResetModel(); + m_settings->removeServer(serverIndex); + m_servers = m_settings->serversArray(); + + if (m_settings->defaultServerIndex() == serverIndex) { + setDefaultServerIndex(0); + } else if (m_settings->defaultServerIndex() > serverIndex) { + setDefaultServerIndex(m_settings->defaultServerIndex() - 1); + } + + if (m_settings->serversCount() == 0) { + setDefaultServerIndex(-1); + } + setProcessedServerIndex(m_defaultServerIndex); + endResetModel(); +} + QHash ServersModel::roleNames() const { QHash roles; diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index 4b790c7a..c4803708 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -90,6 +90,7 @@ public slots: void addServer(const QJsonObject &server); void editServer(const QJsonObject &server, const int serverIndex); void removeServer(); + void removeServer(const int serverIndex); QJsonObject getServerConfig(const int serverIndex); diff --git a/client/ui/qml/Components/ApiPremV1MigrationDrawer.qml b/client/ui/qml/Components/ApiPremV1MigrationDrawer.qml new file mode 100644 index 00000000..113ec1f6 --- /dev/null +++ b/client/ui/qml/Components/ApiPremV1MigrationDrawer.qml @@ -0,0 +1,194 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import QtCore + +import PageEnum 1.0 +import Style 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +DrawerType2 { + id: root + + expandedHeight: parent.height * 0.9 + + Connections { + target: ApiPremV1MigrationController + + function onErrorOccurred(error, goToPageHome) { + PageController.showErrorMessage(error) + root.closeTriggered() + } + } + + expandedStateContent: Item { + implicitHeight: root.expandedHeight + + ListViewType { + id: listView + + anchors.fill: parent + + model: 1 // fake model to force the ListView to be created without a model + snapMode: ListView.NoSnap + + header: ColumnLayout { + width: listView.width + + Header2Type { + id: header + Layout.fillWidth: true + Layout.topMargin: 20 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + headerText: qsTr("Switch to the new Amnezia Premium subscription") + } + } + + delegate: ColumnLayout { + width: listView.width + + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 16 + anchors.rightMargin: 16 + + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 24 + Layout.bottomMargin: 24 + + horizontalAlignment: Text.AlignLeft + textFormat: Text.RichText + text: { + var str = qsTr("We'll preserve all remaining days of your current subscription and give you an extra month as a thank you. ") + str += qsTr("This new subscription type will be actively developed with more locations and features added regularly. Currently available:") + str += "

" + str += qsTr("Old keys will be deactivated after switching.") + } + } + + TextFieldWithHeaderType { + id: emailLabel + Layout.fillWidth: true + + borderColor: AmneziaStyle.color.mutedGray + headerTextColor: AmneziaStyle.color.paleGray + + headerText: qsTr("Email") + textField.placeholderText: qsTr("mail@example.com") + + + textField.onFocusChanged: { + textField.text = textField.text.replace(/^\s+|\s+$/g, '') + } + + Connections { + target: ApiPremV1MigrationController + + function onNoSubscriptionToMigrate() { + emailLabel.errorText = qsTr("No old format subscriptions for a given email") + } + } + } + + CaptionTextType { + Layout.fillWidth: true + Layout.topMargin: 16 + + color: AmneziaStyle.color.mutedGray + + text: qsTr("Enter the email you used for your current subscription") + } + + ApiPremV1SubListDrawer { + id: apiPremV1SubListDrawer + parent: root + + anchors.fill: parent + } + + OtpCodeDrawer { + id: otpCodeDrawer + parent: root + + anchors.fill: parent + } + + BasicButtonType { + id: yesButton + Layout.fillWidth: true + Layout.topMargin: 32 + + text: qsTr("Continue") + + clickedFunc: function() { + PageController.showBusyIndicator(true) + ApiPremV1MigrationController.getSubscriptionList(emailLabel.textField.text) + PageController.showBusyIndicator(false) + } + } + + BasicButtonType { + id: noButton + Layout.fillWidth: true + + defaultColor: AmneziaStyle.color.transparent + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.paleGray + borderWidth: 1 + + text: qsTr("Remind me later") + + clickedFunc: function() { + root.closeTriggered() + } + } + + BasicButtonType { + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: 32 + Layout.bottomMargin: 32 + implicitHeight: 32 + + defaultColor: "transparent" + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + textColor: AmneziaStyle.color.vibrantRed + + text: qsTr("Don't remind me again") + + clickedFunc: function() { + var headerText = qsTr("No more reminders? You can always switch to the new format in the server settings") + var yesButtonText = qsTr("Continue") + var noButtonText = qsTr("Cancel") + + var yesButtonFunction = function() { + ApiPremV1MigrationController.disablePremV1MigrationReminder() + root.closeTriggered() + } + var noButtonFunction = function() { + } + + showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + } + } + } + } + } +} diff --git a/client/ui/qml/Components/ApiPremV1SubListDrawer.qml b/client/ui/qml/Components/ApiPremV1SubListDrawer.qml new file mode 100644 index 00000000..221b7a89 --- /dev/null +++ b/client/ui/qml/Components/ApiPremV1SubListDrawer.qml @@ -0,0 +1,89 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Style 1.0 + +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" + +DrawerType2 { + id: root + + Connections { + target: ApiPremV1MigrationController + + function onSubscriptionsModelChanged() { + if (ApiPremV1MigrationController.subscriptionsModel.length > 1) { + root.openTriggered() + } else { + sendMigrationCode(0) + } + } + } + + function sendMigrationCode(index) { + PageController.showBusyIndicator(true) + ApiPremV1MigrationController.sendMigrationCode(index) + root.closeTriggered() + PageController.showBusyIndicator(false) + } + + expandedHeight: parent.height * 0.9 + + expandedStateContent: Item { + implicitHeight: root.expandedHeight + + ListViewType { + id: listView + + anchors.fill: parent + + model: ApiPremV1MigrationController.subscriptionsModel + + header: ColumnLayout { + width: listView.width + + Header2Type { + id: header + Layout.fillWidth: true + Layout.topMargin: 20 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + headerText: qsTr("Choose Subscription") + } + } + + delegate: Item { + implicitWidth: listView.width + implicitHeight: delegateContent.implicitHeight + + ColumnLayout { + id: delegateContent + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + LabelWithButtonType { + id: server + Layout.fillWidth: true + + text: qsTr("Order ID: ") + modelData.id + + descriptionText: qsTr("Purchase Date: ") + Qt.formatDateTime(new Date(modelData.created_at), "dd.MM.yyyy hh:mm") + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + sendMigrationCode(index) + } + } + + DividerType {} + } + } + } + } +} diff --git a/client/ui/qml/Components/OtpCodeDrawer.qml b/client/ui/qml/Components/OtpCodeDrawer.qml new file mode 100644 index 00000000..e26982b9 --- /dev/null +++ b/client/ui/qml/Components/OtpCodeDrawer.qml @@ -0,0 +1,77 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Style 1.0 + +import "../Controls2" +import "../Controls2/TextTypes" + +import "../Config" + +DrawerType2 { + id: root + + Connections { + target: ApiPremV1MigrationController + + function onOtpSuccessfullySent() { + root.openTriggered() + } + } + + expandedHeight: parent.height * 0.6 + + expandedStateContent: Item { + implicitHeight: root.expandedHeight + + ColumnLayout { + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 16 + anchors.rightMargin: 16 + spacing: 0 + + Header2Type { + id: header + Layout.fillWidth: true + Layout.topMargin: 20 + + headerText: qsTr("OTP code was sent to your email") + } + + TextFieldWithHeaderType { + id: otpFiled + + borderColor: AmneziaStyle.color.mutedGray + headerTextColor: AmneziaStyle.color.paleGray + + Layout.fillWidth: true + Layout.topMargin: 16 + headerText: qsTr("OTP Code") + textField.maximumLength: 30 + checkEmptyText: true + } + + BasicButtonType { + id: saveButton + + Layout.fillWidth: true + Layout.topMargin: 16 + + text: qsTr("Continue") + + clickedFunc: function() { + PageController.showBusyIndicator(true) + ApiPremV1MigrationController.migrate(otpFiled.textField.text) + PageController.showBusyIndicator(false) + root.closeTriggered() + } + } + } + } +} diff --git a/client/ui/qml/Components/QuestionDrawer.qml b/client/ui/qml/Components/QuestionDrawer.qml index 0c14e52d..ddf5cda4 100644 --- a/client/ui/qml/Components/QuestionDrawer.qml +++ b/client/ui/qml/Components/QuestionDrawer.qml @@ -1,3 +1,5 @@ +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -39,7 +41,7 @@ DrawerType2 { Layout.rightMargin: 16 Layout.leftMargin: 16 - text: headerText + text: root.headerText } ParagraphTextType { @@ -48,7 +50,7 @@ DrawerType2 { Layout.rightMargin: 16 Layout.leftMargin: 16 - text: descriptionText + text: root.descriptionText } BasicButtonType { @@ -58,11 +60,11 @@ DrawerType2 { Layout.rightMargin: 16 Layout.leftMargin: 16 - text: yesButtonText + text: root.yesButtonText clickedFunc: function() { - if (yesButtonFunction && typeof yesButtonFunction === "function") { - yesButtonFunction() + if (root.yesButtonFunction && typeof root.yesButtonFunction === "function") { + root.yesButtonFunction() } } } @@ -80,11 +82,13 @@ DrawerType2 { textColor: AmneziaStyle.color.paleGray borderWidth: 1 - text: noButtonText + visible: root.noButtonText !== "" + + text: root.noButtonText clickedFunc: function() { - if (noButtonFunction && typeof noButtonFunction === "function") { - noButtonFunction() + if (root.noButtonFunction && typeof root.noButtonFunction === "function") { + root.noButtonFunction() } } } diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index f7233a89..7934e5fb 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -33,6 +33,31 @@ PageType { } } + Connections { + + target: ApiPremV1MigrationController + + function onMigrationFinished() { + apiPremV1MigrationDrawer.closeTriggered() + + var headerText = qsTr("You've successfully switched to the new Amnezia Premium subscription!") + var descriptionText = qsTr("Old keys will no longer work. Please use your new subscription key to connect. \nThank you for staying with us!") + var yesButtonText = qsTr("Continue") + var noButtonText = "" + + var yesButtonFunction = function() { + } + var noButtonFunction = function() { + } + + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + } + + function onShowMigrationDrawer() { + apiPremV1MigrationDrawer.openTriggered() + } + } + Item { objectName: "homeColumnItem" @@ -429,4 +454,9 @@ PageType { } } } + + ApiPremV1MigrationDrawer { + id: apiPremV1MigrationDrawer + anchors.fill: parent + } } diff --git a/client/ui/qml/Pages2/PageSettingsServerData.qml b/client/ui/qml/Pages2/PageSettingsServerData.qml index 977e669e..995ca74b 100644 --- a/client/ui/qml/Pages2/PageSettingsServerData.qml +++ b/client/ui/qml/Pages2/PageSettingsServerData.qml @@ -257,6 +257,24 @@ PageType { DividerType { visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") } + + LabelWithButtonType { + id: labelWithButton6 + visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") + Layout.fillWidth: true + + text: qsTr("Switch to the new Amnezia Premium subscription") + textColor: AmneziaStyle.color.vibrantRed + + clickedFunction: function() { + PageController.goToPageHome() + ApiPremV1MigrationController.showMigrationDrawer() + } + } + + DividerType { + visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") + } } } }