diff --git a/.gitignore b/.gitignore index b7fb871..1290dfc 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ libs app/build CMakeLists.txt.user generated_code +.cache \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index fe1c452..c4ddce7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,7 +213,7 @@ if(_storage_module_is_source) # Add custom target to run the cpp generator for the storage module add_custom_target(run_cpp_generator_storage_module - COMMAND "${CPP_GENERATOR}" "${PLUGIN_FILE}" --module-only --output-dir "${PLUGINS_OUTPUT_DIR}" + COMMAND "${CPP_GENERATOR}" "${PLUGIN_FILE}" --interface "${LOGOS_STORAGE_ROOT}/storage_module_interface.h" --module-only --output-dir "${PLUGINS_OUTPUT_DIR}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMENT "Running logos-cpp-generator on ${PLUGIN_FILE} with output-dir ${PLUGINS_OUTPUT_DIR}" VERBATIM @@ -349,6 +349,8 @@ endif() if(_cpp_sdk_is_source) target_include_directories(storage_ui PRIVATE + + #${PLUGINS_OUTPUT_DIR} ${LOGOS_CPP_SDK_ROOT}/cpp ${LOGOS_CPP_SDK_ROOT}/cpp/generated ) diff --git a/flake.lock b/flake.lock index ec81a20..ac18b22 100644 --- a/flake.lock +++ b/flake.lock @@ -1,32 +1,38 @@ { "nodes": { - "circom-compat": { + "logos-capability-module": { "inputs": { + "logos-cpp-sdk": [ + "logos-cpp-sdk" + ], + "logos-liblogos": "logos-liblogos", "nixpkgs": [ - "logos-storage-module", - "logos-storage", + "logos-capability-module", + "logos-liblogos", "nixpkgs" ] }, "locked": { - "lastModified": 1736521871, - "narHash": "sha256-d34XNLg9NGPEOARHW+BIOAWalkHdEUAwsv3mpLZQxds=", - "owner": "logos-storage", - "repo": "circom-compat-ffi", - "rev": "8cd4ed44fdafe59d4ec1184420639cae4c4dbab9", + "lastModified": 1767809111, + "narHash": "sha256-jehjsB+BpDJlVu3I7x+vFVOdXmy9MDmFTJtRqzFUONo=", + "owner": "logos-co", + "repo": "logos-capability-module", + "rev": "7b35383e0aa4e28a4633ed18a87efb57636939b1", "type": "github" }, "original": { - "owner": "logos-storage", - "repo": "circom-compat-ffi", + "owner": "logos-co", + "repo": "logos-capability-module", "type": "github" } }, - "logos-capability-module": { + "logos-capability-module_2": { "inputs": { - "logos-cpp-sdk": "logos-cpp-sdk", - "logos-liblogos": "logos-liblogos", + "logos-cpp-sdk": "logos-cpp-sdk_3", + "logos-liblogos": "logos-liblogos_4", "nixpkgs": [ + "logos-storage-module", + "logos-liblogos", "logos-capability-module", "logos-liblogos", "nixpkgs" @@ -68,6 +74,25 @@ "inputs": { "nixpkgs": "nixpkgs_2" }, + "locked": { + "lastModified": 1770978598, + "narHash": "sha256-CR5N5v+y2ZInnBocGgjIeLaS9XqNjAKGXE2abC3E110=", + "owner": "logos-co", + "repo": "logos-cpp-sdk", + "rev": "c3477d29e32cae5f73ca637fb81e547f8a6cba58", + "type": "github" + }, + "original": { + "owner": "logos-co", + "ref": "feat/logos-result", + "repo": "logos-cpp-sdk", + "type": "github" + } + }, + "logos-cpp-sdk_3": { + "inputs": { + "nixpkgs": "nixpkgs_3" + }, "locked": { "lastModified": 1761230734, "narHash": "sha256-CMRUwXH7pJZ1OI6bd/TDDDXKqQ1tQZHQEOOwK8TgYHI=", @@ -82,34 +107,16 @@ "type": "github" } }, - "logos-cpp-sdk_3": { - "inputs": { - "nixpkgs": "nixpkgs_3" - }, - "locked": { - "lastModified": 1767724329, - "narHash": "sha256-UPkqxqxbKwU5Dmu00TnjiJVXUmfVylF3p1qziEuYwIE=", - "owner": "logos-co", - "repo": "logos-cpp-sdk", - "rev": "32f1d7080d784ff044d91d076ef2f0c7305d4784", - "type": "github" - }, - "original": { - "owner": "logos-co", - "repo": "logos-cpp-sdk", - "type": "github" - } - }, "logos-cpp-sdk_4": { "inputs": { "nixpkgs": "nixpkgs_4" }, "locked": { - "lastModified": 1764699992, - "narHash": "sha256-nCmK9C9F31cHvy6lWKR5WGl99aJbS3kIsROAoZ4OrwI=", + "lastModified": 1761230734, + "narHash": "sha256-CMRUwXH7pJZ1OI6bd/TDDDXKqQ1tQZHQEOOwK8TgYHI=", "owner": "logos-co", "repo": "logos-cpp-sdk", - "rev": "5d0bbd0d1e00aad0532ffa7c8bf2c80f460a4f6d", + "rev": "4b143922c190df00bb3835441c9f0075cb28283b", "type": "github" }, "original": { @@ -141,11 +148,11 @@ "nixpkgs": "nixpkgs_6" }, "locked": { - "lastModified": 1764699992, - "narHash": "sha256-nCmK9C9F31cHvy6lWKR5WGl99aJbS3kIsROAoZ4OrwI=", + "lastModified": 1767724329, + "narHash": "sha256-UPkqxqxbKwU5Dmu00TnjiJVXUmfVylF3p1qziEuYwIE=", "owner": "logos-co", "repo": "logos-cpp-sdk", - "rev": "5d0bbd0d1e00aad0532ffa7c8bf2c80f460a4f6d", + "rev": "32f1d7080d784ff044d91d076ef2f0c7305d4784", "type": "github" }, "original": { @@ -156,7 +163,7 @@ }, "logos-liblogos": { "inputs": { - "logos-cpp-sdk": "logos-cpp-sdk_2", + "logos-cpp-sdk": "logos-cpp-sdk", "nixpkgs": [ "logos-capability-module", "logos-liblogos", @@ -180,7 +187,9 @@ }, "logos-liblogos_2": { "inputs": { - "logos-cpp-sdk": "logos-cpp-sdk_4", + "logos-cpp-sdk": [ + "logos-cpp-sdk" + ], "nixpkgs": [ "logos-liblogos", "logos-cpp-sdk", @@ -204,7 +213,9 @@ }, "logos-liblogos_3": { "inputs": { - "logos-cpp-sdk": "logos-cpp-sdk_6", + "logos-capability-module": "logos-capability-module_2", + "logos-cpp-sdk": "logos-cpp-sdk_5", + "logos-module": "logos-module", "nixpkgs": [ "logos-storage-module", "logos-liblogos", @@ -213,11 +224,11 @@ ] }, "locked": { - "lastModified": 1767722338, - "narHash": "sha256-89RYfnramvJRufy47Kdx662TMHaFVSpS88ouWff3Csw=", + "lastModified": 1770837874, + "narHash": "sha256-wr75lv1q4U1FS5+l/6ypwzJFJe06l2RyUvx1npoRS88=", "owner": "logos-co", "repo": "logos-liblogos", - "rev": "ca1fda7cabd70027e51d86392f5e29b3fff385f9", + "rev": "e3741c01fd3abf6b7bd9ff2fa8edf89c41fc0cea", "type": "github" }, "original": { @@ -226,17 +237,67 @@ "type": "github" } }, + "logos-liblogos_4": { + "inputs": { + "logos-cpp-sdk": "logos-cpp-sdk_4", + "nixpkgs": [ + "logos-storage-module", + "logos-liblogos", + "logos-capability-module", + "logos-liblogos", + "logos-cpp-sdk", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1761845775, + "narHash": "sha256-ulK8xq05ejK6qIgZ7WtWb/MJt2rk5BKfDA2z7mM3wq8=", + "owner": "logos-co", + "repo": "logos-liblogos", + "rev": "a92c2c1268bc70764c8f73c7bce07d21024f5af9", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-liblogos", + "type": "github" + } + }, + "logos-module": { + "inputs": { + "logos-cpp-sdk": "logos-cpp-sdk_6", + "nixpkgs": [ + "logos-storage-module", + "logos-liblogos", + "logos-module", + "logos-cpp-sdk", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1770062426, + "narHash": "sha256-zc7ZxDTlqOCYGyEHhrTA/7GS1EWh7+4amdPUKh+gGds=", + "owner": "logos-co", + "repo": "logos-module", + "rev": "f7ee69d9ad9f27c84f04f59896e9194125e951dc", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module", + "type": "github" + } + }, "logos-storage": { "inputs": { - "circom-compat": "circom-compat", "nixpkgs": "nixpkgs_7" }, "locked": { - "lastModified": 1769016529, - "narHash": "sha256-pzUrdFKSEraZQzO2a9Uj5oDMas+Vtpu6+p4bG+lRnsk=", + "lastModified": 1770982130, + "narHash": "sha256-qsTf54BP2yEJEDHVR1lgtZ1u2wm7TK5MUHgwUiX6wU0=", "ref": "refs/heads/master", - "rev": "3c09f008bb5266a669fd19f18368f9e8b861b664", - "revCount": 877, + "rev": "e375223500ca0fd919b0e504eab65c6ddb5e4233", + "revCount": 897, "submodules": true, "type": "git", "url": "https://github.com/logos-storage/logos-storage-nim" @@ -249,7 +310,9 @@ }, "logos-storage-module": { "inputs": { - "logos-cpp-sdk": "logos-cpp-sdk_5", + "logos-cpp-sdk": [ + "logos-cpp-sdk" + ], "logos-liblogos": "logos-liblogos_3", "logos-storage": "logos-storage", "nixpkgs": [ @@ -259,11 +322,11 @@ ] }, "locked": { - "lastModified": 1769611047, - "narHash": "sha256-U8lV4hgntPTMO0wpyIDFKMN1N3jSGnxKDCGHS9WoEb8=", + "lastModified": 1771221082, + "narHash": "sha256-dpygXvcVOExNXHkUH+/XcR/xBVFKNjw/i9aChSXcZNk=", "owner": "logos-co", "repo": "logos-storage-module", - "rev": "14cc30674980685442e66639c37c587263520147", + "rev": "b4ecf7a871233608f63b817eeae426f6273695d9", "type": "github" }, "original": { @@ -370,11 +433,11 @@ }, "nixpkgs_7": { "locked": { - "lastModified": 1736200483, - "narHash": "sha256-JO+lFN2HsCwSLMUWXHeOad6QUxOuwe9UOAF/iSl1J4I=", + "lastModified": 1751274312, + "narHash": "sha256-/bVBlRpECLVzjV19t5KMdMFWSwKLtb5RyXdjz3LJT+g=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3f0a8ac25fb674611b98089ca3a5dd6480175751", + "rev": "50ab793786d9de88ee30ec4e4c24fb4236fc2674", "type": "github" }, "original": { @@ -387,7 +450,7 @@ "root": { "inputs": { "logos-capability-module": "logos-capability-module", - "logos-cpp-sdk": "logos-cpp-sdk_3", + "logos-cpp-sdk": "logos-cpp-sdk_2", "logos-liblogos": "logos-liblogos_2", "logos-storage-module": "logos-storage-module", "nixpkgs": [ diff --git a/flake.nix b/flake.nix index 85f868d..89a364a 100644 --- a/flake.nix +++ b/flake.nix @@ -4,10 +4,16 @@ inputs = { # Follow the same nixpkgs as logos-liblogos to ensure compatibility nixpkgs.follows = "logos-liblogos/nixpkgs"; - logos-cpp-sdk.url = "github:logos-co/logos-cpp-sdk"; + # logos-cpp-sdk.url = "/home/arnaud/Work/logos/logos-cpp-sdk"; + logos-cpp-sdk.url = "github:logos-co/logos-cpp-sdk?ref=feat/logos-result"; logos-liblogos.url = "github:logos-co/logos-liblogos?ref=fix/logos-cleanup-on-terminate"; logos-storage-module.url = "github:logos-co/logos-storage-module"; + #logos-storage-module.url = "path:/home/arnaud/Work/logos/logos-storage-module"; logos-capability-module.url = "github:logos-co/logos-capability-module"; + + logos-liblogos.inputs.logos-cpp-sdk.follows = "logos-cpp-sdk"; + logos-storage-module.inputs.logos-cpp-sdk.follows = "logos-cpp-sdk"; + logos-capability-module.inputs.logos-cpp-sdk.follows = "logos-cpp-sdk"; }; outputs = { self, nixpkgs, logos-cpp-sdk, logos-liblogos, logos-storage-module, logos-capability-module }: diff --git a/src/StorageBackend.cpp b/src/StorageBackend.cpp index 2cab9dc..2496fd4 100644 --- a/src/StorageBackend.cpp +++ b/src/StorageBackend.cpp @@ -1,11 +1,21 @@ #include "StorageBackend.h" #include #include +#include +#include #include #include #include #include +// StorageBackend is responsible for managing the interaction with the storage module. +// It is mocked in the QML. +// There are currently 2 ways to display debug information: +// - the first one is to log only in the console using qDebug/qWarning. This is basically +// for developers: entering a function, sending a command to the storage module... +// - the second one is to use the "debug" helper that logs both in the console and in a +// QString property that can be displayed in the UI. This is more for users to understand +// what is happening. StorageBackend::StorageBackend(LogosAPI* logosAPI, QObject* parent) : QObject(parent), m_status(Destroyed), m_logosAPI(nullptr), m_logos(nullptr) { qDebug() << "Initializing StorageBackend..."; @@ -17,8 +27,6 @@ StorageBackend::StorageBackend(LogosAPI* logosAPI, QObject* parent) } m_logos = new LogosModules(m_logosAPI); - - initStorage(); } StorageBackend::~StorageBackend() @@ -27,97 +35,223 @@ StorageBackend::~StorageBackend() m_logos = nullptr; } -void StorageBackend::initStorage() { +LogosResult StorageBackend::init(const QString& configJson = "{}") { qDebug() << "StorageBackend::initStorage called"; - bool result = m_logos->storage_module.init("{}"); + bool result = m_logos->storage_module.init(m_configJson); - qDebug() << "StorageBackend::initStorage: init result =" << result; + qDebug() << "StorageBackend::initStorage: init"; if (!result) { - qDebug() << "StorageBackend: Failed to initialise Storage module."; - setStatus(Destroyed, "Failed to initialise Storage module."); - return; + setStatus(Destroyed); + debug("Failed to init storage"); + return {false, "", "Filed to init storage"}; } - setStatus(Stopped, "Storage module ready."); + setStatus(Stopped); if (!m_logos->storage_module.on("storageStart", [this](const QVariantList& data) { - int code = data[0].toInt(); + bool success = data[0].toBool(); - if (code != RET_OK) { - qDebug() << "StorageBackend: storageStart event failure with code" << code; - setStatus(Stopped, "Failed to start Storage module."); + if (!success) { + QString message = data[1].toString(); + setStatus(Stopped); + debug("Failed to start Storage module:" + message); } else { - qDebug() << "StorageBackend: storageStart event success"; - setStatus(Running, "Storage module started."); + setStatus(Running); + debug("Storage module started."); } })) { qWarning() << "StorageWidget: failed to subscribe to storageStart events"; } if (!m_logos->storage_module.on("storageStop", [this](const QVariantList& data) { - int code = data[0].toInt(); + bool success = data[0].toBool(); - if (code != RET_OK) { - qDebug() << "StorageBackend: storageStop event failure with code" << code; - setStatus(Running, "Failed to stop Storage module."); + if (!success) { + QString message = data[1].toString(); + setStatus(Running); + debug("Failed to stop Storage module:" + message); } else { - qDebug() << "StorageBackend: storageStop event success"; - setStatus(Stopped, "Storage module stopped."); + setStatus(Stopped); + debug("Storage module stopped."); emit stopped(); } })) { qWarning() << "StorageWidget: failed to subscribe to storageStop events"; } - startStop(); + if (!m_logos->storage_module.on("storageConnect", [this](const QVariantList& data) { + bool success = data[0].toBool(); + + if (!success) { + QString message = data[1].toString(); + debug("Failed to connect: " + message); + } else { + // TODO add the peer id + debug("Successfully connected to peer."); + } + })) { + qWarning() << "StorageWidget: failed to subscribe to storageConnect events"; + } + + if (!m_logos->storage_module.on("storageUploadProgress", [this](const QVariantList& data) { + bool success = data[0].toBool(); + + if (!success) { + QString message = data[1].toString(); + debug("Failure during upload progress: " + message); + m_uploadStatus = "Error: " + message; + emit uploadStatusChanged(); + } else { + QString sessionId = data[1].toString(); + qint64 len = data[2].toLongLong(); + + m_uploadedBytes += len; + + // Calcule le pourcentage + if (m_uploadTotalBytes > 0) { + m_uploadProgress = (m_uploadedBytes * 100) / m_uploadTotalBytes; + } + + m_uploadStatus = QString("Uploading: %1 / %2 bytes (%3%)") + .arg(m_uploadedBytes) + .arg(m_uploadTotalBytes) + .arg(m_uploadProgress); + + emit uploadProgressChanged(); + emit uploadStatusChanged(); + } + })) { + qWarning() << "StorageWidget: failed to subscribe to storageUploadProgress events"; + } + + if (!m_logos->storage_module.on("storageUploadDone", [this](const QVariantList& data) { + bool success = data[0].toBool(); + + if (!success) { + QString message = data[1].toString(); + debug("Failed to upload: " + message); + m_uploadProgress = 0; + m_uploadStatus = "Upload failed"; + emit uploadProgressChanged(); + emit uploadStatusChanged(); + } else { + QString sessionId = data[1].toString(); + m_cid = data[2].toString(); + emit cidChanged(); + debug("Upload completed for session " + sessionId + " with CID " + m_cid); + + // Complète la progress bar + m_uploadProgress = 100; + m_uploadStatus = "Upload completed!"; + emit uploadProgressChanged(); + emit uploadStatusChanged(); + } + })) { + qWarning() << "StorageWidget: failed to subscribe to storageUploadProgress events"; + } + + if (!m_logos->storage_module.on("storageDownloadProgress", [this](const QVariantList& data) { + bool success = data[0].toBool(); + + if (!success) { + QString message = data[1].toString(); + debug("Failure during download progress: " + message); + } else { + QString sessionId = data[1].toString(); + int len = data[2].toInt(); + debug("Downloaded " + QString::number(len) + " bytes for session " + sessionId); + } + })) { + qWarning() << "StorageWidget: failed to subscribe to storageDownloadProgress events"; + } + + if (!m_logos->storage_module.on("storageDownloadDone", [this](const QVariantList& data) { + bool success = data[0].toBool(); + + if (!success) { + QString message = data[1].toString(); + debug("Failed to download: " + message); + } else { + QString sessionId = data[1].toString(); + m_cid = data[2].toString(); + emit cidChanged(); + debug("Download completed for session " + sessionId + " with CID " + m_cid); + } + })) { + qWarning() << "StorageWidget: failed to subscribe to storageDownloadProgress events"; + } + + m_configJson = configJson; + emit configJsonChanged(); + + debug("config.json content is: " + m_configJson); + + return {true, ""}; } -void StorageBackend::setStatus(StorageStatus newStatus, QString statusText) { - if (m_status != newStatus || m_statusText != statusText) { +void StorageBackend::setStatus(StorageStatus newStatus) { + if (m_status != newStatus) { m_status = newStatus; - m_statusText = statusText; emit statusChanged(); - qDebug() << "StorageBackend: Status changed to" << m_status; } } -void StorageBackend::startStop() { - qDebug() << "StorageBackend: startStop method called"; +LogosResult StorageBackend::start(const QString& newConfigJson) { + qDebug() << "StorageBackend: start method called"; - if (m_status == Destroyed || m_status == Starting || m_status == Stopping) { - qDebug() << "StorageBackend: Cannot start/stop Storage in current state:" << m_status; - return; + if (newConfigJson != "") { + reloadIfChanged(newConfigJson); } - if (m_status != Running) { - qDebug() << "StorageBackend: Starting Storage..."; - setStatus(Starting, "Starting Storage module..."); - - bool result = m_logos->storage_module.start(); - - if (!result) { - qDebug() << "StorageBackend: Failed to start Storage."; - setStatus(Stopped, "Failed to start Storage module."); - return; - } - - qDebug() << "StorageBackend: start command sent, waiting for events."; - } else { - stop(); + if (m_status != Stopped) { + debug("The Storage Module is not initialised properly."); + return {false, "", "The Storage Module is not initialised properly."}; } + + if (m_status == Running) { + debug("The Storage Module is already started."); + return {false, "", "The Storage Module is already started."}; + } + + setStatus(Starting); + debug("Starting Storage module..."); + + auto result = m_logos->storage_module.start(); + + if (!result) { + setStatus(Stopped); + debug("Failed to start storage"); + return {false, "", "Failed to start storage"}; + } + + qDebug() << "StorageBackend: start command sent, waiting for events."; + + return {true, ""}; } void StorageBackend::stop() { - qDebug() << "StorageBackend: Stopping Storage..."; - setStatus(Stopping, "Stopping Storage module..."); + qDebug() << "StorageBackend: stop method called"; - bool result = m_logos->storage_module.stop(); + if (m_status == StorageStatus::Stopping) { + debug("The Storage Module is already stopping."); + return; + } - if (!result) { - qDebug() << "StorageBackend: Failed to stop Storage."; - setStatus(Running, "Failed to stop Storage module."); + if (m_status != StorageStatus::Running) { + debug("The Storage Module is not started."); + return; + } + + setStatus(Stopping); + debug("Stopping Storage module..."); + + auto result = m_logos->storage_module.stop(); + + if (!result.success) { + setStatus(Running); + debug(result.getError()); return; } @@ -128,29 +262,495 @@ void StorageBackend::destroy() { qDebug() << "StorageBackend: destroy method called"; StorageStatus status = m_status; - int result = m_logos->storage_module.destroy(); + auto result = m_logos->storage_module.destroy(); - if (!result) { - qDebug() << "StorageBackend: Failed to destroy Storage module." << result; - setStatus(status, "Failed to destroy Storage module."); + if (!result.success) { + debug(result.getError()); return; } qDebug() << "StorageBackend: Storage module destroyed."; } -QString StorageBackend::statusText() const { return m_statusText; } +QString StorageBackend::debugLogs() const { return m_debugLogs; }; -QString StorageBackend::startStopText() const { - if (m_status != Running) { - return "Start"; - } else { - return "Stop"; +void StorageBackend::debug(const QString& log) { + if (!m_debugLogs.isEmpty()) { + m_debugLogs += "\n"; } + + QString timestamp = QDateTime::currentDateTime().toString(Qt::ISODate); + m_debugLogs += timestamp + ": " + log; + emit debugLogsChanged(); + + qDebug() << "StorageBackend: " << log; } -bool StorageBackend::canStartStop() const { return m_status == Running || m_status == Stopped; } +void StorageBackend::tryDebug() { + auto result = m_logos->storage_module.debug(); -bool StorageBackend::isRunning() { return m_status == Running; } + debug("Debug " + result.getString()); +} +void StorageBackend::tryPeerConnect(const QString& peerId) { + qDebug().noquote() << "StorageBackend: tryPeerConnect called with peerId=" << peerId; -bool StorageBackend::isInitialised() { return m_status != Destroyed; } + // LogosResult result2 = m_logos->storage_module.space(); + // QVariantMap space = result2.getValue(); + // int quotaMaxBytes = space["quotaMaxBytes"].toInt(); + // int quotaUsedBytes = space["quotaUsedBytes"].toInt(); + // int quotaReservedBytes = space["quotaReservedBytes"].toInt(); + + // int totalBlocks = result2.getValue("totalBlocks"); + + // debug("totalBlocks " + QString::number(totalBlocks)); + // debug("quotaMaxBytes " + QString::number(quotaMaxBytes)); + // debug("quotaUsedBytes " + QString::number(quotaUsedBytes)); + // debug("quotaReservedBytes " + QString::number(quotaReservedBytes)); + + // LogosResult result = m_logos->storage_module.dataDir(); + // QString myDataDir = result.getString(); + // qDebug() << "StorageBackend: tryPeerConnect dataDir=" << myDataDir; + + // QString peerId = m_logos->storage_module.peerId(); + + // if (peerId.isEmpty()) { + // qDebug() << "StorageBackend: Peer ID is empty."; + // return; + // } + auto result = m_logos->storage_module.connect(peerId, QStringList()); + + qDebug() << "StorageBackend: peerConnect result =" << result.value; + // auto result = m_logos->storage_module.debug(); + + // debug("Debug " + result.getString()); + // QString filename = "test.txt"; + // QString sessionId = m_logos->storage_module.uploadInit(filename); + + // qDebug() << "StorageBackend: uploadInit sessionId =" << sessionId; + + // bool result = m_logos->storage_module.uploadCancel(sessionId); + + // qDebug() << "StorageBackend: uploadCancel result =" << result; +} + +void StorageBackend::tryUpload() { + qDebug() << "StorageBackend: tryUpload called"; + + // QString filename = "test.txt"; + // m_sessionId = m_logos->storage_module.uploadInit(filename); + + // qDebug() << "StorageBackend: uploadInit sessionId =" << m_sessionId; + + // QByteArray chunk = "Sample data chunk for upload."; + // bool result = m_logos->storage_module.uploadChunk(m_sessionId, chunk); + + // qDebug() << "StorageBackend: uploadChunk result =" << result; +} + +void StorageBackend::tryUploadFinalize() { + qDebug() << "StorageBackend: tryFinalize called"; + + // m_cid = m_logos->storage_module.uploadFinalize(m_sessionId); + + // qDebug() << "StorageBackend: uploadFinalize result =" << m_cid; + + // emit cidChanged(); +} + +void StorageBackend::tryUploadFile(const QUrl& url) { + qDebug() << "StorageBackend: tryUploadFile called"; + qDebug() << " URL toString():" << url.toString(); + qDebug() << " URL toLocalFile():" << url.toLocalFile(); + qDebug() << " URL path():" << url.path(); + + if (!url.isLocalFile()) { + qWarning() << "Not a local file"; + debug("The provided URL is not a local file."); + return; + } + + // Reset and initialize progress tracking + m_uploadProgress = 0; + m_uploadedBytes = 0; + m_uploadTotalBytes = QFileInfo(url.toLocalFile()).size(); + m_uploadStatus = "Starting upload..."; + emit uploadProgressChanged(); + emit uploadStatusChanged(); + + debug(QString("Starting upload of file: %1 bytes").arg(m_uploadTotalBytes)); + + // QString filename = url.toLocalFile(); + + // // QString filename = "/home/arnaud/Work/logos/logos-storage-ui/README.md"; + // QString sessionId = m_logos->storage_module.uploadInit(filename); + + // qDebug() << "StorageBackend: uploadInit sessionId =" << sessionId; + + // QtConcurrent::run([this, url]() { + // LogosResult result = m_logos->storage_module.uploadUrl(url); + + // Go back to the main thread + // // Better to use signal + // QMetaObject::invokeMethod( + // this, + // [this, result]() { + // if (!result.success) { + // setStatus(m_status, result.getString()); + // return; + // } + + // QString sessionId = result.value.value(); + + // qDebug() << "StorageBackend: uploadFromPath result =" << sessionId; + // }, + // Qt::QueuedConnection); + // }); + // QTimer::singleShot(0, this, [this, url]() { + LogosResult result = m_logos->storage_module.uploadUrl(url); + + if (!result.success) { + debug(result.getError()); + return; + } + + QString sessionId = result.value.value(); + //}); + qDebug() << "StorageBackend: tryUploadFile result =" << sessionId; +} + +// void StorageBackend::tryUploadFile(const QUrl& url) { +// qDebug() << "StorageBackend:tryUploadFile called"; + +// if (!url.isLocalFile()) { +// qWarning() << "Not a local file"; +// m_statusText = "The provided URL is not a local file."; +// emit statusChanged(); +// return; +// } + +// QString localPath = url.toLocalFile(); +// qDebug() << " Uploading from:" << localPath; + +// QFile file(localPath); +// if (!file.open(QIODevice::ReadOnly)) { +// qWarning() << "Cannot open file for reading:" << localPath; +// m_statusText = "Cannot open file for reading: " + localPath; +// emit statusChanged(); +// return; +// } + +// const qint64 chunkSize = 1024 * 64; // 64KB +// qint64 totalSize = file.size(); +// qint64 bytesRead = 0; + +// QFileInfo fileInfo(localPath); +// QString filename = fileInfo.fileName(); + +// LogosResult result = m_logos->storage_module.uploadInit("test.txt", chunkSize); + +// if (!result.success) { +// debug(result.getString()); +// file.close(); +// return; +// } + +// QString sessionId = result.getString(); + +// while (!file.atEnd()) { +// QByteArray chunk = file.read(chunkSize); +// bytesRead += chunk.size(); + +// qDebug() << " Read chunk:" << chunk.size() << "bytes" +// << "Progress:" << bytesRead << "/" << totalSize; + +// result = m_logos->storage_module.uploadChunk(sessionId, chunk); + +// if (!result.success) { +// qWarning("StorageBackend:tryUploadFile failed to send uploadChunk command"); +// file.close(); +// return; +// } + +// // Calculate progress percentage +// int progress = (bytesRead * 100) / totalSize; +// qDebug() << " Progress:" << progress << "%"; +// } + +// file.close(); + +// result = m_logos->storage_module.uploadFinalize(sessionId); + +// if (!result.success) { +// qWarning("StorageBackend:tryUploadFile failed to send uploadFinalize command"); +// file.close(); +// return; +// } + +// qDebug() << "Upload complete, CID:" << result.getString(); + +// file.close(); +// } + +void StorageBackend::tryDownloadFile(const QString& cid, const QUrl& url) { + qDebug() << "StorageBackend: tryDownloadFile called"; + + if (!url.isLocalFile()) { + qWarning() << "Not a local file"; + debug("The provided URL is not a local file."); + return; + } + + // QString filename = url.toLocalFile(); + + // // QString filename = "/home/arnaud/Work/logos/logos-storage-ui/README.md"; + // QString sessionId = m_logos->storage_module.uploadInit(filename); + + // qDebug() << "StorageBackend: uploadInit sessionId =" << sessionId; + + LogosResult result = m_logos->storage_module.downloadToUrl(cid, url, false); + + if (!result.success) { + debug(result.getError()); + return; + } + + QString sessionId = result.value.value(); + + qDebug() << "StorageBackend: tryDownloadFile result =" << sessionId; +} + +void StorageBackend::exists(const QString& cid) { + qDebug() << "StorageBackend::exists called"; + + LogosResult result = m_logos->storage_module.exists(cid); + + if (!result.success) { + debug("StorageBackend::exists failed with error=" + result.getError()); + return; + } + + debug("Does " + cid + " exists ? " + QVariant(result.getValue()).toString()); +} + +void StorageBackend::remove(const QString& cid) { + qDebug() << "StorageBackend::remove called"; + + LogosResult result = m_logos->storage_module.remove(cid); + + if (!result.success) { + debug("StorageBackend::remove failed with error=" + result.getError()); + return; + } + + debug("Cid " + cid + " removed."); +} + +void StorageBackend::fetch(const QString& cid) { + qDebug() << "StorageBackend::fetch called"; + + LogosResult result = m_logos->storage_module.fetch(cid); + + if (!result.success) { + debug("StorageBackend::fetch failed with error=" + result.getError()); + return; + } + + debug("Cid " + cid + " fetched."); +} + +void StorageBackend::version() { + qDebug() << "StorageBackend::version called"; + + LogosResult result = m_logos->storage_module.version(); + + if (!result.success) { + debug("StorageBackend::version failed with error=" + result.getError()); + return; + } + + debug("Version: " + result.getString()); +} + +void StorageBackend::showPeerId() { + qDebug() << "StorageBackend::peerId called"; + + LogosResult result = m_logos->storage_module.peerId(); + + if (!result.success) { + debug("StorageBackend::peerId failed with error=" + result.getError()); + return; + } + + debug("Peer ID: " + result.getString()); +} + +void StorageBackend::spr() { + qDebug() << "StorageBackend::spr called"; + + LogosResult result = m_logos->storage_module.spr(); + + if (!result.success) { + debug("StorageBackend::spr failed with error=" + result.getError()); + return; + } + + debug("SPR: " + result.getString()); +} + +void StorageBackend::dataDir() { + qDebug() << "StorageBackend::dataDir called"; + + LogosResult result = m_logos->storage_module.dataDir(); + + if (!result.success) { + debug("StorageBackend::dataDir failed with error=" + result.getError()); + return; + } + + debug("Data dir: " + result.getString()); +} + +void StorageBackend::downloadManifest(const QString& cid) { + qDebug() << "StorageBackend::downloadManifest called with cid=" << cid; + + LogosResult result = m_logos->storage_module.downloadManifest(cid); + + if (!result.success) { + debug("StorageBackend::downloadManifest failed with error=" + result.getError()); + return; + } + + debug("Manifest tree cid: " + result.getString("treeCid")); + debug(QString("Manifest datasetSize %1").arg(result.getInt("datasetSize"))); + debug(QString("Manifest blockSize %1").arg(result.getInt("blockSize"))); + debug("Manifest filename: " + result.getString("filename")); + debug("Manifest mimetype: " + result.getString("mimetype")); +} + +void StorageBackend::downloadManifests() { + qDebug() << "StorageBackend::downloadManifests called"; + + LogosResult result = m_logos->storage_module.manifests(); + QString error = result.getError(); + if (!result.success) { + debug("StorageBackend::downloadManifests failed with error=" + result.getError()); + return; + } + + QVariantList manifestsList = result.getList(); + int count = manifestsList.size(); + + debug(QString("Found %1 manifests").arg(count)); + + // for (const QVariant& manifestVariant : manifestsList) { + // QVariantMap manifest = manifestVariant.toMap(); + + // QString cid = manifest["cid"].toString(); + // QString treeCid = manifest["treeCid"].toString(); + // QString filename = manifest["filename"].toString(); + // qint64 datasetSize = manifest["datasetSize"].toLongLong(); + + // debug(QString("Manifest: %1, treeCid: %2, size: %3") + // .arg(filename) + // .arg(treeCid.isEmpty() ? "EMPTY" : treeCid) + // .arg(datasetSize)); + // } +} + +void StorageBackend::space() { + qDebug() << "StorageBackend::space called"; + + LogosResult result = m_logos->storage_module.space(); + + if (!result.success) { + debug("StorageBackend::space failed with error=" + result.getError()); + return; + } + + debug(QString("Space datasetSize %1").arg(result.getInt("totalBlocks"))); + debug(QString("Space quotaMaxBytes %1").arg(result.getInt("quotaMaxBytes"))); + debug(QString("Space quotaUsedBytes %1").arg(result.getInt("quotaUsedBytes"))); + debug(QString("Space quotaReservedBytes %1").arg(result.getInt("quotaReservedBytes"))); +} + +void StorageBackend::updateLogLevel(const QString& logLevel) { + qDebug() << "StorageBackend::updateLogLevel called with logLevel=" << logLevel; + + LogosResult result = m_logos->storage_module.updateLogLevel(logLevel); + + if (!result.success) { + debug("StorageBackend::updateLogLevel failed with error=" + result.getError()); + return; + } + + debug("Log level updated to " + logLevel); +} + +StorageBackend::StorageStatus StorageBackend::status() const { return m_status; } + +QString StorageBackend::cid() const { return m_cid; } + +QString StorageBackend::configJson() const { return m_configJson; } + +int StorageBackend::uploadProgress() const { return m_uploadProgress; } + +QString StorageBackend::uploadStatus() const { return m_uploadStatus; } + +void StorageBackend::reloadIfChanged(const QString& configJson) { + if (configJson == m_configJson) { + return; + } + + debug("New config detected"); + + QJsonDocument doc = QJsonDocument::fromJson(configJson.toUtf8()); + if (doc.isNull()) { + debug("Invalid json detected !"); + + m_configJson = configJson; + emit configJsonChanged(); + + return; + } + + if (m_status == StorageStatus::Running || m_status == StorageStatus::Stopping || + m_status == StorageStatus::Starting) { + debug("Cannot reload the config while running, stopping or starting..."); + return; + } + + if (m_status == StorageStatus::Stopped) { + LogosResult result = m_logos->storage_module.destroy(); + + if (!result.success) { + debug("Failed to destroy the context error=" + result.getError()); + return; + } else { + setStatus(StorageStatus::Destroyed); + } + } + + bool result = m_logos->storage_module.init(configJson); + + if (!result) { + debug("Failed to init context with new config, will rollback."); + + bool result = m_logos->storage_module.init(m_configJson); + + if (!result) { + debug("Failed to init context with old config, that's a serious issue."); + } else { + debug("Old config restored"); + setStatus(StorageStatus::Stopped); + + m_configJson = configJson; + emit configJsonChanged(); + } + return; + } + + debug("New config loaded successfully"); + + m_configJson = configJson; + setStatus(StorageStatus::Stopped); +} diff --git a/src/StorageBackend.h b/src/StorageBackend.h index 76d0b7b..859da3f 100644 --- a/src/StorageBackend.h +++ b/src/StorageBackend.h @@ -8,47 +8,80 @@ #include static const int RET_OK = 0; +static const int RET_PROGRESS = 3; class StorageBackend : public QObject { Q_OBJECT QML_ELEMENT + Q_PROPERTY(QString debugLogs READ debugLogs NOTIFY debugLogsChanged) + Q_PROPERTY(StorageStatus status READ status NOTIFY statusChanged) + Q_PROPERTY(QString cid READ cid NOTIFY cidChanged) + Q_PROPERTY(QString configJson READ configJson NOTIFY configJsonChanged) + Q_PROPERTY(int uploadProgress READ uploadProgress NOTIFY uploadProgressChanged) + Q_PROPERTY(QString uploadStatus READ uploadStatus NOTIFY uploadStatusChanged) public: enum StorageStatus { Stopped = 0, Starting, Running, Stopping, Destroyed }; Q_ENUM(StorageStatus) - Q_PROPERTY(bool canStartStop READ canStartStop NOTIFY statusChanged) - bool canStartStop() const; - - Q_PROPERTY(QString startStopText READ startStopText NOTIFY statusChanged) - QString startStopText() const; - - Q_PROPERTY(QString statusText READ statusText NOTIFY statusChanged) - QString statusText() const; + QString cid() const; + QString debugLogs() const; + StorageStatus status() const; + QString configJson() const; + int uploadProgress() const; + QString uploadStatus() const; explicit StorageBackend(LogosAPI* logosAPI = nullptr, QObject* parent = nullptr); ~StorageBackend(); public slots: - void startStop(); + LogosResult start(const QString& configJson = ""); void destroy(); - bool isRunning(); - bool isInitialised(); void stop(); + void tryPeerConnect(const QString& peerId); + void tryDebug(); + void tryUpload(); + void tryUploadFinalize(); + void exists(const QString& cid); + void remove(const QString& cid); + void fetch(const QString& cid); + void tryUploadFile(const QUrl& url); + void tryDownloadFile(const QString& cid, const QUrl& url); + void dataDir(); + void version(); + void spr(); + void showPeerId(); + void downloadManifest(const QString& cid); + void downloadManifests(); + void space(); + LogosResult init(const QString& configJson); + void updateLogLevel(const QString& logLevel); signals: void statusChanged(); + void debugLogsChanged(); void stopped(); + void cidChanged(); + void configJsonChanged(); + void uploadProgressChanged(); + void uploadStatusChanged(); private slots: private: - void setStatus(StorageStatus newStatus, QString statusText); - void initStorage(); + void setStatus(StorageStatus newStatus); + void peerConnect(const QString& peerId); + void debug(const QString& log); + void reloadIfChanged(const QString& configJson); - StorageStatus m_status; LogosAPI* m_logosAPI; LogosModules* m_logos; - - QString m_statusText; + StorageStatus m_status; + QString m_debugLogs; + QString m_cid; + QString m_configJson; + int m_uploadProgress = 0; + QString m_uploadStatus = ""; + qint64 m_uploadTotalBytes = 0; + qint64 m_uploadedBytes = 0; }; diff --git a/src/StorageUIPlugin.cpp b/src/StorageUIPlugin.cpp index c8badd4..6ab6e03 100644 --- a/src/StorageUIPlugin.cpp +++ b/src/StorageUIPlugin.cpp @@ -31,6 +31,35 @@ QWidget* StorageUIPlugin::createWidget(LogosAPI* logosAPI) { root->setProperty("backend", QVariant::fromValue(static_cast(backend))); + QFileInfo info("config.json"); + QString configJson = "{}"; + + if (info.exists() && info.isFile()) { + qDebug() << "StorageUIPlugin: config.json is found, let's try to load it..."; + + QFile file("config.json"); + if (file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) { + configJson = QString::fromUtf8(file.readAll()); + + qDebug() << "StorageUIPlugin: config.json is found, let's try to load it... configJson=" << configJson; + } else { + qDebug() << "StorageUIPlugin: Failed to load config.json"; + } + } + + LogosResult result = backend->init(configJson); + + if (!result.success) { + QString error = result.getError(); + qWarning() << "StorageUIPlugin: Failed to init backend, will use mock version:" << error; + } else { + result = backend->start(); + + if (!result.success) { + qWarning() << "StorageUIPlugin: Failed to init backend, will use mock version:" << result.getError(); + } + } + return quickWidget; } @@ -66,13 +95,13 @@ void StorageUIPlugin::destroyWidget(QWidget* widget) { return; } - if (!backend->isInitialised()) { + if (backend->status() != StorageBackend::StorageStatus::Destroyed) { qDebug() << "StorageUIPlugin::destroyWidget: backend is not initialised so let's detroy it."; quickWidget->deleteLater(); return; } - if (!backend->isRunning()) { + if (backend->status() == StorageBackend::StorageStatus::Running) { qDebug() << "StorageUIPlugin::destroyWidget: backend is not running so let's detroy it."; backend->destroy(); diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt index ecf03e8..1719efd 100644 --- a/src/qml/CMakeLists.txt +++ b/src/qml/CMakeLists.txt @@ -74,6 +74,7 @@ add_library(storage_generated STATIC ${LOGOS_CPP_SDK_ROOT}/cpp/token_manager.cpp ${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_consumer.cpp ${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_provider.cpp + ${LOGOS_CPP_SDK_ROOT}/cpp/logos_types.cpp ) # Define the dependencies needed for storage_generated diff --git a/src/qml/StorageView.qml b/src/qml/StorageView.qml index 0cf8f89..dd54af4 100644 --- a/src/qml/StorageView.qml +++ b/src/qml/StorageView.qml @@ -1,63 +1,592 @@ import QtQuick import QtQuick.Controls +import QtQuick.Dialogs +import QtQuick.Layouts + +Rectangle { + id: root + width: 400 + height: 700 + color: "#000000" -//import QtQuick.Layouts -Item { property var backend: mockBackend readonly property int stopped: 0 readonly property int starting: 1 readonly property int running: 2 readonly property int stopping: 3 readonly property int destroyed: 4 + property string peerId: "" + property string downloadDestination: "" + property url downloadCid: "" + property string logLevel: "" + property bool showDebug: false + property url uploadCid: root.backend.cid + property url configJson: root.backend.configJson - id: root - width: 400 - height: 300 + function getStatusLabel() { + switch (backend.status) { + case stopped: + return "Logos Storage stopped." + case starting: + return "Logos Storage is starting..." + case running: + return "Logos Storage started." + case stopping: + return "Logos Storage is stopping..." + case destroyed: + return "Logos Storage is not initialised." + } + } + + function startStopText() { + if (backend.status == running) { + return "Stop" + } + return "Start" + } + + function canStartStop() { + return backend.status == running || backend.status == stopped + } + + function isRunning() { + return backend.status == running + } QtObject { id: mockBackend property var status: root.stopped - property var statusText: "Destroyed" - property var startStopText: "Start" - property var canStartStop: true + property var debugLogs: "Hello !" + property var configJson: "{}" + property url cid: "" + property string uploadStatus: "" + property int uploadProgress: 0 - function startStop() { - if (status == root.running) { - status = root.stopped - statusText = "Stopped" - startStopText = "Start" - } else { - status = root.running - statusText = "Started" - startStopText = "Stop" + function start(newConfigJson) { + status = root.running + } + + 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) {} + } + + Text { + id: statusTextElement + objectName: "status" + text: root.getStatusLabel() + color: "white" + font.pointSize: 20 + anchors.top: parent.top + anchors.topMargin: 20 + anchors.horizontalCenter: parent.horizontalCenter + } + + Button { + 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() + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: statusTextElement.bottom + anchors.topMargin: 10 + } + + TextEdit { + id: cidTextEdit + objectName: "cid" + color: "white" + font.pointSize: 14 + readOnly: true + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: startStopButton.bottom + anchors.topMargin: 10 + text: root.uploadCid + } + + Button { + id: openFile + text: "Open file" + onClicked: fileDialog.open() + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: cidTextEdit.bottom + anchors.topMargin: 15 + enabled: root.isRunning + } + + Column { + id: uploadProgressColumn + anchors.top: openFile.bottom + anchors.topMargin: 10 + anchors.horizontalCenter: parent.horizontalCenter + width: 300 + spacing: 5 + visible: root.backend.uploadProgress > 0 + + ProgressBar { + width: parent.width + value: root.backend.uploadProgress / 100.0 + + background: Rectangle { + color: "#333333" + radius: 3 + implicitWidth: 300 + implicitHeight: 6 } + + contentItem: Item { + implicitWidth: 300 + implicitHeight: 6 + + Rectangle { + width: parent.width * parent.parent.visualPosition + height: parent.height + radius: 3 + color: "#4CAF50" + } + } + } + + Text { + text: root.backend.uploadStatus + color: "#888888" + font.pixelSize: 10 + anchors.horizontalCenter: parent.horizontalCenter + } + } + + TextField { + id: peerIdField + placeholderText: "Enter the peer Id" + placeholderTextColor: "#999999" + color: "#000000" + selectByMouse: true + text: root.peerId + onTextChanged: root.peerId = text + anchors.top: uploadProgressColumn.bottom + anchors.topMargin: 50 + anchors.horizontalCenter: parent.horizontalCenter + } + + Button { + id: peerConnectButton + objectName: "peerConnectButton" + text: "Peer connect" + onClicked: root.backend.tryPeerConnect(root.peerId) + anchors.top: peerIdField.bottom + anchors.horizontalCenter: parent.horizontalCenter + enabled: root.isRunning + anchors.topMargin: 10 + } + + Button { + id: debugButton + objectName: "debugButton" + text: "Debug" + onClicked: root.backend.tryDebug() + anchors.top: peerConnectButton.bottom + anchors.horizontalCenter: parent.horizontalCenter + enabled: root.isRunning + anchors.topMargin: 50 + } + + Button { + id: peerIdButton + objectName: "peerIdButton" + text: "Peer Id" + onClicked: root.backend.showPeerId() + anchors.top: peerConnectButton.bottom + anchors.right: debugButton.left + enabled: root.isRunning + anchors.topMargin: 50 + } + + Button { + id: dataDirButton + objectName: "dataDirButton" + text: "Data dir" + onClicked: root.backend.dataDir() + anchors.top: peerConnectButton.bottom + anchors.right: peerIdButton.left + enabled: root.isRunning + anchors.topMargin: 50 + } + + Button { + id: sprButton + objectName: "sprButton" + text: "SPR" + onClicked: root.backend.spr() + anchors.top: peerConnectButton.bottom + anchors.left: debugButton.right + enabled: root.isRunning + anchors.topMargin: 50 + } + + Button { + id: versionButton + objectName: "versionButton" + text: "Version" + onClicked: root.backend.version() + anchors.top: peerConnectButton.bottom + anchors.left: sprButton.right + enabled: root.isRunning + anchors.topMargin: 50 + } + + TextField { + id: cidDownloadField + placeholderTextColor: "#999999" + placeholderText: "Enter the cid to download" + color: "black" + // text: root.downloadCid + onTextChanged: root.downloadCid = text + anchors.top: debugButton.bottom + anchors.topMargin: 50 + anchors.horizontalCenter: parent.horizontalCenter + } + + Button { + id: openFile2 + text: "Open file" + onClicked: fileDialog2.open() + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: cidDownloadField.bottom + anchors.topMargin: 15 + enabled: root.isRunning + } + + Button { + id: cidDownloadButton + objectName: "cidDownloadButton" + text: "Download" + onClicked: root.backend.tryDownloadFile(root.downloadCid, + root.downloadDestination) + anchors.top: openFile2.bottom + anchors.horizontalCenter: parent.horizontalCenter + enabled: root.isRunning + anchors.topMargin: 10 + } + + Button { + id: existsButton + objectName: "existsButton" + text: "Exists" + onClicked: root.backend.exists(root.downloadCid) + anchors.top: openFile2.bottom + anchors.left: cidDownloadButton.right + enabled: root.isRunning + anchors.topMargin: 10 + } + + Button { + id: fetchButton + objectName: "fetchButton" + text: "Fetch" + onClicked: root.backend.fetch(root.downloadCid) + anchors.top: openFile2.bottom + anchors.left: existsButton.right + enabled: root.isRunning + anchors.topMargin: 10 + } + + Button { + id: removeButton + objectName: "removeButton" + text: "Remove" + onClicked: root.backend.remove(root.downloadCid) + anchors.top: openFile2.bottom + anchors.right: cidDownloadButton.left + enabled: root.isRunning + anchors.topMargin: 10 + } + + Button { + id: downloadManifestButton + objectName: "downloadManifestButton" + text: "Download manifest" + onClicked: root.backend.downloadManifest(root.downloadCid) + anchors.top: openFile2.bottom + anchors.right: removeButton.left + enabled: root.isRunning + anchors.topMargin: 10 + } + + Button { + id: downloadManifestsButton + objectName: "downloadManifestsButton" + text: "Manifests" + onClicked: root.backend.downloadManifests() + anchors.top: cidDownloadButton.bottom + anchors.horizontalCenter: parent.horizontalCenter + enabled: root.isRunning + anchors.topMargin: 10 + } + + Button { + id: spaceButton + objectName: "spaceButton" + text: "Space" + onClicked: root.backend.space() + anchors.top: cidDownloadButton.bottom + anchors.right: downloadManifestsButton.left + enabled: root.isRunning + anchors.topMargin: 10 + } + + TextField { + id: logLevelField + placeholderTextColor: "#999999" + placeholderText: "Enter the log level to download" + color: "black" + // text: root.downloadCid + onTextChanged: root.logLevel = text + anchors.top: downloadManifestsButton.bottom + anchors.topMargin: 50 + anchors.horizontalCenter: parent.horizontalCenter + } + + Button { + id: logLevelButton + objectName: "logLevelButton" + text: "Log level" + onClicked: root.backend.updateLogLevel(root.logLevel) + anchors.top: logLevelField.bottom + anchors.horizontalCenter: parent.horizontalCenter + enabled: root.isRunning + anchors.topMargin: 10 + } + + // TextEdit { + // id: selectableText + // anchors.fill: parent + // anchors.margins: 10 + // text: "This text is selectable. You can copy it, but not edit it." + // readOnly: true // Makes the text non-editable + // selectByMouse: true // Enables selection by mouse drag (often the default for desktop) + // // Optional: Change cursor shape to IBeam when hovering + // MouseArea { + // anchors.fill: parent + // cursorShape: Qt.IBeamCursor + // acceptedButtons: Qt.NoButton // Allows TextEdit to handle mouse events + // } + // } + + // Button { + // anchors.left: parent.left + // anchors.bottom: parent.bottom + // objectName: "uploadButton" + // text: "Upload" + // anchors.bottomMargin: 80 + // onClicked: root.backend.tryUpload() + // } + + // Button { + // anchors.left: parent.left + // anchors.bottom: parent.bottom + // objectName: "finalizeButton" + // text: "Finalize" + // onClicked: root.backend.tryUploadFinalize() + // } + // Button { + // anchors.left: parent.left + // anchors.bottom: parent.bottom + // objectName: "uploadFileButton" + // text: "Upload file" + // onClicked: root.backend.tryUploadFile() + // anchors.bottomMargin: 30 + // } + FileDialog { + id: fileDialog + onAccepted: root.backend.tryUploadFile(fileDialog.selectedFile) + } + + FileDialog { + id: fileDialog2 + fileMode: FileDialog.SaveFile + onAccepted: { + root.downloadDestination = fileDialog2.selectedFile + console.log("Destination selected:", + root.backend.downloadDestination) } } Rectangle { - anchors.fill: parent - anchors.leftMargin: 0 - anchors.rightMargin: 0 - anchors.topMargin: 0 - anchors.bottomMargin: 0 - color: "#202428" - } + id: debugPanel + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: 150 + color: "#111111" + visible: root.showDebug // or: visible: showDebug - Text { - objectName: "status" - text: root.backend.statusText - color: "white" - font.pointSize: 20 - anchors.centerIn: parent - anchors.topMargin: 0 - } + TabBar { + id: bar + width: parent.width - Button { - objectName: "startStopButton" - anchors.leftMargin: 50 - text: root.backend.startStopText - enabled: root.backend.canStartStop - onClicked: root.backend.startStop() + TabButton { + text: qsTr("Logs") + } + + TabButton { + text: qsTr("Config") + } + } + + StackLayout { + id: stackLayout + currentIndex: bar.currentIndex + anchors.top: bar.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + Item { + id: homeTab + + Flickable { + id: flick + anchors.fill: parent + clip: true + + contentWidth: width + contentHeight: debugText.paintedHeight + + TextEdit { + id: debugText + width: flick.width + text: root.backend.debugLogs + color: "#dddddd" + font.family: "monospace" + font.pixelSize: 12 + wrapMode: Text.WrapAnywhere + readOnly: true + + onTextChanged: Qt.callLater(function () { + flick.contentY = Math.max( + 0, flick.contentHeight - flick.height) + }) + } + } + } + Rectangle { + id: discoverTab + + ScrollView { + anchors.fill: parent + + TextArea { + id: jsonEditor + font.family: "monospace" + font.pixelSize: 12 + color: "#d4d4d4" + width: parent.width + height: parent.height + + background: Rectangle { + color: "#1e1e1e" + border.color: jsonEditor.isValid ? "#3a3a3a" : "#ff0000" + border.width: 1 + } + + property bool isValid: true + + Connections { + target: root.backend + + function onConfigJsonChanged() { + jsonEditor.text = root.backend.configJson + try { + const jsonData = JSON.parse(jsonEditor.text) + jsonEditor.isValid = true + } catch (e) { + jsonEditor.isValid = false + } + } + } + + Component.onCompleted: { + text = root.backend.configJson + + try { + const jsonData = JSON.parse(text) + isValid = true + } catch (e) { + isValid = false + } + } + + onTextChanged: { + try { + const jsonData = JSON.parse(text) + isValid = true + } catch (e) { + isValid = false + } + } + } + } + } + } + Shortcut { + sequence: "Ctrl+D" + onActivated: root.showDebug = !root.showDebug + } } } diff --git a/vendor/logos-cpp-sdk b/vendor/logos-cpp-sdk index 32f1d70..c3477d2 160000 --- a/vendor/logos-cpp-sdk +++ b/vendor/logos-cpp-sdk @@ -1 +1 @@ -Subproject commit 32f1d7080d784ff044d91d076ef2f0c7305d4784 +Subproject commit c3477d29e32cae5f73ca637fb81e547f8a6cba58 diff --git a/vendor/logos-liblogos b/vendor/logos-liblogos index fb7a9d6..1378757 160000 --- a/vendor/logos-liblogos +++ b/vendor/logos-liblogos @@ -1 +1 @@ -Subproject commit fb7a9d6888dc59dd29437e8d3a2d38fc9f0e8696 +Subproject commit 1378757dc8745941ebee79ae484abe62be2f5db4 diff --git a/vendor/logos-storage-module b/vendor/logos-storage-module index e01d9db..7dceb6a 160000 --- a/vendor/logos-storage-module +++ b/vendor/logos-storage-module @@ -1 +1 @@ -Subproject commit e01d9dbc6159bf6442a670e9432714b024b7698b +Subproject commit 7dceb6a8dfd5a5bbd47dc9b00e55f80b62ab1e6e diff --git a/vendor/logos-storage-nim b/vendor/logos-storage-nim index 7d51740..e375223 160000 --- a/vendor/logos-storage-nim +++ b/vendor/logos-storage-nim @@ -1 +1 @@ -Subproject commit 7d51740f91c0ee3781d40111787f0d450bf729ae +Subproject commit e375223500ca0fd919b0e504eab65c6ddb5e4233