feat: integrate team feedback
Changes: + present not-found page for unhandled uri paths, eg /stuff + display the tip-height on front page + add favicon and neptune logo to front page + use ServerDir instead of ServeFile for /image and /css + add help tooltips for fields on front page + use monospace font for digests
This commit is contained in:
parent
d7d1778fea
commit
faf303bbdd
@ -1,7 +1,9 @@
|
|||||||
use crate::html::component::header::HeaderHtml;
|
use crate::html::component::header::HeaderHtml;
|
||||||
use crate::http_util::not_found_html_err;
|
use crate::http_util::not_found_html_err;
|
||||||
|
use crate::http_util::not_found_html_handler;
|
||||||
use crate::model::app_state::AppState;
|
use crate::model::app_state::AppState;
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
|
use axum::http::StatusCode;
|
||||||
use axum::response::Html;
|
use axum::response::Html;
|
||||||
use axum::response::Response;
|
use axum::response::Response;
|
||||||
use html_escaper::Escape;
|
use html_escaper::Escape;
|
||||||
@ -39,3 +41,7 @@ pub fn not_found_html_response(
|
|||||||
) -> Response {
|
) -> Response {
|
||||||
not_found_html_err(not_found_page(State(state), error_msg))
|
not_found_html_err(not_found_page(State(state), error_msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn not_found_html_fallback(state: Arc<AppState>) -> (StatusCode, Html<String>) {
|
||||||
|
not_found_html_handler(not_found_page(State(state), None))
|
||||||
|
}
|
||||||
|
|||||||
@ -1,22 +1,28 @@
|
|||||||
|
use crate::html::page::not_found::not_found_html_response;
|
||||||
use crate::model::app_state::AppState;
|
use crate::model::app_state::AppState;
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum::response::Html;
|
use axum::response::Html;
|
||||||
|
use axum::response::Response;
|
||||||
use html_escaper::Escape;
|
use html_escaper::Escape;
|
||||||
use std::ops::Deref;
|
use neptune_core::models::blockchain::block::block_height::BlockHeight;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tarpc::context;
|
||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
pub async fn root(State(state): State<Arc<AppState>>) -> Html<String> {
|
pub async fn root(State(state): State<Arc<AppState>>) -> Result<Html<String>, Response> {
|
||||||
#[derive(boilerplate::Boilerplate)]
|
#[derive(boilerplate::Boilerplate)]
|
||||||
#[boilerplate(filename = "web/html/page/root.html")]
|
#[boilerplate(filename = "web/html/page/root.html")]
|
||||||
pub struct RootHtmlPage(Arc<AppState>);
|
pub struct RootHtmlPage {
|
||||||
impl Deref for RootHtmlPage {
|
tip_height: BlockHeight,
|
||||||
type Target = AppState;
|
state: Arc<AppState>,
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let root_page = RootHtmlPage(state);
|
let tip_height = state
|
||||||
Html(root_page.to_string())
|
.rpc_client
|
||||||
|
.block_height(context::current())
|
||||||
|
.await
|
||||||
|
.map_err(|e| not_found_html_response(State(state.clone()), Some(e.to_string())))?;
|
||||||
|
|
||||||
|
let root_page = RootHtmlPage { tip_height, state };
|
||||||
|
Ok(Html(root_page.to_string()))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,10 @@ pub fn not_found_html_err(html: Html<String>) -> Response {
|
|||||||
(StatusCode::NOT_FOUND, html).into_response()
|
(StatusCode::NOT_FOUND, html).into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn not_found_html_handler(html: Html<String>) -> (StatusCode, Html<String>) {
|
||||||
|
(StatusCode::NOT_FOUND, html)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn rpc_err(e: RpcError) -> Response {
|
pub fn rpc_err(e: RpcError) -> Response {
|
||||||
(StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response()
|
(StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response()
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/main.rs
23
src/main.rs
@ -5,6 +5,7 @@ use neptune_core::models::blockchain::block::block_selector::BlockSelector;
|
|||||||
use neptune_core::rpc_server::RPCClient;
|
use neptune_core::rpc_server::RPCClient;
|
||||||
use neptune_explorer::html::page::block::block_page;
|
use neptune_explorer::html::page::block::block_page;
|
||||||
use neptune_explorer::html::page::block::block_page_with_value;
|
use neptune_explorer::html::page::block::block_page_with_value;
|
||||||
|
use neptune_explorer::html::page::not_found::not_found_html_fallback;
|
||||||
use neptune_explorer::html::page::root::root;
|
use neptune_explorer::html::page::root::root;
|
||||||
use neptune_explorer::html::page::utxo::utxo_page;
|
use neptune_explorer::html::page::utxo::utxo_page;
|
||||||
use neptune_explorer::model::app_state::AppState;
|
use neptune_explorer::model::app_state::AppState;
|
||||||
@ -21,7 +22,7 @@ use tarpc::client;
|
|||||||
use tarpc::client::RpcError;
|
use tarpc::client::RpcError;
|
||||||
use tarpc::context;
|
use tarpc::context;
|
||||||
use tarpc::tokio_serde::formats::Json as RpcJson;
|
use tarpc::tokio_serde::formats::Json as RpcJson;
|
||||||
use tower_http::services::ServeFile;
|
use tower_http::services::ServeDir;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), RpcError> {
|
async fn main() -> Result<(), RpcError> {
|
||||||
@ -58,20 +59,16 @@ async fn main() -> Result<(), RpcError> {
|
|||||||
.route("/block/:selector/:value", get(block_page_with_value))
|
.route("/block/:selector/:value", get(block_page_with_value))
|
||||||
.route("/utxo/:value", get(utxo_page))
|
.route("/utxo/:value", get(utxo_page))
|
||||||
// -- Static files --
|
// -- Static files --
|
||||||
.route_service(
|
.nest_service(
|
||||||
"/css/pico.min.css",
|
"/css",
|
||||||
ServeFile::new(concat!(
|
ServeDir::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/web/css")),
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
|
||||||
"/templates/web/css/pico.min.css"
|
|
||||||
)),
|
|
||||||
)
|
)
|
||||||
.route_service(
|
.nest_service(
|
||||||
"/css/styles.css",
|
"/image",
|
||||||
ServeFile::new(concat!(
|
ServeDir::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/web/image")),
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
|
||||||
"/templates/web/css/styles.css"
|
|
||||||
)),
|
|
||||||
)
|
)
|
||||||
|
// handle route not-found
|
||||||
|
.fallback(not_found_html_fallback(shared_state.clone()))
|
||||||
// add state
|
// add state
|
||||||
.with_state(shared_state);
|
.with_state(shared_state);
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,59 @@ div.indent {
|
|||||||
left: 20px;
|
left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mono {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
.center-text {
|
.center-text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tooltip container */
|
||||||
|
.tooltip {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
bottom: 0.1em;
|
||||||
|
transition: 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tooltip text */
|
||||||
|
.tooltip .tooltiptext {
|
||||||
|
transition-delay: 0.5s;
|
||||||
|
top: -5px;
|
||||||
|
left: 115%;
|
||||||
|
visibility: hidden;
|
||||||
|
width: 300px;
|
||||||
|
background-color: white;
|
||||||
|
color: rgb(0, 118, 118);
|
||||||
|
text-align: center;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: solid 1px rgb(1, 220, 220);
|
||||||
|
|
||||||
|
/* Position the tooltip text - see examples below! */
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* adds a speech-bubble thingy at top-left of tooltip */
|
||||||
|
.tooltip .tooltiptext::after {
|
||||||
|
content: " ";
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 100%;
|
||||||
|
/* To the left of the tooltip */
|
||||||
|
margin-top: -5px;
|
||||||
|
border-width: 5px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: transparent rgb(1, 220, 220) transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show the tooltip text when you mouse over the tooltip container */
|
||||||
|
.tooltip:hover .tooltiptext {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip:hover {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
<table class="striped">
|
<table class="striped">
|
||||||
<tr>
|
<tr>
|
||||||
<td>Digest</td>
|
<td>Digest</td>
|
||||||
<td>{{self.block_info.digest.to_hex()}}</td>
|
<td class="mono">{{self.block_info.digest.to_hex()}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Created</td>
|
<td>Created</td>
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Neptune Block Explorer: (network: {{self.network}})</title>
|
<title>Neptune Block Explorer: (network: {{self.state.network}})</title>
|
||||||
<link rel="stylesheet" type="text/css" href="/css/styles.css" media="screen" />
|
<link rel="stylesheet" type="text/css" href="/css/styles.css" media="screen" />
|
||||||
<link rel="stylesheet" type="text/css" href="/css/pico.min.css" media="screen" />
|
<link rel="stylesheet" type="text/css" href="/css/pico.min.css" media="screen" />
|
||||||
|
<link rel="icon" type="image/png" sizes="48x48" href="/image/neptune-favicon.png">
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function handle_submit(form) {
|
function handle_submit(form) {
|
||||||
@ -24,17 +25,29 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="container">
|
<header class="container">
|
||||||
<h1>Neptune Block Explorer (network: {{self.network}})</h1>
|
<h1>
|
||||||
|
<img src="/image/neptune-logo.png" align="right"/>
|
||||||
|
Neptune Block Explorer (network: {{self.state.network}})
|
||||||
|
</h1>
|
||||||
|
The blockchain tip is at height: {{self.tip_height}}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="container">
|
<main class="container">
|
||||||
|
|
||||||
<article>
|
<article>
|
||||||
<details open>
|
<details open>
|
||||||
<summary>Block Lookup</summary>
|
<summary>
|
||||||
|
Block Lookup
|
||||||
|
</summary>
|
||||||
<form action="/block" method="get" onsubmit="return handle_submit(this)">
|
<form action="/block" method="get" onsubmit="return handle_submit(this)">
|
||||||
|
<span class="tooltip">🛈
|
||||||
|
<span class="tooltiptext">
|
||||||
|
Provide a numeric block height or hexadecimal digest identifier to lookup any block in the Neptune blockchain.
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
Block height or digest:
|
Block height or digest:
|
||||||
<input type="text" size="80" name="height_or_digest"/>
|
<input type="text" size="80" name="height_or_digest" class="mono"/>
|
||||||
<input type="submit" name="height" value="Lookup Block"/>
|
<input type="submit" name="height" value="Lookup Block"/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@ -45,19 +58,28 @@ Quick Lookup:
|
|||||||
</article>
|
</article>
|
||||||
|
|
||||||
<article>
|
<article>
|
||||||
<details>
|
<details open>
|
||||||
<summary>Utxo Lookup</summary>
|
<summary>UTXO Lookup</summary>
|
||||||
<form action="/utxo" method="get" onsubmit="return handle_utxo_submit(this)">
|
<form action="/utxo" method="get" onsubmit="return handle_utxo_submit(this)">
|
||||||
Utxo index:
|
<span class="tooltip">🛈
|
||||||
|
<span class="tooltiptext">
|
||||||
|
An Unspent Transaction Output (UTXO) index can be found in the output of <i>neptune-cli wallet-status</i>. Look for the field: <b>aocl_leaf_index</b>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
UTXO index:
|
||||||
<input type="text" size="10" name="utxo" />
|
<input type="text" size="10" name="utxo" />
|
||||||
<input type="submit" name="height" value="Lookup Utxo" />
|
<input type="submit" name="height" value="Lookup Utxo" />
|
||||||
</form>
|
</form>
|
||||||
</details>
|
</details>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<h2>REST RPCs</h2>
|
|
||||||
|
|
||||||
<article>
|
<article>
|
||||||
|
<details>
|
||||||
|
<summary>REST RPCs</summary>
|
||||||
|
<section>
|
||||||
|
RPC endpoints are available for automating block explorer queries:
|
||||||
|
</section>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>/block_info</summary>
|
<summary>/block_info</summary>
|
||||||
<div class="indent">
|
<div class="indent">
|
||||||
@ -67,14 +89,12 @@ Quick Lookup:
|
|||||||
<li><a href="/rpc/block_info/genesis">/rpc/block_info/genesis</a></li>
|
<li><a href="/rpc/block_info/genesis">/rpc/block_info/genesis</a></li>
|
||||||
<li><a href="/rpc/block_info/tip">/rpc/block_info/tip</a></li>
|
<li><a href="/rpc/block_info/tip">/rpc/block_info/tip</a></li>
|
||||||
<li><a href="/rpc/block_info/height/2">/rpc/block_info/height/2</a></li>
|
<li><a href="/rpc/block_info/height/2">/rpc/block_info/height/2</a></li>
|
||||||
<li><a href="/rpc/block_info/digest/{{self.genesis_digest.to_hex()}}">/rpc/block_info/digest/{{self.genesis_digest.to_hex()}}</a></li>
|
<li><a href="/rpc/block_info/digest/{{self.state.genesis_digest.to_hex()}}">/rpc/block_info/digest/{{self.state.genesis_digest.to_hex()}}</a></li>
|
||||||
<li><a href="/rpc/block_info/height_or_digest/1">/rpc/block_info/height_or_digest/1</a></li>
|
<li><a href="/rpc/block_info/height_or_digest/1">/rpc/block_info/height_or_digest/1</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</article>
|
|
||||||
|
|
||||||
<article>
|
|
||||||
<details>
|
<details>
|
||||||
<summary>/block_digest</summary>
|
<summary>/block_digest</summary>
|
||||||
<div class="indent">
|
<div class="indent">
|
||||||
@ -84,14 +104,12 @@ Quick Lookup:
|
|||||||
<li><a href="/rpc/block_digest/genesis">/rpc/block_digest/genesis</a></li>
|
<li><a href="/rpc/block_digest/genesis">/rpc/block_digest/genesis</a></li>
|
||||||
<li><a href="/rpc/block_digest/tip">/rpc/block_digest/tip</a></li>
|
<li><a href="/rpc/block_digest/tip">/rpc/block_digest/tip</a></li>
|
||||||
<li><a href="/rpc/block_digest/height/2">/rpc/block_digest/height/2</a></li>
|
<li><a href="/rpc/block_digest/height/2">/rpc/block_digest/height/2</a></li>
|
||||||
<li><a href="/rpc/block_digest/digest/{{self.genesis_digest.to_hex()}}">/rpc/block_digest/digest/{{self.genesis_digest.to_hex()}}</a></li>
|
<li><a href="/rpc/block_digest/digest/{{self.state.genesis_digest.to_hex()}}">/rpc/block_digest/digest/{{self.state.genesis_digest.to_hex()}}</a></li>
|
||||||
<li><a href="/rpc/block_digest/height_or_digest/{{self.genesis_digest.to_hex()}}">/rpc/block_digest/height_or_digest/{{self.genesis_digest.to_hex()}}</a></li>
|
<li><a href="/rpc/block_digest/height_or_digest/{{self.state.genesis_digest.to_hex()}}">/rpc/block_digest/height_or_digest/{{self.state.genesis_digest.to_hex()}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</article>
|
|
||||||
|
|
||||||
<article>
|
|
||||||
<details>
|
<details>
|
||||||
<summary>/utxo_digest</summary>
|
<summary>/utxo_digest</summary>
|
||||||
<div class="indent">
|
<div class="indent">
|
||||||
@ -101,6 +119,8 @@ Quick Lookup:
|
|||||||
<li><a href="/rpc/utxo_digest/2">/rpc/utxo_digest/2</a><br/></li>
|
<li><a href="/rpc/utxo_digest/2">/rpc/utxo_digest/2</a><br/></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,14 @@
|
|||||||
<main class="container">
|
<main class="container">
|
||||||
|
|
||||||
<article>
|
<article>
|
||||||
<summary>Utxo Information</summary>
|
<summary>
|
||||||
|
<span class="tooltip">🛈
|
||||||
|
<span class="tooltiptext">
|
||||||
|
UTXO = Unspent Transaction Output. It represents an output of transaction A which can also be an input to transaction B.
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
UTXO Information
|
||||||
|
</summary>
|
||||||
<table class="striped">
|
<table class="striped">
|
||||||
<tr>
|
<tr>
|
||||||
<td>Index</td>
|
<td>Index</td>
|
||||||
|
|||||||
BIN
templates/web/image/neptune-favicon.png
Normal file
BIN
templates/web/image/neptune-favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
BIN
templates/web/image/neptune-logo.png
Normal file
BIN
templates/web/image/neptune-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
Loading…
x
Reference in New Issue
Block a user