From f5f2e76a7c6778c7dcd201954fec837f1d718722 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Thu, 7 Dec 2017 19:17:46 -0800 Subject: [PATCH 1/4] update UI and serve node status information in JSON --- server/server.py | 57 ++++----- server/static/favicon.ico | Bin 0 -> 22486 bytes server/static/include/css/index.css | 173 ++++++++++++++++++++++++++++ server/static/index.html | 139 ++++++++++++++-------- 4 files changed, 292 insertions(+), 77 deletions(-) create mode 100644 server/static/favicon.ico create mode 100644 server/static/include/css/index.css diff --git a/server/server.py b/server/server.py index 3d71ac8..290c50b 100644 --- a/server/server.py +++ b/server/server.py @@ -14,6 +14,8 @@ app = Sanic(__name__) app.static('/', './static/index.html') +app.static('/include', './static/include') +app.static('/favicon.ico', './static/favicon.ico') async def boot(): @@ -38,43 +40,34 @@ async def boot(): @app.route("/status") async def status(request): - response_text = "" - response_text += "NODE 1:\n\n" - - proc = subprocess.run( - ["/usr/bin/python3", "/usr/local/bin/validator-info", "-v", "--basedir", "/home/indy/.mnt/node1/sandbox/"], - stdout=subprocess.PIPE, - universal_newlines=True) - - response_text += proc.stdout - response_text += "\n\n" - response_text += "NODE 2:\n\n" - - proc = subprocess.run( - ["/usr/bin/python3", "/usr/local/bin/validator-info", "-v", "--basedir", "/home/indy/.mnt/node2/sandbox/"], - stdout=subprocess.PIPE, - universal_newlines=True) + nodes = ["node1", "node2", "node3", "node4"] - response_text += proc.stdout - response_text += "\n\n" - response_text += "NODE 3:\n\n" + response = [] + for idx,node_name in enumerate(nodes): + proc = subprocess.run( + ["/usr/bin/python3", "/usr/local/bin/validator-info", "-v", "--json", "--basedir", "/home/indy/.mnt/" + node_name + "/sandbox/"], + stdout=subprocess.PIPE, + universal_newlines=True) + parsed = json.loads(proc.stdout) + if parsed: + response.append(parsed) - proc = subprocess.run( - ["/usr/bin/python3", "/usr/local/bin/validator-info", "-v", "--basedir", "/home/indy/.mnt/node3/sandbox/"], - stdout=subprocess.PIPE, - universal_newlines=True) + return text(json.dumps(response)) - response_text += proc.stdout - response_text += "\n\n" - response_text += "NODE 4:\n\n" - proc = subprocess.run( - ["/usr/bin/python3", "/usr/local/bin/validator-info", "-v", "--basedir", "/home/indy/.mnt/node4/sandbox/"], - stdout=subprocess.PIPE, - universal_newlines=True) +@app.route("/status/text") +async def status(request): + nodes = ["node1", "node2", "node3", "node4"] - response_text += proc.stdout - response_text += "\n\n" + response_text = "" + for idx,node_name in enumerate(nodes): + proc = subprocess.run( + ["/usr/bin/python3", "/usr/local/bin/validator-info", "-v", "--basedir", "/home/indy/.mnt/" + node_name + "/sandbox/"], + stdout=subprocess.PIPE, + universal_newlines=True) + if idx > 0: + response_text += "\n\n" + response_text += node_name + "\n\n" + proc.stdout return text(response_text) diff --git a/server/static/favicon.ico b/server/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1841396d21ce974cdec16f7414c88683cd36af16 GIT binary patch literal 22486 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 C+3a-y literal 0 HcmV?d00001 diff --git a/server/static/include/css/index.css b/server/static/include/css/index.css new file mode 100644 index 0000000..ff18ae8 --- /dev/null +++ b/server/static/include/css/index.css @@ -0,0 +1,173 @@ +body { + color: #222; + font-family: 'Open Sans', sans-serif; + font-size: 16px; + line-height: 1.45; + margin: 0; +} + +header { + background-color: #036; + border-bottom: 2px solid #fcba19; + color: #fcba19; + padding: 10px 2rem 10px 2.2rem; +} +header h1 { + color: #e9f5ff; + display: inline-block; + margin: 0; + padding: 0; +} +header p.lead { + display: inline-block; + margin-left: 3rem; +} + +main { + padding: 1rem 1rem; +} + +.row-outer { + display: flex; + flex-flow: row nowrap; + width: 100%; +} + +.col-main { + flex: 1 1 auto; + padding: 1rem; +} +.col-tools { + flex: 1 0 25rem; + padding: 1rem; + width: 25rem; +} + +a { + color: #1a5a96; + text-decoration: none +} +a:hover, a:focus { + color: #0f3355; + text-decoration: underline; +} +a.tool { + font-size: 110%; +} +a.tool-button { + background-color: #f0f3ff; + border: 1px solid #58a; + border-radius: 3px; + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.15); + display: inline-block; + margin: 0.2rem 0; + padding: 0.3rem 0.6rem; +} +a.tool-button:hover, a.tool-button:focus { + background-color: #036; + color: #e9f5ff; + text-decoration: none; +} +a.tool .left, a.tool-button .left { + font-size: 90%; + margin-right: 0.4rem; +} + +small, .small { + font-size: 85%; +} + +code { + -background-color: #fef9f9; + -border: 1px solid #b67; + background-color: #fefdf8; + border: 1px solid #f9b714; + border-radius: 0.3em; + display: inline-block; + padding: 4px; +} + +h2 { + font-weight: 500; +} +h2 .fa { + font-size: 85%; + opacity: 0.9; + vertical-align: 2px; +} + +.panel { + border-radius: 0.5rem; + margin-bottom: 1.5rem; + overflow: hidden; +} +.panel-heading, .panel-body { + overflow: hidden; + padding: 0.25rem 0.7rem; +} +.panel-body p { + margin: 0.5rem 0; +} +.panel-default { + background: #f0f3ff; + border: 1px solid #eee; + box-shadow: 0 4px 3px 3px rgba(0, 0, 0, 0.02); +} +.panel-default .panel-heading { + background: #036; + border-bottom: 1px solid #eee; + color: #fafaec; + margin-bottom: 4px; +} + +.panel-side { + background-color: #f7f6f6; + border: 2px solid #f0ecec; + box-shadow: 0 4px 3px 3px rgba(0, 0, 0, 0.02); +} +.panel-side .panel-heading { + color: #585555; +} +.panel-side .panel-heading::after { + background: #fdfdfd; + border-bottom: 1px solid #ebe6e6; + border-radius: 3px; + content: ' '; + display: block; + height: 3px; +} + +.panel-heading h2 { + padding: 0; + margin: 0; +} + +.top-label { + color: #555; + font-size: 95%; + font-weight: 600; + line-height: 1.6; +} +.tool-tag { + -background: rgba(254, 253, 237, 1.0); + border-radius: 2px; + -border: 1px solid rgba(215, 220, 255, 0.9); + color: #555; + display: inline-block; + font-size: 0.75rem; + font-weight: 600; + margin-left: 1rem; + padding: 1.5px 4px; + vertical-align: 0.1rem; +} + +@media screen and (max-width: 900px) { + .row-outer { + display: block; + } + .col-tools { + padding-top: 0; + width: auto; + } +} + diff --git a/server/static/index.html b/server/static/index.html index 2433c00..cd6abde 100644 --- a/server/static/index.html +++ b/server/static/index.html @@ -2,56 +2,105 @@ - - - + + + BCovrin Indy Network + + + -

BCovrin

-

BC's Provisional Indy Network

+
+

BCovrin

+

BC's Provisional Indy Network

+
-

- Connect to the Network -

-

- Download the genesis transaction file to connect to the network. -
- genesis -

+
+
+
+
+
+

+ + Validator Node Status +

+
+
+

+ View detailed information about the status of the running validator nodes: +

+

+ Detailed Status + JSON +

+
+
-

- Authorize a New DID -

-

- Easily write a new DID to the ledger for new identity owners. -
- Format: /register?seed=BC-Registrar-Agent-0000000000000 -
- register -

+
+
+

+ + Ledger State +

+
+
+

+ View the state of the ledgers: +

+

+ Domain  (pretty) JSON +
+ Pool (pretty) JSON +
+ Config (pretty) JSON +

+
+
+
-

- Validator Node Status -

-

- View detailed information about the status of the running validator nodes. -
- status -

+ +
+
- \ No newline at end of file + + From f417f68e11fdeebfc688f488ddf7b89f502a76af Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Fri, 8 Dec 2017 11:06:22 -0800 Subject: [PATCH 2/4] add visual summary of validator node status --- server/server.py | 9 +++- server/static/include/css/index.css | 64 ++++++++++++++++++++++- server/static/include/js/index.js | 80 +++++++++++++++++++++++++++++ server/static/index.html | 39 ++++++++++++-- 4 files changed, 183 insertions(+), 9 deletions(-) create mode 100644 server/static/include/js/index.js diff --git a/server/server.py b/server/server.py index 290c50b..7d2da9a 100644 --- a/server/server.py +++ b/server/server.py @@ -10,7 +10,7 @@ from sanic import Sanic -from sanic.response import text +from sanic.response import text, json as sanic_json app = Sanic(__name__) app.static('/', './static/index.html') @@ -18,6 +18,11 @@ app.static('/favicon.ico', './static/favicon.ico') +def json_reponse(data): + headers = {'Access-Control-Allow-Origin': '*'} + return sanic_json(data, headers=headers) + + async def boot(): global pool global trust_anchor @@ -52,7 +57,7 @@ async def status(request): if parsed: response.append(parsed) - return text(json.dumps(response)) + return json_reponse(response) @app.route("/status/text") diff --git a/server/static/include/css/index.css b/server/static/include/css/index.css index ff18ae8..d90057c 100644 --- a/server/static/include/css/index.css +++ b/server/static/include/css/index.css @@ -17,6 +17,7 @@ header h1 { display: inline-block; margin: 0; padding: 0; + vertical-align: -0.15em; } header p.lead { display: inline-block; @@ -77,14 +78,14 @@ small, .small { font-size: 85%; } -code { +.tag { -background-color: #fef9f9; -border: 1px solid #b67; background-color: #fefdf8; border: 1px solid #f9b714; border-radius: 0.3em; display: inline-block; - padding: 4px; + padding: 0.25em; } h2 { @@ -161,6 +162,65 @@ h2 .fa { vertical-align: 0.1rem; } +.error { + color: #b55; +} + +.panel-node { + background: #fff; + -box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.08); +} +.panel-node .panel-status { +} +.node-status { + display: flex; + padding: 0.8rem; + position: relative; +} +.node-status.template { + display: none; +} +.node-status:not(.template) + .node-status { + border-top: 1px solid #ddd; +} +.node-status-left { + flex: 0 1 30%; + min-width: 120px; +} +.node-name { + position: absolute; + text-align: center; + top: 50px; + width: 100px; + z-index: 2; +} +.node-indicator { + border: 8px solid #ddd; + border-radius: 50%; + box-sizing: border-box; + width: 100px; + height: 100px; +} +.node-status-right { + flex: 1 1 auto; + overflow: hidden; + word-wrap: break-word; +} + +.props { + font-size: 80%; + margin: 0; +} +.props dt { + display: none; +} +.props dd { + margin: 0; +} +.props label { + color: #888; +} + @media screen and (max-width: 900px) { .row-outer { display: block; diff --git a/server/static/include/js/index.js b/server/static/include/js/index.js new file mode 100644 index 0000000..0be2d82 --- /dev/null +++ b/server/static/include/js/index.js @@ -0,0 +1,80 @@ +function fetch_node_status(callb) { + var oReq = new XMLHttpRequest(); + oReq.addEventListener('load', function(evt) { + callb(oReq.response, evt); + }); + oReq.addEventListener('error', function(evt) { + callb(null, evt); + }); + oReq.responseType = 'json'; + oReq.open('GET', './status'); + oReq.send(); +} + +fetch_node_status(function(status) { + console.log(status); + + var panel = document.querySelector('.panel-node-status'); + var load = panel && panel.querySelector('.loading'); + var err = panel && panel.querySelector('.error'); + if(load) load.style.display = 'none'; + + if(! Array.isArray(status)) { + if(err) err.style.display = null; + return; + } + + if(! panel) return; + var tpl = panel.querySelector('.node-status.template'); + if(! tpl) return; + + for(var idx = 0; idx < status.length; idx ++) { + var node = status[idx]; + var div = tpl.cloneNode(true); + tpl.parentNode.appendChild(div); + div.querySelector('.nodeval-name').innerText = node.alias; + div.querySelector('.nodeval-did').innerText = node.did; + var state = node.state; + if(! state) state = 'unknown'; + if(! node.enabled) state += ' (disabled)' + div.querySelector('.nodeval-state').innerText = state; + div.querySelector('.nodeval-indyver').innerText = node.software['indy-node']; + + var upt = node.metrics.uptime, + upt_s = upt % 60, + upt_m = Math.floor(upt % 3600 / 60), + upt_h = Math.floor(upt % 86400 / 3600), + upt_d = Math.floor(upt / 86400), + upt_parts = []; + if(upt_d) + upt_parts.push('' + upt_d + ' days'); + if(upt_h || upt_parts.length) + upt_parts.push('' + upt_h + ' hours'); + if(upt_m || upt_parts.length) + upt_parts.push('' + upt_m + ' minutes'); + upt_parts.push('' + upt_s + ' seconds'); + div.querySelector('.nodeval-uptime').innerText = upt_parts.join(', '); + + var unreach = div.querySelector('.node-unreach'); + if(node.pool.unreachable.count) { + div.querySelector('.nodeval-unreach').innerText = node.pool.unreachable.list.join(', '); + } else { + unreach.style.display = 'none'; + } + + var txns = [], + tx_avgs = node.metrics['average-per-second'], + tx_counts = node.metrics['transaction-count']; + txns.push('' + tx_counts.config + ' config') + txns.push('' + tx_counts.ledger + ' ledger') + txns.push('' + tx_counts.pool + ' pool') + txns.push('' + tx_avgs['read-transactions'] + '/s read') + txns.push('' + tx_avgs['write-transactions'] + '/s write') + div.querySelector('.nodeval-txns').innerText = txns.join(', '); + + //metrics: {average-per-second: {read-transactions: 0, write-transactions: 0}, transaction-count: {ledger: 5, pool: 4, config: 0}, uptime: 781} + //pool: {reachable: {count: 4, list: ["Node1", "Node2", "Node3", "Node4"]}, total-count: 4, unreachable: {count: 0, list: []}} + div.classList.remove('template'); + } +}); + diff --git a/server/static/index.html b/server/static/index.html index cd6abde..6a3b160 100644 --- a/server/static/index.html +++ b/server/static/index.html @@ -18,7 +18,7 @@

BCovrin

-
+

@@ -26,17 +26,45 @@

+
+
+

+ Loading status ... +

+
+ +
+
+ +
+
+
+
+
Node properties
+
8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb
+
unknown
+
6 days, 11 hours, 9 minutes, 35 seconds
+
0 config, 46 ledger, 4 pool, 0.00/s read, 0.00/s write
+
Node2, Node3
+
1.2.210
+
+
+
+

View detailed information about the status of the running validator nodes:

- Detailed Status - JSON + Detailed Status

-
+

@@ -90,7 +118,7 @@

Easily write a new DID to the ledger for new identity owners.

- Format:
/register?seed=BC-Registrar-Agent-0000000000000 + Format:
/register?seed=BC-Registrar-Agent-0000000000000

Register DID @@ -101,6 +129,7 @@

+ From 06b8e6ec678799ccfcffab66bf277c72d20959d6 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Fri, 8 Dec 2017 15:14:13 -0800 Subject: [PATCH 3/4] remove duplication; support additional read_ledger parameters --- server/server.py | 132 ++++++++++++++++------------------------------- 1 file changed, 45 insertions(+), 87 deletions(-) diff --git a/server/server.py b/server/server.py index 7d2da9a..772b942 100644 --- a/server/server.py +++ b/server/server.py @@ -17,12 +17,46 @@ app.static('/include', './static/include') app.static('/favicon.ico', './static/favicon.ico') +python_path = "/usr/bin/python3" + def json_reponse(data): headers = {'Access-Control-Allow-Origin': '*'} return sanic_json(data, headers=headers) +def validator_info(node_name, as_json=True): + args = [python_path, "/usr/local/bin/validator-info", "-v"] + if as_json: + args.append("--json") + args.extend(["--basedir", "/home/indy/.mnt/" + node_name + "/sandbox/"]) + proc = subprocess.run(args, stdout=subprocess.PIPE, universal_newlines=True) + if as_json: + return json.loads(proc.stdout) + return proc + + +def read_ledger(ledger, seq_no=0, seq_to=100, node_name='node1', pretty=False): + if ledger != "domain" and ledger != "pool" and ledger != "config": + raise ValueError("Unsupported ledger type: {}".format(ledger)) + args = [python_path, "/usr/local/bin/read_ledger", "--type", ledger] + if seq_no > 0: + args.extend(["--seq_no", str(seq_no)]) + args.extend(["--to", str(seq_to)]) + args.extend(["--base_dir", "/home/indy/.mnt/" + node_name]) + proc = subprocess.run(args, stdout=subprocess.PIPE, universal_newlines=True) + + if pretty: + lines = proc.stdout.splitlines() + resp = [] + for line in lines: + parsed = json.loads(line) + resp.append(json.dumps(parsed, indent=4, sort_keys=True)) + return "\n\n".join(resp) + + return proc.stdout + + async def boot(): global pool global trust_anchor @@ -49,11 +83,7 @@ async def status(request): response = [] for idx,node_name in enumerate(nodes): - proc = subprocess.run( - ["/usr/bin/python3", "/usr/local/bin/validator-info", "-v", "--json", "--basedir", "/home/indy/.mnt/" + node_name + "/sandbox/"], - stdout=subprocess.PIPE, - universal_newlines=True) - parsed = json.loads(proc.stdout) + parsed = validator_info(node_name) if parsed: response.append(parsed) @@ -66,96 +96,24 @@ async def status(request): response_text = "" for idx,node_name in enumerate(nodes): - proc = subprocess.run( - ["/usr/bin/python3", "/usr/local/bin/validator-info", "-v", "--basedir", "/home/indy/.mnt/" + node_name + "/sandbox/"], - stdout=subprocess.PIPE, - universal_newlines=True) + proc = validator_info(node_name) if idx > 0: - response_text += "\n\n" + response_text += "\n" response_text += node_name + "\n\n" + proc.stdout return text(response_text) -@app.route("/ledger/domain") -async def ledger_domain(request): - proc = subprocess.run( - ["/usr/bin/python3", "/usr/local/bin/read_ledger", "--type", "domain", "--base_dir", "/home/indy/.mnt/node1"], - stdout=subprocess.PIPE, - universal_newlines=True) - - return text(proc.stdout) - - -@app.route("/ledger/domain/pretty") -async def ledger_domain_pretty(request): - proc = subprocess.run( - ["/usr/bin/python3", "/usr/local/bin/read_ledger", "--type", "domain", "--base_dir", "/home/indy/.mnt/node1"], - stdout=subprocess.PIPE, - universal_newlines=True) - - resp_text = "" - - lines = proc.stdout.splitlines() - for line in lines: - parsed = json.loads(line) - resp_text += json.dumps(parsed, indent=4, sort_keys=True) + "\n\n" - - return text(resp_text) - - -@app.route("/ledger/pool") -async def ledger_pool(request): - proc = subprocess.run( - ["/usr/bin/python3", "/usr/local/bin/read_ledger", "--type", "pool", "--base_dir", "/home/indy/.mnt/node1"], - stdout=subprocess.PIPE, - universal_newlines=True) +@app.route("/ledger/") +async def ledger(request, ledger_name): + response = read_ledger(ledger_name) + return text(response) - return text(proc.stdout) - - -@app.route("/ledger/pool/pretty") -async def ledger_pool_pretty(request): - proc = subprocess.run( - ["/usr/bin/python3", "/usr/local/bin/read_ledger", "--type", "pool", "--base_dir", "/home/indy/.mnt/node1"], - stdout=subprocess.PIPE, - universal_newlines=True) - - resp_text = "" - - lines = proc.stdout.splitlines() - for line in lines: - parsed = json.loads(line) - resp_text += json.dumps(parsed, indent=4, sort_keys=True) + "\n\n" - - return text(resp_text) - - -@app.route("/ledger/config") -async def ledger_config(request): - proc = subprocess.run( - ["/usr/bin/python3", "/usr/local/bin/read_ledger", "--type", "config", "--base_dir", "/home/indy/.mnt/node1"], - stdout=subprocess.PIPE, - universal_newlines=True) - - return text(proc.stdout) - - -@app.route("/ledger/config/pretty") -async def ledger_config_pretty(request): - proc = subprocess.run( - ["/usr/bin/python3", "/usr/local/bin/read_ledger", "--type", "config", "--base_dir", "/home/indy/.mnt/node1"], - stdout=subprocess.PIPE, - universal_newlines=True) - - resp_text = "" - - lines = proc.stdout.splitlines() - for line in lines: - parsed = json.loads(line) - resp_text += json.dumps(parsed, indent=4, sort_keys=True) + "\n\n" - return text(resp_text) +@app.route("/ledger//pretty") +async def ledger_pretty(request, ledger_name): + response = read_ledger(ledger_name, pretty=True) + return text(response) # Expose genesis transaction for easy connection. From d79592c36af687f2d9eac8cc2deb25c40dccaea4 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Fri, 8 Dec 2017 17:00:28 -0800 Subject: [PATCH 4/4] start adding formatting of ledger entries (limited) --- server/server.py | 124 ++++++++++++++++++++++++++---- server/static/include/js/index.js | 6 +- 2 files changed, 111 insertions(+), 19 deletions(-) diff --git a/server/server.py b/server/server.py index 772b942..bde4224 100644 --- a/server/server.py +++ b/server/server.py @@ -1,6 +1,7 @@ #! /usr/bin/python3 import asyncio +from datetime import datetime import json import subprocess @@ -10,7 +11,7 @@ from sanic import Sanic -from sanic.response import text, json as sanic_json +from sanic.response import text as sanic_text, json as sanic_json, html as sanic_html app = Sanic(__name__) app.static('/', './static/index.html') @@ -19,6 +20,31 @@ python_path = "/usr/bin/python3" +indy_txn_types = { + "0": "NODE", + "1": "NYM", + "3": "GET_TXN", + "100": "ATTRIB", + "101": "SCHEMA", + "102": "CLAIM_DEF", + "103": "DISCO", + "104": "GET_ATTR", + "105": "GET_NYM", + "107": "GET_SCHEMA", + "108": "GET_CLAIM_DEF", + "109": "POOL_UPGRADE", + "110": "NODE_UPGRADE", + "111": "POOL_CONFIG", + "112": "CHANGE_KEY", +} + +indy_role_types = { + "0": "TRUSTEE", + "2": "STEWARD", + "100": "TGB", + "101": "TRUST_ANCHOR", +} + def json_reponse(data): headers = {'Access-Control-Allow-Origin': '*'} @@ -26,9 +52,11 @@ def json_reponse(data): def validator_info(node_name, as_json=True): - args = [python_path, "/usr/local/bin/validator-info", "-v"] + args = [python_path, "/usr/local/bin/validator-info"] if as_json: args.append("--json") + else: + args.append("-v") args.extend(["--basedir", "/home/indy/.mnt/" + node_name + "/sandbox/"]) proc = subprocess.run(args, stdout=subprocess.PIPE, universal_newlines=True) if as_json: @@ -36,7 +64,7 @@ def validator_info(node_name, as_json=True): return proc -def read_ledger(ledger, seq_no=0, seq_to=100, node_name='node1', pretty=False): +def read_ledger(ledger, seq_no=0, seq_to=100, node_name='node1', format="data"): if ledger != "domain" and ledger != "pool" and ledger != "config": raise ValueError("Unsupported ledger type: {}".format(ledger)) args = [python_path, "/usr/local/bin/read_ledger", "--type", ledger] @@ -46,14 +74,20 @@ def read_ledger(ledger, seq_no=0, seq_to=100, node_name='node1', pretty=False): args.extend(["--base_dir", "/home/indy/.mnt/" + node_name]) proc = subprocess.run(args, stdout=subprocess.PIPE, universal_newlines=True) - if pretty: + if format == "pretty" or format == "data": lines = proc.stdout.splitlines() resp = [] for line in lines: parsed = json.loads(line) - resp.append(json.dumps(parsed, indent=4, sort_keys=True)) - return "\n\n".join(resp) - + if format == "pretty": + resp.append(json.dumps(parsed, indent=4, sort_keys=True)) + else: + resp.append(parsed) + if format == "pretty": + return "\n\n".join(resp) + return resp + + # format = json return proc.stdout @@ -96,24 +130,84 @@ async def status(request): response_text = "" for idx,node_name in enumerate(nodes): - proc = validator_info(node_name) + proc = validator_info(node_name, as_json=False) if idx > 0: response_text += "\n" response_text += node_name + "\n\n" + proc.stdout - return text(response_text) + return sanic_text(response_text) @app.route("/ledger/") async def ledger(request, ledger_name): - response = read_ledger(ledger_name) - return text(response) + response = read_ledger(ledger_name, format="json") + return sanic_text(response) @app.route("/ledger//pretty") async def ledger_pretty(request, ledger_name): - response = read_ledger(ledger_name, pretty=True) - return text(response) + response = read_ledger(ledger_name, format="pretty") + return sanic_text(response) + + +@app.route("/ledger//text") +async def ledger_text(request, ledger_name): + response = read_ledger(ledger_name) + text = [] + for seq_no, txn in response: + if len(text): + text.append("") + + type_name = indy_txn_types.get(txn['type'], txn['type']) + text.append("[" + str(seq_no) + "] TYPE: " + type_name) + + if type_name == "NYM": + text.append("DEST: " + txn['dest']) + + role = txn.get('role') + if role != None: + role_name = indy_role_types.get(role, role) + text.append("ROLE: " + role_name) + + verkey = txn.get('verkey') + if verkey != None: + text.append("VERKEY: " + verkey) + + ident = txn.get('identifier') + if ident != None: + text.append("IDENT: " + ident) + + txnTime = txn.get('txnTime') + if txnTime != None: + ftime = datetime.fromtimestamp(txnTime).strftime('%Y-%m-%d %H:%M:%S') + text.append("TIME: " + ftime) + + reqId = txn.get('reqId') + if reqId != None: + text.append("REQ ID: " + str(reqId)) + + refNo = txn.get('ref') + if refNo != None: + text.append("REF: " + str(refNo)) + + txnId = txn.get('txnId') + if txnId != None: + text.append("TXN ID: " + txnId) + + if type_name == "SCHEMA" or type_name == "CLAIM_DEF" or type_name == "NODE": + data = txn.get('data') + text.append("DATA:") + text.append(json.dumps(data, indent=4)) + + sig = txn.get('signature') + if sig != None: + text.append("SIGNATURE: " + sig) + + sig_type = txn.get('signature_type') + if sig_type != None: + text.append("SIGNATURE TYPE: " + sig_type) + + return sanic_text("\n".join(text)) # Expose genesis transaction for easy connection. @@ -123,7 +217,7 @@ async def genesis(request): '/home/indy/.indy-cli/networks/sandbox/pool_transactions_genesis', 'r') as content_file: gensis = content_file.read() - return text(gensis) + return sanic_text(gensis) # Easily write dids for new identity owners @@ -152,7 +246,7 @@ async def register(request): print('\n\nSend Nym: ' + str(ag) + '\n\n') await trust_anchor.send_nym(ag.did, ag.verkey) - return text(new_did.did + ' successfully written to ledger') + return sanic_text(new_did.did + ' successfully written to ledger') if __name__ == '__main__': diff --git a/server/static/include/js/index.js b/server/static/include/js/index.js index 0be2d82..caa6267 100644 --- a/server/static/include/js/index.js +++ b/server/static/include/js/index.js @@ -1,4 +1,4 @@ -function fetch_node_status(callb) { +function fetch_validator_status(callb) { var oReq = new XMLHttpRequest(); oReq.addEventListener('load', function(evt) { callb(oReq.response, evt); @@ -11,9 +11,7 @@ function fetch_node_status(callb) { oReq.send(); } -fetch_node_status(function(status) { - console.log(status); - +fetch_validator_status(function(status) { var panel = document.querySelector('.panel-node-status'); var load = panel && panel.querySelector('.loading'); var err = panel && panel.querySelector('.error');