diff --git a/src/.idea/codeStyles/Project.xml b/src/.idea/codeStyles/Project.xml index 213c5ea87..550d10440 100644 --- a/src/.idea/codeStyles/Project.xml +++ b/src/.idea/codeStyles/Project.xml @@ -23,7 +23,7 @@ - + diff --git a/src/httprpc.h b/src/httprpc.h index d7414d594..49a6c9058 100644 --- a/src/httprpc.h +++ b/src/httprpc.h @@ -30,6 +30,7 @@ void StopHTTPRPC(); * Precondition; HTTP and RPC has been started. */ void StartREST(const util::Ref& context); +void StartSTATIC(const util::Ref& context); /** Interrupt RPC REST subsystem. */ void InterruptREST(); @@ -37,5 +38,6 @@ void InterruptREST(); * Precondition; HTTP and RPC has been stopped. */ void StopREST(); +void StopSTATIC(); #endif diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 88d2dcb18..40fc14bd0 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -392,15 +392,17 @@ bool InitHTTPServer(const util::Ref& context) #endif // Additional pocketnet seocket - if (gArgs.GetBoolArg("-api", true)) + if (gArgs.GetBoolArg("-api", DEFAULT_API_ENABLE)) { g_webSocket = new HTTPWebSocket(eventBase, timeout, workQueuePublicDepth, workQueuePostDepth, true); RegisterPocketnetWebRPCCommands(g_webSocket->m_table_rpc, g_webSocket->m_table_post_rpc); + } - // Additional pocketnet static files socket - g_staticSocket = new HTTPSocket(eventBase, timeout, workQueueStaticDepth, true); + if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) g_restSocket = new HTTPSocket(eventBase, timeout, workQueueRestDepth, true); - } + + if (gArgs.GetBoolArg("-static", DEFAULT_STATIC_ENABLE)) + g_staticSocket = new HTTPSocket(eventBase, timeout, workQueueStaticDepth, true); if (!HTTPBindAddresses()) { diff --git a/src/httpserver.h b/src/httpserver.h index 445d75ea4..d65efd358 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -33,6 +33,10 @@ static const int DEFAULT_HTTP_STATIC_WORKQUEUE = 16; static const int DEFAULT_HTTP_REST_WORKQUEUE = 16; static const int DEFAULT_HTTP_SERVER_TIMEOUT = 30; +static const bool DEFAULT_API_ENABLE = true; +static const bool DEFAULT_REST_ENABLE = false; +static const bool DEFAULT_STATIC_ENABLE = false; + struct evhttp_request; class CService; diff --git a/src/init.cpp b/src/init.cpp index d31e025dd..c2a982459 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -100,8 +100,6 @@ static bool fFeeEstimatesInitialized = false; static const bool DEFAULT_PROXYRANDOMIZE = true; -static const bool DEFAULT_API_ENABLE = true; -static const bool DEFAULT_REST_ENABLE = false; static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; Statistic::RequestStatEngine gStatEngineInstance; @@ -229,6 +227,7 @@ void Shutdown(NodeContext& node) StopHTTPRPC(); StopREST(); + StopSTATIC(); StopRPC(); StopHTTPServer(); @@ -628,6 +627,11 @@ void SetupServerArgs(NodeContext& node) argsman.AddArg("-api", strprintf("Enable Public RPC api server (default: %u)", DEFAULT_API_ENABLE), ArgsManager::ALLOW_ANY, OptionsCategory::RPC); argsman.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC); + + argsman.AddArg("-static", strprintf("Accept public requests to static resources (default: %u)", DEFAULT_STATIC_ENABLE), ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + argsman.AddArg("-staticpath", "Path to static resources (default: GetDataDir()/wwwroot", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + + argsman.AddArg("-rpcallowip=", "Allow JSON-RPC connections from specified source. Valid for are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); argsman.AddArg("-rpcauth=", "Username and HMAC-SHA-256 hashed password for JSON-RPC connections. The field comes in the format: :$. A canonical python script is included in share/rpcauth. The client then connects normally using the rpcuser=/rpcpassword= pair of arguments. This option can be specified multiple times", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC); argsman.AddArg("-rpcbind=[:port]", "Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY | ArgsManager::SENSITIVE, OptionsCategory::RPC); @@ -661,6 +665,7 @@ void SetupServerArgs(NodeContext& node) argsman.AddArg("-sqltimeout", strprintf("Timeout for ReadOnly sql querys (default: %ds)", 10), ArgsManager::ALLOW_ANY, OptionsCategory::SQLITE); argsman.AddArg("-sqlsharedcache", strprintf("Experimental: enable shared cache for sqlite connections (default: disabled)"), ArgsManager::ALLOW_ANY, OptionsCategory::SQLITE); argsman.AddArg("-sqlcachesize", strprintf("Experimental: Cache size for SQLite connection in megabytes (default: %d mb)", 5), ArgsManager::ALLOW_ANY, OptionsCategory::SQLITE); + argsman.AddArg("-withoutweb", strprintf("Disable WEB part of database (default: %u)", false), ArgsManager::ALLOW_ANY, OptionsCategory::SQLITE); #if HAVE_DECL_DAEMON @@ -866,7 +871,7 @@ static void ThreadImport(ChainstateManager& chainman, const util::Ref& context, } // .. only web DB - if (fReindex == 5 && args.GetBoolArg("-api", true)) + if (fReindex == 5 && args.GetBoolArg("-api", DEFAULT_API_ENABLE)) { LogPrintf("Building a Web database: 0%%\n"); @@ -971,14 +976,25 @@ static bool AppInitServers(const util::Ref& context, NodeContext& node) const ArgsManager& args = *Assert(node.args); RPCServer::OnStarted(&OnRPCStarted); RPCServer::OnStopped(&OnRPCStopped); + if (!InitHTTPServer(context)) return false; + StartRPC(); + node.rpc_interruption_point = RpcInterruptionPoint; + if (!StartHTTPRPC(context)) return false; - if (args.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST(context); + + if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) + StartREST(context); + + if (gArgs.GetBoolArg("-static", DEFAULT_STATIC_ENABLE)) + StartSTATIC(context); + StartHTTPServer(); + return true; } @@ -1626,7 +1642,8 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA PocketWeb::PocketFrontendInst.Init(); - if (args.GetBoolArg("-api", true)) + // Always start WEB DB building thread + if (!args.GetBoolArg("-withoutweb", false)) PocketServices::WebPostProcessorInst.Start(threadGroup); // ********************************************************* Step 4b: Additional settings @@ -2297,7 +2314,8 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA // ********************************************************* Step 13: finished // Start WebSocket server - if (args.GetBoolArg("-api", true)) InitWS(); + if (args.GetBoolArg("-api", DEFAULT_API_ENABLE)) + InitWS(); gStatEngineInstance.Run(threadGroup, context); diff --git a/src/net.h b/src/net.h index 60cabe87e..f020d1128 100644 --- a/src/net.h +++ b/src/net.h @@ -54,8 +54,8 @@ static const int TIMEOUT_INTERVAL = 20 * 60; static const int FEELER_INTERVAL = 120; /** The maximum number of addresses from our addrman to return in response to a getaddr message. */ static constexpr size_t MAX_ADDR_TO_SEND = 1000; -/** Maximum length of incoming protocol messages (no message over 4 MB is currently acceptable). */ -static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000; +/** Maximum length of incoming protocol messages (no message over 16 MB is currently acceptable). */ +static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 16000000; /** Maximum length of the user agent string in `version` message */ static const unsigned int MAX_SUBVERSION_LENGTH = 256; /** Maximum number of automatic outgoing nodes over which we'll relay everything (blocks, tx, addrs, etc) */ diff --git a/src/pocketdb/consensus/Reputation.h b/src/pocketdb/consensus/Reputation.h index 0ede93c6e..31e6bb3df 100644 --- a/src/pocketdb/consensus/Reputation.h +++ b/src/pocketdb/consensus/Reputation.h @@ -282,8 +282,8 @@ namespace PocketConsensus { 1180000, 0, [](int height) { return make_shared(height); }}, { 1324655, 65000, [](int height) { return make_shared(height); }}, { 1324655, 75000, [](int height) { return make_shared(height); }}, - { 1680000, 761000, [](int height) { return make_shared(height); }}, - { 1680000, 772000, [](int height) { return make_shared(height); }}, + { 1700000, 761000, [](int height) { return make_shared(height); }}, + { 1700000, 772000, [](int height) { return make_shared(height); }}, }; public: shared_ptr Instance(int height) diff --git a/src/pocketdb/consensus/moderation/Flag.hpp b/src/pocketdb/consensus/moderation/Flag.hpp index 43bbb12be..a36a1f5c0 100644 --- a/src/pocketdb/consensus/moderation/Flag.hpp +++ b/src/pocketdb/consensus/moderation/Flag.hpp @@ -137,7 +137,7 @@ namespace PocketConsensus private: const vector> m_rules = { { 0, -1, [](int height) { return make_shared(height); }}, - { 1680000, 761000, [](int height) { return make_shared(height); }}, + { 1700000, 761000, [](int height) { return make_shared(height); }}, }; public: shared_ptr Instance(int height) diff --git a/src/pocketdb/repositories/web/SearchRepository.cpp b/src/pocketdb/repositories/web/SearchRepository.cpp index 4bf789f07..c447ce988 100644 --- a/src/pocketdb/repositories/web/SearchRepository.cpp +++ b/src/pocketdb/repositories/web/SearchRepository.cpp @@ -231,152 +231,59 @@ namespace PocketDb return result; } - UniValue SearchRepository::GetRecomendedAccountsBySubscriptions(const string& address, int cntOut) + //TODO (o1q): remove it + vector SearchRepository::GetRecommendedAccountByAddressSubscriptionsOld(const string& address, string& addressExclude, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth, int cntSubscriptions) { auto func = __func__; - UniValue result(UniValue::VARR); + vector ids; if (address.empty()) - return result; - - string sql = R"sql( - select - recommendation.address, - p.String2 as name, - p.String3 as avatar - - , ifnull(( - select r.Value - from Ratings r indexed by Ratings_Type_Id_Last_Height - where r.Type=0 and r.Id=u.Id and r.Last=1) - ,0) as Reputation - - , ( - select count(*) - from Transactions subs indexed by Transactions_Type_Last_String2_Height - where subs.Type in (302,303) and subs.Height is not null and subs.Last = 1 and subs.String2 = u.String1 - ) as SubscribersCount - from ( - select - t.String2 address - from Transactions t indexed by Transactions_Type_Last_String1_String2_Height - where t.Last = 1 - and t.Type in (302,303) - and t.Height is not null - and t.String2 != ? - and t.String1 in (select s.String1 - from Transactions s indexed by Transactions_Type_Last_String2_Height - where s.Type in (302,303) - and s.Last = 1 - and s.Height is not null - and s.String2 = ?) - group by t.String2 - order by count(*) desc - limit ? - )recommendation - cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id on u.String1 = recommendation.address - and u.Type in (100,101,102) - and u.Last=1 - and u.Height is not null - cross join Payload p on p.TxHash = u.Hash - - )sql"; - - TryTransactionStep(__func__, [&]() - { - auto stmt = SetupSqlStatement(sql); - - int i = 1; - TryBindStatementText(stmt, i++, address); - TryBindStatementText(stmt, i++, address); - TryBindStatementInt(stmt, i++, cntOut); - - while (sqlite3_step(*stmt) == SQLITE_ROW) - { - UniValue record(UniValue::VOBJ); - if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) record.pushKV("address", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 1); ok) record.pushKV("name", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 2); ok) record.pushKV("avatar", value); - if (auto[ok, value] = TryGetColumnInt(*stmt, 3); ok) record.pushKV("reputation", value / 10.0); - if (auto[ok, value] = TryGetColumnInt(*stmt, 4); ok) record.pushKV("subscribers_count", value); - result.push_back(record); - } - - FinalizeSqlStatement(*stmt); - }); + return ids; - return result; - } + string contentTypesFilter = join(vector(contentTypes.size(), "?"), ","); - UniValue SearchRepository::GetRecomendedAccountsByScoresOnSimilarAccounts(const string& address, const vector& contentTypes, int nHeight, int depth, int cntOut) - { - auto func = __func__; - UniValue result(UniValue::VARR); + string excludeAddressFilter = "?"; + if (!addressExclude.empty()) + excludeAddressFilter += ", ?"; - if (address.empty()) - return result; + string langFilter = ""; + if (!lang.empty()) + langFilter = "cross join Payload lang on lang.TxHash = u.Hash and lang.String1 = ?"; - string contentTypesFilter = join(vector(contentTypes.size(), "?"), ","); + int minReputation = 300; string sql = R"sql( - select recommendation.address, - p.String2 as name, - p.String3 as avatar - - , ifnull(( - select r.Value - from Ratings r indexed by Ratings_Type_Id_Last_Height - where r.Type=0 and r.Id=u.Id and r.Last=1) - ,0) as Reputation - - , ( - select count(*) - from Transactions subs indexed by Transactions_Type_Last_String2_Height - where subs.Type in (302,303) and subs.Height is not null and subs.Last = 1 and subs.String2 = u.String1 - ) as SubscribersCount - from ( - select - tOtherContents.String1 as address - from Transactions tOtherContents - indexed by Transactions_Type_Last_String1_String2_Height - where tOtherContents.String2 in ( - select tOtherLikes.String2 as OtherLikedContent - from Transactions tOtherlikes - where tOtherLikes.String1 in ( - select tLikes.String1 as Liker - from Transactions tLikes - where tLikes.String2 in ( - select tContents.String2 as BloggerContent - from Transactions tContents - where tContents.Type in ( )sql" + contentTypesFilter + R"sql( ) - and tContents.Last = 1 - and tContents.String1 = ? - and tContents.Height >= ? - ) - and tLikes.Type in (300) - and tLikes.Last in (1, 0) - and tLikes.Int1 > 3 - and tLikes.Height >= ? - ) - and tOtherLikes.Type in (300) - and tOtherLikes.Last in (1, 0) - and tOtherLikes.Int1 > 3 - and tOtherLikes.Height >= ? + select Contents.String1 + from Transactions Rates indexed by Transactions_Type_Last_String1_Height_Id + cross join Transactions Contents indexed by Transactions_Type_Last_String2_Height + on Contents.String2 = Rates.String2 + and Contents.Last = 1 + and Contents.Height > 0 + and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) + and Contents.String1 not in ( ? ) + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + on u.String1 = Contents.String1 + and u.Type in (100) + and u.Last = 1 + and u.Height > 0 + )sql" + langFilter + R"sql( + where Rates.Type in (300) + and Rates.Int1 = 5 + and Rates.Height > ? + and Rates.Last in (0, 1) + and Rates.String1 in ( + select subscribes.String2 + from Transactions subscribes indexed by Transactions_Type_Last_String1_Height_Id + where subscribes.Type in (302, 303) + and subscribes.Last = 1 + and subscribes.Height > 0 + and subscribes.String1 = ? + limit ? ) - and tOtherContents.Type in ( )sql" + contentTypesFilter + R"sql( ) - and tOtherContents.String1 != ? - and tOtherContents.Last = 1 - and tOtherContents.Height >= ? - group by tOtherContents.String1 - order by count(*) desc - limit ? - )recommendation - cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id - on u.String1 = recommendation.address - and u.Type in (100,101,102) - and u.Last=1 - and u.Height is not null - cross join Payload p on p.TxHash = u.Hash + group by Contents.String1 + having count(*) > 1 + order by count (*) desc + limit ? )sql"; TryTransactionStep(__func__, [&]() @@ -386,253 +293,97 @@ namespace PocketDb int i = 1; for (const auto& contenttype: contentTypes) TryBindStatementInt(stmt, i++, contenttype); - TryBindStatementText(stmt, i++, address); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, nHeight - depth); - for (const auto& contenttype: contentTypes) - TryBindStatementInt(stmt, i++, contenttype); - TryBindStatementText(stmt, i++, address); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, cntOut); - - while (sqlite3_step(*stmt) == SQLITE_ROW) - { - UniValue record(UniValue::VOBJ); - if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) record.pushKV("address", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 1); ok) record.pushKV("name", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 2); ok) record.pushKV("avatar", value); - if (auto[ok, value] = TryGetColumnInt(*stmt, 3); ok) record.pushKV("reputation", value / 10.0); - if (auto[ok, value] = TryGetColumnInt(*stmt, 4); ok) record.pushKV("subscribers_count", value); - result.push_back(record); - } - FinalizeSqlStatement(*stmt); - }); - - return result; - } - - UniValue SearchRepository::GetRecomendedAccountsByScoresFromAddress(const string& address, const vector& contentTypes, int nHeight, int depth, int cntOut) - { - auto func = __func__; - UniValue result(UniValue::VARR); + TryBindStatementText(stmt, i++, address); + if (!addressExclude.empty()) + TryBindStatementText(stmt, i++, addressExclude); - if (address.empty()) - return result; + if (!lang.empty()) + TryBindStatementText(stmt, i++, lang); - string contentTypesFilter = join(vector(contentTypes.size(), "?"), ","); + TryBindStatementInt(stmt, i++, nHeight-depth); - string sql = R"sql( - select recommendation.address, - p.String2 as name, - p.String3 as avatar - - , ifnull(( - select r.Value - from Ratings r indexed by Ratings_Type_Id_Last_Height - where r.Type=0 and r.Id=u.Id and r.Last=1) - ,0) as Reputation - - , ( - select count(*) - from Transactions subs indexed by Transactions_Type_Last_String2_Height - where subs.Type in (302,303) and subs.Height is not null and subs.Last = 1 and subs.String2 = u.String1 - ) as SubscribersCount - from ( - select - tOtherContents.String1 as address - from Transactions tOtherContents - indexed by Transactions_Type_Last_String2_Height - where tOtherContents.String2 in ( - select tOtherLikes.String2 as OtherLikedContent - from Transactions tOtherlikes - where tOtherLikes.String1 in ( - select tLikes.String1 as Liker - from Transactions tLikes - where tLikes.String2 in ( - select tAddressLikes.String2 as ContentsLikedByAddress - from Transactions tAddressLikes - where tAddressLikes.String1 = ? - and tAddressLikes.Type in (300) - and tAddressLikes.Last in (1, 0) - and tAddressLikes.Int1 > 3 - and tAddressLikes.Height >= ? - ) - and tLikes.Type in (300) - and tLikes.Last in (1, 0) - and tLikes.Int1 > 3 - and tLikes.Height >= ? - ) - and tOtherLikes.Type in (300) - and tOtherLikes.Last in (1, 0) - and tOtherLikes.Int1 > 3 - and tOtherLikes.Height >= ? - ) - and tOtherContents.Type in ( )sql" + contentTypesFilter + R"sql( ) - and tOtherContents.String1 != ? - and tOtherContents.Last = 1 - and tOtherContents.Height >= ? - group by tOtherContents.String1 - order by count(*) desc - limit ? - )recommendation - cross join Transactions u on u.String1 = recommendation.address - and u.Type in (100,101,102) - and u.Last=1 - and u.Height is not null - cross join Payload p on p.TxHash = u.Hash - )sql"; + TryBindStatementText(stmt, i++, address); - TryTransactionStep(__func__, [&]() - { - auto stmt = SetupSqlStatement(sql); + TryBindStatementInt(stmt, i++, cntSubscriptions); - int i = 1; - TryBindStatementText(stmt, i++, address); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, nHeight - depth); - for (const auto& contenttype: contentTypes) - TryBindStatementInt(stmt, i++, contenttype); - TryBindStatementText(stmt, i++, address); - TryBindStatementInt(stmt, i++, nHeight - depth); TryBindStatementInt(stmt, i++, cntOut); while (sqlite3_step(*stmt) == SQLITE_ROW) { - UniValue record(UniValue::VOBJ); - if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) record.pushKV("address", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 1); ok) record.pushKV("name", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 2); ok) record.pushKV("avatar", value); - if (auto[ok, value] = TryGetColumnInt(*stmt, 3); ok) record.pushKV("reputation", value / 10.0); - if (auto[ok, value] = TryGetColumnInt(*stmt, 4); ok) record.pushKV("subscribers_count", value); - result.push_back(record); + if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) + ids.push_back(value); } FinalizeSqlStatement(*stmt); }); - return result; + return ids; } - UniValue SearchRepository::GetRecomendedAccountsByTags(const vector& tags, int nHeight, int depth, int cntOut) + //TODO (o1q): remove it + vector SearchRepository::GetRecommendedContentByAddressSubscriptionsOld(const string& contentAddress, string& address, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth, int cntSubscriptions) { auto func = __func__; - UniValue result(UniValue::VARR); - - if (tags.empty()) - return result; - - string tagsFilter = join(vector(tags.size(), "?"), ","); - - string sql = R"sql( - select - recommendation.address, - p.String2 as name, - p.String3 as avatar - - , ifnull(( - select r.Value - from Ratings r indexed by Ratings_Type_Id_Last_Height - where r.Type=0 and r.Id=u.Id and r.Last=1) - ,0) as Reputation - - , ( - select count(*) - from Transactions subs indexed by Transactions_Type_Last_String2_Height - where subs.Type in (302,303) and subs.Height is not null and subs.Last = 1 and subs.String2 = u.String1 - ) as SubscribersCount - from ( - select authors.string1 address - from ( - select c.String1 - from Transactions sc indexed by Transactions_Type_Last_Height_Id - cross join Transactions c indexed by Transactions_Type_Last_String2_Height - on c.String2 = sc.String2 and c.Type in (200, 201, 202) and c.Height > 0 and c.Last = 1 - and c.id in (select tm.ContentId - from web.Tags tag indexed by Tags_Lang_Value_Id - join web.TagsMap tm indexed by TagsMap_TagId_ContentId - on tag.Id = tm.TagId - where tag.Value in ( )sql" + join(vector(tags.size(), "?"), ",") + R"sql( )) - where sc.Type in (300) - and sc.Last in (0, 1) - and sc.Height > ? - and sc.Int1 = 5 - ) authors - group by authors.string1 - order by count(*) desc - limit ? - )recommendation - cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id on u.String1 = recommendation.address - and u.Type in (100,101,102) - and u.Last=1 - and u.Height is not null - cross join Payload p on p.TxHash = u.Hash - - )sql"; - - TryTransactionStep(__func__, [&]() - { - auto stmt = SetupSqlStatement(sql); - - int i = 1; - for (const auto& tag: tags) - TryBindStatementText(stmt, i++, tag); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, cntOut); - - while (sqlite3_step(*stmt) == SQLITE_ROW) - { - UniValue record(UniValue::VOBJ); - if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) record.pushKV("address", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 1); ok) record.pushKV("name", value); - if (auto[ok, value] = TryGetColumnString(*stmt, 2); ok) record.pushKV("avatar", value); - if (auto[ok, value] = TryGetColumnInt(*stmt, 3); ok) record.pushKV("reputation", value / 10.0); - if (auto[ok, value] = TryGetColumnInt(*stmt, 4); ok) record.pushKV("subscribers_count", value); - result.push_back(record); - } + vector ids; - FinalizeSqlStatement(*stmt); - }); + if (contentAddress.empty()) + return ids; - return result; - } + string contentTypesFilter = join(vector(contentTypes.size(), "?"), ","); - UniValue SearchRepository::GetRecomendedContentsByScoresOnSimilarContents(const string& contentid, const vector& contentTypes, int depth, int cntOut) - { - auto func = __func__; - UniValue result(UniValue::VARR); + string excludeAddressFilter = "?"; + if (!address.empty()) + excludeAddressFilter += ", ?"; - if (contentid.empty()) - return result; + string langFilter = ""; + if (!lang.empty()) + langFilter = "cross join Payload lang on lang.TxHash = Contents.Hash and lang.String1 = ?"; - string contentTypesFilter = join(vector(contentTypes.size(), "?"), ","); + int minReputation = 300; string sql = R"sql( - select OtherRaters.String2 OtherScoredContent, count(*) cnt - from Transactions OtherRaters indexed by Transactions_Type_Last_String1_Height_Id + select recomendations.Id + from ( + select Contents.String1, + Contents.Id, + Rates.String2, + count(*) count + from Transactions Rates indexed by Transactions_Type_Last_String1_Height_Id cross join Transactions Contents indexed by Transactions_Type_Last_String2_Height - on OtherRaters.String2 = Contents.String2 and Contents.Last = 1 and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) and Contents.Height > 0 - where OtherRaters.Type in (300) - and OtherRaters.Int1 > 3 - and OtherRaters.Last in (1, 0) - and OtherRaters.Height >= (select Height - from Transactions indexed by Transactions_Type_Last_String2_Height - where Type in ( )sql" + contentTypesFilter + R"sql( ) - and String2 = ? - and Last = 1) - ? - and OtherRaters.String1 in ( - select String1 as Rater - from Transactions Raters indexed by Transactions_Type_Last_String2_Height - where Raters.Type in (300) - and Raters.Int1 > 3 - and Raters.String2 = ? - and Raters.Last in (1, 0) - ) - and OtherRaters.String2 != ? - group by OtherRaters.String2 + on Contents.String2 = Rates.String2 + and Contents.Last = 1 + and Contents.Height > 0 + and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) + and Contents.String1 not in ( )sql" + excludeAddressFilter + R"sql( ) + )sql" + langFilter + R"sql( + where Rates.Type in (300) + and Rates.Int1 = 5 + and Rates.Height > ? + and Rates.Last in (0, 1) + and Rates.String1 in ( + select subscribers.String2 + from Transactions subscribers indexed by Transactions_Type_Last_String1_Height_Id + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + on u.Type in (100) + and u.Last = 1 and u.Height > 0 + and u.String1 = subscribers.String1 + cross join Ratings r indexed by Ratings_Type_Id_Last_Value + on r.Id = u.Id + and r.Type = 0 + and r.Last = 1 + and r.Value > ? + where subscribers.Type in (302, 303) + and subscribers.Last = 1 + and subscribers.Height > 0 + and subscribers.String1 = ? + limit ? + ) + group by Rates.String2 + having count(*) > 1 order by count(*) desc + ) recomendations + group by recomendations.String1 limit ? )sql"; @@ -643,95 +394,36 @@ namespace PocketDb int i = 1; for (const auto& contenttype: contentTypes) TryBindStatementInt(stmt, i++, contenttype); - for (const auto& contenttype: contentTypes) - TryBindStatementInt(stmt, i++, contenttype); - TryBindStatementText(stmt, i++, contentid); - TryBindStatementInt(stmt, i++, depth); - TryBindStatementText(stmt, i++, contentid); - TryBindStatementText(stmt, i++, contentid); - TryBindStatementInt(stmt, i++, cntOut); - - while (sqlite3_step(*stmt) == SQLITE_ROW) - { - UniValue record(UniValue::VOBJ); - if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) record.pushKV("contentid", value); - result.push_back(record); - } - - FinalizeSqlStatement(*stmt); - }); - return result; - } - - UniValue SearchRepository::GetRecomendedContentsByScoresFromAddress(const string& address, const vector& contentTypes, int nHeight, int depth, int cntOut) - { - auto func = __func__; - UniValue result(UniValue::VARR); + TryBindStatementText(stmt, i++, contentAddress); + if (!address.empty()) + TryBindStatementText(stmt, i++, address); - if (address.empty()) - return result; + if (!lang.empty()) + TryBindStatementText(stmt, i++, lang); - string contentTypesFilter = join(vector(contentTypes.size(), "?"), ","); + TryBindStatementInt(stmt, i++, nHeight-depth); - string sql = R"sql( - select OtherRaters.String2 as OtherScoredContent, count(*) cnt - from Transactions OtherRaters indexed by Transactions_Type_Last_String1_Height_Id - cross join Transactions Contents indexed by Transactions_Type_Last_String2_Height - on OtherRaters.String2 = Contents.String2 and Contents.Last = 1 and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) and Contents.Height > 0 - where OtherRaters.String1 in ( - select Scores.String1 as Rater - from Transactions Scores indexed by Transactions_Type_Last_String2_Height - where Scores.String2 in ( - select addressScores.String2 as ContentsScoredByAddress - from Transactions addressScores indexed by Transactions_Type_Last_String1_Height_Id - where addressScores.String1 = ? - and addressScores.Type in (300) - and addressScores.Last in (1, 0) - and addressScores.Int1 > 3 - and addressScores.Height >= ? - ) - and Scores.Type in (300) - and Scores.Last in (1, 0) - and Scores.Int1 > 3 - and Scores.Height >= ? - ) - and OtherRaters.Type in (300) - and OtherRaters.Last in (1, 0) - and OtherRaters.Int1 > 3 - and OtherRaters.Height >= ? - group by OtherRaters.String2 - order by count(*) desc - limit ? - )sql"; + TryBindStatementInt(stmt, i++, minReputation); + TryBindStatementText(stmt, i++, contentAddress); - TryTransactionStep(__func__, [&]() - { - auto stmt = SetupSqlStatement(sql); + TryBindStatementInt(stmt, i++, cntSubscriptions); - int i = 1; - for (const auto& contenttype: contentTypes) - TryBindStatementInt(stmt, i++, contenttype); - TryBindStatementText(stmt, i++, address); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, nHeight - depth); - TryBindStatementInt(stmt, i++, nHeight - depth); TryBindStatementInt(stmt, i++, cntOut); while (sqlite3_step(*stmt) == SQLITE_ROW) { - UniValue record(UniValue::VOBJ); - if (auto[ok, value] = TryGetColumnString(*stmt, 0); ok) record.pushKV("contentid", value); - result.push_back(record); + if (auto[ok, value] = TryGetColumnInt64(*stmt, 0); ok) + ids.push_back(value); } FinalizeSqlStatement(*stmt); }); - return result; + return ids; } - vector SearchRepository::GetRecommendedAccountByAddressSubscriptions(const string& address, string& addressExclude, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth, int cntSubscriptions) + vector SearchRepository::GetRecommendedAccountByAddressSubscriptions(const string& address, string& addressExclude, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth) { auto func = __func__; vector ids; @@ -749,7 +441,10 @@ namespace PocketDb if (!lang.empty()) langFilter = "cross join Payload lang on lang.TxHash = u.Hash and lang.String1 = ?"; - int minReputation = 30; + int minReputation = 300; + int limitSubscriptions = 30; + int limitSubscriptionsTotal = 30; + int cntRates = 1; string sql = R"sql( select Contents.String1 @@ -759,7 +454,7 @@ namespace PocketDb and Contents.Last = 1 and Contents.Height > 0 and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) - and Contents.String1 not in ( ? ) + and Contents.String1 not in ( )sql" + excludeAddressFilter + R"sql( ) cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id on u.String1 = Contents.String1 and u.Type in (100) @@ -770,17 +465,56 @@ namespace PocketDb and Rates.Int1 = 5 and Rates.Height > ? and Rates.Last in (0, 1) - and Rates.String1 in ( - select subscribes.String2 - from Transactions subscribes indexed by Transactions_Type_Last_String1_Height_Id - where subscribes.Type in (302, 303) - and subscribes.Last = 1 - and subscribes.Height > 0 - and subscribes.String1 = ? - limit ? - ) + and Rates.String1 in + ( + select address + from ( + select rnk, address + from ( + select 1 as rnk, subscribes.String2 as address + from Transactions subscribes indexed by Transactions_String1_Last_Height + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + on u.Type in (100) + and u.Last = 1 and u.Height > 0 + and u.String1 = subscribes.String2 + cross join Ratings r indexed by Ratings_Type_Id_Last_Value + on r.Id = u.Id + and r.Type = 0 + and r.Last = 1 + and r.Value > ? + where subscribes.Type in (302, 303) + and subscribes.Last = 1 + and subscribes.String1 = ? + and subscribes.Height is not null + order by subscribes.Height desc + limit ? + ) + union + select rnk, address + from ( + select 2 as rnk, subscribers.String1 as address + from Transactions subscribers indexed by Transactions_Type_Last_String2_Height + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + on u.Type in (100) + and u.Last = 1 and u.Height > 0 + and u.String1 = subscribers.String1 + cross join Ratings r indexed by Ratings_Type_Id_Last_Value + on r.Id = u.Id + and r.Type = 0 + and r.Last = 1 + and r.Value > ? + where subscribers.Type in (302, 303) + and subscribers.Last = 1 + and subscribers.String2 = ? + and subscribers.Height is not null + order by random() + limit ? + )) + order by rnk + limit ? + ) group by Contents.String1 - having count(*) > 1 + having count(*) > ? order by count (*) desc limit ? )sql"; @@ -802,9 +536,17 @@ namespace PocketDb TryBindStatementInt(stmt, i++, nHeight-depth); + TryBindStatementInt(stmt, i++, minReputation); TryBindStatementText(stmt, i++, address); + TryBindStatementInt(stmt, i++, limitSubscriptions); - TryBindStatementInt(stmt, i++, cntSubscriptions); + TryBindStatementInt(stmt, i++, minReputation); + TryBindStatementText(stmt, i++, address); + TryBindStatementInt(stmt, i++, limitSubscriptions); + + TryBindStatementInt(stmt, i++, limitSubscriptionsTotal); + + TryBindStatementInt(stmt, i++, cntRates); TryBindStatementInt(stmt, i++, cntOut); @@ -820,7 +562,7 @@ namespace PocketDb return ids; } - vector SearchRepository::GetRecommendedContentByAddressSubscriptions(const string& contentAddress, string& address, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth, int cntSubscriptions) + vector SearchRepository::GetRecommendedContentByAddressSubscriptions(const string& contentAddress, string& address, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth) { auto func = __func__; vector ids; @@ -836,52 +578,87 @@ namespace PocketDb string langFilter = ""; if (!lang.empty()) - langFilter = "cross join Payload lang on lang.TxHash = Contents.Hash and lang.String1 = ?"; + langFilter = "cross join Payload lang indexed by Payload_String1_TxHash on lang.TxHash = Contents.Hash and lang.String1 = ?"; - int minReputation = 30; + int minReputation = 300; + int limitSubscriptions = 50; + int limitSubscriptionsTotal = 30; + int cntRates = 1; string sql = R"sql( select recomendations.Id from ( - select Contents.String1, - Contents.Id, - Rates.String2, - count(*) count - from Transactions Rates indexed by Transactions_Type_Last_String1_Height_Id - cross join Transactions Contents indexed by Transactions_Type_Last_String2_Height - on Contents.String2 = Rates.String2 - and Contents.Last = 1 - and Contents.Height > 0 - and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) - and Contents.String1 not in ( )sql" + excludeAddressFilter + R"sql( ) - )sql" + langFilter + R"sql( - where Rates.Type in (300) - and Rates.Int1 = 5 - and Rates.Height > ?--0 - and Rates.Last in (0, 1) - and Rates.String1 in ( - select subscribers.String2 - from Transactions subscribers indexed by Transactions_Type_Last_String1_Height_Id - cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id - on u.Type in (100) - and u.Last = 1 and u.Height > 0 - and u.String1 = subscribers.String1 - cross join Ratings r indexed by Ratings_Type_Id_Last_Value - on r.Id = u.Id - and r.Type = 0 - and r.Last = 1 - and r.Value > ? - where subscribers.Type in (302, 303) - and subscribers.Last = 1 - and subscribers.Height > 0 - and subscribers.String1 = ? - limit ? - ) - group by Rates.String2 - having count(*) > 1 - order by count(*) desc - ) recomendations + select Contents.String1, + Contents.Id, + --Rates.String2, + count(*) count + from Transactions Rates indexed by Transactions_Type_Last_String1_Height_Id + cross join Transactions Contents indexed by Transactions_Type_Last_String2_Height + on Contents.String2 = Rates.String2 + and Contents.Last = 1 + and Contents.Height > 0 + and Contents.Type in ( )sql" + contentTypesFilter + R"sql( ) + and Contents.String1 not in ( )sql" + excludeAddressFilter + R"sql( ) + )sql" + langFilter + R"sql( + where Rates.Type in (300) + and Rates.Int1 = 5 + and Rates.Height > ? + and Rates.Last in (0, 1) + and Rates.String1 in + ( + select address + from ( + select rnk, address + from ( + select 1 as rnk, subscribes.String2 as address + from Transactions subscribes indexed by Transactions_String1_Last_Height + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + on u.Type in (100) + and u.Last = 1 and u.Height > 0 + and u.String1 = subscribes.String2 + cross join Ratings r indexed by Ratings_Type_Id_Last_Value + on r.Id = u.Id + and r.Type = 0 + and r.Last = 1 + and r.Value > ? + where subscribes.Type in (302, 303) + and subscribes.Last = 1 + and subscribes.String1 = ? + and subscribes.Height is not null + order by subscribes.Height desc + limit ? + ) + union + select rnk, address + from ( + select 2 as rnk, subscribers.String1 as address + from Transactions subscribers indexed by Transactions_Type_Last_String2_Height + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + on u.Type in (100) + and u.Last = 1 and u.Height > 0 + and u.String1 = subscribers.String1 + cross join Ratings r indexed by Ratings_Type_Id_Last_Value + on r.Id = u.Id + and r.Type = 0 + and r.Last = 1 + and r.Value > ? + where subscribers.Type in (302, 303) + and subscribers.Last = 1 + and subscribers.String2 = ? + and subscribers.Height is not null + order by random() + limit ? + )) + order by rnk + limit ? + ) + --group by Rates.String2 + group by Contents.Id + having count(*) > ? + order by count(*) desc + ) recomendations group by recomendations.String1 + order by recomendations.count desc limit ? )sql"; @@ -904,8 +681,15 @@ namespace PocketDb TryBindStatementInt(stmt, i++, minReputation); TryBindStatementText(stmt, i++, contentAddress); + TryBindStatementInt(stmt, i++, limitSubscriptions); - TryBindStatementInt(stmt, i++, cntSubscriptions); + TryBindStatementInt(stmt, i++, minReputation); + TryBindStatementText(stmt, i++, contentAddress); + TryBindStatementInt(stmt, i++, limitSubscriptions); + + TryBindStatementInt(stmt, i++, limitSubscriptionsTotal); + + TryBindStatementInt(stmt, i++, cntRates); TryBindStatementInt(stmt, i++, cntOut); diff --git a/src/pocketdb/repositories/web/SearchRepository.h b/src/pocketdb/repositories/web/SearchRepository.h index 2ee3f07f7..18ccf9083 100644 --- a/src/pocketdb/repositories/web/SearchRepository.h +++ b/src/pocketdb/repositories/web/SearchRepository.h @@ -34,15 +34,12 @@ namespace PocketDb vector SearchUsersOld(const SearchRequest& request); vector SearchUsers(const string& keyword); - UniValue GetRecomendedAccountsBySubscriptions(const string& address, int cntOut = 10); - UniValue GetRecomendedAccountsByScoresOnSimilarAccounts(const string& address, const vector& contentTypes, int nHeight, int depth = 1000, int cntOut = 10); - UniValue GetRecomendedAccountsByScoresFromAddress(const string& address, const vector& contentTypes, int nHeight, int depth = 1000, int cntOut = 10); - UniValue GetRecomendedAccountsByTags(const vector& tags, int nHeight, int depth = 1000, int cntOut = 10); - UniValue GetRecomendedContentsByScoresOnSimilarContents(const string& contentid, const vector& contentTypes, int depth = 1000, int cntOut = 10); - UniValue GetRecomendedContentsByScoresFromAddress(const string& address, const vector& contentTypes, int nHeight, int depth = 1000, int cntOut = 10); - - vector GetRecommendedAccountByAddressSubscriptions(const string& address, string& addressExclude, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth = 129600 /* about 3 month */, int cntSubscriptions = 100); - vector GetRecommendedContentByAddressSubscriptions(const string& contentAddress, string& address, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth = 129600 /* about 3 month */, int cntSubscriptions = 100); + //TODO (o1q): remove it + vector GetRecommendedAccountByAddressSubscriptionsOld(const string& address, string& addressExclude, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth = 129600 /* about 3 month */, int cntSubscriptions = 100); + vector GetRecommendedContentByAddressSubscriptionsOld(const string& contentAddress, string& address, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth = 129600 /* about 3 month */, int cntSubscriptions = 100); + + vector GetRecommendedAccountByAddressSubscriptions(const string& address, string& addressExclude, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth = 129600 /* about 3 month */); + vector GetRecommendedContentByAddressSubscriptions(const string& contentAddress, string& address, const vector& contentTypes, const string& lang, int cntOut, int nHeight, int depth = 129600 /* about 3 month */); vector GetRandomContentByAddress(const string& contentAddress, const vector& contentTypes, const string& lang, int cntOut); }; diff --git a/src/pocketdb/repositories/web/WebRpcRepository.cpp b/src/pocketdb/repositories/web/WebRpcRepository.cpp index a90b2b2f4..0c090ad8c 100644 --- a/src/pocketdb/repositories/web/WebRpcRepository.cpp +++ b/src/pocketdb/repositories/web/WebRpcRepository.cpp @@ -2921,19 +2921,19 @@ namespace PocketDb string langFilter; if (!lang.empty()) - langFilter += " join Payload p indexed by Payload_String1_TxHash on p.TxHash = t.Hash and p.String1 = ? "; + langFilter += " cross join Payload p indexed by Payload_String1_TxHash on p.TxHash = t.Hash and p.String1 = ? "; string sql = R"sql( select t.Id - from Transactions t indexed by Transactions_Last_Id_Height + from Transactions t indexed by Transactions_Type_Last_Height_Id - join Ratings cr indexed by Ratings_Type_Id_Last_Value + cross join Ratings cr indexed by Ratings_Type_Id_Last_Value on cr.Type = 2 and cr.Last = 1 and cr.Id = t.Id and cr.Value > 0 )sql" + langFilter + R"sql( - join Transactions u indexed by Transactions_Type_Last_String1_Height_Id + cross join Transactions u indexed by Transactions_Type_Last_String1_Height_Id on u.Type in (100) and u.Last = 1 and u.Height > 0 and u.String1 = t.String1 left join Ratings ur indexed by Ratings_Type_Id_Last_Height @@ -2941,7 +2941,7 @@ namespace PocketDb where t.Type in )sql" + contentTypesWhere + R"sql( and t.Last = 1 - and t.String3 is null + --and t.String3 is null and t.Height > ? and t.Height <= ? @@ -2979,7 +2979,7 @@ namespace PocketDb ) )sql"; } - sql += " order by t.Id desc "; + sql += " order by cr.Value desc "; sql += " limit ? "; // --------------------------------------------- diff --git a/src/pocketdb/web/PocketContentRpc.cpp b/src/pocketdb/web/PocketContentRpc.cpp index 7efcae99b..91400c1c2 100644 --- a/src/pocketdb/web/PocketContentRpc.cpp +++ b/src/pocketdb/web/PocketContentRpc.cpp @@ -100,17 +100,19 @@ namespace PocketWeb::PocketWebRpc } } - // feed's address if (request.params.size() > 10) { - RPCTypeCheckArgument(request.params[10], UniValue::VSTR); - address_feed = request.params[10].get_str(); - if (!address_feed.empty()) + // feed's address + if (request.params[10].isStr()) { - CTxDestination dest = DecodeDestination(address_feed); + address_feed = request.params[10].get_str(); + if (!address_feed.empty()) + { + CTxDestination dest = DecodeDestination(address_feed); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Pocketcoin address: ") + address_feed); + if (!IsValidDestination(dest)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Pocketcoin address: ") + address_feed); + } } } } diff --git a/src/pocketdb/web/PocketFrontend.cpp b/src/pocketdb/web/PocketFrontend.cpp index 81fd9ae5e..861108c3f 100644 --- a/src/pocketdb/web/PocketFrontend.cpp +++ b/src/pocketdb/web/PocketFrontend.cpp @@ -5,6 +5,7 @@ // https://www.apache.org/licenses/LICENSE-2.0 #include "pocketdb/web/PocketFrontend.h" +#include "fs.h" namespace PocketWeb { @@ -74,7 +75,20 @@ namespace PocketWeb void PocketFrontend::Init() { - _rootPath = GetDataDir() / "static_files"; + string _argPath = gArgs.GetArg("-staticpath", "wwwroot"); + _rootPath = (_argPath == "wwwroot") ? GetDataDir() / "wwwroot" : _argPath; + + // Create directory structure + try + { + if (!_rootPath.empty()) + fs::create_directories(_rootPath); + } + catch (const fs::filesystem_error&) + { + if (!fs::exists(_rootPath) || !fs::is_directory(_rootPath)) + throw; + } auto testContent = shared_ptr(new StaticFile{ "/404.html", @@ -156,12 +170,7 @@ namespace PocketWeb if (!readOk) { if (!stopRecurse) - { - if (_path.find("explorer") == 0) - return GetFile("/explorer/index.html", true); - return GetFile("/index.html", true); - } return NotFound(); } diff --git a/src/pocketdb/web/PocketRpc.cpp b/src/pocketdb/web/PocketRpc.cpp index e185ddda0..308f4d703 100644 --- a/src/pocketdb/web/PocketRpc.cpp +++ b/src/pocketdb/web/PocketRpc.cpp @@ -45,15 +45,6 @@ static const CRPCCommand commands[] = {"search", "searchusers", &SearchUsers, {"keyword", "fieldtypes", "orderbyrank"}}, // Recomendations - // TODO (o1q): Remove below methods when the client gui switches to new methods - {"search", "getrecomendedaccountsbysubscriptions", &GetRecomendedAccountsBySubscriptions, {"address", "count"}}, - {"search", "getrecomendedaccountsbyscoresonsimilaraccounts", &GetRecomendedAccountsByScoresOnSimilarAccounts, {"address", "contenttypes", "height", "depth", "count"}}, - {"search", "getrecomendedaccountsbyscoresfromaddress", &GetRecomendedAccountsByScoresFromAddress, {"address", "contenttypes", "height", "depth", "count"}}, - {"search", "getrecomendedaccountsbytags", &GetRecomendedAccountsByTags, {"tags", "count"}}, - {"search", "getrecomendedcontentsbyscoresonsimilarcontents", &GetRecomendedContentsByScoresOnSimilarContents, {"contentid", "contenttypes", "depth", "count"}}, - {"search", "getrecomendedcontentsbyscoresfromaddress", &GetRecomendedContentsByScoresFromAddress, {"address", "contenttypes", "height", "depth", "count"}}, - // TODO (o1q): Remove above methods when the client gui switches to new (below) methods - {"search", "getrecommendedcontentbycontentid", &GetRecommendedContentByContentId, {"contentid", "address", "contenttypes", "lang", "count"}}, {"search", "getrecommendedcontentbyaddress", &GetRecommendedContentByAddress, {"address", "addressExclude", "contenttypes", "lang", "count"}}, {"search", "getrecommendedaccountbyaddress", &GetRecommendedAccountByAddress, {"address", "addressExclude", "contenttypes", "lang", "count"}}, diff --git a/src/pocketdb/web/SearchRpc.cpp b/src/pocketdb/web/SearchRpc.cpp index c6dc30110..276f5b241 100644 --- a/src/pocketdb/web/SearchRpc.cpp +++ b/src/pocketdb/web/SearchRpc.cpp @@ -322,442 +322,101 @@ namespace PocketWeb::PocketWebRpc }; } - #pragma region Recomendations OLD - // TODO (o1q): Remove below methods when the client gui switches to new methods - RPCHelpMan GetRecomendedAccountsBySubscriptions() + RPCHelpMan GetRecommendedContentByAddress() { - return RPCHelpMan{"getrecomendedaccountsbysubscriptions", - "\nAccounts recommendations by subscriptions.\n", - { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "Address for recommendations"}, - {"count", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Number of resulting records. Default 10"}, - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("getrecomendedaccountsbysubscriptions", "") + - HelpExampleRpc("getrecomendedaccountsbysubscriptions", "") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue + return RPCHelpMan{"GetRecommendedContentByAddress", + "\n\n", // TODO (rpc): provide description + { + // TODO (rpc): args + }, + { + // TODO (rpc): provide return description + }, + RPCExamples{ + // TODO (team): examples + HelpExampleCli("GetRecommendedContentByAddress", "") + + HelpExampleRpc("GetRecommendedContentByAddress", "") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string address = request.params[0].get_str(); - CTxDestination dest = DecodeDestination(address); - - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Pocketcoin address: ") + address); - - int cntOut = 10; - if (request.params.size() > 1 && request.params[1].isNum()) - cntOut = request.params[1].get_int(); + // if (request.fHelp) + // throw runtime_error( + // "getrecommendedcontentbyaddress \"address\", \"addressExclude\", \"contenttypes\", \"lang\", count\n" + // "\nContents recommendations by content address.\n" + // "\nArguments:\n" + // "1. \"address\" (string) Address for recommendations\n" + // "2. \"addressExclude\" (string, optional) Address for exclude from recommendations\n" + // "3. \"contenttypes\" (string or array of strings, optional) type(s) of content posts/videos/articles\n" + // "3. \"lang\" (string, optional) Language for recommendations\n" + // "4. \"count\" (int, optional) Number of recommendations records and number of other contents from addres. Default 15\n" + // ); - return request.DbConnection()->SearchRepoInst->GetRecomendedAccountsBySubscriptions(address, cntOut); - }, - }; - } - - RPCHelpMan GetRecomendedAccountsByScoresOnSimilarAccounts() - { - return RPCHelpMan{"getrecomendedaccountsbyscoresonsimilaraccounts", - "\nAccounts recommendations by likes based on address.\n", - { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "Address for recommendations" }, - {"contenttypes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "type(s) of content posts/video", - { - {"contenttype", RPCArg::Type::STR, RPCArg::Optional::NO, "" }, - } - }, - {"height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Maximum search height. Default is current chain height" }, - {"depth", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Depth of statistic. Default 1000 blocks" }, - {"count", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Number of resulting records. Default 10" }, - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("getrecomendedaccountsbyscoresonsimilaraccounts", "\"address\", \"contenttypes\", height, depth, count") + - HelpExampleRpc("getrecomendedaccountsbyscoresonsimilaraccounts", "\"address\", \"contenttypes\", height, depth, count") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string address = request.params[0].get_str(); - CTxDestination dest = DecodeDestination(address); - - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Pocketcoin address: ") + address); - - vector contentTypes; - ParseRequestContentTypes(request.params[1], contentTypes); - - int nHeight = ChainActive().Height(); - int depth = 1000; - int cntOut = 10; - - if (request.params.size() > 2 && request.params[2].isNum() && request.params[2].get_int() > 0) - nHeight = request.params[2].get_int(); - - if (request.params.size() > 3 && request.params[3].isNum()) - depth = request.params[3].get_int(); - - if (request.params.size() > 4 && request.params[4].isNum()) - cntOut = request.params[4].get_int(); - - return request.DbConnection()->SearchRepoInst->GetRecomendedAccountsByScoresOnSimilarAccounts(address, contentTypes, nHeight, depth, cntOut); - }, - }; - } + string address = ""; + if (request.params.size() > 0 && request.params[0].isStr()) { + address = request.params[0].get_str(); + + if(!address.empty()) { + CTxDestination dest = DecodeDestination(address); + if (!IsValidDestination(dest)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + address); + } + } - RPCHelpMan GetRecomendedAccountsByScoresFromAddress() - { - return RPCHelpMan{"getrecomendedaccountsbyscoresfromaddress", - "\nAccounts recommendations by likes.\n", - { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "Address for recommendations" }, - {"contenttypes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "type(s) of content posts/video", - { - {"contenttype", RPCArg::Type::STR, RPCArg::Optional::NO, "" } - } - }, - {"height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Maximum search height. Default is current chain height" }, - {"depth", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Depth of statistic. Default 1000 blocks" }, - {"count", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Number of resulting records. Default 10" }, - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("getrecomendedaccountsbyscoresfromaddress", "\"address\", \"contenttypes\", height, depth, count") + - HelpExampleRpc("getrecomendedaccountsbyscoresfromaddress", "\"address\", \"contenttypes\", height, depth, count") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string address = request.params[0].get_str(); - CTxDestination dest = DecodeDestination(address); + string addressExclude = ""; + if (request.params.size() > 1 && request.params[1].isStr()) { + addressExclude = request.params[1].get_str(); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Pocketcoin address: ") + address); + if(!addressExclude.empty()) { + CTxDestination dest = DecodeDestination(addressExclude); + if (!IsValidDestination(dest)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + addressExclude); + } + } vector contentTypes; - ParseRequestContentTypes(request.params[1], contentTypes); + if(request.params.size()>2) + ParseRequestContentTypes(request.params[2], contentTypes); - int nHeight = ChainActive().Height(); - int depth = 1000; - int cntOut = 10; - - if (request.params.size() > 2 && request.params[2].isNum() && request.params[2].get_int() > 0) - nHeight = request.params[2].get_int(); - - if (request.params.size() > 3 && request.params[3].isNum()) - depth = request.params[3].get_int(); + string lang = ""; + if (request.params.size() > 3 && request.params[3].isStr()) + lang = request.params[3].get_str(); + int cntOut = 15; if (request.params.size() > 4 && request.params[4].isNum()) cntOut = request.params[4].get_int(); - return request.DbConnection()->SearchRepoInst->GetRecomendedAccountsByScoresFromAddress(address, contentTypes, nHeight, depth, cntOut); - }, - }; - } - - RPCHelpMan GetRecomendedAccountsByTags() - { - return RPCHelpMan{"getrecomendedaccountsbytags", - "\nAccounts recommendations by tags.\n", - { - {"tags", RPCArg::Type::ARR, RPCArg::Optional::NO, "Tags for recommendations", - { - {"tag", RPCArg::Type::STR, RPCArg::Optional::NO, "" } - } - }, - {"count", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Number of resulting records. Default 10" }, - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("getrecomendedaccountsbytags", "\"tags\", count") + - HelpExampleRpc("getrecomendedaccountsbytags", "\"tags\", count") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - vector tags; - if (request.params.size() > 0) - ParseRequestTags(request.params[0], tags); - - if (tags.empty()) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("There are no tags in the input parameters.")); - int nHeight = ChainActive().Height(); - int depth = 60 * 24 * 30; // about 1 month - - int cntOut = 10; - if (request.params.size() > 1 && request.params[1].isNum()) - cntOut = request.params[1].get_int(); + if (request.params.size() > 5 && request.params[5].isNum() && request.params[5].get_int() > 0) + nHeight = request.params[5].get_int(); - return request.DbConnection()->SearchRepoInst->GetRecomendedAccountsByTags(tags, nHeight, depth, cntOut); - }, - }; - } - - RPCHelpMan GetRecomendedContentsByScoresOnSimilarContents() - { - return RPCHelpMan{"getrecomendedcontentsbyscoresonsimilarcontents", - "\n\n", // TODO (team): provide description - { - {"contentid", RPCArg::Type::STR, RPCArg::Optional::NO, "Content hash for recommendations" }, - {"contenttypes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "type(s) of content posts/video", - { - {"contenttype", RPCArg::Type::STR, RPCArg::Optional::NO, "" } - } - }, - {"depth", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Depth of statistic. Default 1000 blocks" }, - {"count", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Number of resulting records. Default 10" }, - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("getrecomendedcontentsbyscoresonsimilarcontents", "\"contentid\", \"contenttypes\", depth, count") + - HelpExampleRpc("getrecomendedcontentsbyscoresonsimilarcontents", "\"contentid\", \"contenttypes\", depth, count") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string contentid = request.params[0].get_str(); - - vector contentTypes; - ParseRequestContentTypes(request.params[1], contentTypes); - - int depth = 1000; - int cntOut = 10; - - if (request.params.size() > 2 && request.params[2].isNum()) - depth = request.params[2].get_int(); - - if (request.params.size() > 3 && request.params[3].isNum()) - cntOut = request.params[3].get_int(); - - return request.DbConnection()->SearchRepoInst->GetRecomendedContentsByScoresOnSimilarContents(contentid, contentTypes, depth, cntOut); - }, - }; - } - - RPCHelpMan GetRecomendedContentsByScoresFromAddress() - { - return RPCHelpMan{"getrecomendedcontentsbyscoresfromaddress", - "\nContents recommendations for address by likes.\n", // TODO (team): provide description - { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "Address for recommendations" }, - {"contenttypes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "type(s) of content posts/video", - { - {"contenttype", RPCArg::Type::STR, RPCArg::Optional::NO, "" } - } - }, - {"height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Maximum search height. Default is current chain height" }, - {"depth", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Depth of statistic. Default 1000 blocks" }, - {"count", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Number of resulting records. Default 10" }, - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("getrecomendedcontentsbyscoresfromaddress", "\"address\", \"contenttypes\", height, depth, count") + - HelpExampleRpc("getrecomendedcontentsbyscoresfromaddress", "\"address\", \"contenttypes\", height, depth, count") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string address = request.params[0].get_str(); - CTxDestination dest = DecodeDestination(address); - - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Pocketcoin address: ") + address); - - vector contentTypes; - ParseRequestContentTypes(request.params[1], contentTypes); - - int nHeight = ChainActive().Height(); - int depth = 1000; - int cntOut = 10; - - if (request.params.size() > 2 && request.params[2].isNum() && request.params[2].get_int() > 0) - nHeight = request.params[2].get_int(); + int depth = (60 * 24 * 30 * 3); //about 3 month as default + if (request.params.size() > 6 && request.params[6].isNum()) + { + depth = std::max(request.params[6].get_int(), (60 * 24 * 30 * 6)); // not greater than about 6 month + } - if (request.params.size() > 3 && request.params[3].isNum()) - depth = request.params[3].get_int(); + UniValue resultContent(UniValue::VARR); + auto ids = request.DbConnection()->SearchRepoInst->GetRecommendedContentByAddressSubscriptions(address, addressExclude, contentTypes, lang, cntOut, nHeight, depth); + if (!ids.empty()) + { + auto contents = request.DbConnection()->WebRpcRepoInst->GetContentsData(ids, ""); + resultContent.push_backV(contents); + } - if (request.params.size() > 4 && request.params[4].isNum()) - cntOut = request.params[4].get_int(); + ids = request.DbConnection()->SearchRepoInst->GetRandomContentByAddress(address, contentTypes, lang, cntOut); + if (!ids.empty()) + { + auto contents = request.DbConnection()->WebRpcRepoInst->GetContentsData(ids, ""); + resultContent.push_backV(contents); + } - return request.DbConnection()->SearchRepoInst->GetRecomendedContentsByScoresFromAddress(address, contentTypes, nHeight, depth, cntOut); + UniValue result(UniValue::VOBJ); + result.pushKV("contents", resultContent); + return result; }, }; } - #pragma endregion - - RPCHelpMan GetRecommendedContentByContentId() - { - return RPCHelpMan{"GetRecommendedContentByContentId", - "\n\n", // TODO (rpc): provide description - { - // TODO (rpc): args - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("GetRecommendedContentByContentId", "") + - HelpExampleRpc("GetRecommendedContentByContentId", "") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - if (request.fHelp) - throw runtime_error( - "getrecommendedcontentbycontentid \"contentid\", \"address\", \"contenttypes\", \"lang\", count\n" - "\nContents recommendations by contentid for address.\n" - "\nArguments:\n" - "1. \"contentid\" (string) Content Id Hash for recommendations\n" - "2. \"address\" (string, optional) Address for which recommendations\n" - "3. \"contenttypes\" (string or array of strings, optional) type(s) of content posts/videos/articles\n" - "3. \"lang\" (string, optional) Language for recommendations\n" - "4. \"count\" (int, optional) Number of resulting records. Default 15\n" - ); - - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string contenthash = request.params[0].get_str(); - - string address = ""; - if (request.params.size() > 1 && request.params[1].isStr()) { - address = request.params[1].get_str(); - - if(!address.empty()) { - CTxDestination dest = DecodeDestination(address); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + address); - } - } - - vector contentTypes; - if(request.params.size()>2) - ParseRequestContentTypes(request.params[2], contentTypes); - - string lang = ""; - if (request.params.size() > 3 && request.params[3].isStr()) - lang = request.params[3].get_str(); - - int cntOut = 15; - if (request.params.size() > 4 && request.params[4].isNum()) - cntOut = request.params[4].get_int(); - - auto contentsAddresses = request.DbConnection()->WebRpcRepoInst->GetContentsAddresses(vector{contenthash}); - - UniValue result(UniValue::VARR); - auto ids = request.DbConnection()->SearchRepoInst->GetRecommendedContentByAddressSubscriptions(contentsAddresses[contenthash], address, contentTypes, lang, cntOut, ChainActive().Height(), (60 * 24 * 30 * 6), 100); - if (!ids.empty()) - { - auto contents = request.DbConnection()->WebRpcRepoInst->GetContentsData(ids, address); - result.push_backV(contents); - } - - return result; - }, - }; - } - - RPCHelpMan GetRecommendedContentByAddress() - { - return RPCHelpMan{"GetRecommendedContentByAddress", - "\n\n", // TODO (rpc): provide description - { - // TODO (rpc): args - }, - { - // TODO (rpc): provide return description - }, - RPCExamples{ - // TODO (team): examples - HelpExampleCli("GetRecommendedContentByAddress", "") + - HelpExampleRpc("GetRecommendedContentByAddress", "") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - if (request.fHelp) - throw runtime_error( - "getrecommendedcontentbyaddress \"address\", \"addressExclude\", \"contenttypes\", \"lang\", count\n" - "\nContents recommendations by content address.\n" - "\nArguments:\n" - "1. \"address\" (string) Address for recommendations\n" - "2. \"addressExclude\" (string, optional) Address for exclude from recommendations\n" - "3. \"contenttypes\" (string or array of strings, optional) type(s) of content posts/videos/articles\n" - "3. \"lang\" (string, optional) Language for recommendations\n" - "4. \"count\" (int, optional) Number of recommendations records and number of other contents from addres. Default 15\n" - ); - - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string address = ""; - if (request.params.size() > 0 && request.params[0].isStr()) { - address = request.params[0].get_str(); - - if(!address.empty()) { - CTxDestination dest = DecodeDestination(address); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + address); - } - } - - string addressExclude = ""; - if (request.params.size() > 1 && request.params[1].isStr()) { - addressExclude = request.params[1].get_str(); - - if(!addressExclude.empty()) { - CTxDestination dest = DecodeDestination(addressExclude); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + addressExclude); - } - } - - vector contentTypes; - if(request.params.size()>2) - ParseRequestContentTypes(request.params[2], contentTypes); - - string lang = ""; - if (request.params.size() > 3 && request.params[3].isStr()) - lang = request.params[3].get_str(); - - int cntOut = 15; - if (request.params.size() > 4 && request.params[4].isNum()) - cntOut = request.params[4].get_int(); - - UniValue resultContent(UniValue::VARR); - auto ids = request.DbConnection()->SearchRepoInst->GetRecommendedContentByAddressSubscriptions(address, addressExclude, contentTypes, lang, cntOut, ChainActive().Height(), (60 * 24 * 30 * 3), 20); - if (!ids.empty()) - { - auto contents = request.DbConnection()->WebRpcRepoInst->GetContentsData(ids, ""); - resultContent.push_backV(contents); - } - - ids = request.DbConnection()->SearchRepoInst->GetRandomContentByAddress(address, contentTypes, lang, cntOut); - if (!ids.empty()) - { - auto contents = request.DbConnection()->WebRpcRepoInst->GetContentsData(ids, ""); - resultContent.push_backV(contents); - } - - UniValue result(UniValue::VOBJ); - result.pushKV("contents", resultContent); - return result; - }, - }; - } RPCHelpMan GetRecommendedAccountByAddress() { @@ -774,66 +433,75 @@ namespace PocketWeb::PocketWebRpc HelpExampleCli("GetRecommendedContentByAddress", "") + HelpExampleRpc("GetRecommendedContentByAddress", "") }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - if (request.fHelp) - throw runtime_error( - "getrecommendedaccountbyaddress \"address\", \"addressExclude\", \"contenttypes\", \"lang\", count\n" - "\nAccounts recommendations by address.\n" - "\nArguments:\n" - "1. \"address\" (string) Address for recommendations\n" - "2. \"addressExclude\" (string, optional) Address for exclude from recommendations\n" - "3. \"contenttypes\" (string or array of strings, optional) type(s) of content posts/videos/articles\n" - "3. \"lang\" (string, optional) Language for recommendations\n" - "4. \"count\" (int, optional) Number of recommendations records and number of other contents from addres. Default 15\n" - ); - - RPCTypeCheckArgument(request.params[0], UniValue::VSTR); - string address = ""; - if (request.params.size() > 0 && request.params[0].isStr()) { - address = request.params[0].get_str(); - - if(!address.empty()) { - CTxDestination dest = DecodeDestination(address); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + address); - } - } - - string addressExclude = ""; - if (request.params.size() > 1 && request.params[1].isStr()) { - addressExclude = request.params[1].get_str(); - - if(!addressExclude.empty()) { - CTxDestination dest = DecodeDestination(addressExclude); - if (!IsValidDestination(dest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + addressExclude); - } - } - - vector contentTypes; - if(request.params.size()>2) - ParseRequestContentTypes(request.params[2], contentTypes); - - string lang = ""; - if (request.params.size() > 3 && request.params[3].isStr()) - lang = request.params[3].get_str(); - - int cntOut = 15; - if (request.params.size() > 4 && request.params[4].isNum()) - cntOut = request.params[4].get_int(); - - UniValue result(UniValue::VARR); - auto ids = request.DbConnection()->SearchRepoInst->GetRecommendedAccountByAddressSubscriptions(address, addressExclude, contentTypes, lang, cntOut, ChainActive().Height(), (60 * 24 * 30 * 3), 10); - if (!ids.empty()) - { - auto profiles = request.DbConnection()->WebRpcRepoInst->GetAccountProfiles(ids, true); - for (const auto[id, record] : profiles) - result.push_back(record); - } - - return result; - }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue + { + // if (request.fHelp) + // throw runtime_error( + // "getrecommendedaccountbyaddress \"address\", \"addressExclude\", \"contenttypes\", \"lang\", count\n" + // "\nAccounts recommendations by address.\n" + // "\nArguments:\n" + // "1. \"address\" (string) Address for recommendations\n" + // "2. \"addressExclude\" (string, optional) Address for exclude from recommendations\n" + // "3. \"contenttypes\" (string or array of strings, optional) type(s) of content posts/videos/articles\n" + // "3. \"lang\" (string, optional) Language for recommendations\n" + // "4. \"count\" (int, optional) Number of recommendations records and number of other contents from addres. Default 15\n" + // ); + RPCTypeCheckArgument(request.params[0], UniValue::VSTR); + string address = ""; + if (request.params.size() > 0 && request.params[0].isStr()) { + address = request.params[0].get_str(); + + if(!address.empty()) { + CTxDestination dest = DecodeDestination(address); + if (!IsValidDestination(dest)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + address); + } + } + + string addressExclude = ""; + if (request.params.size() > 1 && request.params[1].isStr()) { + addressExclude = request.params[1].get_str(); + + if(!addressExclude.empty()) { + CTxDestination dest = DecodeDestination(addressExclude); + if (!IsValidDestination(dest)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid address: ") + addressExclude); + } + } + + vector contentTypes; + if(request.params.size()>2) + ParseRequestContentTypes(request.params[2], contentTypes); + + string lang = ""; + if (request.params.size() > 3 && request.params[3].isStr()) + lang = request.params[3].get_str(); + + int cntOut = 15; + if (request.params.size() > 4 && request.params[4].isNum()) + cntOut = request.params[4].get_int(); + + int nHeight = ChainActive().Height(); + if (request.params.size() > 5 && request.params[5].isNum() && request.params[5].get_int() > 0) + nHeight = request.params[5].get_int(); + + int depth = (60 * 24 * 30 * 3); //about 3 month as default + if (request.params.size() > 6 && request.params[6].isNum()) + { + depth = std::max(request.params[6].get_int(), (60 * 24 * 30 * 6)); // not greater than about 6 month + } + + UniValue result(UniValue::VARR); + auto ids = request.DbConnection()->SearchRepoInst->GetRecommendedAccountByAddressSubscriptions(address, addressExclude, contentTypes, lang, cntOut, nHeight, depth); + if (!ids.empty()) + { + auto profiles = request.DbConnection()->WebRpcRepoInst->GetAccountProfiles(ids, true); + for (const auto[id, record] : profiles) + result.push_back(record); + } + + return result; + }, }; } } diff --git a/src/pocketdb/web/SearchRpc.h b/src/pocketdb/web/SearchRpc.h index 79a877735..656bc160b 100644 --- a/src/pocketdb/web/SearchRpc.h +++ b/src/pocketdb/web/SearchRpc.h @@ -22,37 +22,7 @@ namespace PocketWeb::PocketWebRpc RPCHelpMan SearchLinks(); RPCHelpMan SearchContents(); - #pragma region Recomendations OLD - // TODO (o1q): Remove below methods when the client gui switches to new methods - // Accounts recommendations based on subscriptions - // Get some accounts that were followed by people who've followed this Account - // This should be run only if Account already has followers - RPCHelpMan GetRecomendedAccountsBySubscriptions(); - - // Accounts recommendations based on scores on other Account - // Get some Accounts that were scored by people who've scored this Account (not long ago - several blocks ago) - RPCHelpMan GetRecomendedAccountsByScoresOnSimilarAccounts(); - - // Accounts recommendations based on address scores - // Get some Accounts that were scored by people who've scored like address (not long ago - several blocks ago) - // Get address scores -> Get scored contents -> Get Scores to these contents -> Get scores accounts -> Get their scores -> Get their scored contents -> Get these contents Authors - RPCHelpMan GetRecomendedAccountsByScoresFromAddress(); - - // Accounts recommendations based on tags - RPCHelpMan GetRecomendedAccountsByTags(); - - // Contents recommendations by others contents - // Get some contents that were liked by people who've seen this content (not long ago - several blocks ago) - // This should be run only if Content already has >XXXX likes - RPCHelpMan GetRecomendedContentsByScoresOnSimilarContents(); - - // Contents recommendations by address scores - // Get some contents that were liked by people who've scored like address (not long ago - several blocks ago) - RPCHelpMan GetRecomendedContentsByScoresFromAddress(); - #pragma endregion - #pragma region Recomendations - RPCHelpMan GetRecommendedContentByContentId(); RPCHelpMan GetRecommendedContentByAddress(); RPCHelpMan GetRecommendedAccountByAddress(); #pragma endregion diff --git a/src/rest.cpp b/src/rest.cpp index d982a6260..ddc360302 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -777,38 +777,8 @@ static bool rest_getutxos(const util::Ref& context, HTTPRequest* req, const std: } } -static bool rest_topaddresses(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart) +static double getEmission(int height) { - if (!CheckWarmup(req)) - return false; - - auto[rf, uriParts] = ParseParams(strURIPart); - - int count = 30; - if (!uriParts.empty()) - { - count = std::stoi(uriParts[0]); - if (count > 1000) - count = 1000; - } - - auto result = req->DbConnection()->WebRpcRepoInst->GetTopAddresses(count); - req->WriteHeader("Content-Type", "application/json"); - req->WriteReply(HTTP_OK, result.write() + "\n"); - return true; -} - -static bool rest_emission(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart) -{ - if (!CheckWarmup(req)) - return false; - - auto[rf, uriParts] = ParseParams(strURIPart); - - int height = ChainActive().Height(); - if (auto[ok, result] = TryGetParamInt(uriParts, 0); ok) - height = result; - int first75 = 3750000; int halvblocks = 2'100'000; double emission = 0; @@ -838,25 +808,73 @@ static bool rest_emission(const util::Ref& context, HTTPRequest* req, const std: } } - switch (rf) - { - case RetFormat::JSON: - { - UniValue result(UniValue::VOBJ); - result.pushKV("height", height); - result.pushKV("emission", emission); + return emission; +} - req->WriteHeader("Content-Type", "application/json"); - req->WriteReply(HTTP_OK, result.write() + "\n"); - return true; - } - default: - { - req->WriteHeader("Content-Type", "text/plain"); - req->WriteReply(HTTP_OK, std::to_string(emission) + "\n"); - return true; - } +static bool get_static_status(HTTPRequest* req, const std::string& strURIPart) +{ + if (!CheckWarmup(req)) + return false; + + // TODO (brangr): join with PocketSystemRpc.cpp::GetNodeInfo + + UniValue entry(UniValue::VOBJ); + + // General information about instance + entry.pushKV("version", FormatVersion(CLIENT_VERSION)); + entry.pushKV("time", GetAdjustedTime()); + entry.pushKV("chain", Params().NetworkIDString()); + entry.pushKV("proxy", true); + + // Network information + uint64_t nNetworkWeight = GetPoSKernelPS(); + entry.pushKV("netstakeweight", (uint64_t)nNetworkWeight); + entry.pushKV("emission", getEmission(ChainActive().Height())); + + // Last block + CBlockIndex* pindex = ChainActive().Tip(); + UniValue oblock(UniValue::VOBJ); + oblock.pushKV("height", pindex->nHeight); + oblock.pushKV("hash", pindex->GetBlockHash().GetHex()); + oblock.pushKV("time", (int64_t)pindex->nTime); + oblock.pushKV("ntx", (int)pindex->nTx); + entry.pushKV("lastblock", oblock); + + UniValue proxies(UniValue::VARR); + if (WSConnections) { + auto fillProxy = [&proxies](const std::pair& it) { + if (it.second.Service) { + UniValue proxy(UniValue::VOBJ); + proxy.pushKV("address", it.second.Address); + proxy.pushKV("ip", it.second.Ip); + proxy.pushKV("port", it.second.MainPort); + proxy.pushKV("portWss", it.second.WssPort); + proxies.push_back(proxy); + } + }; + WSConnections->Iterate(fillProxy); } + entry.pushKV("proxies", proxies); + + // Ports information + int64_t nodePort = gArgs.GetArg("-port", Params().GetDefaultPort()); + int64_t publicPort = gArgs.GetArg("-publicrpcport", BaseParams().PublicRPCPort()); + int64_t staticPort = gArgs.GetArg("-staticrpcport", BaseParams().StaticRPCPort()); + int64_t restPort = gArgs.GetArg("-restport", BaseParams().RestPort()); + int64_t wssPort = gArgs.GetArg("-wsport", 8087); // TODO (brangr): move 8087 to BaseParams().WSPort() + + UniValue ports(UniValue::VOBJ); + ports.pushKV("node", nodePort); + ports.pushKV("api", publicPort); + ports.pushKV("rest", restPort); + ports.pushKV("wss", wssPort); + ports.pushKV("http", staticPort); + ports.pushKV("https", staticPort); + entry.pushKV("ports", ports); + + req->WriteHeader("Content-Type", "application/json"); + req->WriteReply(HTTP_OK, entry.write() + "\n"); + return true; } static bool debug_index_block(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart) @@ -998,6 +1016,9 @@ static bool get_static_web(const util::Ref& context, HTTPRequest* req, const std return true; } + if (strURIPart.find("status") == 0) + return get_static_status(req, strURIPart); + if (auto[code, file] = PocketWeb::PocketFrontendInst.GetFile(strURIPart); code == HTTP_OK) { req->WriteHeader("Content-Type", file->ContentType); @@ -1075,25 +1096,26 @@ static const struct {"/rest/headers/", rest_headers}, {"/rest/getutxos", rest_getutxos}, {"/rest/blockhashbyheight/", rest_blockhash_by_height}, - {"/rest/emission", rest_emission}, - {"/rest/getemission", rest_emission}, - {"/rest/topaddresses", rest_topaddresses}, - {"/rest/gettopaddresses", rest_topaddresses}, {"/rest/blockhash", rest_blockhash}, }; void StartREST(const util::Ref& context) { - if(g_restSocket) + if(g_restSocket) { for (const auto& up : uri_prefixes) { auto handler = [&context, up](HTTPRequest* req, const std::string& prefix) { return up.handler(context, req, prefix); }; g_restSocket->RegisterHTTPHandler(up.prefix, false, handler, g_restSocket->m_workQueue); } + } +} + +// Register web content route +void StartSTATIC(const util::Ref& context) +{ if(g_staticSocket) { auto handler = [&context](HTTPRequest* req, const std::string& prefix) { return get_static_web(context, req, prefix); }; g_staticSocket->RegisterHTTPHandler("/", false, handler, g_staticSocket->m_workQueue); - } } @@ -1105,8 +1127,11 @@ void StopREST() { if (g_restSocket) for (auto uri_prefixe: uri_prefixes) - g_restSocket->UnregisterHTTPHandler(uri_prefixe.prefix, false); + g_restSocket->UnregisterHTTPHandler(uri_prefixe.prefix, false); +} +void StopSTATIC() +{ if (g_staticSocket) g_staticSocket->UnregisterHTTPHandler("/", false); } diff --git a/src/rpc/cache.h b/src/rpc/cache.h index 79514bd22..88bda9a37 100644 --- a/src/rpc/cache.h +++ b/src/rpc/cache.h @@ -36,6 +36,7 @@ class RPCCache "gethierarchicalfeed", "gethierarchicalstrip", "gethotposts", + "gettopfeed", "getuserprofile", "getprofilefeed", "getsubscribesfeed", @@ -50,15 +51,15 @@ class RPCCache "getusersubscribes", "getusersubscribers", "getuserblockings", + "gettopaccounts", "getaddressscores", "getpostscores", "getstatisticbyhours", "getstatisticbydays", "getstatisticcontentbyhours", "getstatisticcontentbydays", - "getrecomendedaccountsbytags", - "getrecomendedcontentsbyscoresonsimilarcontents", - "getrecommendedcontentbycontentid", + "getrecommendedcontentbyaddress", + "getrecommendedaccountbyaddress", "getaddressinfo", "getcompactblock", "getlastblocks", diff --git a/src/validation.cpp b/src/validation.cpp index 57427ccd7..146746b1a 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -54,6 +54,7 @@ #include #include +#include #include "pocketdb/services/ChainPostProcessing.h" #include "pocketdb/services/Accessor.h" @@ -2511,7 +2512,7 @@ bool CChainState::ConnectBlock(const CBlock& block, const PocketBlockRef& pocket // ----------------------------------------------------------------------------------------------------------------- // Extend WEB database - if (gArgs.GetBoolArg("-api", true) && enablePocketConnect) + if (!gArgs.GetBoolArg("-withoutweb", false) && enablePocketConnect) PocketServices::WebPostProcessorInst.Enqueue(block.GetHash().GetHex()); // -----------------------------------------------------------------------------------------------------------------