Temporary commit

This commit is contained in:
Arnaud 2026-02-19 08:51:01 +04:00
parent 2855adeb86
commit fa7664720c
No known key found for this signature in database
GPG Key ID: 20E40A5D3110766F
9 changed files with 226 additions and 188 deletions

View File

@ -130,7 +130,7 @@ endif()
# Discover the required dependencies.
# Without this discovery part, the dependencies cannot be found.
# COMPONENTS is kind of generic for Qt modules.
find_package(Qt6 REQUIRED COMPONENTS Core Widgets RemoteObjects Quick QuickWidgets)
find_package(Qt6 REQUIRED COMPONENTS Core Widgets RemoteObjects Quick QuickWidgets Network)
# Get the real path to handle symlinks correctly
#get_filename_component(REAL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" REALPATH)
@ -419,6 +419,7 @@ target_link_libraries(storage_ui PRIVATE
Qt6::RemoteObjects
Qt6::Quick
Qt6::QuickWidgets
Qt6::Network
component-interfaces
)

View File

@ -8,6 +8,8 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QLocale>
#include <QNetworkAccessManager>
#include <QNetworkReply>
// StorageBackend is responsible for managing the interaction with the storage module.
// It is mocked in the QML.
@ -866,6 +868,55 @@ QString StorageBackend::buildConfig(const QString& dataDir, int discPort, int tc
return QJsonDocument(obj).toJson(QJsonDocument::Indented);
}
QString StorageBackend::buildUpnpConfig(const QString& dataDir) {
debug("StorageBackend::buildUpnpConfig called with dataDir=" + dataDir);
QJsonDocument doc = QJsonDocument::fromJson(m_configJson.toUtf8());
QJsonObject obj = doc.object();
obj["data-dir"] = dataDir;
QJsonArray bootstrapArray;
for (const QString& node : BOOTSTRAP_NODES) {
bootstrapArray.append(node);
}
obj["nat"] = "upnp";
return QJsonDocument(obj).toJson(QJsonDocument::Indented);
}
QString StorageBackend::buildNatExtConfig(const QString& dataDir, int tcpPort) {
debug("StorageBackend::buildUpnpConfig called with dataDir=" + dataDir +
" and tcpPort=" + QString::number(tcpPort));
QJsonDocument doc = QJsonDocument::fromJson(m_configJson.toUtf8());
QJsonObject obj = doc.object();
obj["data-dir"] = dataDir;
QJsonArray bootstrapArray;
for (const QString& node : BOOTSTRAP_NODES) {
bootstrapArray.append(node);
}
debug("Retrieving the public IP");
QNetworkAccessManager manager;
QEventLoop loop;
QNetworkReply* reply = manager.get(QNetworkRequest(QUrl("https://echo.codex.storage/")));
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
QString ip = reply->readAll().trimmed();
reply->deleteLater();
debug("Public IP detected:" + ip);
obj["nat"] = "extip:" + ip;
return QJsonDocument(obj).toJson(QJsonDocument::Indented);
}
QString StorageBackend::buildConfigFromFile(const QString& path) {
qDebug() << "StorageBackend::buildConfigFromFile called";

View File

@ -85,6 +85,8 @@ class StorageBackend : public QObject {
void reloadIfChanged(const QString& configJson);
void status(StorageStatus status);
QString buildConfig(const QString& dataDir, int discPort, int tcpPort);
QString buildUpnpConfig(const QString& dataDir);
QString buildNatExtConfig(const QString& dataDir, int tcpPort);
QString buildConfigFromFile(const QString& path);
void saveUserConfig(const QString& configJson);

View File

@ -132,6 +132,9 @@ qt_add_qml_module(appqml
StartNode.qml
LogosTextField.qml
LogosStorageButton.qml
Nat.qml
LogosStorageLayout.qml
PortForwarding.qml
)
# Set up QML module directory for runtime

View File

@ -1,50 +1,28 @@
import QtQuick
import QtQuick.Controls
import QtCore
import Logos.Theme
import Logos.Controls
// qmllint disable unqualified
Item {
id: root
implicitWidth: 600
implicitHeight: 400
implicitWidth: 800
implicitHeight: 800
property var backend: mockBackend
// Timer {
// readonly property int running: 2
// id: timer
// interval: 2000
// repeat: false
// onTriggered: {
// console.log("timer triggered")
// // root.backend.status = running
// // root.backend.startCompleted()
// // console.info(root.backend.status)
// }
// }
QtObject {
id: mockBackend
readonly property bool isMock: true
property int status
signal startCompleted
signal startFailed
signal stopCompleted
function updateBasicConfig(dataDir, discPort) {
console.log("updateBasicConfig", dataDir, discPort)
}
signal initCompleted
function start() {
// timer.start()
console.log("mock start callde")
}
function stop() {
root.backend.stopCompleted()
console.log("mock start called")
}
function defaultDataDir() {
@ -53,9 +31,15 @@ Item {
function buildConfig() {}
function saveUserConfig() {}
function reloadIfChanged() {}
function init() {}
function buildUpnpConfig() {}
function buildNatExtConfig() {}
function stop() {}
}
Settings {
@ -66,59 +50,90 @@ Item {
property int tcpPort: 0
property string dataDir: ""
property bool onboardingCompleted: false
property string natStrategy: "any"
}
StackView {
id: stackView
anchors.fill: parent
initialItem: onboarding
initialItem: onboardingComponent
}
Component {
id: onboarding
id: onboardingComponent
OnBoarding {
id: onboardingInstance
backend: root.backend
discoveryPort: settings.discoveryPort
tcpPort: settings.tcpPort
dataDir: settings.dataDir.length > 0 ? settings.dataDir : root.backend.defaultDataDir()
// discoveryPort: settings.discoveryPort
// tcpPort: settings.tcpPort
dataDir: settings.dataDir
onCompleted: {
settings.discoveryPort = discoveryPort
// settings.discoveryPort = discoveryPort
settings.dataDir = dataDir
settings.tcpPort = tcpPort
// settings.tcpPort = tcpPort
settings.onboardingCompleted = true
let config = root.backend.buildConfig(dataDir,
discoveryPort, tcpPort)
root.backend.saveUserConfig(config)
root.backend.reloadIfChanged(config)
root.backend.start()
stackView.push(startNodeView)
stackView.push(natComponent)
}
}
}
Component {
id: storageView
id: natComponent
Nat {
onCompleted: function (enabled) {
if (enabled) {
settings.natStrategy = "upnp"
let config = root.backend.buildUpnpConfig(settings.dataDir)
root.backend.reloadIfChanged(config)
root.backend.start()
stackView.push(startNodeComponent)
} else {
stackView.push(portForwardingComponent)
}
}
}
}
Component {
id: storageComponent
StorageView {
backend: root.backend
}
}
Component {
id: startNodeView
id: startNodeComponent
StartNode {
backend: root.backend
onBack: {
root.backend.stop()
stackView.pop()
}
onNext: {
stackView.push(storageView)
stackView.push(storageComponent)
}
}
}
Component {
id: portForwardingComponent
PortForwarding {
onPortTcpSelected: function (port) {
settings.tcpPort = port
settings.natStrategy = "extip"
let config = root.backend.buildNatExtConfig(settings.dataDir,
port)
root.backend.reloadIfChanged(config)
root.backend.start()
stackView.push(startNodeComponent)
}
}
}
@ -133,7 +148,7 @@ Item {
function onInitCompleted() {
if (settings.onboardingCompleted) {
root.backend.start()
stackView.replace(storageView, StackView.Immediate)
stackView.replace(storageComponent, StackView.Immediate)
}
}
}

View File

@ -1,22 +1,18 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import Logos.Theme
import Logos.Controls
Rectangle {
LogosStorageLayout {
id: root
color: Theme.palette.background
Layout.fillWidth: true
Layout.fillHeight: true
implicitWidth: 600
implicitHeight: 400
property int discoveryPort: 8090
property int tcpPort: 0
property var backend: mockBackend
property var local: false
property string dataDir: backend.defaultDataDir()
signal completed
QtObject {
@ -30,79 +26,52 @@ Rectangle {
ColumnLayout {
anchors.centerIn: parent
spacing: Theme.spacing.medium
width: 400
LogosText {
id: titleText
font.pixelSize: Theme.typography.titleText
text: "Logos Storage"
// anchors.verticalCenter: parent.verticalCenter
Layout.alignment: Qt.AlignCenter
}
ColumnLayout {
id: discoveryPortColumn
spacing: Theme.spacing.tiny
Layout.fillWidth: true
LogosText {
text: "Discovery port"
font.pixelSize: Theme.typography.secondaryText
color: Theme.palette.text
}
LogosTextField {
isValid: acceptableInput && text.length > 0
id: discoveryPortTextField
placeholderText: "Enter the discovery port"
text: root.discoveryPort
validator: IntValidator {
bottom: 1
top: 65535
}
onTextChanged: {
if (isValid) {
root.discoveryPort = parseInt(text)
}
}
}
LogosText {
id: questionText
font.pixelSize: Theme.typography.titleText
text: "First, let's choose the storage folder"
Layout.alignment: Qt.AlignCenter
}
ColumnLayout {
id: tcpPortColumn
spacing: Theme.spacing.tiny
Layout.fillWidth: true
// ColumnLayout {
// id: discoveryPortColumn
// spacing: Theme.spacing.tiny
// Layout.fillWidth: true
LogosText {
text: "TCP port"
font.pixelSize: Theme.typography.secondaryText
color: Theme.palette.text
}
LogosTextField {
isValid: acceptableInput && text.length > 0
id: tcpPortTextField
placeholderText: "Enter the TCP port"
text: root.tcpPort
validator: IntValidator {
bottom: 0
top: 65535
}
onTextChanged: {
if (isValid) {
root.tcpPort = parseInt(text)
}
}
}
}
// LogosText {
// text: "Discovery port"
// font.pixelSize: Theme.typography.secondaryText
// color: Theme.palette.text
// }
// LogosTextField {
// isValid: acceptableInput && text.length > 0
// id: discoveryPortTextField
// placeholderText: "Enter the discovery port"
// text: root.discoveryPort
// validator: IntValidator {
// bottom: 1
// top: 65535
// }
// onTextChanged: {
// if (isValid) {
// root.discoveryPort = parseInt(text)
// }
// }
// }
// }
ColumnLayout {
spacing: Theme.spacing.tiny
Layout.fillWidth: true
LogosText {
text: "Data dir"
}
RowLayout {
spacing: Theme.spacing.tiny
@ -130,6 +99,20 @@ Rectangle {
}
}
}
// Column {
// CheckBox {
// text: "Do you want to connect to a local network ?"
// checked: false
// onCheckedChanged: root.local = checked
// }
// LogosText {
// font.pixelSize: Theme.typography.secondaryText
// text: "You will not "
// Layout.alignment: Qt.AlignCenter
// }
// }
}
LogosStorageButton {
@ -138,8 +121,11 @@ Rectangle {
anchors.right: parent.right
anchors.bottomMargin: 10
anchors.rightMargin: 10
enabled: discoveryPortTextField.acceptableInput
&& tcpPortTextField.acceptableInput && dataDirTextField.isValid
onClicked: root.completed()
enabled: dataDirTextField.isValid
// enabled: discoveryPortTextField.acceptableInput
// && tcpPortTextField.acceptableInput && dataDirTextField.isValid
onClicked: function () {
root.completed()
}
}
}

View File

@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Logos.Controls
import Logos.Theme
@ -11,40 +12,67 @@ Rectangle {
implicitWidth: 600
implicitHeight: 400
property var backend
property var backend: mockBackend
property string status: ""
property string title: "Starting your node...."
property bool starting: true
property bool success: false
signal back
signal next
function onNodeStarted() {
root.starting = false
root.status = "Logos Storage started successfully."
root.title = "Success"
root.success = true
}
QtObject {
id: mockBackend
readonly property bool isMock: true
property string configJson: "{}"
signal startCompleted
signal startFailed
signal nodeStarted
}
Timer {
interval: 2000
running: root.backend && root.backend.isMock === true
onTriggered: {
console.log("timer triggered")
root.onNodeStarted()
}
}
Connections {
target: root.backend
function onStartCompleted() {
console.log("onStartCompleted received")
root.starting = false
root.status = "Logos Storage started successfully."
root.success = true
console.log("onStartCompleted")
root.onNodeStarted()
}
function onStartFailed(error) {
console.log("onStartFailed received")
root.starting = false
root.title = "Error"
root.status = "Failed to start: " + error
}
}
ColumnLayout {
anchors.centerIn: parent
anchors.fill: parent
anchors.margins: 20
anchors.bottomMargin: 60
spacing: Theme.spacing.medium
width: 400
LogosText {
id: titleText
font.pixelSize: Theme.typography.titleText
text: "Starting your node...."
text: root.title
Layout.alignment: Qt.AlignHCenter
}

View File

@ -4,12 +4,13 @@ import QtQuick.Dialogs
import QtQuick.Layouts
import QtCore
// qmllint disable unqualified
Rectangle {
id: root
Layout.fillWidth: true
Layout.fillHeight: true
implicitWidth: 600
implicitHeight: 600
implicitWidth: 800
implicitHeight: 800
color: "#000000"
property var backend: mockBackend
@ -63,9 +64,12 @@ Rectangle {
property var status: root.stopped
property var debugLogs: "Hello !"
property var configJson: "{}"
property url cid: ""
property string uploadStatus: ""
property int uploadProgress: 0
property var manifests: []
property var quotaMaxBytes: 20 * 1024 * 1024 * 1024 // 20 GB default
property var quotaUsedBytes: 0
property var quotaReservedBytes: 0
function start(newConfigJson) {
status = root.running
@ -74,63 +78,6 @@ Rectangle {
function stop() {
status = root.stopped
}
function tryPeerConnect(peerId) {
console.log("Attempting peer connection...")
}
function tryDebug() {
console.log("Attempting peer connection...")
}
function spr() {}
function showPeerId() {}
function version() {}
function dataDir() {}
function tryUploadFinalize() {
console.log("Attempting upload finalize")
}
function tryUploadFile(file) {
console.log("Attempting upload file")
}
function tryDownloadFile(cid, file) {
console.log("Attempting download a file", cid, file)
}
function exists(cid) {
console.log("Attempting exists", cid)
}
function fetch(cid) {
console.log("Attempting fetch", cid)
}
function remove(cid) {
console.log("Attempting remove", cid)
}
function downloadManifest(cid) {
console.log("Attempting downloadManifest", cid)
}
function downloadManifests() {
console.log("Attempting downloadManifests")
}
function space() {}
function updateLogLevel(logLevel) {}
property var manifests: []
property var quotaMaxBytes: 20 * 1024 * 1024 * 1024 // 20 GB default
property var quotaUsedBytes: 0
property var quotaReservedBytes: 0
}
function formatBytes(bytes) {
@ -157,13 +104,15 @@ Rectangle {
}
Button {
property var isStopped: root.backend.status == root.stopped
id: startStopButton
objectName: "startStopButton"
anchors.leftMargin: 50
text: root.startStopText()
enabled: root.canStartStop()
onClicked: root.backend.status == root.stopped ? root.backend.start(
jsonEditor.text) : root.backend.stop()
onClicked: isStopped ? root.backend.start(
jsonEditor.text) : root.backend.stop()
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: statusTextElement.bottom
anchors.topMargin: 10
@ -216,7 +165,7 @@ Rectangle {
implicitHeight: 6
Rectangle {
width: parent.width * parent.parent.visualPosition
width: parent.width * control.visualPosition
height: parent.height
radius: 3
color: "#4CAF50"

View File

@ -1,6 +1,9 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QQmlDebuggingEnabler>
static QQmlTriviallyDestructibleDebuggingEnabler enabler;
int main(int argc, char* argv[]) {
QGuiApplication app(argc, argv);