Added Latest Blocks and improved network ifs

This commit is contained in:
2024-05-26 23:54:35 +01:00
parent 559aac0994
commit 584c0d3be8
3 changed files with 185 additions and 31 deletions

View File

@@ -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)
} }

View File

@@ -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()
} }

View File

@@ -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>