mirror of
https://github.com/oxen-io/oxen-observer.git
synced 2023-12-14 09:22:54 +01:00
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:
parent
81d9dfdef7
commit
0bce9ab758
4 changed files with 108 additions and 23 deletions
77
observer.py
77
observer.py
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
Loading…
Reference in a new issue