diff --git a/programs/keeper/dashboard.html b/programs/keeper/dashboard.html index d4143251e50..171e5cfc515 100644 --- a/programs/keeper/dashboard.html +++ b/programs/keeper/dashboard.html @@ -26,76 +26,45 @@ @@ -440,18 +461,88 @@
+
+
+ 🌑🌞 +
+
+
- + - +
-

Storage

+
-
+ +
+
+
+ +
+
+
+

--

+
+
+ + +
+
+ +
+
+

numChildren:

+

--

+

dataLength:

+

--

+

version:

+

--

+

aversion:

+

--

+

cversion:

+

--

+

ctime:

+

--

+
+ +
+

ephemeralOwner:

+

--

+

cZxid:

+

--

+

pZxid:

+

--

+

mZxid:

+

--

+

mtime:

+

--

+
+
+ +
+
+

Node Data:

+
+
+ +
+
+ +
+
@@ -491,13 +582,6 @@
-
-
- 🌑🌞 -
-
- -

" : ""; + + span_icon.addEventListener("click", async function (e) { + e.stopPropagation(); + var cur_el = e.target; + + while (typeof cur_el.storage_node === "undefined" || cur_el.classList.contains("storage_viewer_container")) { + cur_el = cur_el.parentElement; + } + + var node_cur = cur_el.storage_node; + + if (typeof node_cur === "undefined") { + return; + } + + if (!node_cur.isLeaf()) { + await node_cur.toggleExpanded(); + } + storage_viewer.reload(); + }); + + span_desc.appendChild(span_icon); + span_desc.appendChild(document.createTextNode(this.name())); + result.appendChild(span_desc); + + if (this.expanded) { + var ul_container = document.createElement("ul"); + + if (this.stub_child) { + ul_container.appendChild(this.stub_child.render()); + } + + this.children.forEach(function (child) { + ul_container.appendChild(child.render()); + }); + + result.appendChild(ul_container) + } + } + + return result; + } + + async toggleExpanded() { + if (this.isLeaf()) { + return; + } + if (this.expanded) { + this.collapse(); + } else { + await this.expand(); + } + }; + + select() { + storage_viewer.updateSelectedNode(this); + }; + + name() { + if (this.path === '/') { + return '/'; + } + const split_path = this.path.split('/'); + if (!split_path[split_path.length - 1]) { + split_path.pop(); + } + return split_path.length > 0 ? split_path[split_path.length - 1] : 'unknown'; + } + + getPathOfChild(child_name) { + return (this.path == '/' ? '' : this.path) + `/${child_name}`; + } + + hasData() { + return (this.stat?.dataLength ?? 0) !== 0; + } + + async loadData() { + var server_url = new URL(window.location); + server_url.pathname = `/api/v1/storage/get`; + + const res = await fetch(server_url.toString() + this.path); + if (res.ok) { + const buffer = await res.arrayBuffer(); + this.data = new Uint8Array(buffer); + } else { + this.data = undefined; + } + } + + removeChild(child_name) { + this.children_names = this.children_names.filter(name => name !== child_name); + this.children = this.children.filter(child => child.name() !== child_name); + } + + makeChildStub() { + this.stub_child = new StorageNodeStub(this); + } + + async createChild(child_name) { + var server_url = new URL(window.location); + server_url.pathname = `/api/v1/storage/create`; + var create_url = server_url.toString(); + + const res = await fetch(create_url + this.getPathOfChild(child_name), + { + method: 'POST' + }); + + if (res.ok) { + this.children_names.push(child_name); + var new_child_node = new StorageNode(this.getPathOfChild(child_name), this, undefined, []); + // load necessary data for a new node + await new_child_node.reloadContent(); + await this.reloadContent() + this.children.push(new_child_node); + new_child_node.select(); + } else { + // TODO render creation error. + } + } + + isChildOf(node) { + return this.path.startsWith(node.path); + } + } + const current_url = new URL(window.location); const opened_locally = location.protocol == 'file:'; @@ -606,7 +1151,6 @@ window.localStorage.setItem('theme', theme); } document.documentElement.setAttribute('data-theme', theme); - redrawChart(); } if (theme) { @@ -700,6 +1244,9 @@ } } + var storage_viewer = new StorageViewer(document.getElementById("storage-viewer-container")); + storage_viewer.init(); + start(); diff --git a/src/Server/KeeperDashboardRequestHandler.cpp b/src/Server/KeeperDashboardRequestHandler.cpp index 68a56d5cad5..5d7414aadaf 100644 --- a/src/Server/KeeperDashboardRequestHandler.cpp +++ b/src/Server/KeeperDashboardRequestHandler.cpp @@ -26,7 +26,8 @@ INCBIN(resource_keeper_dashboard_html, SOURCE_DIR "/programs/keeper/dashboard.ht namespace DB { -void KeeperDashboardWebUIRequestHandler::handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event &) +void KeeperDashboardWebUIRequestHandler::handleRequest( + HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event &) { /// Raw config reference is used here to avoid dependency on Context and ServerSettings. /// This is painful, because this class is also used in a build with CLICKHOUSE_KEEPER_STANDALONE_BUILD=1 @@ -65,7 +66,8 @@ try response_json.set("ch_version", VERSION_DESCRIBE); - if (keeper_dispatcher->isServerActive()) { + if (keeper_dispatcher->isServerActive()) + { Poco::JSON::Object keeper_details; auto & stats = keeper_dispatcher->getKeeperConnectionStats(); Keeper4LWInfo keeper_info = keeper_dispatcher->getKeeper4LWInfo(); @@ -78,11 +80,11 @@ try response_json.set("keeper_details", keeper_details); } - std::ostringstream oss; // STYLE_CHECK_ALLOW_STD_STRING_STREAM + std::ostringstream oss; // STYLE_CHECK_ALLOW_STD_STRING_STREAM oss.exceptions(std::ios::failbit); Poco::JSON::Stringifier::stringify(response_json, oss); - response.setContentType("application/json"); + response.setContentType("application/json"); *response.send() << oss.str(); } catch (...) diff --git a/src/Server/KeeperDashboardRequestHandler.h b/src/Server/KeeperDashboardRequestHandler.h index 0ab056cf370..402fc9cb1e6 100644 --- a/src/Server/KeeperDashboardRequestHandler.h +++ b/src/Server/KeeperDashboardRequestHandler.h @@ -19,7 +19,7 @@ private: const IServer & server; public: - explicit KeeperDashboardWebUIRequestHandler(const IServer & server_) : server(server_) {} + explicit KeeperDashboardWebUIRequestHandler(const IServer & server_) : server(server_) { } void handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & write_event) override; }; @@ -30,7 +30,10 @@ private: std::shared_ptr keeper_dispatcher; public: - explicit KeeperDashboardContentRequestHandler(std::shared_ptr keeper_dispatcher_) : keeper_dispatcher(keeper_dispatcher_) {} + explicit KeeperDashboardContentRequestHandler(std::shared_ptr keeper_dispatcher_) + : keeper_dispatcher(keeper_dispatcher_) + { + } void handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & write_event) override; }; diff --git a/src/Server/KeeperHTTPHandlerFactory.cpp b/src/Server/KeeperHTTPHandlerFactory.cpp index 2ab3603c1c6..6d465bafb6f 100644 --- a/src/Server/KeeperHTTPHandlerFactory.cpp +++ b/src/Server/KeeperHTTPHandlerFactory.cpp @@ -28,20 +28,26 @@ namespace DB namespace ErrorCodes { - extern const int INVALID_CONFIG_PARAMETER; +extern const int INVALID_CONFIG_PARAMETER; } -KeeperHTTPRequestHandlerFactory::KeeperHTTPRequestHandlerFactory(const std::string & name_) - : log(getLogger(name_)), name(name_) +KeeperHTTPRequestHandlerFactory::KeeperHTTPRequestHandlerFactory(const std::string & name_) : log(getLogger(name_)), name(name_) { } std::unique_ptr KeeperHTTPRequestHandlerFactory::createRequestHandler(const HTTPServerRequest & request) { - LOG_TRACE(log, "HTTP Request for {}. Method: {}, Address: {}, User-Agent: {}{}, Content Type: {}, Transfer Encoding: {}, X-Forwarded-For: {}", - name, request.getMethod(), request.clientAddress().toString(), request.get("User-Agent", "(none)"), + LOG_TRACE( + log, + "HTTP Request for {}. Method: {}, Address: {}, User-Agent: {}{}, Content Type: {}, Transfer Encoding: {}, X-Forwarded-For: {}", + name, + request.getMethod(), + request.clientAddress().toString(), + request.get("User-Agent", "(none)"), (request.hasContentLength() ? (", Length: " + std::to_string(request.getContentLength())) : ("")), - request.getContentType(), request.getTransferEncoding(), request.get("X-Forwarded-For", "(none)")); + request.getContentType(), + request.getTransferEncoding(), + request.get("X-Forwarded-For", "(none)")); for (auto & handler_factory : child_factories) { @@ -50,8 +56,7 @@ std::unique_ptr KeeperHTTPRequestHandlerFactory::createReque return handler; } - if (request.getMethod() == Poco::Net::HTTPRequest::HTTP_GET - || request.getMethod() == Poco::Net::HTTPRequest::HTTP_HEAD + if (request.getMethod() == Poco::Net::HTTPRequest::HTTP_GET || request.getMethod() == Poco::Net::HTTPRequest::HTTP_HEAD || request.getMethod() == Poco::Net::HTTPRequest::HTTP_POST) { return std::unique_ptr(new KeeperNotFoundHandler(hints.getHints(request.getURI()))); @@ -66,8 +71,7 @@ void addDashboardHandlersToFactory( auto dashboard_ui_creator = [&server]() -> std::unique_ptr { return std::make_unique(server); }; - auto dashboard_handler - = std::make_shared>(dashboard_ui_creator); + auto dashboard_handler = std::make_shared>(dashboard_ui_creator); dashboard_handler->attachStrictPath("/dashboard"); dashboard_handler->allowGetAndHeadRequest(); factory.addPathToHints("/dashboard"); @@ -118,9 +122,7 @@ void addDefaultHandlersToFactory( const Poco::Util::AbstractConfiguration & config) { auto readiness_creator = [keeper_dispatcher]() -> std::unique_ptr - { - return std::make_unique(keeper_dispatcher); - }; + { return std::make_unique(keeper_dispatcher); }; auto readiness_handler = std::make_shared>(std::move(readiness_creator)); readiness_handler->attachStrictPath(config.getString("keeper_server.http_control.readiness.endpoint", "/ready")); readiness_handler->allowGetAndHeadRequest(); @@ -155,28 +157,34 @@ static inline auto createHandlersFactoryFromConfig( const auto & handler_type = config.getString(prefix + "." + key + ".handler.type", ""); if (handler_type.empty()) - throw Exception(ErrorCodes::INVALID_CONFIG_PARAMETER, "Handler type in config is not specified here: " - "{}.{}.handler.type", prefix, key); + throw Exception( + ErrorCodes::INVALID_CONFIG_PARAMETER, + "Handler type in config is not specified here: " + "{}.{}.handler.type", + prefix, + key); if (handler_type == "dashboard") - { addDashboardHandlersToFactory(*main_handler_factory, server, keeper_dispatcher); - } if (handler_type == "commands") - { addCommandsHandlersToFactory(*main_handler_factory, server, keeper_dispatcher); - } if (handler_type == "storage") - { addStorageHandlersToFactory(*main_handler_factory, server, keeper_dispatcher); - } else - throw Exception(ErrorCodes::INVALID_CONFIG_PARAMETER, "Unknown handler type '{}' in config here: {}.{}.handler.type", - handler_type, prefix, key); + throw Exception( + ErrorCodes::INVALID_CONFIG_PARAMETER, + "Unknown handler type '{}' in config here: {}.{}.handler.type", + handler_type, + prefix, + key); } else - throw Exception(ErrorCodes::UNKNOWN_ELEMENT_IN_CONFIG, "Unknown element in config: " - "{}.{}, must be 'rule' or 'defaults'", prefix, key); + throw Exception( + ErrorCodes::UNKNOWN_ELEMENT_IN_CONFIG, + "Unknown element in config: " + "{}.{}, must be 'rule' or 'defaults'", + prefix, + key); } return main_handler_factory; @@ -185,10 +193,10 @@ static inline auto createHandlersFactoryFromConfig( KeeperHTTPReadinessHandler::KeeperHTTPReadinessHandler(std::shared_ptr keeper_dispatcher_) : log(getLogger("KeeperHTTPReadinessHandler")), keeper_dispatcher(keeper_dispatcher_) { - } -void KeeperHTTPReadinessHandler::handleRequest(HTTPServerRequest & /*request*/, HTTPServerResponse & response, const ProfileEvents::Event & /*write_event*/) +void KeeperHTTPReadinessHandler::handleRequest( + HTTPServerRequest & /*request*/, HTTPServerResponse & response, const ProfileEvents::Event & /*write_event*/) { try { @@ -207,7 +215,7 @@ void KeeperHTTPReadinessHandler::handleRequest(HTTPServerRequest & /*request*/, json.set("details", details); json.set("status", status ? "ok" : "fail"); - std::ostringstream oss; // STYLE_CHECK_ALLOW_STD_STRING_STREAM + std::ostringstream oss; // STYLE_CHECK_ALLOW_STD_STRING_STREAM oss.exceptions(std::ios::failbit); Poco::JSON::Stringifier::stringify(json, oss); @@ -245,7 +253,8 @@ KeeperHTTPCommandsHandler::KeeperHTTPCommandsHandler(const IServer & server_, st { } -void KeeperHTTPCommandsHandler::handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & /*write_event*/) +void KeeperHTTPCommandsHandler::handleRequest( + HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & /*write_event*/) try { std::vector uri_segments; @@ -261,7 +270,7 @@ try return; } - // non-strict path "/api/v1/commands" filter is already attached + /// non-strict path "/api/v1/commands" filter is already attached if (uri_segments.size() != 4) { response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST, "Invalid command path"); @@ -339,9 +348,7 @@ HTTPRequestHandlerFactoryPtr createKeeperHTTPHandlerFactory( const std::string & name) { if (config.has("keeper_server.http_control.handlers")) - { return createHandlersFactoryFromConfig(server, keeper_dispatcher, config, name, "keeper_server.http_control.handlers"); - } auto factory = std::make_shared(name); addDefaultHandlersToFactory(*factory, server, keeper_dispatcher, config); diff --git a/src/Server/KeeperHTTPStorageHandler.cpp b/src/Server/KeeperHTTPStorageHandler.cpp index 4c4c55d19f4..3756c7bdcfe 100644 --- a/src/Server/KeeperHTTPStorageHandler.cpp +++ b/src/Server/KeeperHTTPStorageHandler.cpp @@ -24,7 +24,7 @@ extern const int LOGICAL_ERROR; extern const int TIMEOUT_EXCEEDED; } -Poco::JSON::Object toJson(const Coordination::Stat & stat) +Poco::JSON::Object toJSON(const Coordination::Stat & stat) { Poco::JSON::Object result; result.set("cZxid", stat.czxid); @@ -173,7 +173,7 @@ void KeeperHTTPStorageHandler::performZooKeeperExistsRequest(const std::string & return; Poco::JSON::Object response_json; - response_json.set("stat", toJson(exists_result_ptr->stat)); + response_json.set("stat", toJSON(exists_result_ptr->stat)); std::ostringstream oss; // STYLE_CHECK_ALLOW_STD_STRING_STREAM oss.exceptions(std::ios::failbit); @@ -200,7 +200,7 @@ void KeeperHTTPStorageHandler::performZooKeeperListRequest(const std::string & s Poco::JSON::Object response_json; response_json.set("child_node_names", list_result_ptr->names); - response_json.set("stat", toJson(list_result_ptr->stat)); + response_json.set("stat", toJSON(list_result_ptr->stat)); std::ostringstream oss; // STYLE_CHECK_ALLOW_STD_STRING_STREAM oss.exceptions(std::ios::failbit); diff --git a/src/Server/KeeperHTTPStorageHandler.h b/src/Server/KeeperHTTPStorageHandler.h index ad0223ec2f0..d65e8053603 100644 --- a/src/Server/KeeperHTTPStorageHandler.h +++ b/src/Server/KeeperHTTPStorageHandler.h @@ -32,7 +32,8 @@ public: private: Coordination::ResponsePtr awaitKeeperResponse(std::shared_ptr request); - void performZooKeeperRequest(const Coordination::OpNum opnum, const std::string & storage_path, HTTPServerRequest & request, HTTPServerResponse & response); + void performZooKeeperRequest( + const Coordination::OpNum opnum, const std::string & storage_path, HTTPServerRequest & request, HTTPServerResponse & response); void performZooKeeperExistsRequest(const std::string & storage_path, HTTPServerResponse & response); void performZooKeeperListRequest(const std::string & storage_path, HTTPServerResponse & response);