Allow editing Syncthing config via JavaScript
This commit is contained in:
parent
bf4b26c6f8
commit
b61592fbbd
|
@ -5,6 +5,7 @@ set(META_PROJECT_NAME syncthingctl)
|
|||
set(META_APP_NAME "Syncthing control")
|
||||
set(META_APP_DESCRIPTION "Command line app to control Syncthing")
|
||||
set(META_PROJECT_TYPE application)
|
||||
set(META_JS_SRC_DIR .)
|
||||
|
||||
# add project files
|
||||
set(HEADER_FILES
|
||||
|
@ -32,6 +33,11 @@ use_syncthingconnector()
|
|||
|
||||
# include modules to apply configuration
|
||||
include(BasicConfig)
|
||||
include(JsProviderConfig)
|
||||
if(JS_PROVIDER)
|
||||
list(APPEND HEADER_FILES jsconsole.h)
|
||||
list(APPEND SRC_FILES jsconsole.cpp)
|
||||
endif()
|
||||
include(QtConfig)
|
||||
include(WindowsResources)
|
||||
include(AppTarget)
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include "./application.h"
|
||||
#include "./helper.h"
|
||||
#include "./jsconsole.h"
|
||||
#include "./jsdefs.h"
|
||||
#include "./jsincludes.h"
|
||||
|
||||
#include "../connector/syncthingconfig.h"
|
||||
#include "../connector/utils.h"
|
||||
|
@ -8,6 +11,8 @@
|
|||
#define SYNCTHINGTESTHELPER_FOR_CLI
|
||||
#include "../testhelper/helper.h"
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
#include <c++utilities/application/failure.h>
|
||||
#include <c++utilities/chrono/timespan.h>
|
||||
#include <c++utilities/conversion/stringconversion.h>
|
||||
|
@ -244,6 +249,16 @@ bool Application::waitForConfig(int timeout)
|
|||
signalInfo(&m_connection, &SyncthingConnection::newDirs), signalInfo(&m_connection, &SyncthingConnection::newDevices));
|
||||
}
|
||||
|
||||
bool Application::waitForConfigAndStatus(int timeout)
|
||||
{
|
||||
using namespace TestUtilities;
|
||||
m_connection.applySettings(m_settings);
|
||||
return waitForSignalsOrFail(bind(&SyncthingConnection::requestConfigAndStatus, ref(m_connection)), timeout,
|
||||
signalInfo(&m_connection, &SyncthingConnection::error), signalInfo(&m_connection, &SyncthingConnection::newConfig),
|
||||
signalInfo(&m_connection, &SyncthingConnection::newDirs), signalInfo(&m_connection, &SyncthingConnection::newDevices),
|
||||
signalInfo(&m_connection, &SyncthingConnection::myIdChanged));
|
||||
}
|
||||
|
||||
void Application::handleStatusChanged(SyncthingStatus newStatus)
|
||||
{
|
||||
Q_UNUSED(newStatus)
|
||||
|
@ -586,7 +601,6 @@ void Application::printConfig(const ArgumentOccurrence &)
|
|||
return;
|
||||
}
|
||||
cerr << Phrases::Override;
|
||||
|
||||
cout << QJsonDocument(m_connection.rawConfig()).toJson(QJsonDocument::Indented).data() << flush;
|
||||
}
|
||||
|
||||
|
@ -595,13 +609,47 @@ void Application::editConfig(const ArgumentOccurrence &)
|
|||
// disable main event loop since this method is invoked directly as argument callback and we're doing all required async operations during the waitForConfig() call already
|
||||
m_requiresMainEventLoop = false;
|
||||
|
||||
// wait until config is available
|
||||
if (!(m_args.script.isPresent() ? waitForConfigAndStatus() : waitForConfig())) {
|
||||
return;
|
||||
}
|
||||
cerr << Phrases::Override;
|
||||
|
||||
const auto newConfig(m_args.script.isPresent() ? editConfigViaScript() : editConfigViaEditor());
|
||||
if (newConfig.isEmpty()) {
|
||||
// just return here; an error message should have already been printed by editConfigVia*()
|
||||
return;
|
||||
}
|
||||
|
||||
// handle "dry-run" case
|
||||
if (m_args.dryRun.isPresent()) {
|
||||
cout << newConfig.data();
|
||||
if (!newConfig.endsWith('\n')) {
|
||||
cout << '\n';
|
||||
}
|
||||
cout << flush;
|
||||
return;
|
||||
}
|
||||
|
||||
// post new config
|
||||
using namespace TestUtilities;
|
||||
cerr << Phrases::Info << "Posting new configuration ..." << TextAttribute::Reset << flush;
|
||||
if (!waitForSignalsOrFail(bind(&SyncthingConnection::postConfigFromByteArray, ref(m_connection), ref(newConfig)), 0,
|
||||
signalInfo(&m_connection, &SyncthingConnection::error), signalInfo(&m_connection, &SyncthingConnection::newConfigTriggered))) {
|
||||
return;
|
||||
}
|
||||
cerr << Phrases::Override << Phrases::Info << "Configuration posted successfully" << Phrases::EndFlush;
|
||||
}
|
||||
|
||||
QByteArray Application::editConfigViaEditor() const
|
||||
{
|
||||
// read editor command and options
|
||||
const auto *const editorArgValue(m_args.editor.firstValue());
|
||||
const auto editorCommand(editorArgValue ? QString::fromLocal8Bit(editorArgValue) : QString());
|
||||
if (editorCommand.isEmpty()) {
|
||||
cerr << Phrases::Error << "No editor command specified. It must be either passed via --editor argument or EDITOR environment variable."
|
||||
<< Phrases::EndFlush;
|
||||
return;
|
||||
return QByteArray();
|
||||
}
|
||||
QStringList editorOptions;
|
||||
if (m_args.editor.isPresent()) {
|
||||
|
@ -614,17 +662,11 @@ void Application::editConfig(const ArgumentOccurrence &)
|
|||
}
|
||||
}
|
||||
|
||||
// wait until config is available
|
||||
if (!waitForConfig()) {
|
||||
return;
|
||||
}
|
||||
cerr << Phrases::Override;
|
||||
|
||||
// write config to temporary file
|
||||
QTemporaryFile tempFile(QStringLiteral("syncthing-config-XXXXXX.json"));
|
||||
if (!tempFile.open() || !tempFile.write(QJsonDocument(m_connection.rawConfig()).toJson(QJsonDocument::Indented))) {
|
||||
cerr << Phrases::Error << "Unable to write the configuration to a temporary file." << Phrases::EndFlush;
|
||||
return;
|
||||
return QByteArray();
|
||||
}
|
||||
editorOptions << tempFile.fileName();
|
||||
tempFile.close();
|
||||
|
@ -650,19 +692,19 @@ void Application::editConfig(const ArgumentOccurrence &)
|
|||
}
|
||||
}
|
||||
cerr << endl;
|
||||
return;
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
// read (altered) configuration again
|
||||
QFile tempFile2(editorOptions.back());
|
||||
if (!tempFile2.open(QIODevice::ReadOnly)) {
|
||||
cerr << Phrases::Error << "Unable to open temporary file containing the configuration again." << Phrases::EndFlush;
|
||||
return;
|
||||
return QByteArray();
|
||||
}
|
||||
const auto newConfig(tempFile2.readAll());
|
||||
if (newConfig.isEmpty()) {
|
||||
cerr << Phrases::Error << "Unable to read any bytes from temporary file containing the configuration." << Phrases::EndFlush;
|
||||
return;
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
// convert the config to JSON again (could send it to Syncthing as it is, but this allows us to check whether the JSON is valid)
|
||||
|
@ -671,42 +713,113 @@ void Application::editConfig(const ArgumentOccurrence &)
|
|||
if (error.error != QJsonParseError::NoError) {
|
||||
cerr << Phrases::Error << "Unable to parse new configuration" << Phrases::End << "reason: " << error.errorString().toLocal8Bit().data()
|
||||
<< " at character " << error.offset << endl;
|
||||
return;
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
// perform at least some checks before sending the configuration
|
||||
const auto configObj(configDoc.object());
|
||||
if (configObj.isEmpty()) {
|
||||
cerr << Phrases::Error << "New config object seems empty." << Phrases::EndFlush;
|
||||
return;
|
||||
return QByteArray();
|
||||
}
|
||||
for (const auto &arrayName : {QStringLiteral("devices"), QStringLiteral("folders")}) {
|
||||
for (const auto &arrayName : { QStringLiteral("devices"), QStringLiteral("folders") }) {
|
||||
if (!configObj.value(arrayName).isArray()) {
|
||||
cerr << Phrases::Error << "Array \"" << arrayName.toLocal8Bit().data() << "\" is not present." << Phrases::EndFlush;
|
||||
return;
|
||||
return QByteArray();
|
||||
}
|
||||
}
|
||||
for (const auto &objectName : {QStringLiteral("options"), QStringLiteral("gui")}) {
|
||||
for (const auto &objectName : { QStringLiteral("options"), QStringLiteral("gui") }) {
|
||||
if (!configObj.value(objectName).isObject()) {
|
||||
cerr << Phrases::Error << "Object \"" << objectName.toLocal8Bit().data() << "\" is not present." << Phrases::EndFlush;
|
||||
return;
|
||||
return QByteArray();
|
||||
}
|
||||
}
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
QByteArray Application::editConfigViaScript() const
|
||||
{
|
||||
#if defined(SYNCTHINGCTL_USE_SCRIPT) || defined(SYNCTHINGCTL_USE_JSENGINE)
|
||||
// read script file
|
||||
QFile scriptFile(QString::fromLocal8Bit(m_args.script.firstValue()));
|
||||
if (!scriptFile.open(QFile::ReadOnly)) {
|
||||
cerr << Phrases::Error << "Unable to open specified script file \"" << m_args.script.firstValue() << "\"." << Phrases::EndFlush;
|
||||
return QByteArray();
|
||||
}
|
||||
const auto script(scriptFile.readAll());
|
||||
if (script.isEmpty()) {
|
||||
cerr << Phrases::Error << "Unable to read any bytes from specified script file \"" << m_args.script.firstValue() << "\"."
|
||||
<< Phrases::EndFlush;
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
// define function to print error
|
||||
const auto printError([](const auto &object) {
|
||||
cerr << object.toString().toLocal8Bit().data() << "\nin line " << SYNCTHINGCTL_JS_INT(object.property(QStringLiteral("lineNumber"))) << endl;
|
||||
});
|
||||
|
||||
// evaluate config via JSON.parse()
|
||||
SYNCTHINGCTL_JS_ENGINE engine;
|
||||
auto globalObject(engine.globalObject());
|
||||
const auto configString(QJsonDocument(m_connection.rawConfig()).toJson(QJsonDocument::Indented));
|
||||
globalObject.setProperty(QStringLiteral("configStr"), SYNCTHINGCTL_JS_VALUE(QString::fromUtf8(configString)) SYNCTHINGCTL_JS_READONLY);
|
||||
const auto configObj(engine.evaluate(QStringLiteral("JSON.parse(configStr)")));
|
||||
if (configObj.isError()) {
|
||||
cerr << Phrases::Error << "Unable to evaluate the current Syncthing configuration." << Phrases::End;
|
||||
printError(configObj);
|
||||
cerr << "Syncthing configuration: " << configString.data() << flush;
|
||||
return QByteArray();
|
||||
}
|
||||
globalObject.setProperty(QStringLiteral("config"), configObj SYNCTHINGCTL_JS_UNDELETABLE);
|
||||
|
||||
// provide additional values
|
||||
globalObject.setProperty(QStringLiteral("ownID"), m_connection.myId() SYNCTHINGCTL_JS_UNDELETABLE);
|
||||
globalObject.setProperty(QStringLiteral("url"), m_connection.syncthingUrl() SYNCTHINGCTL_JS_UNDELETABLE);
|
||||
|
||||
// provide console.log() which is not available in QJSEngine and QScriptEngine by default (note that print() is only available when using Qt Script)
|
||||
JSConsole console;
|
||||
engine.globalObject().setProperty("console", engine.newQObject(&console));
|
||||
|
||||
// evaluate the user provided script
|
||||
const auto res(engine.evaluate(QString::fromUtf8(script), scriptFile.fileName()));
|
||||
if (res.isError()) {
|
||||
cerr << Phrases::Error << "Unable to evaluate the specified script file \"" << m_args.script.firstValue() << "\"." << Phrases::End;
|
||||
printError(res);
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
// validate the altered configuration
|
||||
const auto newConfigObj(globalObject.property(QStringLiteral("config")));
|
||||
if (!newConfigObj.isObject()) {
|
||||
cerr << Phrases::Error << "New config object seems empty." << Phrases::EndFlush;
|
||||
return QByteArray();
|
||||
}
|
||||
for (const auto &arrayName : { QStringLiteral("devices"), QStringLiteral("folders") }) {
|
||||
if (!newConfigObj.property(arrayName).isArray()) {
|
||||
cerr << Phrases::Error << "Array \"" << arrayName.toLocal8Bit().data() << "\" is not present." << Phrases::EndFlush;
|
||||
return QByteArray();
|
||||
}
|
||||
}
|
||||
for (const auto &objectName : { QStringLiteral("options"), QStringLiteral("gui") }) {
|
||||
if (!newConfigObj.property(objectName).isObject()) {
|
||||
cerr << Phrases::Error << "Object \"" << objectName.toLocal8Bit().data() << "\" is not present." << Phrases::EndFlush;
|
||||
return QByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
// handle "dry-run" case
|
||||
if (m_args.dryRun.isPresent()) {
|
||||
cout << newConfig.data() << flush;
|
||||
return;
|
||||
// serilaize the altered configuration via JSON.stringify()
|
||||
const auto newConfigJson(engine.evaluate(QStringLiteral("JSON.stringify(config, null, 4)")));
|
||||
if (!newConfigJson.isString()) {
|
||||
cerr << Phrases::Error << "Unable to convert the config object to JSON via JSON.stringify()." << Phrases::End;
|
||||
cerr << configObj.toString().toLocal8Bit().data() << endl;
|
||||
return QByteArray();
|
||||
}
|
||||
return newConfigJson.toString().toUtf8();
|
||||
|
||||
// post new config
|
||||
using namespace TestUtilities;
|
||||
cerr << Phrases::Info << "Posting new configuration ..." << TextAttribute::Reset << flush;
|
||||
if (!waitForSignalsOrFail(bind(&SyncthingConnection::postConfig, ref(m_connection), ref(configObj)), 0,
|
||||
signalInfo(&m_connection, &SyncthingConnection::error), signalInfo(&m_connection, &SyncthingConnection::newConfigTriggered))) {
|
||||
return;
|
||||
}
|
||||
cerr << Phrases::Override << Phrases::Info << "Configuration posted successfully" << Phrases::EndFlush;
|
||||
#else
|
||||
cerr << Phrases::Error << PROJECT_NAME " has not been built with JavaScript support." << Phrases::EndFlush;
|
||||
return QByteArray();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::waitForIdle(const ArgumentOccurrence &)
|
||||
|
|
|
@ -55,6 +55,7 @@ private:
|
|||
int loadConfig();
|
||||
bool waitForConnected(int timeout = 2000);
|
||||
bool waitForConfig(int timeout = 2000);
|
||||
bool waitForConfigAndStatus(int timeout = 2000);
|
||||
void requestLog(const ArgumentOccurrence &);
|
||||
void requestShutdown(const ArgumentOccurrence &);
|
||||
void requestRestart(const ArgumentOccurrence &);
|
||||
|
@ -67,6 +68,8 @@ private:
|
|||
static void printLog(const std::vector<Data::SyncthingLogEntry> &logEntries);
|
||||
void printConfig(const ArgumentOccurrence &);
|
||||
void editConfig(const ArgumentOccurrence &);
|
||||
QByteArray editConfigViaEditor() const;
|
||||
QByteArray editConfigViaScript() const;
|
||||
void waitForIdle(const ArgumentOccurrence &);
|
||||
bool checkWhetherIdle() const;
|
||||
void checkPwdOperationPresent(const ArgumentOccurrence &occurrence);
|
||||
|
|
|
@ -22,6 +22,7 @@ Args::Args()
|
|||
, rescanPwd("rescan", 'r', "rescans the current working directory")
|
||||
, pausePwd("pause", 'p', "pauses the current working directory")
|
||||
, resumePwd("resume", '\0', "resumes the current working directory")
|
||||
, script("script", '\0', "runs the specified UTF-8 encoded ECMAScript on the configuration rather than opening an editor", { "path" })
|
||||
, dryRun("dry-run", '\0', "writes the altered configuration to stdout instead of posting it to Syncthing")
|
||||
, dir("dir", 'd', "specifies a directory by ID", { "ID" })
|
||||
, dev("dev", '\0', "specifies a device by ID or name", { "ID/name" })
|
||||
|
@ -48,7 +49,7 @@ Args::Args()
|
|||
waitForIdle.setExample(PROJECT_NAME " wait-for-idle --timeout 1800000 --at-least 5000 && systemctl poweroff\n" PROJECT_NAME
|
||||
" wait-for-idle --dir dir1 --dir dir2 --dev dev1 --dev dev2 --at-least 5000");
|
||||
pwd.setSubArguments({ &statusPwd, &rescanPwd, &pausePwd, &resumePwd });
|
||||
edit.setSubArguments({ &editor, &dryRun });
|
||||
edit.setSubArguments({ &editor, &script, &dryRun });
|
||||
|
||||
rescan.setValueNames({ "dir ID" });
|
||||
rescan.setRequiredValueCount(Argument::varValueCount);
|
||||
|
|
|
@ -14,7 +14,7 @@ struct Args {
|
|||
NoColorArgument noColor;
|
||||
OperationArgument status, log, stop, restart, rescan, rescanAll, pause, resume, waitForIdle, pwd, cat, edit;
|
||||
OperationArgument statusPwd, rescanPwd, pausePwd, resumePwd;
|
||||
ConfigValueArgument dryRun;
|
||||
ConfigValueArgument script, dryRun;
|
||||
ConfigValueArgument dir, dev, allDirs, allDevs;
|
||||
ConfigValueArgument atLeast, timeout;
|
||||
ConfigValueArgument editor;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
#include "./jsconsole.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
JSConsole::JSConsole(QObject *parent) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void JSConsole::log(const QString &msg) const
|
||||
{
|
||||
cerr << "script: "<< msg.toLocal8Bit().data() << endl;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef CLI_JS_CONSOLE_H
|
||||
#define CLI_JS_CONSOLE_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class JSConsole : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit JSConsole(QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void log(const QString &msg) const;
|
||||
};
|
||||
|
||||
#endif // CLI_JS_CONSOLE_H
|
|
@ -0,0 +1,36 @@
|
|||
// Created via CMake from template jsdefs.h.in
|
||||
// WARNING! Any changes to this file will be overwritten by the next CMake run!
|
||||
|
||||
#ifndef SYNCTHINGCTL_JAVA_SCRIPT_DEFINES
|
||||
#define SYNCTHINGCTL_JAVA_SCRIPT_DEFINES
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#if defined(SYNCTHINGCTL_USE_JSENGINE)
|
||||
# define SYNCTHINGCTL_JS_ENGINE QJSEngine
|
||||
# define SYNCTHINGCTL_JS_VALUE QJSValue
|
||||
# define SYNCTHINGCTL_JS_READONLY
|
||||
# define SYNCTHINGCTL_JS_UNDELETABLE
|
||||
# define SYNCTHINGCTL_JS_QOBJECT(engine, obj) engine.newQObject(obj)
|
||||
# define SYNCTHINGCTL_JS_INT(value) value.toInt()
|
||||
# define SYNCTHINGCTL_JS_IS_VALID_PROG(program) (!program.isError() && program.isCallable())
|
||||
#elif defined(SYNCTHINGCTL_USE_SCRIPT)
|
||||
# define SYNCTHINGCTL_JS_ENGINE QScriptEngine
|
||||
# define SYNCTHINGCTL_JS_VALUE QScriptValue
|
||||
# define SYNCTHINGCTL_JS_READONLY ,QScriptValue::ReadOnly
|
||||
# define SYNCTHINGCTL_JS_UNDELETABLE ,QScriptValue::Undeletable
|
||||
# define SYNCTHINGCTL_JS_QOBJECT(engine, obj) engine.newQObject(obj, QScriptEngine::ScriptOwnership)
|
||||
# define SYNCTHINGCTL_JS_INT(value) value.toInt32()
|
||||
# define SYNCTHINGCTL_JS_IS_VALID_PROG(program) (!program.isError() && program.isFunction())
|
||||
#elif !defined(SYNCTHINGCTL_NO_JSENGINE)
|
||||
# error "No definition for JavaScript provider present."
|
||||
#endif
|
||||
|
||||
#ifdef SYNCTHINGCTL_JS_ENGINE
|
||||
QT_FORWARD_DECLARE_CLASS(SYNCTHINGCTL_JS_ENGINE)
|
||||
#endif
|
||||
#ifdef SYNCTHINGCTL_JS_VALUE
|
||||
QT_FORWARD_DECLARE_CLASS(SYNCTHINGCTL_JS_VALUE)
|
||||
#endif
|
||||
|
||||
#endif // SYNCTHINGCTL_JAVA_SCRIPT_DEFINES
|
|
@ -0,0 +1,19 @@
|
|||
// Created via CMake from template jsincludes.h.in
|
||||
// WARNING! Any changes to this file will be overwritten by the next CMake run!
|
||||
|
||||
#ifndef SYNCTHINGCTL_JAVA_SCRIPT_INCLUDES
|
||||
#define SYNCTHINGCTL_JAVA_SCRIPT_INCLUDES
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#if defined(SYNCTHINGCTL_USE_JSENGINE)
|
||||
# include <QJSEngine>
|
||||
# include <QJSValue>
|
||||
#elif defined(SYNCTHINGCTL_USE_SCRIPT)
|
||||
# include <QScriptEngine>
|
||||
# include <QScriptValue>
|
||||
#elif !defined(SYNCTHINGCTL_NO_JSENGINE)
|
||||
# error "No definition for JavaScript provider present."
|
||||
#endif
|
||||
|
||||
#endif // SYNCTHINGCTL_JAVA_SCRIPT_INCLUDES
|
|
@ -0,0 +1,27 @@
|
|||
// example script for changing configuration with syncthingctl
|
||||
// can be executed like: syncthingctl edit --script example.js
|
||||
|
||||
// alter some options
|
||||
config.gui.useTLS = true;
|
||||
config.options.relaysEnabled = false;
|
||||
|
||||
// enable file system watcher for all folders starting with "docs-"
|
||||
var folders = config.folders;
|
||||
for (var i = 0, count = folders.length; i !== count; ++i) {
|
||||
var folder = folders[i];
|
||||
if (folder.id.indexOf("docs-") === 0) {
|
||||
//folder.fsWatcherDelayS = 50;
|
||||
//folder.fsWatcherEnabled = true;
|
||||
console.log("enabling file system watcher for folder " + folder.id);
|
||||
}
|
||||
}
|
||||
|
||||
// ensure all devices are enabled
|
||||
var devices = config.devices;
|
||||
for (var i = 0, count = devices.length; i !== count; ++i) {
|
||||
var device = devices[i];
|
||||
if (device.paused) {
|
||||
device.paused = false;
|
||||
console.log("unpausing device " + (device.name ? device.name : device.deviceID));
|
||||
}
|
||||
}
|
|
@ -756,7 +756,7 @@ void SyncthingConnection::requestConfig()
|
|||
/*!
|
||||
* \brief Requests the Syncthing status asynchronously.
|
||||
*
|
||||
* The signal configDirChanged() and myIdChanged() emitted when those values have changed; error() is emitted in the error case.
|
||||
* The signals configDirChanged() and myIdChanged() are emitted when those values have changed; error() is emitted in the error case.
|
||||
*/
|
||||
void SyncthingConnection::requestStatus()
|
||||
{
|
||||
|
@ -764,6 +764,17 @@ void SyncthingConnection::requestStatus()
|
|||
m_statusReply = requestData(QStringLiteral("system/status"), QUrlQuery()), &QNetworkReply::finished, this, &SyncthingConnection::readStatus);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Requests the Syncthing configuration and status asynchronously.
|
||||
*
|
||||
* \sa requestConfig() and requestStatus() for emitted signals.
|
||||
*/
|
||||
void SyncthingConnection::requestConfigAndStatus()
|
||||
{
|
||||
requestConfig();
|
||||
requestStatus();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Requests current connections asynchronously.
|
||||
*
|
||||
|
@ -846,12 +857,24 @@ void SyncthingConnection::requestDeviceStatistics()
|
|||
* \remarks The signal newConfigTriggered() is emitted when the config has been posted sucessfully. In the error case, error() is emitted.
|
||||
* Besides, the newConfig() signal should be emitted as well, indicating Syncthing has actually applied the new configuration.
|
||||
*/
|
||||
void SyncthingConnection::postConfig(const QJsonObject &rawConfig)
|
||||
void SyncthingConnection::postConfigFromJsonObject(const QJsonObject &rawConfig)
|
||||
{
|
||||
QObject::connect(postData(QStringLiteral("system/config"), QUrlQuery(), QJsonDocument(rawConfig).toJson(QJsonDocument::Compact)),
|
||||
&QNetworkReply::finished, this, &SyncthingConnection::readPostConfig);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Posts the specified \a rawConfig.
|
||||
* \param rawConfig A valid JSON document containing the configuration. It is directly passed to Syncthing.
|
||||
* \remarks The signal newConfigTriggered() is emitted when the config has been posted sucessfully. In the error case, error() is emitted.
|
||||
* Besides, the newConfig() signal should be emitted as well, indicating Syncthing has actually applied the new configuration.
|
||||
*/
|
||||
void SyncthingConnection::postConfigFromByteArray(const QByteArray &rawConfig)
|
||||
{
|
||||
QObject::connect(
|
||||
postData(QStringLiteral("system/config"), QUrlQuery(), rawConfig), &QNetworkReply::finished, this, &SyncthingConnection::readPostConfig);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Requests the Syncthing events (since the last successful call) asynchronously.
|
||||
*
|
||||
|
|
|
@ -165,6 +165,7 @@ public Q_SLOTS:
|
|||
|
||||
void requestConfig();
|
||||
void requestStatus();
|
||||
void requestConfigAndStatus();
|
||||
void requestErrors();
|
||||
void requestConnections();
|
||||
void requestClearingErrors();
|
||||
|
@ -172,7 +173,8 @@ public Q_SLOTS:
|
|||
void requestDirStatus(const QString &dirId);
|
||||
void requestCompletion(const QString &devId, const QString &dirId);
|
||||
void requestDeviceStatistics();
|
||||
void postConfig(const QJsonObject &rawConfig);
|
||||
void postConfigFromJsonObject(const QJsonObject &rawConfig);
|
||||
void postConfigFromByteArray(const QByteArray &rawConfig);
|
||||
|
||||
Q_SIGNALS:
|
||||
void newConfig(const QJsonObject &rawConfig);
|
||||
|
|
Loading…
Reference in New Issue