Files
amnezia-client/client/ui/qml/Pages2/PageSetupWizardEasy.qml
T

505 lines
19 KiB
QML
Raw Normal View History

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import ContainerProps 1.0
import ProtocolProps 1.0
2024-07-07 13:42:38 +03:00
import Style 1.0
import "./"
import "../Controls2"
import "../Config"
PageType {
id: root
2026-02-04 23:03:38 +03:00
objectName: "pageSetupWizardEasy"
property bool isEasySetup: true
2026-02-04 23:03:38 +03:00
property bool isRestoreFromBackup: false
property string backupFilePath: ""
property string restoreHostname: ""
property string restoreUsername: ""
property string restoreSecretData: ""
property bool waitingForServerToAdd: false
2026-02-06 17:14:08 +03:00
// For installing containers from backup
2026-02-05 10:29:18 +03:00
property var containersToInstall: []
property int currentContainerIndex: 0
property bool isInstallingContainers: false
2026-02-06 17:14:08 +03:00
// Connections for ServersBackupController
Connections {
target: ServersBackupController
function onReadyForRestore(backupFilePath, hostname, username, secretData, serverIp, fileName) {
console.log("onReadyForRestore received from C++")
console.log(" backupFilePath:", backupFilePath)
console.log(" hostname:", hostname)
console.log(" serverIp:", serverIp)
console.log(" fileName:", fileName)
// Scan backup to determine containers (C++ already did this, but needed for QML)
var foundContainers = ServersBackupController.scanBackupForContainers(backupFilePath)
console.log("Found containers:", foundContainers)
if (foundContainers.length === 0) {
PageController.showErrorMessage(qsTr("No containers found in backup file"))
root.isRestoreFromBackup = false
return
}
root.containersToInstall = foundContainers
root.currentContainerIndex = 0
// Now add empty server with these credentials
InstallController.setProcessedServerCredentials(hostname, username, secretData)
// Set waiting flag
root.waitingForServerToAdd = true
console.log("Backup scanned, adding server...")
// Add server (asynchronously)
InstallController.addEmptyServer()
// Further execution will happen in onInstallServerFinished
}
}
// Connections for tracking server addition
2026-02-04 23:03:38 +03:00
Connections {
target: InstallController
function onInstallServerFinished(finishedMessage) {
if (root.waitingForServerToAdd && root.isRestoreFromBackup && root.backupFilePath.length > 0) {
2026-02-05 10:29:18 +03:00
console.log("Server added successfully, now installing containers from backup...")
2026-02-04 23:03:38 +03:00
root.waitingForServerToAdd = false
2026-02-06 17:14:08 +03:00
// Start installing containers
2026-02-05 10:29:18 +03:00
root.isInstallingContainers = true
installNextContainer()
}
}
function onInstallContainerFinished(finishedMessage, isServiceInstall) {
if (root.isInstallingContainers) {
console.log("Container installed:", finishedMessage)
2026-02-06 17:14:08 +03:00
// Move to next container
2026-02-05 10:29:18 +03:00
root.currentContainerIndex++
if (root.currentContainerIndex < root.containersToInstall.length) {
2026-02-06 17:14:08 +03:00
// Install next container
2026-02-05 10:29:18 +03:00
installNextContainer()
} else {
2026-02-06 17:14:08 +03:00
// All containers installed, now do restore
2026-02-05 10:29:18 +03:00
console.log("All containers installed, starting restore...")
root.isInstallingContainers = false
2026-02-06 17:14:08 +03:00
// IMPORTANT: Turn off busy indicator before navigation
2026-02-05 10:29:18 +03:00
PageController.showBusyIndicator(false)
2026-02-06 17:14:08 +03:00
// Start navigation to restore mode selection page
2026-02-05 10:29:18 +03:00
navigationTimer.start()
}
2026-02-04 23:03:38 +03:00
}
}
}
2026-02-06 17:14:08 +03:00
// Function to install next container from list
2026-02-05 10:29:18 +03:00
function installNextContainer() {
if (root.currentContainerIndex >= root.containersToInstall.length) {
return
}
var containerName = root.containersToInstall[root.currentContainerIndex]
console.log("Installing container:", containerName, "(", root.currentContainerIndex + 1, "/", root.containersToInstall.length, ")")
2026-02-06 17:14:08 +03:00
// Convert container name to DockerContainer enum
2026-02-05 10:29:18 +03:00
var dockerContainer = ContainerProps.containerFromString(containerName)
if (dockerContainer === 0) { // None
console.log("Unknown container:", containerName, "skipping...")
root.currentContainerIndex++
installNextContainer()
return
}
2026-02-06 17:14:08 +03:00
// Get default settings for container
2026-02-05 10:29:18 +03:00
var defaultProtocol = ContainerProps.defaultProtocol(dockerContainer)
var defaultPort = InstallController.getPortForInstall(defaultProtocol)
var defaultTransport = InstallController.defaultTransportProto(defaultProtocol)
// Set server index
var serverIdx = ServersModel.getServersCount() - 1
ServersModel.processedIndex = serverIdx
var serverId = ServersUiController.getServerId(serverIdx)
2026-02-06 17:14:08 +03:00
// Show loading indicator with message
2026-02-05 10:29:18 +03:00
PageController.showBusyIndicator(true)
PageController.showNotificationMessage(qsTr("Installing %1 (%2/%3)...")
.arg(containerName)
.arg(root.currentContainerIndex + 1)
.arg(root.containersToInstall.length))
2026-02-06 17:14:08 +03:00
// Ensure credentials are set
2026-02-05 10:29:18 +03:00
InstallController.setProcessedServerCredentials(root.restoreHostname, root.restoreUsername, root.restoreSecretData)
2026-02-06 17:14:08 +03:00
// Install container
console.log("Installing container:", containerName, "serverId:", serverId)
2026-02-05 10:29:18 +03:00
ContainersModel.setProcessedContainerIndex(dockerContainer)
InstallController.install(dockerContainer, defaultPort, defaultTransport, serverId)
2026-02-05 10:29:18 +03:00
}
2026-02-06 17:14:08 +03:00
// Timer for navigating to restore mode selection page after file selection
2026-02-04 23:03:38 +03:00
Timer {
id: navigationTimer
interval: 500
repeat: false
onTriggered: {
if (root.backupFilePath.length > 0 && root.isRestoreFromBackup) {
console.log("Navigation timer triggered, going to restore mode page")
console.log("Credentials available:", root.restoreHostname, root.restoreUsername, root.restoreSecretData.length > 0 ? "***" : "EMPTY")
2026-02-06 17:14:08 +03:00
// Get filename
2026-02-04 23:03:38 +03:00
var fileName = SystemController.getFileNameFromPath(root.backupFilePath)
if (!fileName || fileName === undefined || fileName.length === 0) {
var fallbackName = root.backupFilePath.split('/').pop()
fileName = (fallbackName && fallbackName.length > 0) ? fallbackName : qsTr("backup.tgz")
}
fileName = String(fileName)
2026-02-06 17:14:08 +03:00
// Extract IP address from filename
2026-02-04 23:03:38 +03:00
var serverIp = ""
var ipMatch = fileName.match(/^([\d_]+)\s*-/)
if (ipMatch && ipMatch.length > 1) {
serverIp = ipMatch[1].replace(/_/g, ".")
}
if (!serverIp || serverIp.length === 0) {
serverIp = root.restoreHostname
}
var serverName = root.restoreHostname
if (!serverName || serverName.length === 0) {
serverName = qsTr("RestoredServer")
}
2026-02-06 17:14:08 +03:00
// Navigate to installation page
2026-02-04 23:03:38 +03:00
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
2026-02-06 17:14:08 +03:00
// Immediately find StackView and navigate to restore page
// Server already added, as we waited for onInstallServerFinished
2026-02-04 23:03:38 +03:00
Qt.callLater(function() {
var pagePath = "qrc:/ui/qml/Pages2/PageSettingsServerRestoreMode.qml"
// Traverse upward from root to find the containing StackView.
// StackView has both `push` function and `depth` property.
// This avoids a recursive downward search that causes stack overflow
// on iOS when the component tree is large (many VPN managers).
var stackView = root.parent
while (stackView) {
if (typeof stackView.push === "function" && stackView.hasOwnProperty("depth")) {
break
2026-02-04 23:03:38 +03:00
}
stackView = stackView.parent
2026-02-04 23:03:38 +03:00
}
2026-02-04 23:03:38 +03:00
if (stackView) {
console.log("Found StackView, pushing restore mode page")
stackView.push(pagePath, {
"backupFilePath": root.backupFilePath,
"backupFileName": fileName,
"serverName": "",
2026-02-04 23:03:38 +03:00
"serverIp": serverIp,
"isFromSetupWizard": true,
"wizardHostname": root.restoreHostname,
"wizardUsername": root.restoreUsername,
"wizardSecretData": root.restoreSecretData
})
} else {
console.error("Could not find StackView")
}
})
}
}
}
SortFilterProxyModel {
id: proxyContainersModel
sourceModel: ContainersModel
filters: [
ValueFilter {
roleName: "isEasySetupContainer"
value: true
}
]
sorters: RoleSorter {
2023-09-18 21:06:10 +05:00
roleName: "easySetupOrder"
sortOrder: Qt.DescendingOrder
}
}
2023-06-05 15:49:10 +08:00
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20 + PageController.safeAreaTopMargin
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
}
ButtonGroup {
id: buttonGroup
2023-06-05 15:49:10 +08:00
}
ListViewType {
id: listView
property int dockerContainer
property int containerDefaultPort
property int containerDefaultTransportProto
2023-06-05 15:49:10 +08:00
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
spacing: 16
header: ColumnLayout {
width: listView.width
spacing: 16
2025-05-02 23:54:36 -07:00
BaseHeaderType {
id: header
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
2023-10-02 16:31:50 +05:00
headerTextMaximumLineCount: 10
2025-02-04 15:53:40 +00:00
headerText: qsTr("Choose Installation Type")
}
}
model: proxyContainersModel
currentIndex: 0
delegate: ColumnLayout {
width: listView.width
CardType {
id: card
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
headerText: easySetupHeader
bodyText: easySetupDescription
ButtonGroup.group: buttonGroup
onClicked: function() {
isEasySetup = true
2026-01-30 06:42:29 +02:00
checked = true
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
listView.dockerContainer = dockerContainer
listView.containerDefaultPort = InstallController.getPortForInstall(defaultContainerProto)
listView.containerDefaultTransportProto = InstallController.defaultTransportProto(defaultContainerProto)
}
Keys.onReturnPressed: this.clicked()
Keys.onEnterPressed: this.clicked()
}
}
footer: ColumnLayout {
width: listView.width
spacing: 16
DividerType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
}
2023-08-20 13:36:54 +05:00
CardType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
2025-02-04 15:53:40 +00:00
headerText: qsTr("Manual")
bodyText: qsTr("Choose a VPN protocol")
ButtonGroup.group: buttonGroup
onClicked: function() {
isEasySetup = false
checked = true
}
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
}
2026-02-04 23:03:38 +03:00
DividerType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
}
CardType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Restore from backup")
bodyText: qsTr("Restoration of VPN protocols, services, all server settings and users")
ButtonGroup.group: buttonGroup
onClicked: function() {
var filter = GC.isMobile() ? "*.gz *.tgz *.tar.gz" : "Backup files (*.tar.gz *.backup *.tgz *.gz)"
var localPath = SystemController.getFileName(
qsTr("Select Backup to Restore"),
filter,
"",
false,
""
)
console.log("Selected file path:", localPath)
if (!localPath || localPath.length === 0) {
console.log("No file selected")
return
}
2026-02-06 17:14:08 +03:00
// Save backup file path
2026-02-04 23:03:38 +03:00
root.backupFilePath = localPath
root.isRestoreFromBackup = true
2026-02-06 17:14:08 +03:00
// Get credentials from PageSetupWizardCredentials via StackView search
2026-02-04 23:03:38 +03:00
var credentialsPage = null
var item = root
2026-02-06 17:14:08 +03:00
// Find StackView
2026-02-04 23:03:38 +03:00
while (item && !item.hasOwnProperty("depth")) {
item = item.parent
}
2026-02-06 17:14:08 +03:00
// If found StackView, search for PageSetupWizardCredentials in its history
2026-02-04 23:03:38 +03:00
if (item && item.depth > 0) {
for (var i = 0; i < item.depth; i++) {
var page = item.get(i)
if (page && page.hasOwnProperty("savedHostname")) {
credentialsPage = page
break
}
}
}
if (credentialsPage && credentialsPage.savedHostname.length > 0) {
root.restoreHostname = credentialsPage.savedHostname
root.restoreUsername = credentialsPage.savedUsername
root.restoreSecretData = credentialsPage.savedSecretData
console.log("Got credentials from PageSetupWizardCredentials:", root.restoreHostname, root.restoreUsername)
2026-02-06 17:14:08 +03:00
// Call C++ method to prepare restore
// It will scan backup and send readyForRestore signal
ServersBackupController.prepareRestoreFromBackup(localPath, root.restoreHostname, root.restoreUsername, root.restoreSecretData)
2026-02-04 23:03:38 +03:00
} else {
console.log("WARNING: No credentials found")
return
}
}
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
}
BasicButtonType {
2023-06-05 15:49:10 +08:00
id: continueButton
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Continue")
clickedFunc: function() {
if (root.isEasySetup) {
ServersUiController.processedContainerIndex = listView.dockerContainer
2023-09-06 13:37:37 +05:00
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.install(listView.dockerContainer,
listView.containerDefaultPort,
listView.containerDefaultTransportProto,
2026-05-28 10:57:08 +08:00
ServersUiController.processedServerId)
} else {
2023-09-06 13:37:37 +05:00
PageController.goToPage(PageEnum.PageSetupWizardProtocols)
}
}
}
2023-08-20 13:36:54 +05:00
BasicButtonType {
id: setupLaterButton
Layout.fillWidth: true
Layout.topMargin: 8
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
2023-08-20 13:36:54 +05:00
2024-07-07 13:42:38 +03:00
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
2023-08-20 13:36:54 +05:00
borderWidth: 1
visible: {
2023-10-03 22:38:17 +05:00
if (PageController.isTriggeredByConnectButton()) {
PageController.setTriggeredByConnectButton(false)
return false
}
return true
}
2024-03-26 20:05:04 +02:00
text: qsTr("Skip setup")
2023-08-20 13:36:54 +05:00
clickedFunc: function() {
2023-09-06 13:37:37 +05:00
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
2023-08-20 13:36:54 +05:00
InstallController.addEmptyServer()
}
}
}
Component.onCompleted: {
var item = listView.itemAtIndex(listView.currentIndex)
if (item !== null) {
var button = item.children[0]
button.checked = true
button.clicked()
}
}
}
}