mirror of
https://github.com/transatoshi-mw/grin-explorer.git
synced 2025-10-21 13:33:41 +00:00
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -736,7 +736,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "grin-explorer"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "grin-explorer"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
@@ -118,6 +118,7 @@ pub struct Kernel {
|
||||
pub excess: String,
|
||||
pub ker_type: String,
|
||||
pub fee: String,
|
||||
pub status: String,
|
||||
pub raw_data: String,
|
||||
}
|
||||
|
||||
@@ -128,6 +129,7 @@ impl Kernel {
|
||||
excess: String::new(),
|
||||
ker_type: String::new(),
|
||||
fee: String::new(),
|
||||
status: String::new(),
|
||||
raw_data: String::new(),
|
||||
}
|
||||
}
|
||||
|
46
src/main.rs
46
src/main.rs
@@ -126,7 +126,7 @@ async fn kernel(excess: &str) -> Template {
|
||||
|
||||
let _ = requests::get_kernel(&excess, &mut kernel).await;
|
||||
|
||||
if kernel.height.is_empty() == false {
|
||||
if kernel.excess.is_empty() == false {
|
||||
return Template::render("kernel", context! {
|
||||
route: "kernel",
|
||||
kernel,
|
||||
@@ -140,28 +140,30 @@ async fn kernel(excess: &str) -> Template {
|
||||
|
||||
|
||||
// Handling search request.
|
||||
#[post("/search", data="<input>")]
|
||||
fn search(input: &str) -> Either<Template, Redirect> {
|
||||
//Check input length
|
||||
if input.len() > "search=".len() {
|
||||
// Trim 'search=' from the request data
|
||||
let input = &input[7..].to_lowercase();
|
||||
// Using Option<&str> to match '/search' query without input params.
|
||||
// https://github.com/rwf2/Rocket/issues/608
|
||||
#[get("/search?<input>")]
|
||||
fn search(input: Option<&str>) -> Either<Template, Redirect> {
|
||||
// Unwrap Option and forward to Search page if no parameters
|
||||
let input = match input {
|
||||
Some(value) => value,
|
||||
None => return Either::Left(Template::render("search", context! { route: "search", })),
|
||||
};
|
||||
|
||||
// Check for valid chars
|
||||
if input.chars().all(|x| (x >= 'a' && x <= 'f') || (x >= '0' && x <= '9')) == true {
|
||||
// Check for valid chars
|
||||
if input.chars().all(|x| (x >= 'a' && x <= 'f') || (x >= '0' && x <= '9')) == true {
|
||||
|
||||
// Block number
|
||||
if input.chars().all(char::is_numeric) == true {
|
||||
return Either::Right(Redirect::to(uri!(block_details_by_height(input))));
|
||||
// Block number
|
||||
if input.chars().all(char::is_numeric) == true {
|
||||
return Either::Right(Redirect::to(uri!(block_details_by_height(input))));
|
||||
|
||||
// Block hash
|
||||
} else if input.len() == 64 {
|
||||
return Either::Right(Redirect::to(uri!(block_header_by_hash(input))));
|
||||
// Block hash
|
||||
} else if input.len() == 64 {
|
||||
return Either::Right(Redirect::to(uri!(block_header_by_hash(input))));
|
||||
|
||||
// Kernel
|
||||
} else if input.len() == 66 {
|
||||
return Either::Right(Redirect::to(uri!(kernel(input))));
|
||||
}
|
||||
// Kernel
|
||||
} else if input.len() == 66 {
|
||||
return Either::Right(Redirect::to(uri!(kernel(input))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,8 +220,6 @@ async fn api_foreign(data: &str) -> String {
|
||||
_ => return "{\"error\":\"bad syntax\"}".to_string(),
|
||||
};
|
||||
|
||||
println!("{}", method);
|
||||
println!("{}", data);
|
||||
let resp = requests::call(method, v["params"].to_string().as_str(), v["id"].to_string().as_str(), "foreign").await;
|
||||
|
||||
let result = match resp {
|
||||
@@ -255,9 +255,9 @@ fn sync_status(dashboard: &State<Arc<Mutex<Dashboard>>>) -> String {
|
||||
if data.sync == "no_sync" {
|
||||
"Synced".to_string()
|
||||
} else {
|
||||
format!("Syncing ({})
|
||||
format!("Syncing
|
||||
<div class='spinner-grow spinner-grow-sm' role='status'>
|
||||
<span class='visually-hidden'>Syncing...</span></div>", data.sync)
|
||||
<span class='visually-hidden'>Syncing...</span></div>")
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -404,15 +404,35 @@ pub async fn get_block_header(hash: &str, height: &mut String)
|
||||
|
||||
|
||||
// Get kernel.
|
||||
pub async fn get_kernel(excess: &str, kernel: &mut Kernel)
|
||||
-> Result<(), anyhow::Error> {
|
||||
let params = &format!("[\"{}\", null, null]", excess)[..];
|
||||
|
||||
let resp = call("get_kernel", params, "1", "foreign").await?;
|
||||
pub async fn get_kernel(excess: &str, kernel: &mut Kernel) -> Result<(), anyhow::Error> {
|
||||
// First check whether kernel is broadcasted but not confirmed yet (in mempool)
|
||||
let mut resp = call("get_unconfirmed_transactions", "[]", "1", "foreign").await?;
|
||||
|
||||
if resp["result"]["Ok"].is_null() == false {
|
||||
kernel.height = resp["result"]["Ok"]["height"].to_string();
|
||||
kernel.excess = resp["result"]["Ok"]["tx_kernel"]["excess"].as_str().unwrap().to_string();
|
||||
for tx in resp["result"]["Ok"].as_array().unwrap() {
|
||||
for ker in tx["tx"]["body"]["kernels"].as_array().unwrap() {
|
||||
if ker["excess"].as_str().unwrap() == excess {
|
||||
// Only Plain kernels in the mempool
|
||||
kernel.ker_type = "Plain".to_string();
|
||||
kernel.excess = ker["excess"].as_str().unwrap().to_string();
|
||||
kernel.status = "Unconfirmed".to_string();
|
||||
kernel.fee = format!("ツ {}",
|
||||
ker["features"]["Plain"]["fee"]
|
||||
.to_string().parse::<f64>().unwrap() / 1000000000.0);
|
||||
// Found it, no need to continue
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let params = &format!("[\"{}\", null, null]", excess)[..];
|
||||
|
||||
resp = call("get_kernel", params, "1", "foreign").await?;
|
||||
|
||||
if resp["result"]["Ok"].is_null() == false {
|
||||
kernel.height = resp["result"]["Ok"]["height"].to_string();
|
||||
kernel.excess = resp["result"]["Ok"]["tx_kernel"]["excess"].as_str().unwrap().to_string();
|
||||
if resp["result"]["Ok"]["tx_kernel"]["features"]["Plain"].is_null() == false {
|
||||
kernel.ker_type = "Plain".to_string();
|
||||
kernel.fee = format!("ツ {}",
|
||||
@@ -420,7 +440,6 @@ pub async fn get_kernel(excess: &str, kernel: &mut Kernel)
|
||||
.to_string().parse::<f64>().unwrap() / 1000000000.0);
|
||||
} else {
|
||||
kernel.ker_type = resp["result"]["Ok"]["tx_kernel"]["features"].as_str().unwrap().to_string();
|
||||
kernel.fee = "ツ 0".to_string();
|
||||
}
|
||||
|
||||
kernel.raw_data = serde_json::to_string_pretty(&resp).unwrap()
|
||||
|
@@ -61,8 +61,8 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<form class="input-group my-3" role="search" action="/search" method="POST" autocomplete="off">
|
||||
<input class="form-control ms-0 me-2" type="search" placeholder="Explore Grin Network" aria-label="Search" name="search" required>
|
||||
<form class="input-group my-3" role="search" action="/search" method="GET" autocomplete="off">
|
||||
<input class="form-control ms-0 me-2" type="search" placeholder="Explore Grin Network" aria-label="Search" name="input" required>
|
||||
<button class="btn btn-outline-secondary btn-search" type="submit">
|
||||
<i class="bi bi-search"></i>
|
||||
</button>
|
||||
@@ -211,8 +211,11 @@
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="col d-flex justify-content-center">
|
||||
<a class="text-decoration-none" href="https://github.com/aglkm/grin-explorer">
|
||||
<span style="color:grey">v.0.1.3 <i class="bi bi-github"></i></span>
|
||||
<a class="text-decoration-none me-2" href="https://github.com/aglkm/grin-explorer">
|
||||
<span style="color:grey"><i class="bi bi-github me-1"></i>v.0.1.4</span>
|
||||
</a>
|
||||
<a class="text-decoration-none" href="/search">
|
||||
<span style="color:grey"><i class="bi bi-search me-1"></i>search</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -7,7 +7,10 @@
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4>No results found.</h4><br>
|
||||
Explorer supports requests by block number, block hash or kernel.<br>
|
||||
<div class="value-text mb-2">Supported search inputs:</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> block number</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> block hash</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> kernel</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -6,12 +6,18 @@
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="darkorange-text"><i class="bi bi-card-text"></i> KERNEL</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="darkorange-text"><i class="bi bi-card-text"></i> KERNEL</div>
|
||||
{% if kernel.status == "Unconfirmed" %}
|
||||
<span class="badge text-bg-warning px-2 py-2">UNCONFIRMED</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<br>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="value-text">Excess </div>
|
||||
<div class="value-text text-break text-end">{{ kernel.excess }}</div>
|
||||
</div>
|
||||
{% if kernel.status != "Unconfirmed" %}
|
||||
<br>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="value-text">Block Height </div>
|
||||
@@ -21,28 +27,32 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<br>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="value-text">Type </div>
|
||||
<div class="value-text text-end">{{ kernel.ker_type }}</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="value-text">Fee </div>
|
||||
<div class="value-text text-end">{{ kernel.fee }}</div>
|
||||
{% if kernel.ker_type == "Plain" %}
|
||||
<br>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="value-text">Fee </div>
|
||||
<div class="value-text text-end">{{ kernel.fee }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if kernel.status != "Unconfirmed" %}
|
||||
<br>
|
||||
<div class="card">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-layout-text-sidebar-reverse"></i> RAW DATA</div>
|
||||
<br>
|
||||
<div class="value-text">{{ kernel.raw_data }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-layout-text-sidebar-reverse"></i> RAW DATA</div>
|
||||
<br>
|
||||
<div class="value-text">{{ kernel.raw_data }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<br>
|
||||
|
||||
|
32
templates/search.html.tera
Normal file
32
templates/search.html.tera
Normal file
@@ -0,0 +1,32 @@
|
||||
{% extends "base" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<code>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body mx-2 mt-2 mb-3">
|
||||
<div class="d-flex justify-content-start mb-1">
|
||||
<i class="bi bi-box me-2"></i>
|
||||
<div class="value-text text-end" hx-get="/rpc/block/latest" hx-trigger="load, every 10s"></div>
|
||||
</div>
|
||||
<form class="input-group" role="search" action="/search" method="GET" autocomplete="off">
|
||||
<input class="form-control text-center ms-0 me-2" type="search" placeholder="Explore Grin Network" aria-label="Search" name="input" required>
|
||||
<button class="btn btn-outline-secondary btn-search" type="submit">
|
||||
<i class="bi bi-search"></i>
|
||||
</button>
|
||||
</form>
|
||||
<br><br>
|
||||
<div class="value-text mb-2">Supported search inputs:</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> block number</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> block hash</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> kernel</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
</code>
|
||||
|
||||
{% endblock %}
|
||||
|
Reference in New Issue
Block a user