mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-23 02:00:20 +07:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2acf199755 | |||
| a983d0504e | |||
| d0b8535395 | |||
| f84480cf56 | |||
| de7a026ec1 | |||
| a128c7d247 | |||
| f316f0e25a | |||
| ea5242e29b | |||
| b31a62c55f | |||
| 02e3107a23 | |||
| 1862850108 | |||
| f73792844c | |||
| a7199ca6f5 | |||
| 5e757cdd3b | |||
| 92af1f3268 | |||
| aad9d6dae2 | |||
| 423fe3fd4f |
@@ -10,10 +10,10 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Build-Linux-Ubuntu:
|
Build-Linux-Ubuntu:
|
||||||
runs-on: 4-core
|
runs-on: android-runner
|
||||||
|
|
||||||
env:
|
env:
|
||||||
QT_VERSION: 6.8.3
|
QT_VERSION: 6.10.1
|
||||||
QIF_VERSION: 4.7
|
QIF_VERSION: 4.7
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
|
|
||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install libxkbcommon-x11-0
|
sudo apt-get install libxkbcommon-x11-0 libsecret-1-dev
|
||||||
export QT_BIN_DIR=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64/bin
|
export QT_BIN_DIR=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64/bin
|
||||||
export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin
|
export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin
|
||||||
bash deploy/build_linux.sh
|
bash deploy/build_linux.sh
|
||||||
@@ -537,7 +537,7 @@ jobs:
|
|||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-Android:
|
Build-Android:
|
||||||
runs-on: 4-core
|
runs-on: android-runner
|
||||||
|
|
||||||
env:
|
env:
|
||||||
ANDROID_BUILD_PLATFORM: android-36
|
ANDROID_BUILD_PLATFORM: android-36
|
||||||
|
|||||||
@@ -12,19 +12,16 @@ jobs:
|
|||||||
Upload-S3:
|
Upload-S3:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ inputs.RELEASE_VERSION }}
|
fetch-depth: 0 # Fetch full history to access default branch
|
||||||
sparse-checkout: |
|
|
||||||
CMakeLists.txt
|
|
||||||
deploy/deploy_s3.sh
|
|
||||||
sparse-checkout-cone-mode: false
|
|
||||||
|
|
||||||
- name: Verify git tag
|
- name: Checkout tag and verify version
|
||||||
run: |
|
run: |
|
||||||
|
git checkout ${{ inputs.RELEASE_VERSION }}
|
||||||
TAG_NAME=${{ inputs.RELEASE_VERSION }}
|
TAG_NAME=${{ inputs.RELEASE_VERSION }}
|
||||||
CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/')
|
CMAKE_TAG=$(grep 'set(AMNEZIAVPN_VERSION' CMakeLists.txt | sed -E 's/.*AMNEZIAVPN_VERSION ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*/\1/')
|
||||||
if [[ "$TAG_NAME" == "$CMAKE_TAG" ]]; then
|
if [[ "$TAG_NAME" == "$CMAKE_TAG" ]]; then
|
||||||
echo "Git tag ($TAG_NAME) matches CMakeLists.txt version ($CMAKE_TAG)."
|
echo "Git tag ($TAG_NAME) matches CMakeLists.txt version ($CMAKE_TAG)."
|
||||||
else
|
else
|
||||||
@@ -32,6 +29,32 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Get deployment script from default branch
|
||||||
|
run: |
|
||||||
|
# Detect default branch (dev, main, or master)
|
||||||
|
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "")
|
||||||
|
if [[ -z "$DEFAULT_BRANCH" ]]; then
|
||||||
|
# Try to detect default branch (prioritize dev)
|
||||||
|
if git show-ref --verify --quiet refs/remotes/origin/dev; then
|
||||||
|
DEFAULT_BRANCH="dev"
|
||||||
|
elif git show-ref --verify --quiet refs/remotes/origin/main; then
|
||||||
|
DEFAULT_BRANCH="main"
|
||||||
|
elif git show-ref --verify --quiet refs/remotes/origin/master; then
|
||||||
|
DEFAULT_BRANCH="master"
|
||||||
|
else
|
||||||
|
echo "::error::Could not determine default branch"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "Default branch: $DEFAULT_BRANCH"
|
||||||
|
|
||||||
|
# Get the script from default branch
|
||||||
|
mkdir -p deploy
|
||||||
|
git show origin/$DEFAULT_BRANCH:deploy/deploy_s3.sh > deploy/deploy_s3.sh || \
|
||||||
|
(echo "::error::Could not fetch deploy_s3.sh from default branch ($DEFAULT_BRANCH)" && exit 1)
|
||||||
|
chmod +x deploy/deploy_s3.sh
|
||||||
|
echo "✓ Using deployment script from $DEFAULT_BRANCH branch"
|
||||||
|
|
||||||
- name: Setup Rclone
|
- name: Setup Rclone
|
||||||
uses: AnimMouse/setup-rclone@v1
|
uses: AnimMouse/setup-rclone@v1
|
||||||
with:
|
with:
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
set(AMNEZIAVPN_VERSION 4.8.12.8)
|
set(AMNEZIAVPN_VERSION 4.8.12.9)
|
||||||
|
|
||||||
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
||||||
DESCRIPTION "AmneziaVPN"
|
DESCRIPTION "AmneziaVPN"
|
||||||
@@ -12,7 +12,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
|||||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||||
|
|
||||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
set(APP_ANDROID_VERSION_CODE 2104)
|
set(APP_ANDROID_VERSION_CODE 2105)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
|
|||||||
+1
-1
Submodule client/3rd-prebuilt updated: adf5eb920f...579673b2ed
@@ -1,7 +1,10 @@
|
|||||||
package org.amnezia.vpn
|
package org.amnezia.vpn
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
@@ -11,7 +14,25 @@ private const val TAG = "TvFilePicker"
|
|||||||
|
|
||||||
class TvFilePicker : ComponentActivity() {
|
class TvFilePicker : ComponentActivity() {
|
||||||
|
|
||||||
private val fileChooseResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) {
|
private val fileChooseResultLauncher = registerForActivityResult(object : ActivityResultContracts.OpenDocument() {
|
||||||
|
override fun createIntent(context: Context, input: Array<String>): Intent {
|
||||||
|
val intent = super.createIntent(context, input)
|
||||||
|
|
||||||
|
val activitiesToResolveIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
context.packageManager.queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong()))
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
context.packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
|
||||||
|
}
|
||||||
|
if (activitiesToResolveIntent.all {
|
||||||
|
val name = it.activityInfo.packageName
|
||||||
|
name.startsWith("com.google.android.tv.frameworkpackagestubs") || name.startsWith("com.android.tv.frameworkpackagestubs")
|
||||||
|
}) {
|
||||||
|
throw ActivityNotFoundException()
|
||||||
|
}
|
||||||
|
return intent
|
||||||
|
}
|
||||||
|
}) {
|
||||||
setResult(RESULT_OK, Intent().apply { data = it })
|
setResult(RESULT_OK, Intent().apply { data = it })
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
@@ -31,7 +52,7 @@ class TvFilePicker : ComponentActivity() {
|
|||||||
private fun getFile() {
|
private fun getFile() {
|
||||||
try {
|
try {
|
||||||
Log.v(TAG, "getFile")
|
Log.v(TAG, "getFile")
|
||||||
fileChooseResultLauncher.launch("*/*")
|
fileChooseResultLauncher.launch(arrayOf("*/*"))
|
||||||
} catch (_: ActivityNotFoundException) {
|
} catch (_: ActivityNotFoundException) {
|
||||||
Log.w(TAG, "Activity not found")
|
Log.w(TAG, "Activity not found")
|
||||||
setResult(RESULT_CANCELED, Intent().apply { putExtra("activityNotFound", true) })
|
setResult(RESULT_CANCELED, Intent().apply { putExtra("activityNotFound", true) })
|
||||||
|
|||||||
@@ -419,6 +419,18 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
|
|||||||
cbReadStdOut, cbReadStdErr);
|
cbReadStdOut, cbReadStdErr);
|
||||||
|
|
||||||
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
|
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
|
||||||
|
if (container == DockerContainer::Awg2) {
|
||||||
|
QRegularExpression regex(R"(Linux\s+(\d+)\.(\d+)[^\d]*)");
|
||||||
|
QRegularExpressionMatch match = regex.match(stdOut);
|
||||||
|
if (match.hasMatch()) {
|
||||||
|
int majorVersion = match.captured(1).toInt();
|
||||||
|
int minorVersion = match.captured(2).toInt();
|
||||||
|
|
||||||
|
if (majorVersion < 4 || (majorVersion == 4 && minorVersion < 14)) {
|
||||||
|
return ErrorCode::ServerLinuxKernelTooOld;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (stdOut.contains("lock"))
|
if (stdOut.contains("lock"))
|
||||||
return ErrorCode::ServerPacketManagerError;
|
return ErrorCode::ServerPacketManagerError;
|
||||||
if (stdOut.contains("command not found"))
|
if (stdOut.contains("command not found"))
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ namespace amnezia
|
|||||||
ServerDockerOnCgroupsV2 = 211,
|
ServerDockerOnCgroupsV2 = 211,
|
||||||
ServerCgroupMountpoint = 212,
|
ServerCgroupMountpoint = 212,
|
||||||
DockerPullRateLimit = 213,
|
DockerPullRateLimit = 213,
|
||||||
|
ServerLinuxKernelTooOld = 214,
|
||||||
|
|
||||||
// Ssh connection errors
|
// Ssh connection errors
|
||||||
SshRequestDeniedError = 300,
|
SshRequestDeniedError = 300,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ QString errorString(ErrorCode code) {
|
|||||||
case(ErrorCode::ServerDockerOnCgroupsV2): errorMessage = QObject::tr("Docker error: runc doesn't work on cgroups v2"); break;
|
case(ErrorCode::ServerDockerOnCgroupsV2): errorMessage = QObject::tr("Docker error: runc doesn't work on cgroups v2"); break;
|
||||||
case(ErrorCode::ServerCgroupMountpoint): errorMessage = QObject::tr("Server error: cgroup mountpoint does not exist"); break;
|
case(ErrorCode::ServerCgroupMountpoint): errorMessage = QObject::tr("Server error: cgroup mountpoint does not exist"); break;
|
||||||
case(ErrorCode::DockerPullRateLimit): errorMessage = QObject::tr("Docker error: The pull rate limit has been reached"); break;
|
case(ErrorCode::DockerPullRateLimit): errorMessage = QObject::tr("Docker error: The pull rate limit has been reached"); break;
|
||||||
|
case(ErrorCode::ServerLinuxKernelTooOld): errorMessage = QObject::tr("Server error: Linux kernel is too old"); break;
|
||||||
|
|
||||||
// Libssh errors
|
// Libssh errors
|
||||||
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ if [ "$(systemctl is-active docker)" != "active" ]; then \
|
|||||||
sleep 5; sudo systemctl start docker; sleep 5;\
|
sleep 5; sudo systemctl start docker; sleep 5;\
|
||||||
fi;\
|
fi;\
|
||||||
if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install sudo, command not found"; exit 1; fi;\
|
if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install sudo, command not found"; exit 1; fi;\
|
||||||
docker --version
|
docker --version;\
|
||||||
|
uname -sr
|
||||||
|
|||||||
@@ -94,6 +94,15 @@ public:
|
|||||||
setValue("Conf/startMinimized", enabled);
|
setValue("Conf/startMinimized", enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isNewsNotifications() const
|
||||||
|
{
|
||||||
|
return value("Conf/newsNotifications", true).toBool();
|
||||||
|
}
|
||||||
|
void setNewsNotifications(bool enabled)
|
||||||
|
{
|
||||||
|
setValue("Conf/newsNotifications", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
bool isSaveLogs() const
|
bool isSaveLogs() const
|
||||||
{
|
{
|
||||||
return value("Conf/saveLogs", false).toBool();
|
return value("Conf/saveLogs", false).toBool();
|
||||||
|
|||||||
@@ -308,6 +308,15 @@ void SettingsController::toggleStartMinimized(bool enable)
|
|||||||
emit startMinimizedChanged();
|
emit startMinimizedChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isNewsNotificationsEnabled()
|
||||||
|
{
|
||||||
|
return m_settings->isNewsNotifications();
|
||||||
|
}
|
||||||
|
void SettingsController::toggleNewsNotificationsEnabled(bool enable)
|
||||||
|
{
|
||||||
|
m_settings->setNewsNotifications(enable);
|
||||||
|
}
|
||||||
|
|
||||||
bool SettingsController::isScreenshotsEnabled()
|
bool SettingsController::isScreenshotsEnabled()
|
||||||
{
|
{
|
||||||
return m_settings->isScreenshotsEnabled();
|
return m_settings->isScreenshotsEnabled();
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ public slots:
|
|||||||
bool isStartMinimizedEnabled();
|
bool isStartMinimizedEnabled();
|
||||||
void toggleStartMinimized(bool enable);
|
void toggleStartMinimized(bool enable);
|
||||||
|
|
||||||
|
bool isNewsNotificationsEnabled();
|
||||||
|
void toggleNewsNotificationsEnabled(bool enable);
|
||||||
|
|
||||||
bool isScreenshotsEnabled();
|
bool isScreenshotsEnabled();
|
||||||
void toggleScreenshotsEnabled(bool enable);
|
void toggleScreenshotsEnabled(bool enable);
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,36 @@ Item {
|
|||||||
return drawerContent.state === stateName
|
return drawerContent.state === stateName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findComponent(obj, typeCtor) {
|
||||||
|
if (!obj)
|
||||||
|
return null
|
||||||
|
|
||||||
|
if (obj instanceof typeCtor)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
if (obj.children && obj.children.length > 0) {
|
||||||
|
for (var i = 0; i < obj.children.length; i++) {
|
||||||
|
var matchingChildren = findComponent(obj.children[i], typeCtor)
|
||||||
|
if (matchingChildren) return matchingChildren
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.contentItem) {
|
||||||
|
var matchingContentItem = findComponent(obj.contentItem, typeCtor)
|
||||||
|
if (matchingContentItem) return matchingContentItem
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function setParentInteractive(value) {
|
||||||
|
var flickableType = findComponent(root.parent, Flickable)
|
||||||
|
var listViewType = findComponent(root.parent, ListView)
|
||||||
|
|
||||||
|
if (flickableType) flickableType.interactive = value
|
||||||
|
if (listViewType) listViewType.interactive = value
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Qt.application
|
target: Qt.application
|
||||||
|
|
||||||
@@ -93,6 +123,8 @@ Item {
|
|||||||
|
|
||||||
aboutToHide()
|
aboutToHide()
|
||||||
|
|
||||||
|
setParentInteractive(true)
|
||||||
|
|
||||||
closed()
|
closed()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +150,8 @@ Item {
|
|||||||
|
|
||||||
root.aboutToShow()
|
root.aboutToShow()
|
||||||
|
|
||||||
|
setParentInteractive(false)
|
||||||
|
|
||||||
root.opened()
|
root.opened()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ Item {
|
|||||||
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
|
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
hoverEnabled: root.enabled
|
hoverEnabled: root.enabled
|
||||||
@@ -296,13 +298,13 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
Keys.onEnterPressed: {
|
||||||
if (clickedFunction && typeof clickedFunction === "function") {
|
if (!rightImageSource && clickedFunction && typeof clickedFunction === "function") {
|
||||||
clickedFunction()
|
clickedFunction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
Keys.onReturnPressed: {
|
||||||
if (clickedFunction && typeof clickedFunction === "function") {
|
if (!rightImageSource && clickedFunction && typeof clickedFunction === "function") {
|
||||||
clickedFunction()
|
clickedFunction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,16 @@ PageType {
|
|||||||
ListElement { name : "aes-128-gcm" }
|
ListElement { name : "aes-128-gcm" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSelectedIndex() {
|
||||||
|
cipherDropDown.text = cipher
|
||||||
|
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||||
|
if (cipherListView.model.get(i).name === cipher) {
|
||||||
|
selectedIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
cipherDropDown.text = selectedText
|
cipherDropDown.text = selectedText
|
||||||
cipher = cipherDropDown.text
|
cipher = cipherDropDown.text
|
||||||
@@ -147,13 +157,14 @@ PageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
cipherDropDown.text = cipher
|
updateSelectedIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
Connections {
|
||||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
target: listView.model
|
||||||
selectedIndex = i
|
function onDataChanged() {
|
||||||
}
|
cipherListView.updateSelectedIndex()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,6 +192,16 @@ PageType {
|
|||||||
ListElement { name : qsTr("SHA1") }
|
ListElement { name : qsTr("SHA1") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSelectedIndex() {
|
||||||
|
hashDropDown.text = hash
|
||||||
|
for (var i = 0; i < hashListView.model.count; i++) {
|
||||||
|
if (hashListView.model.get(i).name === hash) {
|
||||||
|
selectedIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
hashDropDown.text = selectedText
|
hashDropDown.text = selectedText
|
||||||
hash = hashDropDown.text
|
hash = hashDropDown.text
|
||||||
@@ -199,13 +209,14 @@ PageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
hashDropDown.text = hash
|
updateSelectedIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < hashListView.model.count; i++) {
|
Connections {
|
||||||
if (hashListView.model.get(i).name === hashDropDown.text) {
|
target: listView.model
|
||||||
currentIndex = i
|
function onDataChanged() {
|
||||||
}
|
hashListView.updateSelectedIndex()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,6 +253,16 @@ PageType {
|
|||||||
ListElement { name : qsTr("none") }
|
ListElement { name : qsTr("none") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSelectedIndex() {
|
||||||
|
cipherDropDown.text = cipher
|
||||||
|
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||||
|
if (cipherListView.model.get(i).name === cipher) {
|
||||||
|
selectedIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
cipherDropDown.text = selectedText
|
cipherDropDown.text = selectedText
|
||||||
cipher = cipherDropDown.text
|
cipher = cipherDropDown.text
|
||||||
@@ -249,13 +270,14 @@ PageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
cipherDropDown.text = cipher
|
updateSelectedIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
Connections {
|
||||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
target: listView.model
|
||||||
currentIndex = i
|
function onDataChanged() {
|
||||||
}
|
cipherListView.updateSelectedIndex()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,16 @@ PageType {
|
|||||||
ListElement { name : "aes-128-gcm" }
|
ListElement { name : "aes-128-gcm" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSelectedIndex() {
|
||||||
|
cipherDropDown.text = cipher
|
||||||
|
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||||
|
if (cipherListView.model.get(i).name === cipher) {
|
||||||
|
selectedIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
cipherDropDown.text = selectedText
|
cipherDropDown.text = selectedText
|
||||||
cipher = cipherDropDown.text
|
cipher = cipherDropDown.text
|
||||||
@@ -116,13 +126,14 @@ PageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
cipherDropDown.text = cipher
|
updateSelectedIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
Connections {
|
||||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
target: listView.model
|
||||||
currentIndex = i
|
function onDataChanged() {
|
||||||
}
|
cipherListView.updateSelectedIndex()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ PageType {
|
|||||||
id: news
|
id: news
|
||||||
|
|
||||||
property string title: qsTr("News & Notifications")
|
property string title: qsTr("News & Notifications")
|
||||||
readonly property string leftImagePath: NewsModel.hasUnread ? "qrc:/images/controls/news-unread.svg" : "qrc:/images/controls/news.svg"
|
readonly property string leftImagePath: NewsModel.hasUnread && SettingsController.isNewsNotificationsEnabled() ? "qrc:/images/controls/news-unread.svg" : "qrc:/images/controls/news.svg"
|
||||||
property bool isVisible: ServersModel.hasServersFromGatewayApi
|
property bool isVisible: ServersModel.hasServersFromGatewayApi
|
||||||
readonly property var clickedHandler: function() {
|
readonly property var clickedHandler: function() {
|
||||||
if (!ServersModel.hasServersFromGatewayApi) {
|
if (!ServersModel.hasServersFromGatewayApi) {
|
||||||
|
|||||||
@@ -168,6 +168,29 @@ PageType {
|
|||||||
DividerType {
|
DividerType {
|
||||||
visible: !GC.isMobile()
|
visible: !GC.isMobile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SwitcherType {
|
||||||
|
id: switcherNewsNotificationEnabled
|
||||||
|
|
||||||
|
visible: ServersModel.hasServersFromGatewayApi
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: 16
|
||||||
|
|
||||||
|
text: qsTr("News Notification")
|
||||||
|
descriptionText: qsTr("Show notification icon when has unread news")
|
||||||
|
|
||||||
|
checked: SettingsController.isNewsNotificationsEnabled()
|
||||||
|
onToggled: function() {
|
||||||
|
if (checked !== SettingsController.isNewsNotificationsEnabled()) {
|
||||||
|
SettingsController.toggleNewsNotificationsEnabled(checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {
|
||||||
|
visible: !GC.isMobile()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: ColumnLayout {
|
footer: ColumnLayout {
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ PageType {
|
|||||||
|
|
||||||
onClicked: function() {
|
onClicked: function() {
|
||||||
isEasySetup = true
|
isEasySetup = true
|
||||||
|
checked = true
|
||||||
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
|
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
|
||||||
|
|
||||||
listView.dockerContainer = dockerContainer
|
listView.dockerContainer = dockerContainer
|
||||||
|
|||||||
@@ -383,7 +383,7 @@ PageType {
|
|||||||
objectName: "settingsTabButton"
|
objectName: "settingsTabButton"
|
||||||
|
|
||||||
isSelected: tabBar.currentIndex === 2
|
isSelected: tabBar.currentIndex === 2
|
||||||
image: (ServersModel.hasServersFromGatewayApi && NewsModel.hasUnread) ? "qrc:/images/controls/settings-news.svg" : "qrc:/images/controls/settings.svg"
|
image: (ServersModel.hasServersFromGatewayApi && NewsModel.hasUnread && SettingsController.isNewsNotificationsEnabled()) ? "qrc:/images/controls/settings-news.svg" : "qrc:/images/controls/settings.svg"
|
||||||
Binding {
|
Binding {
|
||||||
target: settingsTabButton
|
target: settingsTabButton
|
||||||
property: "defaultColor"
|
property: "defaultColor"
|
||||||
|
|||||||
@@ -499,7 +499,7 @@ bool VpnConnection::startNetworkCheckIfReady()
|
|||||||
|
|
||||||
return IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
return IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||||
QRemoteObjectPendingReply<bool> reply = iface->startNetworkCheck(gateway, localAddress);
|
QRemoteObjectPendingReply<bool> reply = iface->startNetworkCheck(gateway, localAddress);
|
||||||
return reply.waitForFinished() && reply.returnValue();
|
return reply.waitForFinished(1000) && reply.returnValue();
|
||||||
});
|
});
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
</array>
|
</array>
|
||||||
<key>KeepAlive</key>
|
<key>KeepAlive</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>RunAtLoad</key>
|
||||||
|
<true/>
|
||||||
<key>Sockets</key>
|
<key>Sockets</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>Listeners</key>
|
<key>Listeners</key>
|
||||||
|
|||||||
@@ -7,42 +7,54 @@ LOG_FOLDER=/var/log/$APP_NAME
|
|||||||
LOG_FILE="$LOG_FOLDER/post-install.log"
|
LOG_FILE="$LOG_FOLDER/post-install.log"
|
||||||
APP_PATH=/Applications/$APP_NAME.app
|
APP_PATH=/Applications/$APP_NAME.app
|
||||||
|
|
||||||
|
rm -rf "$LOG_FOLDER"
|
||||||
|
mkdir -p "$LOG_FOLDER"
|
||||||
|
echo "`date` Script started" > "$LOG_FILE"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "`date` $*" >> "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_cmd() {
|
||||||
|
log "CMD: $*"
|
||||||
|
"$@" >> "$LOG_FILE" 2>&1
|
||||||
|
local ec=$?
|
||||||
|
log "EXIT: $ec"
|
||||||
|
return $ec
|
||||||
|
}
|
||||||
|
|
||||||
# Handle new installations unpacked into localized folder
|
# Handle new installations unpacked into localized folder
|
||||||
if [ -d "/Applications/${APP_NAME}.localized" ]; then
|
if [ -d "/Applications/${APP_NAME}.localized" ]; then
|
||||||
echo "`date` Detected ${APP_NAME}.localized, migrating to standard path" >> $LOG_FILE
|
log "Detected ${APP_NAME}.localized, migrating to standard path"
|
||||||
sudo rm -rf "$APP_PATH"
|
run_cmd sudo rm -rf "$APP_PATH"
|
||||||
sudo mv "/Applications/${APP_NAME}.localized/${APP_NAME}.app" "$APP_PATH"
|
run_cmd sudo mv "/Applications/${APP_NAME}.localized/${APP_NAME}.app" "$APP_PATH"
|
||||||
sudo rm -rf "/Applications/${APP_NAME}.localized"
|
run_cmd sudo rm -rf "/Applications/${APP_NAME}.localized"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if launchctl list "$APP_NAME-service" &> /dev/null; then
|
run_cmd launchctl bootout system "$LAUNCH_DAEMONS_PLIST_NAME" || run_cmd launchctl unload "$LAUNCH_DAEMONS_PLIST_NAME"
|
||||||
launchctl unload "$LAUNCH_DAEMONS_PLIST_NAME"
|
run_cmd rm -f "$LAUNCH_DAEMONS_PLIST_NAME"
|
||||||
rm -f "$LAUNCH_DAEMONS_PLIST_NAME"
|
|
||||||
fi
|
|
||||||
|
|
||||||
sudo chmod -R a-w "$APP_PATH/"
|
run_cmd sudo chmod -R a-w "$APP_PATH/"
|
||||||
sudo chown -R root "$APP_PATH/"
|
run_cmd sudo chown -R root "$APP_PATH/"
|
||||||
sudo chgrp -R wheel "$APP_PATH/"
|
run_cmd sudo chgrp -R wheel "$APP_PATH/"
|
||||||
|
|
||||||
rm -rf $LOG_FOLDER
|
log "Requesting ${APP_NAME} to quit gracefully"
|
||||||
mkdir -p $LOG_FOLDER
|
run_cmd osascript -e 'tell application "AmneziaVPN" to quit' || true
|
||||||
|
|
||||||
echo "`date` Script started" > $LOG_FILE
|
|
||||||
|
|
||||||
echo "Requesting ${APP_NAME} to quit gracefully" >> "$LOG_FILE"
|
|
||||||
osascript -e 'tell application "AmneziaVPN" to quit'
|
|
||||||
|
|
||||||
PLIST_SOURCE="$APP_PATH/Contents/Resources/$PLIST_NAME"
|
PLIST_SOURCE="$APP_PATH/Contents/Resources/$PLIST_NAME"
|
||||||
if [ -f "$PLIST_SOURCE" ]; then
|
if [ -f "$PLIST_SOURCE" ]; then
|
||||||
mv -f "$PLIST_SOURCE" "$LAUNCH_DAEMONS_PLIST_NAME" 2>> $LOG_FILE
|
run_cmd mv -f "$PLIST_SOURCE" "$LAUNCH_DAEMONS_PLIST_NAME"
|
||||||
else
|
else
|
||||||
echo "`date` ERROR: service plist not found at $PLIST_SOURCE" >> $LOG_FILE
|
log "ERROR: service plist not found at $PLIST_SOURCE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
chown root:wheel "$LAUNCH_DAEMONS_PLIST_NAME"
|
run_cmd chown root:wheel "$LAUNCH_DAEMONS_PLIST_NAME"
|
||||||
launchctl load "$LAUNCH_DAEMONS_PLIST_NAME"
|
run_cmd chmod 644 "$LAUNCH_DAEMONS_PLIST_NAME"
|
||||||
echo "`date` Launching ${APP_NAME} application" >> $LOG_FILE
|
run_cmd launchctl bootstrap system "$LAUNCH_DAEMONS_PLIST_NAME" || run_cmd launchctl load "$LAUNCH_DAEMONS_PLIST_NAME"
|
||||||
open -a "$APP_PATH" 2>> $LOG_FILE || true
|
run_cmd launchctl enable "system/$APP_NAME-service" || true
|
||||||
|
run_cmd launchctl kickstart -k "system/$APP_NAME-service" || true
|
||||||
|
run_cmd launchctl print "system/$APP_NAME-service" || true
|
||||||
|
log "Launching ${APP_NAME} application"
|
||||||
|
run_cmd open -a "$APP_PATH" || true
|
||||||
|
|
||||||
echo "`date` Service status: $?" >> $LOG_FILE
|
log "Script finished"
|
||||||
echo "`date` Script finished" >> $LOG_FILE
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ fi
|
|||||||
|
|
||||||
# Unload the service if loaded and remove its plist file regardless
|
# Unload the service if loaded and remove its plist file regardless
|
||||||
if launchctl list "${APP_NAME}-service" &> /dev/null; then
|
if launchctl list "${APP_NAME}-service" &> /dev/null; then
|
||||||
sudo launchctl unload "$LAUNCH_DAEMONS_PLIST_NAME"
|
sudo launchctl bootout system "$LAUNCH_DAEMONS_PLIST_NAME" || sudo launchctl unload "$LAUNCH_DAEMONS_PLIST_NAME"
|
||||||
fi
|
fi
|
||||||
sudo rm -f "$LAUNCH_DAEMONS_PLIST_NAME"
|
sudo rm -f "$LAUNCH_DAEMONS_PLIST_NAME"
|
||||||
|
|
||||||
|
|||||||
+29
-16
@@ -1,9 +1,9 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
VERSION=$1
|
VERSION=$1
|
||||||
|
|
||||||
if [[ $VERSION = '' ]]; then
|
if [[ -z "$VERSION" ]]; then
|
||||||
echo '::error::VERSION does not set. Exiting with error...'
|
echo '::error::VERSION does not set. Exiting with error...'
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -14,25 +14,38 @@ cd dist
|
|||||||
|
|
||||||
echo $VERSION >> VERSION
|
echo $VERSION >> VERSION
|
||||||
curl -s https://api.github.com/repos/amnezia-vpn/amnezia-client/releases/tags/$VERSION | jq -r .body | tr -d '\r' > CHANGELOG
|
curl -s https://api.github.com/repos/amnezia-vpn/amnezia-client/releases/tags/$VERSION | jq -r .body | tr -d '\r' > CHANGELOG
|
||||||
|
curl -s https://api.github.com/repos/amnezia-vpn/amnezia-client/releases/tags/$VERSION | jq -r .published_at > RELEASE_DATE
|
||||||
if [[ $(cat CHANGELOG) = null ]]; then
|
if [[ $(cat CHANGELOG) = null ]]; then
|
||||||
echo '::error::Release does not exists. Exiting with error...'
|
echo '::error::Release does not exists. Exiting with error...'
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android8+_arm64-v8a.apk
|
# Download files with error handling
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android8+_armeabi-v7a.apk
|
download_file() {
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android8+_x86.apk
|
local url=$1
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android8+_x86_64.apk
|
local filename=$(basename "$url")
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_arm64-v8a.apk
|
echo "Downloading $filename..."
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_armeabi-v7a.apk
|
if ! wget -q "$url"; then
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86.apk
|
echo "::error::Failed to download $filename from $url"
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86_64.apk
|
exit 8
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_linux_x64.tar.zip
|
fi
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos.dmg
|
echo "Successfully downloaded $filename"
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos_old.dmg
|
}
|
||||||
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_windows_x64.exe
|
|
||||||
|
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android9+_arm64-v8a.apk
|
||||||
|
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android9+_armeabi-v7a.apk
|
||||||
|
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android9+_x86.apk
|
||||||
|
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android9+_x86_64.apk
|
||||||
|
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_linux_x64.tar
|
||||||
|
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos.pkg
|
||||||
|
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_x64.exe
|
||||||
|
|
||||||
cd ../
|
cd ../
|
||||||
|
|
||||||
rclone sync ./dist/ r2:/updates/
|
echo "Syncing to R2..."
|
||||||
|
if ! rclone sync ./dist/ r2:/updates/; then
|
||||||
|
echo "::error::Failed to sync files to R2"
|
||||||
|
exit 8
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Deployment completed successfully!"
|
||||||
|
|||||||
Reference in New Issue
Block a user