diff --git a/app/rpcApi.js b/app/rpcApi.js index e65e795..3f48667 100644 --- a/app/rpcApi.js +++ b/app/rpcApi.js @@ -462,6 +462,80 @@ function getBlockData(rpcClient, blockHash, txLimit, txOffset) { }); } +function getHelp() { + return new Promise(function(resolve, reject) { + client.cmd('help', function(err, result, resHeaders) { + if (err) { + console.log("Error 32907th429ghf: " + err); + + reject(err); + + return; + } + + var lines = result.split("\n"); + var sections = []; + + lines.forEach(function(line) { + if (line.startsWith("==")) { + var sectionName = line.substring(2); + sectionName = sectionName.substring(0, sectionName.length - 2).trim(); + + sections.push({name:sectionName, methods:[]}); + + } else if (line.trim().length > 0) { + var methodName = line.trim(); + + if (methodName.includes(" ")) { + methodName = methodName.substring(0, methodName.indexOf(" ")); + } + + sections[sections.length - 1].methods.push({name:methodName, content:line.trim()}); + } + }); + + resolve(sections); + }); + }); +} + +function getRpcMethodHelp(methodName) { + return new Promise(function(resolve, reject) { + client.cmd('help', methodName, function(err, result, resHeaders) { + if (err) { + console.log("Error 237hwerf07wehg: " + err); + + reject(err); + + return; + } + + var str = result; + + var lines = str.split("\n"); + var argumentLines = []; + var catchArgs = false; + lines.forEach(function(line) { + if (line.trim().length == 0) { + catchArgs = false; + } + + if (catchArgs) { + argumentLines.push(line); + } + + if (line.trim() == "Arguments:") { + catchArgs = true; + } + }); + + console.log("argLines: " + argumentLines); + + resolve(result); + }); + }); +} + module.exports = { getBlockchainInfo: getBlockchainInfo, getNetworkInfo: getNetworkInfo, @@ -475,5 +549,7 @@ module.exports = { getRawTransaction: getRawTransaction, getRawTransactions: getRawTransactions, getMempoolStats: getMempoolStats, - getUptimeSeconds: getUptimeSeconds + getUptimeSeconds: getUptimeSeconds, + getHelp: getHelp, + getRpcMethodHelp: getRpcMethodHelp }; \ No newline at end of file diff --git a/public/css/styling.css b/public/css/styling.css index f2f0246..8e97597 100755 --- a/public/css/styling.css +++ b/public/css/styling.css @@ -3,7 +3,7 @@ body { } hr { - margin: 15px 0; + margin: 5px 0 15px 0; } img.header-image { @@ -40,6 +40,6 @@ code, .monospace { margin-right: 20px; } -.data-cell, .data-header { - text-align: right; +.nav-link { + padding: 0.5rem 1rem 0.5rem 0; } \ No newline at end of file diff --git a/routes/baseActionsRouter.js b/routes/baseActionsRouter.js index 3980ee9..8fa4772 100644 --- a/routes/baseActionsRouter.js +++ b/routes/baseActionsRouter.js @@ -382,7 +382,7 @@ router.get("/tx/:transactionId", function(req, res) { }); }); -router.get("/terminal", function(req, res) { +router.get("/rpc-terminal", function(req, res) { if (!env.debug) { res.send("Debug mode is off."); @@ -392,7 +392,7 @@ router.get("/terminal", function(req, res) { res.render("terminal"); }); -router.post("/terminal", function(req, res) { +router.post("/rpc-terminal", function(req, res) { if (!env.debug) { res.send("Debug mode is off."); @@ -437,5 +437,67 @@ router.post("/terminal", function(req, res) { }); }); +router.get("/rpc-browser", function(req, res) { + if (!env.debug) { + res.send("Debug mode is off."); + + return; + } + + rpcApi.getHelp().then(function(result) { + res.locals.gethelp = result; + + if (req.query.method) { + res.locals.method = req.query.method; + + rpcApi.getRpcMethodHelp(req.query.method.trim()).then(function(result2) { + res.locals.methodhelp = result2; + + var lines = result2.split("\n"); + + var params = []; + var line1Parts = lines[0].trim().split(" "); + line1Parts.shift(); + + params = line1Parts; + + res.locals.methodParams = params; + + console.log("params: " + params); + + if (req.query.execute) { + client.cmd([{method:req.query.method, params:[]}], function(err3, result3, resHeaders3) { + if (err3) { + res.locals.methodResult = err3; + + } else if (result3) { + res.locals.methodResult = result3; + + } else { + res.locals.methodResult = {"Error":"No response from node."}; + } + + res.render("browser"); + }); + } else { + res.render("browser"); + } + }).catch(function(err) { + res.locals.userMessage = "Error loading help content for method " + req.query.method + ": " + err; + + res.render("browser"); + }); + + } else { + res.render("browser"); + } + + }).catch(function(err) { + res.locals.userMessage = "Error loading help content: " + err; + + res.render("browser"); + }); +}); + module.exports = router; diff --git a/views/browser.pug b/views/browser.pug new file mode 100644 index 0000000..82116b0 --- /dev/null +++ b/views/browser.pug @@ -0,0 +1,56 @@ +extends layout + +block headContent + title RPC Browser + + style. + pre { + white-space: pre-wrap; /* Since CSS 2.1 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + } + +block content + h1 RPC Browser + hr + + + + div(class="row") + div(class="col-md-3") + each section, sectionIndex in gethelp + h4 #{section.name} + small (#{section.methods.length}) + hr + + div(class="mb-4") + ol(style="padding-left: 30px;") + each methodX, methodIndex in section.methods + li + a(href=("/rpc-browser?method=" + methodX.name), style=(methodX.name == method ? "font-weight: bold; font-style: italic;" : false)) #{methodX.name} + + div(class="col-md-9") + if (methodhelp) + div(class="row") + div(class="col-md-6") + h4(style="display: inline-block;") Command: #{method} + div(class="col-md-6") + a(href=("https://bitcoin.org/en/developer-reference#" + method), class="float-md-right") See developer docs » + + + + hr + + pre #{methodhelp} + + hr + + form(method="get") + input(type="hidden", name="method", value=method) + input(type="submit", name="execute", value="Execute", class="btn btn-primary btn-block") + + if (methodResult) + h5(class="mt-3") Result + + pre + code #{JSON.stringify(methodResult, null, 4)} + diff --git a/views/includes/blocks-list.pug b/views/includes/blocks-list.pug index 85bd34d..8002792 100644 --- a/views/includes/blocks-list.pug +++ b/views/includes/blocks-list.pug @@ -1,22 +1,22 @@ -table(class="table table-striped") +table(class="table table-striped table-responsive-sm") thead tr - th + //th th(class="data-header") Height th(class="data-header") Timestamp (utc) - th(class="data-header") Age - th(class="data-header") Transactions - th(class="data-header") Size (bytes) + th(class="data-header text-right") Age + th(class="data-header text-right") Transactions + th(class="data-header text-right") Size (bytes) tbody each block, blockIndex in blocks if (block) tr - th #{(blockIndex + blockOffset + 1).toLocaleString()} + //th #{(blockIndex + blockOffset + 1).toLocaleString()} td(class="data-cell monospace") a(href=("/block-height/" + block.height)) #{block.height.toLocaleString()} td(class="data-cell monospace") #{moment.utc(new Date(parseInt(block.time) * 1000)).format("Y-MM-DD HH:mm:ss")} - var timeAgo = moment.duration(moment.utc(new Date()).diff(moment.utc(new Date(parseInt(block.time) * 1000)))); - td(class="data-cell monospace") #{timeAgo.format()} - td(class="data-cell monospace") #{block.tx.length.toLocaleString()} - td(class="data-cell monospace") #{block.size.toLocaleString()} \ No newline at end of file + td(class="data-cell monospace text-right") #{timeAgo.format()} + td(class="data-cell monospace text-right") #{block.tx.length.toLocaleString()} + td(class="data-cell monospace text-right") #{block.size.toLocaleString()} \ No newline at end of file diff --git a/views/includes/pagination.pug b/views/includes/pagination.pug index 4f216a8..153810c 100644 --- a/views/includes/pagination.pug +++ b/views/includes/pagination.pug @@ -4,7 +4,7 @@ - } nav(aria-label="Page navigation") - ul(class="pagination pagination-lg justify-content-center") + ul(class="pagination pagination-lg justify-content-center flex-wrap") li(class="page-item", class=(pageNumber == 1 ? "disabled" : false)) a(class="page-link", href=(pageNumber == 1 ? "javascript:void(0)" : paginationUrlFunction(pageNumber - 1)), aria-label="Previous") span(aria-hidden="true") « diff --git a/views/index.pug b/views/index.pug index 309ec29..ed5be69 100644 --- a/views/index.pug +++ b/views/index.pug @@ -16,15 +16,15 @@ block content if (getblockchaininfo.initialblockdownload) small (#{(getblockchaininfo.headers - getblockchaininfo.blocks).toLocaleString()} behind) + span(style="float: right; font-size: 75%;") + a(href="/blocks") Browse Blocks » + - var blocks = latestBlocks; - var blockOffset = 0; include includes/blocks-list.pug - hr - a(href="/blocks", class="btn btn-primary btn-block") See more - hr div(class="") diff --git a/views/layout.pug b/views/layout.pug index 16b41b9..ac2f7d0 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -33,17 +33,26 @@ html ul(class="navbar-nav mr-auto") if (debug) li(class="nav-item") - a(href="/terminal", class="nav-link") RPC Terminal + a(href="/rpc-terminal", class="nav-link") RPC Terminal li(class="nav-item") - a(href="/node-info", class="nav-link") Node Info - li(class="nav-item") - a(href="/mempool", class="nav-link") Mempool Info + a(href="/node-info", class="nav-link") Node Details form(method="post", action="/search", class="form-inline") div(class="input-group") input(type="text", class="form-control form-control-sm", name="query", placeholder="block height, block hash, txid", value=(query), style="width: 250px;") span(class="input-group-btn") input(type="submit", class="btn btn-primary", value="Search") - + + div(class="container", style="margin-top: -1.0rem;") + ul(class="nav") + li(class="nav-item") + a(href="/rpc-browser", class="nav-link") RPC Browser + li(class="nav-item") + a(href="/rpc-terminal", class="nav-link") RPC Terminal + li(class="nav-item") + a(href="/mempool", class="nav-link") Mempool Summary + + hr + div(class="container") if (userMessage) div(class="alert", class=(userMessageType ? ("alert-" + userMessageType) : "alert-info"), role="alert") diff --git a/views/terminal.pug b/views/terminal.pug index d5762f0..368c41f 100644 --- a/views/terminal.pug +++ b/views/terminal.pug @@ -1,7 +1,7 @@ extends layout block content - h1 Terminal + h1 RPC Terminal hr :markdown-it @@ -32,7 +32,7 @@ block endOfBody postData.cmd = cmd; $.post( - "/terminal", + "/rpc-terminal", postData, function(response, textStatus, jqXHR) { var t = new Date().getTime();