Added Latest Blocks and improved network ifs
This commit is contained in:
114
src/main.rs
114
src/main.rs
@@ -1,18 +1,20 @@
|
|||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
use readable::byte::Byte;
|
use readable::byte::Byte;
|
||||||
use readable::up::UptimeFull;
|
use readable::up::UptimeFull;
|
||||||
use rocket::serde::{Serialize, Deserialize};
|
use readable::date::Date;
|
||||||
use rocket::{get, launch, routes, State, fairing::AdHoc};
|
use readable::time::{secs_to_clock, unix_clock};
|
||||||
use rocket_dyn_templates::{context, Template};
|
|
||||||
use std::path::Path;
|
|
||||||
use reqwest;
|
use reqwest;
|
||||||
|
use rocket::serde::{Deserialize, Serialize};
|
||||||
|
use rocket::{fairing::AdHoc, get, launch, routes, State};
|
||||||
|
use rocket_dyn_templates::{context, Template};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use sysinfo::{Components, Disks, Networks, System};
|
use std::path::Path;
|
||||||
use vnstat_parse::{Error, Vnstat};
|
use sysinfo::{Components, Disks, System};
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
use vnstat_parse::Vnstat;
|
||||||
|
|
||||||
mod monero_rpc;
|
mod monero_rpc;
|
||||||
use monero_rpc::{NodeInfo, NodeInfoInner};
|
use monero_rpc::{NodeInfoOuter, NodeInfo, BlockHeaderInfosOuter, BlockHeaderInfo};
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
struct ComponentInfo {
|
struct ComponentInfo {
|
||||||
@@ -53,6 +55,7 @@ struct SysInfo {
|
|||||||
disks: Vec<DiskInfo>,
|
disks: Vec<DiskInfo>,
|
||||||
components: Vec<ComponentInfo>,
|
components: Vec<ComponentInfo>,
|
||||||
network_ifs: Vec<NetworkInfo>,
|
network_ifs: Vec<NetworkInfo>,
|
||||||
|
network_ifs_errored: Vec<(String, String)>,
|
||||||
hostname: Option<String>,
|
hostname: Option<String>,
|
||||||
operating_system: Option<String>,
|
operating_system: Option<String>,
|
||||||
uptime: String,
|
uptime: String,
|
||||||
@@ -96,16 +99,19 @@ fn get_system_info(network_if_names: &Vec<String>) -> SysInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut network_ifs = Vec::new();
|
let mut network_ifs = Vec::new();
|
||||||
|
let mut network_ifs_errored = Vec::new();
|
||||||
for network_if_name in network_if_names {
|
for network_if_name in network_if_names {
|
||||||
// TODO: Fail more gracefully
|
match Vnstat::get(&network_if_name) {
|
||||||
let vnstat_data = Vnstat::get(&network_if_name).unwrap();
|
Ok(vns) => {
|
||||||
println!("{:?}", vnstat_data);
|
network_ifs.push(NetworkInfo {
|
||||||
network_ifs.push(NetworkInfo{
|
name: network_if_name.to_string(),
|
||||||
name: network_if_name.to_string(),
|
total_sent_today: format!("{} {}", vns.day_tx, vns.day_tx_unit),
|
||||||
total_sent_today: format!("{} {}", vnstat_data.day_tx, vnstat_data.day_tx_unit),
|
total_received_today: format!("{} {}", vns.day_rx, vns.day_rx_unit),
|
||||||
total_received_today: format!("{} {}", vnstat_data.day_rx, vnstat_data.day_rx_unit),
|
average_rate_today: format!("{} {}", vns.day_avg_rate, vns.day_avg_rate_unit),
|
||||||
average_rate_today: format!("{} {}", vnstat_data.day_avg_rate, vnstat_data.day_avg_rate_unit),
|
});
|
||||||
})
|
}
|
||||||
|
Err(e) => network_ifs_errored.push((network_if_name.to_string(), e.to_string())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SysInfo {
|
SysInfo {
|
||||||
@@ -118,6 +124,7 @@ fn get_system_info(network_if_names: &Vec<String>) -> SysInfo {
|
|||||||
used_swap: Byte::from(sys.used_swap()).to_string(),
|
used_swap: Byte::from(sys.used_swap()).to_string(),
|
||||||
},
|
},
|
||||||
network_ifs: network_ifs,
|
network_ifs: network_ifs,
|
||||||
|
network_ifs_errored: network_ifs_errored,
|
||||||
hostname: System::host_name(),
|
hostname: System::host_name(),
|
||||||
operating_system: System::long_os_version(),
|
operating_system: System::long_os_version(),
|
||||||
uptime: UptimeFull::from(System::uptime()).to_string(),
|
uptime: UptimeFull::from(System::uptime()).to_string(),
|
||||||
@@ -126,8 +133,7 @@ fn get_system_info(network_if_names: &Vec<String>) -> SysInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_node_info(json_rpc_url: &str) -> NodeInfo {
|
||||||
fn get_node_info() -> NodeInfoInner {
|
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert("jsonrpc", "2.0");
|
map.insert("jsonrpc", "2.0");
|
||||||
map.insert("id", "0");
|
map.insert("id", "0");
|
||||||
@@ -137,9 +143,14 @@ fn get_node_info() -> NodeInfoInner {
|
|||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
// TODO: Properly handle this unwrap - half the point of this is to capture
|
// TODO: Properly handle this unwrap - half the point of this is to capture
|
||||||
// when the node is down - don't want it crashing when
|
// when the node is down - don't want it crashing when
|
||||||
let resp = client.post("http://127.0.0.1:18081/json_rpc").json(&map).send().unwrap();
|
let resp = client
|
||||||
resp.json::<NodeInfo>()
|
.post(json_rpc_url)
|
||||||
}).unwrap();
|
.json(&map)
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
resp.json::<NodeInfoOuter>()
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
let mut node_info = node_info.result;
|
let mut node_info = node_info.result;
|
||||||
// TODO: Put this into a Serialize method
|
// TODO: Put this into a Serialize method
|
||||||
let approx_uptime = node_info.adjusted_time - node_info.start_time;
|
let approx_uptime = node_info.adjusted_time - node_info.start_time;
|
||||||
@@ -149,19 +160,78 @@ fn get_node_info() -> NodeInfoInner {
|
|||||||
node_info
|
node_info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct RpcOuter {
|
||||||
|
jsonrpc: String,
|
||||||
|
id: String,
|
||||||
|
method: String,
|
||||||
|
params: HeightRange,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct HeightRange {
|
||||||
|
start_height: u64,
|
||||||
|
end_height: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_latest_twenty_blocks(json_rpc_url: &str, current_height: u64) -> Vec<BlockHeaderInfo> {
|
||||||
|
let rpc_call = RpcOuter {
|
||||||
|
jsonrpc: "2.0".to_string(),
|
||||||
|
id: "0".to_string(),
|
||||||
|
method: "get_block_headers_range".to_string(),
|
||||||
|
params: HeightRange {
|
||||||
|
start_height: current_height - 20,
|
||||||
|
end_height: current_height - 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let blocks = task::block_in_place(move || {
|
||||||
|
let client = reqwest::blocking::Client::new();
|
||||||
|
// TODO: Properly handle this unwrap - half the point of this is to capture
|
||||||
|
// when the node is down - don't want it crashing when
|
||||||
|
let resp = client
|
||||||
|
.post(json_rpc_url)
|
||||||
|
.json(&rpc_call)
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
resp.json::<BlockHeaderInfosOuter>()
|
||||||
|
}).unwrap();
|
||||||
|
let mut latest_twenty_blocks = blocks.result.headers;
|
||||||
|
for i in 0..latest_twenty_blocks.len() {
|
||||||
|
let clock_time = secs_to_clock(unix_clock(latest_twenty_blocks[i].timestamp));
|
||||||
|
let date = Date::from_unix(latest_twenty_blocks[i].timestamp).unwrap();
|
||||||
|
latest_twenty_blocks[i].timestamp_pretty = format!("{} {:02}-{:02}-{:02}", date, clock_time.0, clock_time.1, clock_time.2);
|
||||||
|
}
|
||||||
|
latest_twenty_blocks
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
struct Config {
|
struct Config {
|
||||||
network_interfaces: Vec<String>,
|
network_interfaces: Vec<String>,
|
||||||
|
json_rpc_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Figure out why this isn't being loaded with the AdHoc thing
|
||||||
|
// I suspect I need to do the full fairing thingy.
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
network_interfaces: Vec::new(),
|
||||||
|
json_rpc_url: "http://127.0.0.1:18081/json_rpc".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn index(config: &State<Config>) -> Template {
|
fn index(config: &State<Config>) -> Template {
|
||||||
let node_info = get_node_info();
|
let node_info = get_node_info(&config.json_rpc_url);
|
||||||
|
let latest_twenty_blocks = get_latest_twenty_blocks(&config.json_rpc_url, node_info.height);
|
||||||
let system_context = get_system_info(&config.network_interfaces);
|
let system_context = get_system_info(&config.network_interfaces);
|
||||||
let context = context! {
|
let context = context! {
|
||||||
system: system_context,
|
system: system_context,
|
||||||
node_info: node_info,
|
node_info: node_info,
|
||||||
|
latest_twenty_blocks: latest_twenty_blocks,
|
||||||
};
|
};
|
||||||
Template::render("index", &context)
|
Template::render("index", &context)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct NodeInfo {
|
pub struct NodeInfoOuter {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub jsonrpc: String,
|
pub jsonrpc: String,
|
||||||
pub result: NodeInfoInner
|
pub result: NodeInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct NodeInfoInner {
|
pub struct NodeInfo {
|
||||||
pub adjusted_time: u64,
|
pub adjusted_time: u64,
|
||||||
alt_blocks_count: u64,
|
alt_blocks_count: u64,
|
||||||
block_size_limit: u64,
|
block_size_limit: u64,
|
||||||
@@ -25,7 +25,7 @@ pub struct NodeInfoInner {
|
|||||||
difficulty_top64: u64,
|
difficulty_top64: u64,
|
||||||
pub free_space: u64,
|
pub free_space: u64,
|
||||||
grey_peerlist_size: u64,
|
grey_peerlist_size: u64,
|
||||||
height: u64,
|
pub height: u64,
|
||||||
height_without_bootstrap: u64,
|
height_without_bootstrap: u64,
|
||||||
incoming_connections_count: u64,
|
incoming_connections_count: u64,
|
||||||
mainnet: bool,
|
mainnet: bool,
|
||||||
@@ -60,6 +60,51 @@ pub struct NodeInfoInner {
|
|||||||
pub db_size_pretty: String,
|
pub db_size_pretty: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct BlockHeaderInfosOuter {
|
||||||
|
pub id: String,
|
||||||
|
pub jsonrpc: String,
|
||||||
|
pub result: BlockHeaderInfos,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct BlockHeaderInfos {
|
||||||
|
credits: u64,
|
||||||
|
pub headers: Vec<BlockHeaderInfo>,
|
||||||
|
status: String,
|
||||||
|
top_hash: String,
|
||||||
|
untrusted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct BlockHeaderInfo {
|
||||||
|
block_size: u64,
|
||||||
|
block_weight: u64,
|
||||||
|
cumulative_difficulty: u64,
|
||||||
|
cumulative_difficulty_top64: u64,
|
||||||
|
depth: u64,
|
||||||
|
difficulty: u64,
|
||||||
|
difficulty_top64: u64,
|
||||||
|
hash: String,
|
||||||
|
height: u64,
|
||||||
|
long_term_weight: u64,
|
||||||
|
major_version: u64,
|
||||||
|
miner_tx_hash: String,
|
||||||
|
minor_version: u64,
|
||||||
|
nonce: u64,
|
||||||
|
num_txes: u64,
|
||||||
|
orphan_status: bool,
|
||||||
|
pow_hash: String,
|
||||||
|
prev_hash: String,
|
||||||
|
reward: u64,
|
||||||
|
pub timestamp: u64,
|
||||||
|
wide_cumulative_difficulty: String,
|
||||||
|
wide_difficulty: String,
|
||||||
|
#[serde(skip_deserializing, default = "none")]
|
||||||
|
pub timestamp_pretty: String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn none() -> String {
|
fn none() -> String {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,16 +18,29 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
span.success {
|
.success {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: green;
|
color: green;
|
||||||
}
|
}
|
||||||
.warning {
|
.warning {
|
||||||
color: yellow;
|
color: yellow;
|
||||||
}
|
}
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
.node_data {
|
.node_data {
|
||||||
font-family: monospace, monospace;
|
font-family: monospace, monospace;
|
||||||
}
|
}
|
||||||
|
table {
|
||||||
|
border: 2px solid;
|
||||||
|
}
|
||||||
|
thead {
|
||||||
|
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
border: 1px solid;
|
||||||
|
padding: 7px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -77,7 +90,10 @@
|
|||||||
<p>Total Received Today: <span class="node_data">{{ network_if.total_received_today }}</span></p>
|
<p>Total Received Today: <span class="node_data">{{ network_if.total_received_today }}</span></p>
|
||||||
<p>Average Rate: <span class="node_data">{{ network_if.average_rate_today }}</span></p>
|
<p>Average Rate: <span class="node_data">{{ network_if.average_rate_today }}</span></p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<p>
|
{% for network_if in system.network_ifs_errored %}
|
||||||
|
<h3 class="error">interface <span class="node_data">{{ network_if[0] }}</span> has an error</h3>
|
||||||
|
<p>Error: <span class="node_data">{{ network_if[1] }}</span></p>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h1>Monerod Status</h1>
|
<h1>Monerod Status</h1>
|
||||||
@@ -114,10 +130,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1>Node Peers</h1>
|
<h1>Latest 20 Blocks</h1>
|
||||||
<div class="nodelist">
|
<div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Height</th>
|
||||||
|
<th scope="col">Size</th>
|
||||||
|
<th scope="col">Transactions</th>
|
||||||
|
<th scope="col">Timestamp (UTC)</th>
|
||||||
|
<th scope="col">Hash</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
{% for block in latest_twenty_blocks | reverse %}
|
||||||
|
<tr>
|
||||||
|
<th scope="row" class="node_data">{{ block.height }}</th>
|
||||||
|
<td class="node_data">{{ block.block_size }}</td>
|
||||||
|
<td class="node_data">{{ block.num_txes }}</td>
|
||||||
|
<td class="node_data">{{ block.timestamp_pretty }}</td>
|
||||||
|
<td class="node_data">{{ block.hash }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<hr>
|
||||||
{{__tera_context}}
|
{{__tera_context}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user