mirror of
https://github.com/RetroShare/RetroShare.git
synced 2026-09-12 19:50:17 +05:00
Merge 774cfda33c into 03daec3e2a
This commit is contained in:
commit
0fdd4e4b6d
@ -17,6 +17,7 @@ set(CMAKE_AUTOUIC ON)
|
||||
set(FEEDREADER_SOURCES
|
||||
FeedReaderPlugin.cpp
|
||||
services/p3FeedReader.cc
|
||||
services/FeedReaderJsonApi.cpp
|
||||
services/p3FeedReaderThread.cc
|
||||
services/rsFeedReaderItems.cc
|
||||
gui/FeedReaderDialog.cpp
|
||||
@ -105,7 +106,7 @@ if(WIN32)
|
||||
# (Linux) the loader resolves these symbols from the hosting retroshare-gui
|
||||
# process at load time, so this explicit link is only needed on Windows.
|
||||
target_link_libraries(FeedReader PRIVATE retroshare retroshare-gui)
|
||||
target_compile_definitions(FeedReader PRIVATE WINDOWS_SYS)
|
||||
target_compile_definitions(FeedReader PRIVATE WINDOWS_SYS WIN_DLL_EXPORT)
|
||||
endif()
|
||||
|
||||
target_link_libraries(FeedReader PRIVATE
|
||||
@ -123,6 +124,13 @@ target_link_libraries(FeedReader PRIVATE
|
||||
target_compile_options(FeedReader PRIVATE "-Wno-deprecated-declarations")
|
||||
target_compile_definitions(FeedReader PRIVATE RS_NO_WARN_DEPRECATED)
|
||||
|
||||
# RapidJSON 1.1 contains an intentionally unusable assignment operator for
|
||||
# GenericStringRef. GCC 15 diagnoses its body eagerly unless this warning is
|
||||
# disabled (older GCC versions do not know this option).
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15)
|
||||
target_compile_options(FeedReader PRIVATE "-Wno-template-body")
|
||||
endif()
|
||||
|
||||
if(RS_DEVELOPMENT_BUILD)
|
||||
target_compile_options(FeedReader PRIVATE "-g" "-O0")
|
||||
endif()
|
||||
@ -138,7 +146,14 @@ target_include_directories(FeedReader PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/retroshare-gui/src
|
||||
${CMAKE_SOURCE_DIR}/retroshare-gui/src/gui
|
||||
${CMAKE_SOURCE_DIR}/supportlibs/rapidjson/include
|
||||
${CMAKE_SOURCE_DIR}/supportlibs/restbed/include
|
||||
)
|
||||
|
||||
if(TARGET restbed-static)
|
||||
target_link_libraries(FeedReader PRIVATE restbed-static)
|
||||
elseif(TARGET restbed-shared)
|
||||
target_link_libraries(FeedReader PRIVATE restbed-shared)
|
||||
endif()
|
||||
|
||||
# --- Installation ---
|
||||
install(TARGETS FeedReader DESTINATION lib/retroshare/plugins)
|
||||
|
||||
@ -30,9 +30,15 @@ greaterThan(QT_MAJOR_VERSION, 4) {
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION,5): QT += core5compat
|
||||
|
||||
# RapidJSON 1.1 triggers GCC 15's eager template-body diagnostic.
|
||||
gcc:greaterThan(QMAKE_GCC_MAJOR_VERSION, 14): QMAKE_CXXFLAGS += -Wno-template-body
|
||||
|
||||
INCLUDEPATH += ../../supportlibs/restbed/include
|
||||
|
||||
target.files = lib/libFeedReader.so
|
||||
|
||||
SOURCES = FeedReaderPlugin.cpp \
|
||||
services/FeedReaderJsonApi.cpp \
|
||||
services/p3FeedReader.cc \
|
||||
services/p3FeedReaderThread.cc \
|
||||
services/rsFeedReaderItems.cc \
|
||||
@ -54,6 +60,7 @@ SOURCES = FeedReaderPlugin.cpp \
|
||||
util/XPathWrapper.cpp
|
||||
|
||||
HEADERS = FeedReaderPlugin.h \
|
||||
services/FeedReaderJsonApi.h \
|
||||
interface/rsFeedReader.h \
|
||||
services/p3FeedReader.h \
|
||||
services/p3FeedReaderThread.h \
|
||||
@ -117,10 +124,11 @@ linux-* {
|
||||
}
|
||||
|
||||
win32 {
|
||||
DEFINES += CURL_STATICLIB LIBXML_STATIC LIBXSLT_STATIC LIBEXSLT_STATIC
|
||||
DEFINES += CURL_STATICLIB LIBXML_STATIC LIBXSLT_STATIC LIBEXSLT_STATIC WIN_DLL_EXPORT
|
||||
|
||||
#Have to reorder libs, else got /libs/lib/libcrypto.a(bio_lib.o):bio_lib.c:(.text+0x0): multiple definition of `BIO_new'
|
||||
LIBS = -lcurl -lxml2 -lz -lxslt -lws2_32 -lwldap32 -lssl -lcrypto -lgdi32 $${LIBS}
|
||||
LIBS += -L$$PWD/../../supportlibs/restbed -lrestbed
|
||||
|
||||
isEmpty(QMAKE_SH) {
|
||||
# MinGW
|
||||
|
||||
@ -30,6 +30,9 @@
|
||||
#include "gui/FeedReaderConfig.h"
|
||||
#include "gui/FeedReaderFeedNotify.h"
|
||||
#include "services/p3FeedReader.h"
|
||||
#include "services/FeedReaderJsonApi.h"
|
||||
#include <retroshare/rsjsonapi.h>
|
||||
#include <util/rsdebug.h>
|
||||
|
||||
#include <libxml/xmlversion.h>
|
||||
#include <libxslt/xsltconfig.h>
|
||||
@ -83,6 +86,7 @@ FeedReaderPlugin::FeedReaderPlugin()
|
||||
mFeedReader = NULL;
|
||||
mNotify = NULL;
|
||||
mFeedNotify = NULL;
|
||||
mJsonApiProvider = NULL;
|
||||
|
||||
Q_INIT_RESOURCE(FeedReader_images);
|
||||
Q_INIT_RESOURCE(FeedReader_qss);
|
||||
@ -97,6 +101,19 @@ void FeedReaderPlugin::setInterfaces(RsPlugInInterfaces &interfaces)
|
||||
|
||||
mNotify = new FeedReaderNotify();
|
||||
mFeedReader->setNotify(mNotify);
|
||||
|
||||
if(mInterfaces.mJsonApi)
|
||||
{
|
||||
mJsonApiProvider = new FeedReaderJsonApi(*mFeedReader, *mInterfaces.mJsonApi);
|
||||
mInterfaces.mJsonApi->registerResourceProvider(*mJsonApiProvider);
|
||||
|
||||
// No restart here: the core publishes every provider together once all
|
||||
// plugins have received their interfaces, which avoids one
|
||||
// burst-protected JSON API restart per plugin.
|
||||
RsDbg() << "FeedReader: JSON API routes registered.";
|
||||
}
|
||||
else
|
||||
RsInfo() << "FeedReader: JSON API not available, routes not registered.";
|
||||
}
|
||||
|
||||
ConfigPage *FeedReaderPlugin::qt_config_page() const
|
||||
@ -123,6 +140,19 @@ FeedNotify *FeedReaderPlugin::qt_feedNotify()
|
||||
|
||||
void FeedReaderPlugin::stop()
|
||||
{
|
||||
if(mJsonApiProvider)
|
||||
{
|
||||
if(mInterfaces.mJsonApi)
|
||||
mInterfaces.mJsonApi->unregisterResourceProvider(*mJsonApiProvider);
|
||||
|
||||
/* No restart here. The core stops the JSON API before it stops the
|
||||
* plugins, so the restbed resources whose handlers capture this
|
||||
* provider are already gone by now and deleting it is safe. Restarting
|
||||
* would only wait out RESTART_BURST_PROTECTION and bring the server
|
||||
* back up in the middle of the core teardown. */
|
||||
delete mJsonApiProvider;
|
||||
mJsonApiProvider = NULL;
|
||||
}
|
||||
if (mFeedReader) {
|
||||
mFeedReader->setNotify(NULL);
|
||||
mFeedReader->stop();
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
class p3FeedReader;
|
||||
class FeedReaderNotify;
|
||||
class RsForums;
|
||||
class FeedReaderJsonApi;
|
||||
|
||||
class FeedReaderPlugin: public RsPlugin
|
||||
{
|
||||
@ -65,4 +66,5 @@ private:
|
||||
mutable MainPage *mainpage;
|
||||
mutable QIcon *mIcon;
|
||||
mutable FeedNotify *mFeedNotify;
|
||||
mutable FeedReaderJsonApi *mJsonApiProvider;
|
||||
};
|
||||
|
||||
@ -99,11 +99,14 @@ public:
|
||||
public:
|
||||
FeedInfo()
|
||||
{
|
||||
feedId = 0;
|
||||
parentId = 0;
|
||||
proxyPort = 0;
|
||||
updateInterval = 0;
|
||||
lastUpdate = 0;
|
||||
storageTime = 0;
|
||||
errorState = RS_FEED_ERRORSTATE_OK;
|
||||
workstate = WAITING;
|
||||
flag.folder = false;
|
||||
flag.infoFromFeed = false;
|
||||
flag.standardStorageTime = false;
|
||||
@ -115,6 +118,9 @@ public:
|
||||
flag.updateForumInfo = false;
|
||||
flag.posted = false;
|
||||
flag.updatePostedInfo = false;
|
||||
flag.postedFirstImage = false;
|
||||
flag.postedOnlyImage = false;
|
||||
flag.postedShrinkImage = false;
|
||||
flag.embedImages = false;
|
||||
flag.saveCompletePage = false;
|
||||
flag.preview = false;
|
||||
|
||||
337
plugins/FeedReader/services/FeedReaderJsonApi.cpp
Normal file
337
plugins/FeedReader/services/FeedReaderJsonApi.cpp
Normal file
@ -0,0 +1,337 @@
|
||||
/*******************************************************************************
|
||||
* plugins/FeedReader/services/FeedReaderJsonApi.cpp *
|
||||
* *
|
||||
* Copyright (C) 2026 RetroShare Team *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#include "FeedReaderJsonApi.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
#include <rapidjson/writer.h>
|
||||
|
||||
#include "interface/rsFeedReader.h"
|
||||
#include "retroshare/rsjsonapi.h"
|
||||
#include "util/radix64.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
using Document = rapidjson::Document;
|
||||
using Value = rapidjson::Value;
|
||||
using Allocator = Document::AllocatorType;
|
||||
|
||||
const std::multimap<std::string, std::string> jsonHeaders = {
|
||||
{"Access-Control-Allow-Origin", "*"},
|
||||
{"Access-Control-Allow-Headers", "Authorization, Content-Type"},
|
||||
{"Access-Control-Allow-Methods", "POST, OPTIONS"},
|
||||
{"Content-Type", "application/json"}
|
||||
};
|
||||
|
||||
void closeJson(const std::shared_ptr<restbed::Session>& session, int status, const Document& doc)
|
||||
{
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
doc.Accept(writer);
|
||||
auto headers = jsonHeaders;
|
||||
headers.emplace("Content-Length", std::to_string(buffer.GetSize()));
|
||||
session->close(status, buffer.GetString(), headers);
|
||||
}
|
||||
|
||||
void closeError(const std::shared_ptr<restbed::Session>& session, int status, const std::string& message)
|
||||
{
|
||||
Document doc;
|
||||
doc.SetObject();
|
||||
doc.AddMember("ok", false, doc.GetAllocator());
|
||||
doc.AddMember("error", Value(message.c_str(), doc.GetAllocator()), doc.GetAllocator());
|
||||
closeJson(session, status, doc);
|
||||
}
|
||||
|
||||
bool authenticate(const std::shared_ptr<restbed::Session>& session, RsJsonApi& jsonApi)
|
||||
{
|
||||
std::istringstream header(session->get_request()->get_header("Authorization"));
|
||||
std::string scheme, encoded;
|
||||
header >> scheme >> encoded;
|
||||
if(scheme != "Basic" || encoded.empty()) return false;
|
||||
|
||||
const auto decoded = Radix64::decode(encoded);
|
||||
if(decoded.empty()) return false;
|
||||
const std::string token(reinterpret_cast<const char*>(decoded.data()), decoded.size());
|
||||
return jsonApi.isAuthTokenValid(token);
|
||||
}
|
||||
|
||||
void addString(Value& object, const char* name, const std::string& value, Allocator& allocator)
|
||||
{
|
||||
object.AddMember(Value(name, allocator), Value(value.c_str(), value.size(), allocator), allocator);
|
||||
}
|
||||
|
||||
Value feedToJson(const FeedInfo& feed, Allocator& allocator)
|
||||
{
|
||||
Value out(rapidjson::kObjectType);
|
||||
out.AddMember("feedId", feed.feedId, allocator);
|
||||
out.AddMember("parentId", feed.parentId, allocator);
|
||||
addString(out, "url", feed.url, allocator);
|
||||
addString(out, "name", feed.name, allocator);
|
||||
addString(out, "description", feed.description, allocator);
|
||||
addString(out, "icon", feed.icon, allocator);
|
||||
out.AddMember("updateInterval", feed.updateInterval, allocator);
|
||||
out.AddMember("lastUpdate", static_cast<int64_t>(feed.lastUpdate), allocator);
|
||||
out.AddMember("storageTime", feed.storageTime, allocator);
|
||||
out.AddMember("workState", static_cast<unsigned>(feed.workstate), allocator);
|
||||
out.AddMember("errorState", static_cast<unsigned>(feed.errorState), allocator);
|
||||
addString(out, "errorString", feed.errorString, allocator);
|
||||
out.AddMember("folder", feed.flag.folder, allocator);
|
||||
out.AddMember("deactivated", feed.flag.deactivated, allocator);
|
||||
out.AddMember("authentication", feed.flag.authentication, allocator);
|
||||
return out;
|
||||
}
|
||||
|
||||
Value messageToJson(const FeedMsgInfo& msg, Allocator& allocator)
|
||||
{
|
||||
Value out(rapidjson::kObjectType);
|
||||
addString(out, "msgId", msg.msgId, allocator);
|
||||
out.AddMember("feedId", msg.feedId, allocator);
|
||||
addString(out, "title", msg.title, allocator);
|
||||
addString(out, "link", msg.link, allocator);
|
||||
addString(out, "author", msg.author, allocator);
|
||||
addString(out, "description", msg.description, allocator);
|
||||
addString(out, "descriptionTransformed", msg.descriptionTransformed, allocator);
|
||||
out.AddMember("pubDate", static_cast<int64_t>(msg.pubDate), allocator);
|
||||
addString(out, "attachmentLink", msg.attachmentLink, allocator);
|
||||
addString(out, "attachmentMimeType", msg.attachmentMimeType, allocator);
|
||||
out.AddMember("isNew", msg.flag.isnew, allocator);
|
||||
out.AddMember("read", msg.flag.read, allocator);
|
||||
out.AddMember("deleted", msg.flag.deleted, allocator);
|
||||
return out;
|
||||
}
|
||||
|
||||
bool uintMember(const Document& doc, const char* name, uint32_t& value)
|
||||
{
|
||||
if(!doc.HasMember(name) || !doc[name].IsUint()) return false;
|
||||
value = doc[name].GetUint();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stringMember(const Document& doc, const char* name, std::string& value)
|
||||
{
|
||||
if(!doc.HasMember(name) || !doc[name].IsString()) return false;
|
||||
value = doc[name].GetString();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Handler>
|
||||
std::shared_ptr<restbed::Resource> makeResource(
|
||||
const std::string& path, RsJsonApi& jsonApi, Handler handler )
|
||||
{
|
||||
auto resource = std::make_shared<restbed::Resource>();
|
||||
resource->set_path(path);
|
||||
resource->set_method_handler("OPTIONS", [](const std::shared_ptr<restbed::Session> session)
|
||||
{
|
||||
session->close(restbed::NO_CONTENT, jsonHeaders);
|
||||
});
|
||||
resource->set_authentication_handler([jsonApiPtr = &jsonApi](
|
||||
const std::shared_ptr<restbed::Session> session,
|
||||
const std::function<void(const std::shared_ptr<restbed::Session>)>& callback)
|
||||
{
|
||||
if( session->get_request()->get_method() == "OPTIONS" ||
|
||||
authenticate(session, *jsonApiPtr) ) callback(session);
|
||||
else session->close(restbed::UNAUTHORIZED, jsonHeaders);
|
||||
});
|
||||
resource->set_method_handler("POST", [handler](const std::shared_ptr<restbed::Session> session)
|
||||
{
|
||||
const auto size = session->get_request()->get_header("Content-Length", 0);
|
||||
session->fetch(static_cast<size_t>(size), [handler](
|
||||
const std::shared_ptr<restbed::Session> fetched, const restbed::Bytes& body)
|
||||
{
|
||||
Document request;
|
||||
request.Parse(reinterpret_cast<const char*>(body.data()), body.size());
|
||||
if(request.HasParseError() || !request.IsObject())
|
||||
{
|
||||
closeError(fetched, restbed::BAD_REQUEST, "Invalid JSON request body");
|
||||
return;
|
||||
}
|
||||
handler(fetched, request);
|
||||
});
|
||||
});
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
||||
FeedReaderJsonApi::FeedReaderJsonApi(
|
||||
RsFeedReader& feedReader, RsJsonApi& jsonApi ):
|
||||
mFeedReader(feedReader), mJsonApi(jsonApi) {}
|
||||
|
||||
std::vector<std::shared_ptr<restbed::Resource>> FeedReaderJsonApi::getResources() const
|
||||
{
|
||||
std::vector<std::shared_ptr<restbed::Resource>> resources;
|
||||
auto resource = [this](const std::string& path, auto handler)
|
||||
{
|
||||
return makeResource(path, mJsonApi, handler);
|
||||
};
|
||||
|
||||
resources.push_back(resource("/rsFeedReader/getFeeds", [this](auto session, const Document& request)
|
||||
{
|
||||
uint32_t parentId = 0;
|
||||
if(request.HasMember("parentId") && !uintMember(request, "parentId", parentId))
|
||||
return closeError(session, restbed::BAD_REQUEST, "parentId must be an unsigned integer");
|
||||
std::list<FeedInfo> feeds;
|
||||
mFeedReader.getFeedList(parentId, feeds);
|
||||
Document response;
|
||||
response.SetObject();
|
||||
response.AddMember("ok", true, response.GetAllocator());
|
||||
Value array(rapidjson::kArrayType);
|
||||
for(const auto& feed: feeds) array.PushBack(feedToJson(feed, response.GetAllocator()), response.GetAllocator());
|
||||
response.AddMember("feeds", array, response.GetAllocator());
|
||||
closeJson(session, restbed::OK, response);
|
||||
}));
|
||||
|
||||
resources.push_back(resource("/rsFeedReader/getMessages", [this](auto session, const Document& request)
|
||||
{
|
||||
uint32_t feedId = 0;
|
||||
if(!uintMember(request, "feedId", feedId)) return closeError(session, restbed::BAD_REQUEST, "feedId is required");
|
||||
std::list<FeedMsgInfo> messages;
|
||||
if(!mFeedReader.getFeedMsgList(feedId, messages)) return closeError(session, restbed::NOT_FOUND, "Feed not found");
|
||||
Document response;
|
||||
response.SetObject();
|
||||
response.AddMember("ok", true, response.GetAllocator());
|
||||
Value array(rapidjson::kArrayType);
|
||||
for(const auto& msg: messages) array.PushBack(messageToJson(msg, response.GetAllocator()), response.GetAllocator());
|
||||
response.AddMember("messages", array, response.GetAllocator());
|
||||
closeJson(session, restbed::OK, response);
|
||||
}));
|
||||
|
||||
resources.push_back(resource("/rsFeedReader/addFolder", [this](auto session, const Document& request)
|
||||
{
|
||||
uint32_t parentId = 0, feedId = 0;
|
||||
std::string name;
|
||||
if(!stringMember(request, "name", name) || name.empty()) return closeError(session, restbed::BAD_REQUEST, "name is required");
|
||||
if(request.HasMember("parentId") && !uintMember(request, "parentId", parentId)) return closeError(session, restbed::BAD_REQUEST, "Invalid parentId");
|
||||
const auto result = mFeedReader.addFolder(parentId, name, feedId);
|
||||
Document response;
|
||||
response.SetObject();
|
||||
response.AddMember("ok", result == RS_FEED_RESULT_SUCCESS, response.GetAllocator());
|
||||
response.AddMember("result", static_cast<unsigned>(result), response.GetAllocator());
|
||||
response.AddMember("feedId", feedId, response.GetAllocator());
|
||||
closeJson(session, result == RS_FEED_RESULT_SUCCESS ? restbed::OK : restbed::BAD_REQUEST, response);
|
||||
}));
|
||||
|
||||
resources.push_back(resource("/rsFeedReader/addFeed", [this](auto session, const Document& request)
|
||||
{
|
||||
FeedInfo feed;
|
||||
uint32_t feedId = 0;
|
||||
if(!stringMember(request, "url", feed.url) || feed.url.empty()) return closeError(session, restbed::BAD_REQUEST, "url is required");
|
||||
stringMember(request, "name", feed.name);
|
||||
if(request.HasMember("parentId") && !uintMember(request, "parentId", feed.parentId)) return closeError(session, restbed::BAD_REQUEST, "Invalid parentId");
|
||||
if(request.HasMember("updateInterval")) uintMember(request, "updateInterval", feed.updateInterval);
|
||||
else feed.flag.standardUpdateInterval = true;
|
||||
if(request.HasMember("storageTime")) uintMember(request, "storageTime", feed.storageTime);
|
||||
else feed.flag.standardStorageTime = true;
|
||||
if(request.HasMember("deactivated") && request["deactivated"].IsBool()) feed.flag.deactivated = request["deactivated"].GetBool();
|
||||
if(request.HasMember("user") && request["user"].IsString()) feed.user = request["user"].GetString();
|
||||
if(request.HasMember("password") && request["password"].IsString()) feed.password = request["password"].GetString();
|
||||
feed.flag.authentication = !feed.user.empty();
|
||||
const auto result = mFeedReader.addFeed(feed, feedId);
|
||||
Document response;
|
||||
response.SetObject();
|
||||
response.AddMember("ok", result == RS_FEED_RESULT_SUCCESS, response.GetAllocator());
|
||||
response.AddMember("result", static_cast<unsigned>(result), response.GetAllocator());
|
||||
response.AddMember("feedId", feedId, response.GetAllocator());
|
||||
closeJson(session, result == RS_FEED_RESULT_SUCCESS ? restbed::OK : restbed::BAD_REQUEST, response);
|
||||
}));
|
||||
|
||||
resources.push_back(resource("/rsFeedReader/updateFeed", [this](auto session, const Document& request)
|
||||
{
|
||||
uint32_t feedId = 0;
|
||||
if(!uintMember(request, "feedId", feedId)) return closeError(session, restbed::BAD_REQUEST, "feedId is required");
|
||||
FeedInfo feed;
|
||||
if(!mFeedReader.getFeedInfo(feedId, feed)) return closeError(session, restbed::NOT_FOUND, "Feed not found");
|
||||
if(feed.flag.folder)
|
||||
{
|
||||
std::string name;
|
||||
if(!stringMember(request, "name", name) || name.empty()) return closeError(session, restbed::BAD_REQUEST, "name is required");
|
||||
const auto result = mFeedReader.setFolder(feedId, name);
|
||||
Document response; response.SetObject(); response.AddMember("ok", result == RS_FEED_RESULT_SUCCESS, response.GetAllocator());
|
||||
return closeJson(session, result == RS_FEED_RESULT_SUCCESS ? restbed::OK : restbed::BAD_REQUEST, response);
|
||||
}
|
||||
stringMember(request, "url", feed.url);
|
||||
stringMember(request, "name", feed.name);
|
||||
if(request.HasMember("updateInterval")) uintMember(request, "updateInterval", feed.updateInterval);
|
||||
if(request.HasMember("storageTime")) uintMember(request, "storageTime", feed.storageTime);
|
||||
if(request.HasMember("deactivated") && request["deactivated"].IsBool()) feed.flag.deactivated = request["deactivated"].GetBool();
|
||||
if(request.HasMember("user") && request["user"].IsString()) feed.user = request["user"].GetString();
|
||||
if(request.HasMember("password") && request["password"].IsString()) feed.password = request["password"].GetString();
|
||||
feed.flag.authentication = !feed.user.empty();
|
||||
const auto result = mFeedReader.setFeed(feedId, feed);
|
||||
Document response; response.SetObject(); response.AddMember("ok", result == RS_FEED_RESULT_SUCCESS, response.GetAllocator());
|
||||
response.AddMember("result", static_cast<unsigned>(result), response.GetAllocator());
|
||||
closeJson(session, result == RS_FEED_RESULT_SUCCESS ? restbed::OK : restbed::BAD_REQUEST, response);
|
||||
}));
|
||||
|
||||
auto idAction = [this, &resource](const std::string& path, auto action)
|
||||
{
|
||||
return resource(path, [this, action](auto session, const Document& request)
|
||||
{
|
||||
uint32_t feedId = 0;
|
||||
if(!uintMember(request, "feedId", feedId)) return closeError(session, restbed::BAD_REQUEST, "feedId is required");
|
||||
const bool ok = action(feedId);
|
||||
Document response; response.SetObject(); response.AddMember("ok", ok, response.GetAllocator());
|
||||
closeJson(session, ok ? restbed::OK : restbed::NOT_FOUND, response);
|
||||
});
|
||||
};
|
||||
resources.push_back(idAction("/rsFeedReader/removeFeed", [this](uint32_t id){ return mFeedReader.removeFeed(id); }));
|
||||
resources.push_back(idAction("/rsFeedReader/refreshFeed", [this](uint32_t id){ return mFeedReader.processFeed(id); }));
|
||||
|
||||
resources.push_back(resource("/rsFeedReader/setMessageRead", [this](auto session, const Document& request)
|
||||
{
|
||||
uint32_t feedId = 0; std::string msgId;
|
||||
if(!uintMember(request, "feedId", feedId) || !stringMember(request, "msgId", msgId) || !request.HasMember("read") || !request["read"].IsBool())
|
||||
return closeError(session, restbed::BAD_REQUEST, "feedId, msgId and read are required");
|
||||
const bool ok = mFeedReader.setMessageRead(feedId, msgId, request["read"].GetBool());
|
||||
Document response; response.SetObject(); response.AddMember("ok", ok, response.GetAllocator());
|
||||
closeJson(session, ok ? restbed::OK : restbed::NOT_FOUND, response);
|
||||
}));
|
||||
|
||||
resources.push_back(resource("/rsFeedReader/removeMessage", [this](auto session, const Document& request)
|
||||
{
|
||||
uint32_t feedId = 0; std::string msgId;
|
||||
if(!uintMember(request, "feedId", feedId) || !stringMember(request, "msgId", msgId)) return closeError(session, restbed::BAD_REQUEST, "feedId and msgId are required");
|
||||
const bool ok = mFeedReader.removeMsg(feedId, msgId);
|
||||
Document response; response.SetObject(); response.AddMember("ok", ok, response.GetAllocator());
|
||||
closeJson(session, ok ? restbed::OK : restbed::NOT_FOUND, response);
|
||||
}));
|
||||
|
||||
resources.push_back(resource("/rsFeedReader/getSettings", [this](auto session, const Document&)
|
||||
{
|
||||
Document response; response.SetObject(); response.AddMember("ok", true, response.GetAllocator());
|
||||
response.AddMember("storageTime", mFeedReader.getStandardStorageTime(), response.GetAllocator());
|
||||
response.AddMember("updateInterval", mFeedReader.getStandardUpdateInterval(), response.GetAllocator());
|
||||
response.AddMember("saveInBackground", mFeedReader.getSaveInBackground(), response.GetAllocator());
|
||||
closeJson(session, restbed::OK, response);
|
||||
}));
|
||||
|
||||
resources.push_back(resource("/rsFeedReader/setSettings", [this](auto session, const Document& request)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
if(request.HasMember("storageTime") && uintMember(request, "storageTime", value)) mFeedReader.setStandardStorageTime(value);
|
||||
if(request.HasMember("updateInterval") && uintMember(request, "updateInterval", value)) mFeedReader.setStandardUpdateInterval(value);
|
||||
if(request.HasMember("saveInBackground") && request["saveInBackground"].IsBool()) mFeedReader.setSaveInBackground(request["saveInBackground"].GetBool());
|
||||
Document response; response.SetObject(); response.AddMember("ok", true, response.GetAllocator()); closeJson(session, restbed::OK, response);
|
||||
}));
|
||||
|
||||
return resources;
|
||||
}
|
||||
42
plugins/FeedReader/services/FeedReaderJsonApi.h
Normal file
42
plugins/FeedReader/services/FeedReaderJsonApi.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*******************************************************************************
|
||||
* plugins/FeedReader/services/FeedReaderJsonApi.h *
|
||||
* *
|
||||
* Copyright (C) 2026 RetroShare Team *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "jsonapi/jsonapi.h"
|
||||
|
||||
class RsFeedReader;
|
||||
|
||||
class FeedReaderJsonApi final: public JsonApiResourceProvider
|
||||
{
|
||||
public:
|
||||
FeedReaderJsonApi(RsFeedReader& feedReader, RsJsonApi& jsonApi);
|
||||
|
||||
std::vector<std::shared_ptr<restbed::Resource>> getResources() const override;
|
||||
std::string getName() const override { return "FeedReader"; }
|
||||
|
||||
private:
|
||||
RsFeedReader& mFeedReader;
|
||||
RsJsonApi& mJsonApi;
|
||||
};
|
||||
@ -67,6 +67,10 @@ settings::PluginsPage::PluginsPage(QWidget * parent, Qt::WindowFlags flags)
|
||||
short_status_string = tr("[loading problem]");
|
||||
break ;
|
||||
|
||||
case PLUGIN_STATUS_WRONG_API: status_string = tr("Incompatible plugin API version. Rebuild or update this plugin.");
|
||||
short_status_string = tr("[incompatible]");
|
||||
break ;
|
||||
|
||||
case PLUGIN_STATUS_MISSING_SVN: status_string = tr("No SVN number supplied. Please read plugin development manual.") ;
|
||||
short_status_string = tr("[loading problem]");
|
||||
break ;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user