Implement search

This works and displays a little differently than before:

- Only one search box for block/tx/SN instead of one for block/tx and
  one for SN
- Put the search box at the top right when the page is wide enough.
- When a search result is found we redirect to its proper URL rather
  than displaying on the search URL
This commit is contained in:
Jason Rhinelander 2020-08-31 16:56:21 -03:00
parent 81d9dfdef7
commit 0bce9ab758
4 changed files with 108 additions and 23 deletions

View File

@ -6,6 +6,7 @@ import babel.dates
import json
import sys
import statistics
import string
from base64 import b32encode, b16decode
from werkzeug.routing import BaseConverter
from pygments import highlight
@ -237,7 +238,7 @@ def main(refresh=None, page=0, per_page=None, first=None, last=None):
end_height = max(0, height - per_page*page - 1)
start_height = max(0, end_height - per_page + 1)
blocks = FutureJSON(lmq, lokid, 'rpc.get_block_headers_range', args={
blocks = FutureJSON(lmq, lokid, 'rpc.get_block_headers_range', cache_key='main', args={
'start_height': start_height,
'end_height': end_height,
'get_tx_hashes': True,
@ -322,20 +323,41 @@ def sns():
inactive_sns=inactive,
)
def tx_req(lmq, lokid, txid, **kwargs):
return FutureJSON(lmq, lokid, 'rpc.get_transactions', cache_seconds=10, cache_key='single',
args={
"txs_hashes": [txid],
"decode_as_json": True,
"tx_extra": True,
"prune": True,
},
**kwargs)
def sn_req(lmq, lokid, pubkey, **kwargs):
return FutureJSON(lmq, lokid, 'rpc.get_service_nodes', 5, cache_key='single',
args={"service_node_pubkeys": [pubkey]}, **kwargs
)
def block_req(lmq, lokid, hash_or_height, **kwargs):
if len(hash_or_height) <= 10 and hash_or_height.isdigit():
return FutureJSON(lmq, lokid, 'rpc.get_block_header_by_height', cache_key='single',
args={ "height": int(hash_or_height) }, **kwargs)
else:
return FutureJSON(lmq, lokid, 'rpc.get_block_header_by_hash', cache_key='single',
args={ "hash": hash_or_height }, **kwargs)
@app.route('/sn/<hex64:pubkey>')
@app.route('/service_node/<hex64:pubkey>') # For backwards compatibility with old explorer URLs
@app.route('/sn/<hex64:pubkey>')
def show_sn(pubkey):
lmq, lokid = lmq_connection()
info = FutureJSON(lmq, lokid, 'rpc.get_info', 1)
hfinfo = FutureJSON(lmq, lokid, 'rpc.hard_fork_info', 10)
sn = FutureJSON(lmq, lokid, 'rpc.get_service_nodes', 5, cache_key='single', args={
"service_node_pubkeys": [pubkey]}).get()
sn = sn_req(lmq, lokid, pubkey).get()
if 'service_node_states' not in sn or not sn['service_node_states']:
return flask.render_template('not_found.html',
info=info.get(),
hf=hfinfo.get(),
type='sn',
id=pubkey,
)
@ -362,12 +384,7 @@ def show_sn(pubkey):
def show_tx(txid, more_details=False):
lmq, lokid = lmq_connection()
info = FutureJSON(lmq, lokid, 'rpc.get_info', 1)
txs = FutureJSON(lmq, lokid, 'rpc.get_transactions', cache_seconds=10, args={
"txs_hashes": [txid],
"decode_as_json": True,
"tx_extra": True,
"prune": True,
}).get()
txs = tx_req(lmq, lokid, txid).get()
if 'txs' not in txs or not txs['txs']:
return flask.render_template('not_found.html',
@ -434,3 +451,41 @@ def show_tx(txid, more_details=False):
block_info=block_info,
**more_details,
)
@app.route('/search')
def search():
lmq, lokid = lmq_connection()
info = FutureJSON(lmq, lokid, 'rpc.get_info', 1)
val = flask.request.args.get('value')
if val and len(val) < 10 and val.isdigit(): # Block height
return show_block(val)
if not val or len(val) != 64 or any(c not in string.hexdigits for c in val):
return flask.render_template('not_found.html',
info=info.get(),
type='bad_search',
id=val,
)
# Initiate all the lookups at once, then redirect to whichever one responds affirmatively
snreq = sn_req(lmq, lokid, val)
blreq = block_req(lmq, lokid, val, fail_okay=True)
txreq = tx_req(lmq, lokid, val)
sn = snreq.get()
if 'service_node_states' in sn and sn['service_node_states']:
return flask.redirect(flask.url_for('show_sn', pubkey=val), code=301)
bl = blreq.get()
if bl and 'block_header' in bl and bl['block_header']:
return flask.redirect(flask.url_for('show_block', hash=val), code=301)
tx = txreq.get()
if tx and 'txs' in tx and tx['txs']:
return flask.redirect(flask.url_for('show_tx', txid=val), code=301)
return flask.render_template('not_found.html',
info=info.get(),
type='search',
id=val,
)

View File

@ -13,6 +13,32 @@ h1, h2, h3, h4, h5, h6 {
margin-bottom: 0.2em;
}
#header>#header-content {
display: flex;
flex-wrap: wrap;
padding-top: 12px;
padding-bottom: 12px;
}
#header h1 {
flex: 50%;
min-width: 500px;
}
#header form.top-search {
flex: 50%;
min-width: 500px;
display: flex;
}
#header form.top-search input[type="text"] {
flex-grow: 1;
flex-shrink: 1;
}
#header form.top-search input[type="submit"] {
flex: 0 0 12ex;
margin-right: 0;
}
.general_info {
font-size: 12px;
margin-top: 5px;
@ -180,7 +206,7 @@ form {
display: inline-block;
}
.style-1 input[type="text"] {
.top-search input[type="text"] {
padding: 2px;
border: solid 1px rgba(255, 255, 255, 0.25);
background-color: #1a1a1a;
@ -190,8 +216,8 @@ form {
color: white;
}
.style-1 input[type="text"]:focus,
.style-1 input[type="text"].focus {
.top-search input[type="text"]:focus,
.top-search input[type="text"].focus {
border: solid 1px #008522;
}
h1, h2, h3, h4, h5, h6 { color: white; }

View File

@ -15,7 +15,7 @@
<body>
<div id="header" class="Wrapper">
{% block header %}
<div>
<div id="header-content">
<h1 class="Header"><a href="/">Loki
{%if info and info.testnet%}
<span style="color:#ff6b62">Testnet</span>
@ -23,16 +23,12 @@
<span style="color:#af5bd2">Devnet</span>
{%endif%}
Blockchain Explorer</a></h1>
<form action="/search" method="get" style="width: 100%; margin-top:15px;" class="style-1">
<input type="text" name="value" size="120" placeholder="Block Height, Block Hash, Transaction Hash">
<form action="/search" method="get" class="top-search">
<input type="text" name="value" size="64" placeholder="Block height, Block hash, Transaction ID, or Service Node pubkey">
<input type="submit" class="PageButton" value="Search">
</form>
<form action="/search_service_node" method="get" style="width: 100%; margin-top:15px; margin-bottom: 1em;" class="style-1">
<input type="text" name="value" size="120" placeholder="Service Node Public Key">
<input type="submit" class="PageButton" value="Search">
</form>
<div class="TitleUnderliner" style="margin-bottom: 1em"></div>
</div>
<div class="TitleUnderliner" style="margin-bottom: 1em"></div>
{% endblock %}
</div>

View File

@ -7,8 +7,16 @@
{%if type == 'tx'%}
<h2>The transaction with id <code>{{id}}</code> was not found on the blockchain.</h2>
{%elif type == 'sn'%}
<h2>The service node with pubkey <code>{{id}}</code> is not currently registered.</h2>
{%elif type == 'bad_search'%}
<h2>Invalid search request: <code>{{id}}</code></h2>
<h3>Please enter a block hash, height, transaction id, or service node public key.</h3>
{%elif type == 'search'%}
<h2>Not found: <code>{{id}}</code></h2>
<h3>Couldn't find a block, transaction, or service node matching your query.</h3>
{%else%}
<h3>Whoops! Couldn't find what you were looking for.</h3>
<h3>Whoops! Couldn't find what you were looking for.</h3>
{%endif%}
</div>