mirror of
https://github.com/logos-storage/logos-storage-app-skeleton.git
synced 2026-06-13 20:09:28 +00:00
Refactor configuration management
This commit is contained in:
parent
6e58476176
commit
340d3f774f
@ -10,6 +10,7 @@
|
||||
#include <QLocale>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QSslSocket>
|
||||
|
||||
// StorageBackend is responsible for managing the interaction with the storage module.
|
||||
// It is mocked in the QML.
|
||||
@ -30,6 +31,8 @@ StorageBackend::StorageBackend(LogosAPI* logosAPI, QObject* parent)
|
||||
}
|
||||
|
||||
m_logos = new LogosModules(m_logosAPI);
|
||||
|
||||
emit ready();
|
||||
}
|
||||
|
||||
StorageBackend::~StorageBackend()
|
||||
@ -38,20 +41,24 @@ StorageBackend::~StorageBackend()
|
||||
m_logos = nullptr;
|
||||
}
|
||||
|
||||
LogosResult StorageBackend::init(const QString& configJson = "{}") {
|
||||
LogosResult StorageBackend::init(const QString& configJson) {
|
||||
qDebug() << "StorageBackend::initStorage called";
|
||||
|
||||
if (configJson != "{}") {
|
||||
m_configJson = configJson;
|
||||
m_config = QJsonDocument::fromJson(configJson.toUtf8());
|
||||
if (m_config.isNull()) {
|
||||
qDebug() << "StorageBackend::initStorage invalid json config" << configJson;
|
||||
emit initFailed();
|
||||
return {false, "", "Failed to create the storage, invalid json config"};
|
||||
}
|
||||
|
||||
bool result = m_logos->storage_module.init(m_configJson);
|
||||
bool result = m_logos->storage_module.init(configJson);
|
||||
|
||||
qDebug() << "StorageBackend::initStorage: init";
|
||||
|
||||
if (!result) {
|
||||
setStatus(Destroyed);
|
||||
debug("Failed to init storage");
|
||||
emit initFailed();
|
||||
return {false, "", "Filed to init storage"};
|
||||
}
|
||||
|
||||
@ -197,13 +204,8 @@ LogosResult StorageBackend::init(const QString& configJson = "{}") {
|
||||
qWarning() << "StorageWidget: failed to subscribe to storageDownloadProgress events";
|
||||
}
|
||||
|
||||
if (configJson != "{}") {
|
||||
m_configJson = configJson;
|
||||
emit configJsonChanged();
|
||||
debug("new config is: " + m_configJson);
|
||||
}
|
||||
|
||||
emit initCompleted();
|
||||
debug("new config is: " + configJson);
|
||||
|
||||
return {true, ""};
|
||||
}
|
||||
@ -725,12 +727,7 @@ void StorageBackend::space() {
|
||||
static constexpr qint64 DEFAULT_QUOTA = 20LL * 1024 * 1024 * 1024; // 20 GB
|
||||
|
||||
// Check config for a quota-max-bytes override
|
||||
qint64 configQuota = 0;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(m_configJson.toUtf8());
|
||||
if (!doc.isNull()) {
|
||||
configQuota = doc.object().value("quota-max-bytes").toVariant().toLongLong();
|
||||
}
|
||||
|
||||
qint64 configQuota = m_config.object().value("quota-max-bytes").toVariant().toLongLong();
|
||||
qint64 apiQuota = result.getInt("quotaMaxBytes");
|
||||
m_quotaMaxBytes = apiQuota > 0 ? apiQuota : (configQuota > 0 ? configQuota : DEFAULT_QUOTA);
|
||||
m_quotaUsedBytes = result.getInt("quotaUsedBytes");
|
||||
@ -764,30 +761,26 @@ StorageBackend::StorageStatus StorageBackend::status() const { return m_status;
|
||||
|
||||
QString StorageBackend::cid() const { return m_cid; }
|
||||
|
||||
QString StorageBackend::configJson() const { return m_configJson; }
|
||||
QString StorageBackend::configJson() const { return QString::fromUtf8(m_config.toJson(QJsonDocument::Compact)); }
|
||||
|
||||
int StorageBackend::uploadProgress() const { return m_uploadProgress; }
|
||||
|
||||
QString StorageBackend::uploadStatus() const { return m_uploadStatus; }
|
||||
|
||||
void StorageBackend::reloadIfChanged(const QString& configJson) {
|
||||
if (configJson == m_configJson) {
|
||||
QJsonDocument config = QJsonDocument::fromJson(configJson.toUtf8());
|
||||
if (config.isNull()) {
|
||||
debug("Invalid json detected !");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_config == config) {
|
||||
debug("No change detected in the config");
|
||||
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...");
|
||||
@ -805,154 +798,124 @@ void StorageBackend::reloadIfChanged(const QString& configJson) {
|
||||
}
|
||||
}
|
||||
|
||||
bool result = m_logos->storage_module.init(configJson);
|
||||
LogosResult result = 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();
|
||||
}
|
||||
if (!result.success) {
|
||||
debug("Failed to init context with new config: " + result.getError());
|
||||
return;
|
||||
}
|
||||
|
||||
debug("New config loaded successfully");
|
||||
|
||||
m_configJson = configJson;
|
||||
m_config = config;
|
||||
setStatus(StorageStatus::Stopped);
|
||||
emit configJsonChanged();
|
||||
}
|
||||
|
||||
void StorageBackend::saveCurrentConfig() {
|
||||
qDebug() << "StorageBackend::saveUserConfig";
|
||||
saveUserConfig(QString::fromUtf8(m_config.toJson(QJsonDocument::Compact)));
|
||||
}
|
||||
|
||||
void StorageBackend::saveUserConfig(const QString& configJson) {
|
||||
qDebug() << "StorageBackend::saveUserConfig";
|
||||
|
||||
QString configPath = getUserConfigPath();
|
||||
QString folderPath = QFileInfo(configPath).absolutePath();
|
||||
QString folderPath = QFileInfo(USER_CONFIG_PATH).absolutePath();
|
||||
QDir().mkpath(folderPath);
|
||||
QFile file(configPath);
|
||||
QFile file(USER_CONFIG_PATH);
|
||||
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
file.write(configJson.toUtf8());
|
||||
file.close();
|
||||
debug("Config saved to " + configPath);
|
||||
debug("Config saved to " + USER_CONFIG_PATH);
|
||||
} else {
|
||||
debug("Failed to save config to " + configPath);
|
||||
debug("Failed to save config to " + USER_CONFIG_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
QString StorageBackend::buildConfig(const QString& dataDir, int discPort, int tcpPort) {
|
||||
debug("StorageBackend::updateBasicConfig called with dataDir=" + dataDir);
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(m_configJson.toUtf8());
|
||||
QJsonDocument StorageBackend::defaultConfig() {
|
||||
QJsonDocument doc = QJsonDocument();
|
||||
QJsonObject obj = doc.object();
|
||||
|
||||
obj["data-dir"] = dataDir;
|
||||
obj["disc-port"] = discPort;
|
||||
|
||||
QJsonArray listenAddrs = {QString("/ip4/0.0.0.0/tcp/%1").arg(tcpPort)};
|
||||
obj["listen-addrs"] = listenAddrs;
|
||||
|
||||
QJsonArray bootstrapArray;
|
||||
for (const QString& node : BOOTSTRAP_NODES) {
|
||||
bootstrapArray.append(node);
|
||||
}
|
||||
obj["bootstrap-node"] = bootstrapArray;
|
||||
|
||||
return QJsonDocument(obj).toJson(QJsonDocument::Indented);
|
||||
obj["data-dir"] = DEFAULT_DATA_DIR;
|
||||
|
||||
return QJsonDocument(obj);
|
||||
}
|
||||
|
||||
QString StorageBackend::buildUpnpConfig(const QString& dataDir) {
|
||||
debug("StorageBackend::buildUpnpConfig called with dataDir=" + dataDir);
|
||||
void StorageBackend::enableUpnpConfig() {
|
||||
debug("StorageBackend::enableUpnpConfig called");
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(m_configJson.toUtf8());
|
||||
QJsonDocument doc = defaultConfig();
|
||||
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);
|
||||
reloadIfChanged(QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)));
|
||||
}
|
||||
|
||||
QString StorageBackend::buildNatExtConfig(const QString& dataDir, int tcpPort) {
|
||||
debug("StorageBackend::buildUpnpConfig called with dataDir=" + dataDir +
|
||||
" and tcpPort=" + QString::number(tcpPort));
|
||||
void StorageBackend::enableNatExtConfig(int tcpPort) {
|
||||
qDebug() << "StorageBackend::enableNatExtConfig called with tcpPort" << tcpPort;
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(m_configJson.toUtf8());
|
||||
QJsonDocument doc = defaultConfig();
|
||||
QJsonObject obj = doc.object();
|
||||
|
||||
obj["data-dir"] = dataDir;
|
||||
QJsonArray listenAddrs = {QString("/ip4/0.0.0.0/tcp/%1").arg(tcpPort)};
|
||||
obj["listen-addrs"] = listenAddrs;
|
||||
|
||||
QJsonArray bootstrapArray;
|
||||
for (const QString& node : BOOTSTRAP_NODES) {
|
||||
bootstrapArray.append(node);
|
||||
}
|
||||
qDebug() << "StorageBackend::enableNatExtConfig Retrieving the public IP";
|
||||
|
||||
debug("Retrieving the public IP");
|
||||
// Create the network manager
|
||||
QNetworkAccessManager* manager = new QNetworkAccessManager(this);
|
||||
QNetworkRequest request(QUrl("https://echo.codex.storage/"));
|
||||
request.setRawHeader("Accept", "text/plain");
|
||||
QNetworkReply* reply = manager->get(request);
|
||||
|
||||
QNetworkAccessManager manager;
|
||||
QEventLoop loop;
|
||||
QNetworkReply* reply = manager.get(QNetworkRequest(QUrl("https://echo.codex.storage/")));
|
||||
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply, manager, obj]() {
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
|
||||
QString ip = reply->readAll().trimmed();
|
||||
reply->deleteLater();
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
emit natExtConfigFailed(reply->errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
debug("Public IP detected:" + ip);
|
||||
QString ip = reply->readAll().trimmed();
|
||||
|
||||
obj["nat"] = "extip:" + ip;
|
||||
qDebug() << "StorageBackend::enableNatExtConfig ip=" << ip;
|
||||
|
||||
return QJsonDocument(obj).toJson(QJsonDocument::Indented);
|
||||
}
|
||||
obj["nat"] = "extip:" + ip;
|
||||
|
||||
QString StorageBackend::buildConfigFromFile(const QString& path) {
|
||||
qDebug() << "StorageBackend::buildConfigFromFile called";
|
||||
qDebug() << "StorageBackend::enableNatExtConfig config="
|
||||
<< QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
|
||||
|
||||
QFile file(path);
|
||||
if (file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QString configJson = QString::fromUtf8(file.readAll());
|
||||
reloadIfChanged(QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)));
|
||||
|
||||
debug("StorageUIPlugin: config.json is found, configJson=" + configJson);
|
||||
|
||||
return configJson;
|
||||
}
|
||||
|
||||
debug("StorageUIPlugin: Failed to load config.json");
|
||||
return "{}";
|
||||
emit natExtConfigCompleted();
|
||||
});
|
||||
}
|
||||
|
||||
void StorageBackend::status(StorageStatus status) { m_status = status; }
|
||||
|
||||
QString StorageBackend::getUserConfigPath() { return QDir::homePath() + "/.logos_storage/config.json"; }
|
||||
void StorageBackend::loadUserConfig() {
|
||||
qDebug() << "StorageBackend::loadUserConfig called.";
|
||||
|
||||
QFile file(USER_CONFIG_PATH);
|
||||
LogosResult result;
|
||||
|
||||
QString StorageBackend::getUserConfig() {
|
||||
QFile file(getUserConfigPath());
|
||||
if (file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return QString::fromUtf8(file.readAll());
|
||||
result = init(QString::fromUtf8(file.readAll()));
|
||||
} else {
|
||||
qWarning() << "StorageBackend::loadUserConfig Failed to read the user config file, fallback to default config";
|
||||
result = init(QString::fromUtf8(defaultConfig().toJson(QJsonDocument::Compact)));
|
||||
}
|
||||
|
||||
return "{}";
|
||||
}
|
||||
|
||||
QString StorageBackend::defaultDataDir() {
|
||||
QString home = QDir::homePath();
|
||||
#ifdef Q_OS_WIN
|
||||
return home + "/AppData/Roaming/Storage";
|
||||
#elif defined(Q_OS_MACOS)
|
||||
return home + "/Library/Application Support/Storage";
|
||||
#else
|
||||
return home + "/.cache/storage";
|
||||
#endif
|
||||
if (!result.success) {
|
||||
qWarning() << "StorageBackend::loadUserConfig Failed to load the user config: " + result.getError();
|
||||
} else {
|
||||
debug("User config loaded successfully");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "logos_api.h"
|
||||
#include "logos_sdk.h"
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
@ -10,6 +11,10 @@
|
||||
|
||||
static const int RET_OK = 0;
|
||||
static const int RET_PROGRESS = 3;
|
||||
static const QUrl ECHO_PROVIDER("https://echo.codex.storage/");
|
||||
static const QString APP_HOME = QDir::homePath() + "/.logos_storage";
|
||||
static const QString DEFAULT_DATA_DIR = APP_HOME + "/data";
|
||||
static const QString USER_CONFIG_PATH = APP_HOME + "/config.json";
|
||||
|
||||
// Add manual SPR from https://spr.codex.storage/devnet
|
||||
static const QStringList BOOTSTRAP_NODES = {
|
||||
@ -30,7 +35,6 @@ class StorageBackend : public QObject {
|
||||
Q_PROPERTY(QString debugLogs READ debugLogs NOTIFY debugLogsChanged)
|
||||
Q_PROPERTY(StorageStatus status READ status WRITE 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)
|
||||
Q_PROPERTY(QVariantList manifests READ manifests NOTIFY manifestsChanged)
|
||||
@ -39,7 +43,20 @@ class StorageBackend : public QObject {
|
||||
Q_PROPERTY(qint64 quotaReservedBytes READ quotaReservedBytes NOTIFY quotaChanged)
|
||||
|
||||
public:
|
||||
enum StorageStatus { Stopped = 0, Starting, Running, Stopping, Destroyed };
|
||||
enum StorageStatus {
|
||||
// Stopped means that the context is created but the module is not started
|
||||
Stopped = 0,
|
||||
|
||||
Starting,
|
||||
|
||||
// Running means the module is started
|
||||
Running,
|
||||
|
||||
Stopping,
|
||||
|
||||
// Destroyed means the context is not created (or has been destroyed).
|
||||
Destroyed
|
||||
};
|
||||
Q_ENUM(StorageStatus)
|
||||
|
||||
QString cid() const;
|
||||
@ -53,9 +70,7 @@ class StorageBackend : public QObject {
|
||||
qint64 quotaUsedBytes() const;
|
||||
qint64 quotaReservedBytes() const;
|
||||
|
||||
Q_INVOKABLE static QString defaultDataDir();
|
||||
static QString getUserConfig();
|
||||
static QString getUserConfigPath();
|
||||
static QJsonDocument defaultConfig();
|
||||
|
||||
explicit StorageBackend(LogosAPI* logosAPI = nullptr, QObject* parent = nullptr);
|
||||
~StorageBackend();
|
||||
@ -82,27 +97,58 @@ class StorageBackend : public QObject {
|
||||
void space();
|
||||
LogosResult init(const QString& configJson);
|
||||
void updateLogLevel(const QString& logLevel);
|
||||
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);
|
||||
|
||||
// Save the user config passed in parameter
|
||||
// into the user config json.
|
||||
void saveUserConfig(const QString& configJson);
|
||||
|
||||
// Save the current config object
|
||||
// into the user config json.
|
||||
void saveCurrentConfig();
|
||||
|
||||
// Load the user config saved previously
|
||||
void loadUserConfig();
|
||||
|
||||
// Take a new config json and reload the Storage context
|
||||
// if the configuration has changed.
|
||||
//
|
||||
// This method cannot be used if the Storage Module
|
||||
// is running, starting or stopping.
|
||||
//
|
||||
// If the Storage Module was already created,
|
||||
// it will be destroyed first.
|
||||
//
|
||||
// On success, the status will be set to Stopped.
|
||||
//
|
||||
// Emit initCompleted on success.
|
||||
// Emit initFailed on failure.
|
||||
void reloadIfChanged(const QString& configJson);
|
||||
|
||||
// Enables the upnp in the config
|
||||
// and re-create a context with the new configuration
|
||||
void enableUpnpConfig();
|
||||
|
||||
// Enables the net external in the config
|
||||
// and re-create a context with the new configuration
|
||||
void enableNatExtConfig(int tcpPort);
|
||||
|
||||
signals:
|
||||
void ready();
|
||||
void startCompleted();
|
||||
void startFailed(const QString& error);
|
||||
void statusChanged();
|
||||
void debugLogsChanged();
|
||||
void stopCompleted();
|
||||
void cidChanged();
|
||||
void configJsonChanged();
|
||||
void uploadProgressChanged();
|
||||
void uploadStatusChanged();
|
||||
void manifestsChanged();
|
||||
void quotaChanged();
|
||||
void initCompleted();
|
||||
void initFailed();
|
||||
void natExtConfigFailed(const QString& error);
|
||||
void natExtConfigCompleted();
|
||||
|
||||
private slots:
|
||||
|
||||
@ -116,7 +162,6 @@ class StorageBackend : public QObject {
|
||||
StorageStatus m_status;
|
||||
QString m_debugLogs;
|
||||
QString m_cid;
|
||||
QString m_configJson;
|
||||
int m_uploadProgress = 0;
|
||||
QString m_uploadStatus = "";
|
||||
qint64 m_uploadTotalBytes = 0;
|
||||
@ -125,4 +170,5 @@ class StorageBackend : public QObject {
|
||||
qint64 m_quotaMaxBytes = 0;
|
||||
qint64 m_quotaUsedBytes = 0;
|
||||
qint64 m_quotaReservedBytes = 0;
|
||||
QJsonDocument m_config;
|
||||
};
|
||||
|
||||
@ -54,29 +54,19 @@ QWidget* StorageUIPlugin::createWidget(LogosAPI* logosAPI) {
|
||||
|
||||
root->setProperty("backend", QVariant::fromValue(static_cast<QObject*>(backend)));
|
||||
|
||||
backend->ready();
|
||||
// Storage init is done in the QML
|
||||
// Build config from settings if onboarding was done, otherwise use empty config
|
||||
QString configJson = StorageBackend::getUserConfig();
|
||||
qDebug() << "UserConfig" << StorageBackend::getUserConfigPath();
|
||||
qDebug() << "configJson" << configJson;
|
||||
// if (onboardingCompleted && !dataDir.isEmpty()) {
|
||||
// configJson = backend->buildConfig(dataDir, discoveryPort, tcpPort);
|
||||
// QString configJson = StorageBackend::getUserConfig();
|
||||
// qDebug() << "UserConfig" << StorageBackend::getUserConfigPath();
|
||||
// qDebug() << "configJson" << configJson;
|
||||
|
||||
// LogosResult result = backend->init(configJson);
|
||||
|
||||
// if (!result.success) {
|
||||
// qWarning() << "StorageUIPlugin: Failed to init backend:" << result.getError();
|
||||
// }
|
||||
|
||||
// config.json overrides everything (dev/debug use)
|
||||
// QFileInfo info("config.json");
|
||||
// if (info.exists() && info.isFile()) {
|
||||
// qWarning() << "StorageUIPlugin: config.json found — overriding settings config";
|
||||
// configJson = backend->buildConfigFromFile("config.json");
|
||||
// }
|
||||
|
||||
// qDebug() << "StorageUIPlugin: configJson=" << configJson;
|
||||
|
||||
LogosResult result = backend->init(configJson);
|
||||
|
||||
if (!result.success) {
|
||||
qWarning() << "StorageUIPlugin: Failed to init backend:" << result.getError();
|
||||
}
|
||||
|
||||
return quickWidget;
|
||||
}
|
||||
|
||||
|
||||
@ -132,7 +132,6 @@ qt_add_qml_module(appqml
|
||||
StartNode.qml
|
||||
LogosTextField.qml
|
||||
LogosStorageButton.qml
|
||||
Nat.qml
|
||||
LogosStorageLayout.qml
|
||||
PortForwarding.qml
|
||||
)
|
||||
|
||||
@ -25,19 +25,17 @@ Item {
|
||||
console.log("mock start called")
|
||||
}
|
||||
|
||||
function defaultDataDir() {
|
||||
return ".cache/storage"
|
||||
}
|
||||
|
||||
function buildConfig() {}
|
||||
|
||||
function saveUserConfig() {}
|
||||
|
||||
function loadUserConfig() {}
|
||||
|
||||
function reloadIfChanged() {}
|
||||
|
||||
function buildUpnpConfig() {}
|
||||
function enableUpnpConfig() {}
|
||||
|
||||
function buildNatExtConfig() {}
|
||||
function enableNatExtConfig() {}
|
||||
|
||||
function saveCurrentConfig() {}
|
||||
|
||||
function stop() {}
|
||||
}
|
||||
@ -51,6 +49,10 @@ Item {
|
||||
property string dataDir: ""
|
||||
property bool onboardingCompleted: false
|
||||
property string natStrategy: "any"
|
||||
|
||||
Component.onCompleted: {
|
||||
console.info("Settings completed")
|
||||
}
|
||||
}
|
||||
|
||||
StackView {
|
||||
@ -63,33 +65,9 @@ Item {
|
||||
id: onboardingComponent
|
||||
|
||||
OnBoarding {
|
||||
backend: root.backend
|
||||
// discoveryPort: settings.discoveryPort
|
||||
// tcpPort: settings.tcpPort
|
||||
dataDir: settings.dataDir
|
||||
|
||||
onCompleted: {
|
||||
// settings.discoveryPort = discoveryPort
|
||||
settings.dataDir = dataDir
|
||||
// settings.tcpPort = tcpPort
|
||||
settings.onboardingCompleted = true
|
||||
|
||||
stackView.push(natComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
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)
|
||||
onCompleted: function (upnpEnabled) {
|
||||
if (upnpEnabled) {
|
||||
root.backend.enableUpnpConfig()
|
||||
} else {
|
||||
stackView.push(portForwardingComponent)
|
||||
}
|
||||
@ -117,6 +95,8 @@ Item {
|
||||
}
|
||||
|
||||
onNext: {
|
||||
settings.onboardingCompleted = true
|
||||
root.backend.saveCurrentConfig()
|
||||
stackView.push(storageComponent)
|
||||
}
|
||||
}
|
||||
@ -126,14 +106,8 @@ Item {
|
||||
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)
|
||||
onCompleted: function (port) {
|
||||
root.backend.enableNatExtConfig(port)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -145,11 +119,23 @@ Item {
|
||||
stackView.pop()
|
||||
}
|
||||
|
||||
function onInitCompleted() {
|
||||
function onInitCompleted() {}
|
||||
|
||||
function onReady() {
|
||||
console.info("i am ready")
|
||||
if (settings.onboardingCompleted) {
|
||||
console.info("onboardingCompleted completed")
|
||||
root.backend.loadUserConfig()
|
||||
root.backend.start()
|
||||
stackView.replace(storageComponent, StackView.Immediate)
|
||||
}
|
||||
}
|
||||
|
||||
function onNatExtConfigFailed(error) {}
|
||||
|
||||
function onNatExtConfigCompleted(error) {
|
||||
root.backend.start()
|
||||
stackView.push(startNodeComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Logos.Theme
|
||||
import Logos.Controls
|
||||
|
||||
LogosStorageLayout {
|
||||
id: root
|
||||
|
||||
signal completed(bool enabled)
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacing.medium
|
||||
width: 400
|
||||
|
||||
LogosText {
|
||||
id: questionText
|
||||
font.pixelSize: Theme.typography.titleText
|
||||
text: "Is UPnP enabled on your router ?"
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
}
|
||||
|
||||
LogosText {
|
||||
id: questionDescriptionText
|
||||
font.pixelSize: Theme.typography.primaryText
|
||||
text: "UPnP simplifies configuration by handling port forwarding automatically."
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Theme.spacing.medium
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
|
||||
LogosStorageButton {
|
||||
text: "No / I don't know"
|
||||
onClicked: root.completed(false)
|
||||
}
|
||||
|
||||
LogosStorageButton {
|
||||
text: "Yes, I use UPnP"
|
||||
onClicked: root.completed(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,125 +7,169 @@ import Logos.Controls
|
||||
LogosStorageLayout {
|
||||
id: root
|
||||
|
||||
property int discoveryPort: 8090
|
||||
property int tcpPort: 0
|
||||
property var backend: mockBackend
|
||||
property var local: false
|
||||
property string dataDir: backend.defaultDataDir()
|
||||
|
||||
signal completed
|
||||
|
||||
QtObject {
|
||||
id: mockBackend
|
||||
|
||||
function defaultDataDir() {
|
||||
return ".cache/storage"
|
||||
}
|
||||
}
|
||||
signal completed(bool enabled)
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacing.medium
|
||||
Layout.fillWidth: true
|
||||
anchors.centerIn: parent
|
||||
|
||||
LogosText {
|
||||
id: titleText
|
||||
font.pixelSize: Theme.typography.titleText
|
||||
text: "Logos Storage"
|
||||
text: "Welcome to Logos Storage"
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
}
|
||||
|
||||
LogosText {
|
||||
id: questionText
|
||||
font.pixelSize: Theme.typography.titleText
|
||||
text: "First, let's choose the storage folder"
|
||||
text: "Is UPnP enabled on your router ?"
|
||||
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)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
ColumnLayout {
|
||||
spacing: Theme.spacing.tiny
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
spacing: Theme.spacing.tiny
|
||||
|
||||
LogosTextField {
|
||||
isValid: text.trim().length > 0
|
||||
id: dataDirTextField
|
||||
placeholderText: "Enter the data dir"
|
||||
text: root.dataDir
|
||||
Layout.fillWidth: true
|
||||
onTextChanged: {
|
||||
root.dataDir = text
|
||||
}
|
||||
}
|
||||
|
||||
LogosStorageButton {
|
||||
text: "Choose"
|
||||
onClicked: folderDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
FolderDialog {
|
||||
id: folderDialog
|
||||
onAccepted: {
|
||||
dataDirTextField.text = selectedFolder
|
||||
}
|
||||
}
|
||||
LogosText {
|
||||
id: questionDescriptionText
|
||||
font.pixelSize: Theme.typography.primaryText
|
||||
text: "UPnP simplifies configuration by handling port forwarding automatically."
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
}
|
||||
|
||||
// Column {
|
||||
// CheckBox {
|
||||
// text: "Do you want to connect to a local network ?"
|
||||
// checked: false
|
||||
// onCheckedChanged: root.local = checked
|
||||
// }
|
||||
RowLayout {
|
||||
spacing: Theme.spacing.medium
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
|
||||
// LogosText {
|
||||
// font.pixelSize: Theme.typography.secondaryText
|
||||
// text: "You will not "
|
||||
// Layout.alignment: Qt.AlignCenter
|
||||
// }
|
||||
// }
|
||||
}
|
||||
LogosStorageButton {
|
||||
text: "No / I don't know"
|
||||
onClicked: root.completed(false)
|
||||
}
|
||||
|
||||
LogosStorageButton {
|
||||
text: "Next"
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.bottomMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
enabled: dataDirTextField.isValid
|
||||
// enabled: discoveryPortTextField.acceptableInput
|
||||
// && tcpPortTextField.acceptableInput && dataDirTextField.isValid
|
||||
onClicked: function () {
|
||||
root.completed()
|
||||
LogosStorageButton {
|
||||
text: "Yes, I use UPnP"
|
||||
onClicked: root.completed(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// property int discoveryPort: 8090
|
||||
// property int tcpPort: 0
|
||||
// property var backend: mockBackend
|
||||
// property var local: false
|
||||
// property string dataDir: backend.defaultDataDir()
|
||||
|
||||
// signal completed
|
||||
|
||||
// QtObject {
|
||||
// id: mockBackend
|
||||
|
||||
// function defaultDataDir() {
|
||||
// return ".cache/storage"
|
||||
// }
|
||||
// }
|
||||
|
||||
// ColumnLayout {
|
||||
// anchors.centerIn: parent
|
||||
// spacing: Theme.spacing.medium
|
||||
|
||||
// LogosText {
|
||||
// id: titleText
|
||||
// font.pixelSize: Theme.typography.titleText
|
||||
// text: "Logos Storage"
|
||||
// Layout.alignment: Qt.AlignCenter
|
||||
// }
|
||||
|
||||
// LogosText {
|
||||
// id: questionText
|
||||
// font.pixelSize: Theme.typography.titleText
|
||||
// text: "First, let's choose the storage folder"
|
||||
// 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)
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// ColumnLayout {
|
||||
// spacing: Theme.spacing.tiny
|
||||
// Layout.fillWidth: true
|
||||
|
||||
// RowLayout {
|
||||
// spacing: Theme.spacing.tiny
|
||||
|
||||
// LogosTextField {
|
||||
// isValid: text.trim().length > 0
|
||||
// id: dataDirTextField
|
||||
// placeholderText: "Enter the data dir"
|
||||
// text: root.dataDir
|
||||
// Layout.fillWidth: true
|
||||
// onTextChanged: {
|
||||
// root.dataDir = text
|
||||
// }
|
||||
// }
|
||||
|
||||
// LogosStorageButton {
|
||||
// text: "Choose"
|
||||
// onClicked: folderDialog.open()
|
||||
// }
|
||||
// }
|
||||
|
||||
// FolderDialog {
|
||||
// id: folderDialog
|
||||
// onAccepted: {
|
||||
// dataDirTextField.text = selectedFolder
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 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 {
|
||||
// text: "Next"
|
||||
// anchors.bottom: parent.bottom
|
||||
// anchors.right: parent.right
|
||||
// anchors.bottomMargin: 10
|
||||
// anchors.rightMargin: 10
|
||||
// enabled: dataDirTextField.isValid
|
||||
// // enabled: discoveryPortTextField.acceptableInput
|
||||
// // && tcpPortTextField.acceptableInput && dataDirTextField.isValid
|
||||
// onClicked: function () {
|
||||
// root.completed()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@ -8,12 +8,13 @@ LogosStorageLayout {
|
||||
|
||||
property var tcpPort: 0
|
||||
|
||||
signal portTcpSelected(int port)
|
||||
signal completed(int port)
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacing.medium
|
||||
width: 400
|
||||
Layout.fillWidth: true
|
||||
|
||||
LogosText {
|
||||
id: questionText
|
||||
@ -30,6 +31,7 @@ LogosStorageLayout {
|
||||
}
|
||||
|
||||
LogosTextField {
|
||||
Layout.fillWidth: true
|
||||
isValid: acceptableInput && text.length > 0
|
||||
id: tcpPortTextField
|
||||
placeholderText: "Enter the TCP port"
|
||||
@ -44,15 +46,12 @@ LogosStorageLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogosStorageButton {
|
||||
text: "Next"
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.bottomMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
enabled: tcpPortTextField.isValid
|
||||
onClicked: root.portTcpSelected(root.tcpPort)
|
||||
LogosStorageButton {
|
||||
text: "Next"
|
||||
|
||||
enabled: tcpPortTextField.isValid
|
||||
onClicked: root.completed(root.tcpPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,13 +4,8 @@ import QtQuick.Controls
|
||||
import Logos.Controls
|
||||
import Logos.Theme
|
||||
|
||||
Rectangle {
|
||||
LogosStorageLayout {
|
||||
id: root
|
||||
color: Theme.palette.background
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: 600
|
||||
implicitHeight: 400
|
||||
|
||||
property var backend: mockBackend
|
||||
property string status: ""
|
||||
@ -64,9 +59,8 @@ Rectangle {
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 20
|
||||
anchors.bottomMargin: 60
|
||||
Layout.fillWidth: true
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacing.medium
|
||||
|
||||
LogosText {
|
||||
|
||||
@ -6,5 +6,7 @@
|
||||
<file alias="StorageView.qml">qml/StorageView.qml</file>
|
||||
<file alias="LogosTextField.qml">qml/LogosTextField.qml</file>
|
||||
<file alias="LogosStorageButton.qml">qml/LogosStorageButton.qml</file>
|
||||
<file alias="LogosStorageLayout.qml">qml/LogosStorageLayout.qml</file>
|
||||
<file alias="PortForwarding.qml">qml/PortForwarding.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user