diff --git a/src/BlockchainBackend.cpp b/src/BlockchainBackend.cpp index 96c3f93..dbe1f90 100644 --- a/src/BlockchainBackend.cpp +++ b/src/BlockchainBackend.cpp @@ -359,12 +359,20 @@ QVariantMap BlockchainBackend::generateConfig( QVariantMap normalized; - QString out = outputPath.trimmed(); - if (out.isEmpty()) - out = generatedUserConfigPath(); - else - out = toLocalPath(out); - normalized.insert("output", out); + // The output path drives persistence routing through the module's single + // switch (use_persistence_paths), which routes output + state + storage + + // logs under the host-provisioned per-instance dir: + // - empty → omit "output"; module writes "/user_config.yaml". + // - relative → pass it through; module resolves it under . + // - absolute → write exactly there; no persistence routing. + const QString rawOut = outputPath.trimmed(); + const QString localOut = rawOut.isEmpty() ? QString() : toLocalPath(rawOut); + const QString chosenOut = !localOut.isEmpty() ? localOut : rawOut; + const bool absoluteOut = !chosenOut.isEmpty() && QDir::isAbsolutePath(chosenOut); + if (!rawOut.isEmpty()) + normalized.insert("output", absoluteOut ? chosenOut : rawOut); + if (!absoluteOut) + normalized.insert("use_persistence_paths", true); if (!initialPeers.isEmpty()) { QVariantList peersList; @@ -396,6 +404,8 @@ QVariantMap BlockchainBackend::generateConfig( toLocalPath(deploymentConfigPath.trimmed())); normalized.insert("deployment", deployment); } + // An explicit node state dir still wins: the module leaves a pinned path + // untouched even when use_persistence_paths routing is on. if (!statePath.trimmed().isEmpty()) normalized.insert("state_path", toLocalPath(statePath.trimmed())); diff --git a/src/qml/BlockchainView.qml b/src/qml/BlockchainView.qml index 6656a5d..fdd8543 100644 --- a/src/qml/BlockchainView.qml +++ b/src/qml/BlockchainView.qml @@ -273,13 +273,20 @@ Rectangle { ? qsTr("Config generated successfully.") : qsTr("Generate failed: %1").arg(result.error) if (result.success) { - root.backend.userConfig = (outputPath !== "") - ? outputPath : root.backend.generatedUserConfigPath + // The module writes the config and returns the + // absolute path it used; use that for start(). + root.backend.userConfig = + (result.value !== undefined && result.value !== "") + ? result.value + : (outputPath !== "" ? outputPath : root.backend.generatedUserConfigPath) root.backend.deploymentConfig = (deploymentMode === 1 && deploymentConfigPath !== "") ? deploymentConfigPath : "" root.backend.useGeneratedConfig = true - _d.currentPage = 1 + // Finalize: move to the "set path" window, now + // showing the resolved config path, ready for + // the user to continue and start the node. + configChoiceView.showSetConfigPath() } }, function(error) { diff --git a/src/qml/views/ConfigChoiceView.qml b/src/qml/views/ConfigChoiceView.qml index 8c4fbd0..7620c55 100644 --- a/src/qml/views/ConfigChoiceView.qml +++ b/src/qml/views/ConfigChoiceView.qml @@ -25,6 +25,11 @@ ColumnLayout { property int selectedOption: 0 } + // Switch to the "set path to config" sub-view. Used after a successful + // generate to land on the resolved-config-path screen, from which the user + // continues to start the node. + function showSetConfigPath() { d.selectedOption = 2 } + spacing: Theme.spacing.large LogosText { diff --git a/src/qml/views/GenerateConfigView.qml b/src/qml/views/GenerateConfigView.qml index 5adb508..80572bb 100644 --- a/src/qml/views/GenerateConfigView.qml +++ b/src/qml/views/GenerateConfigView.qml @@ -7,6 +7,8 @@ import QtCore import Logos.Theme import Logos.Controls +import "../controls" + ColumnLayout { id: root @@ -17,10 +19,9 @@ ColumnLayout { signal generateRequested(string outputPath, var initialPeers, int netPort, int blendPort, string httpAddr, string externalAddress, bool noPublicIpCheck, int deploymentMode, string deploymentConfigPath, string statePath) - Component.onCompleted: { - if (outputField.text === "" && root.generatedUserConfigPath !== "") - outputField.text = root.generatedUserConfigPath - } + // Leave the output field empty by default: the module writes the config to + // its own host-provisioned writable location and returns that path. A user + // can still type/browse an explicit path to override. QtObject { id: d @@ -64,12 +65,21 @@ ColumnLayout { LogosTextField { id: outputField Layout.fillWidth: true - placeholderText: root.generatedUserConfigPath || qsTr("Output config path (e.g. node_config.yaml)") + placeholderText: qsTr("Output config path (optional — defaults to module data dir)") } LogosButton { text: qsTr("Browse…") onClicked: outputFolderDialog.open() } + InfoButton { + text: qsTr("Leave empty (or use a relative path) to let the module store the " + + "config and node state in its own per-instance data directory — " + + "the safe default.\n\n" + + "Enter a full absolute path only to write the config there yourself. " + + "Note: depending on where the app runs (e.g. embedded in Basecamp, " + + "sandboxed, or read-only locations) an arbitrary absolute path may not " + + "be writable and starting the node can fail.") + } } // Initial peers (multi-line)