mirror of
https://github.com/threefoldtech/mycelium.git
synced 2026-10-23 22:12:20 +00:00
Refactoring peer table to be more like route table + splitted off API calls
This commit is contained in:
1 parent
b1dbba96e1
commit
109cc80ca0
3 files changed
+151
-164
No files matched your search
@@ -0,0 +1,51 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
pub async fn get_peers(
|
||||
server_addr: SocketAddr,
|
||||
) -> Result<Vec<mycelium::peer_manager::PeerStats>, reqwest::Error> {
|
||||
let request_url = format!("http://{server_addr}/api/v1/admin/peers");
|
||||
match reqwest::get(&request_url).await {
|
||||
Err(e) => Err(e),
|
||||
Ok(resp) => match resp.json::<Vec<mycelium::peer_manager::PeerStats>>().await {
|
||||
Err(e) => Err(e),
|
||||
Ok(peers) => Ok(peers),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_selected_routes(
|
||||
server_addr: SocketAddr,
|
||||
) -> Result<Vec<mycelium_api::Route>, reqwest::Error> {
|
||||
let request_url = format!("http://{server_addr}/api/v1/admin/routes/selected");
|
||||
match reqwest::get(&request_url).await {
|
||||
Err(e) => Err(e),
|
||||
Ok(resp) => match resp.json::<Vec<mycelium_api::Route>>().await {
|
||||
Err(e) => Err(e),
|
||||
Ok(selected_routes) => Ok(selected_routes),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_fallback_routes(
|
||||
server_addr: SocketAddr,
|
||||
) -> Result<Vec<mycelium_api::Route>, reqwest::Error> {
|
||||
let request_url = format!("http://{server_addr}/api/v1/admin/routes/fallback");
|
||||
match reqwest::get(&request_url).await {
|
||||
Err(e) => Err(e),
|
||||
Ok(resp) => match resp.json::<Vec<mycelium_api::Route>>().await {
|
||||
Err(e) => Err(e),
|
||||
Ok(selected_routes) => Ok(selected_routes),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_node_info(server_addr: SocketAddr) -> Result<mycelium_api::Info, reqwest::Error> {
|
||||
let request_url = format!("http://{server_addr}/api/v1/admin");
|
||||
match reqwest::get(&request_url).await {
|
||||
Err(e) => Err(e),
|
||||
Ok(resp) => match resp.json::<mycelium_api::Info>().await {
|
||||
Err(e) => Err(e),
|
||||
Ok(node_info) => Ok(node_info),
|
||||
},
|
||||
}
|
||||
}
|
||||
+99
-163
@@ -1,5 +1,7 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
mod api;
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use mycelium::peer_manager::PeerType;
|
||||
use std::cmp::Ordering;
|
||||
@@ -24,56 +26,6 @@ enum Route {
|
||||
PageNotFound { route: Vec<String> },
|
||||
}
|
||||
|
||||
pub async fn get_peers(
|
||||
server_addr: SocketAddr,
|
||||
) -> Result<Vec<mycelium::peer_manager::PeerStats>, reqwest::Error> {
|
||||
let request_url = format!("http://{server_addr}/api/v1/admin/peers");
|
||||
match reqwest::get(&request_url).await {
|
||||
Err(e) => Err(e),
|
||||
Ok(resp) => match resp.json::<Vec<mycelium::peer_manager::PeerStats>>().await {
|
||||
Err(e) => Err(e),
|
||||
Ok(peers) => Ok(peers),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_selected_routes(
|
||||
server_addr: SocketAddr,
|
||||
) -> Result<Vec<mycelium_api::Route>, reqwest::Error> {
|
||||
let request_url = format!("http://{server_addr}/api/v1/admin/routes/selected");
|
||||
match reqwest::get(&request_url).await {
|
||||
Err(e) => Err(e),
|
||||
Ok(resp) => match resp.json::<Vec<mycelium_api::Route>>().await {
|
||||
Err(e) => Err(e),
|
||||
Ok(selected_routes) => Ok(selected_routes),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_fallback_routes(
|
||||
server_addr: SocketAddr,
|
||||
) -> Result<Vec<mycelium_api::Route>, reqwest::Error> {
|
||||
let request_url = format!("http://{server_addr}/api/v1/admin/routes/fallback");
|
||||
match reqwest::get(&request_url).await {
|
||||
Err(e) => Err(e),
|
||||
Ok(resp) => match resp.json::<Vec<mycelium_api::Route>>().await {
|
||||
Err(e) => Err(e),
|
||||
Ok(selected_routes) => Ok(selected_routes),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_node_info(server_addr: SocketAddr) -> Result<mycelium_api::Info, reqwest::Error> {
|
||||
let request_url = format!("http://{server_addr}/api/v1/admin");
|
||||
match reqwest::get(&request_url).await {
|
||||
Err(e) => Err(e),
|
||||
Ok(resp) => match resp.json::<mycelium_api::Info>().await {
|
||||
Err(e) => Err(e),
|
||||
Ok(node_info) => Ok(node_info),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Init logger
|
||||
dioxus_logger::init(Level::INFO).expect("failed to init logger");
|
||||
@@ -115,7 +67,7 @@ fn Sidebar() -> Element {
|
||||
|
||||
#[component]
|
||||
fn Home() -> Element {
|
||||
let fetched_node_info = use_resource(move || get_node_info(SERVER_ADDR));
|
||||
let fetched_node_info = use_resource(move || api::get_node_info(SERVER_ADDR));
|
||||
match &*fetched_node_info.read_unchecked() {
|
||||
Some(Ok(info)) => rsx! {
|
||||
div { class: "node-info",
|
||||
@@ -131,12 +83,11 @@ fn Home() -> Element {
|
||||
|
||||
#[component]
|
||||
fn Peers() -> Element {
|
||||
rsx! {
|
||||
div { class: "peers",
|
||||
h2 { "Peers" }
|
||||
{ PeersTable() }
|
||||
}
|
||||
Outlet::<Route> {}
|
||||
let fetched_peers = use_resource(move || api::get_peers(SERVER_ADDR));
|
||||
match &*fetched_peers.read_unchecked() {
|
||||
Some(Ok(peers)) => rsx! { {PeersTable(peers.clone()) } },
|
||||
Some(Err(e)) => rsx! { div { "An error has occurred while fetching the peers: {e}" } },
|
||||
None => rsx! { div { "Loading peers..." } },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,11 +95,14 @@ fn Peers() -> Element {
|
||||
fn Routes() -> Element {
|
||||
rsx! {
|
||||
SelectedRoutesTable {}
|
||||
div { class: "fallback-routes",
|
||||
h2 { "Fallback Routes" }
|
||||
{ FallbackRoutesTable() }
|
||||
}
|
||||
// FallbackRoutesTable {}
|
||||
FallbackRoutesTable {}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn PageNotFound(route: Vec<String>) -> Element {
|
||||
rsx! {
|
||||
p { "Page not found"}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,21 +141,41 @@ enum SortDirection {
|
||||
Descending,
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn PeersTable() -> Element {
|
||||
let fetched_peers = use_resource(move || get_peers(SERVER_ADDR));
|
||||
fn sort_peers(
|
||||
peers: &mut [mycelium::peer_manager::PeerStats],
|
||||
column: &str,
|
||||
direction: &SortDirection,
|
||||
) {
|
||||
peers.sort_by(|a, b| {
|
||||
let cmp = match column {
|
||||
//"Endpoint" => a.endpoint.cmp(&b.endpoint),
|
||||
"Type" => PeerTypeWrapper(a.pt.clone()).cmp(&PeerTypeWrapper(b.pt.clone())),
|
||||
_ => Ordering::Equal,
|
||||
};
|
||||
match direction {
|
||||
SortDirection::Ascending => cmp,
|
||||
SortDirection::Descending => cmp.reverse(),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn PeersTable(peers: Vec<mycelium::peer_manager::PeerStats>) -> Element {
|
||||
let mut current_page = use_signal(|| 0);
|
||||
let items_per_page = 20;
|
||||
|
||||
let mut change_page = move |delta: i32| {
|
||||
current_page.set(current_page + delta as usize);
|
||||
};
|
||||
|
||||
// Sorting
|
||||
let mut sort_column = use_signal(|| "Type".to_string());
|
||||
let mut sort_direction = use_signal(|| SortDirection::Ascending);
|
||||
let peers_len = peers.len();
|
||||
|
||||
let sort_peers = move |column: String| {
|
||||
let mut change_page = move |delta: i32| {
|
||||
let cur_page = *current_page.read() as i32;
|
||||
current_page.set(
|
||||
(cur_page + delta)
|
||||
.max(0)
|
||||
.min((peers_len - 1) as i32 / items_per_page as i32) as usize,
|
||||
);
|
||||
};
|
||||
|
||||
let mut sort_peers_signal = move |column: String| {
|
||||
if column == *sort_column.read() {
|
||||
let new_sort_direction = match *sort_direction.read() {
|
||||
SortDirection::Ascending => SortDirection::Descending,
|
||||
@@ -210,55 +184,71 @@ fn PeersTable() -> Element {
|
||||
sort_direction.set(new_sort_direction);
|
||||
} else {
|
||||
sort_column.set(column);
|
||||
sort_direction.set(SortDirection::Ascending);
|
||||
sort_direction.set(SortDirection::Descending);
|
||||
}
|
||||
};
|
||||
|
||||
match &*fetched_peers.read_unchecked() {
|
||||
Some(Ok(peers)) => {
|
||||
// Sort peers based on the current sort column and sort direction
|
||||
let mut sorted_peers = peers.clone();
|
||||
sorted_peers.sort_by(|a, b| {
|
||||
let cmp = match sort_column.read().as_str() {
|
||||
// "Endpoint" => a.endpoint.cmp(&b.endpoint),
|
||||
"Type" => PeerTypeWrapper(a.pt.clone()).cmp(&PeerTypeWrapper(b.pt.clone())),
|
||||
_ => std::cmp::Ordering::Equal,
|
||||
};
|
||||
match *sort_direction.read() {
|
||||
SortDirection::Descending => cmp.reverse(),
|
||||
SortDirection::Ascending => cmp,
|
||||
}
|
||||
});
|
||||
let sorted_peers = use_memo(move || {
|
||||
let mut sorted = peers.clone();
|
||||
sort_peers(&mut sorted, &sort_column.read(), &sort_direction.read());
|
||||
sorted
|
||||
});
|
||||
|
||||
let start = current_page * items_per_page;
|
||||
let end = (start + items_per_page).min(peers.len());
|
||||
let current_peers = &sorted_peers[start..end];
|
||||
rsx! {
|
||||
{create_peer_table(current_peers, sort_column, sort_direction, sort_peers)}
|
||||
div { class: "pagination",
|
||||
button {
|
||||
disabled: *current_page.read() == 0,
|
||||
onclick: move |_| change_page(-1),
|
||||
"Previous"
|
||||
}
|
||||
span { "Page {current_page + 1}"}
|
||||
button {
|
||||
disabled: (current_page + 1) * items_per_page >= peers.len(),
|
||||
onclick: move |_| change_page(1),
|
||||
"Next"
|
||||
let start = current_page * items_per_page;
|
||||
let end = (start + items_per_page).min(peers_len);
|
||||
let current_peers = &sorted_peers.read()[start..end];
|
||||
|
||||
rsx! {
|
||||
div { class: "peers-table",
|
||||
h2 { "Peers" }
|
||||
table {
|
||||
thead {
|
||||
tr {
|
||||
th {
|
||||
onclick: move |_| sort_peers_signal("Endpoint".to_string()),
|
||||
"Endpoint {get_sort_indicator(sort_column, sort_direction, \"Endpoint\".to_string())}"
|
||||
}
|
||||
th {
|
||||
onclick: move |_| sort_peers_signal("Type".to_string()),
|
||||
"Type {get_sort_indicator(sort_column, sort_direction, \"Type\".to_string())}"
|
||||
}
|
||||
th { "Connection State" }
|
||||
th { "Tx bytes" }
|
||||
th { "Rx bytes" }
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
for peer in current_peers {
|
||||
tr {
|
||||
td { "{peer.endpoint}" }
|
||||
td { "{peer.pt}" }
|
||||
td { "{peer.connection_state}" }
|
||||
td { "{peer.tx_bytes}" }
|
||||
td { "{peer.rx_bytes}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
div { class: "pagination",
|
||||
button {
|
||||
disabled: *current_page.read() == 0,
|
||||
onclick: move |_| change_page(-1),
|
||||
"Previous"
|
||||
}
|
||||
span { "Page {current_page + 1}" }
|
||||
button {
|
||||
disabled: (current_page + 1) * items_per_page >= peers_len,
|
||||
onclick: move |_| change_page(1),
|
||||
"Next"
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Err(e)) => rsx! { div { "An error has occurred while fetching peers: {e}" }},
|
||||
None => rsx! { div { "Loading peers..." }},
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn SelectedRoutesTable() -> Element {
|
||||
let fetched_selected_routes = use_resource(move || get_selected_routes(SERVER_ADDR));
|
||||
let fetched_selected_routes = use_resource(move || api::get_selected_routes(SERVER_ADDR));
|
||||
|
||||
match &*fetched_selected_routes.read_unchecked() {
|
||||
Some(Ok(routes)) => {
|
||||
@@ -271,7 +261,7 @@ fn SelectedRoutesTable() -> Element {
|
||||
|
||||
#[component]
|
||||
fn FallbackRoutesTable() -> Element {
|
||||
let fetched_fallback_routes = use_resource(move || get_selected_routes(SERVER_ADDR));
|
||||
let fetched_fallback_routes = use_resource(move || api::get_fallback_routes(SERVER_ADDR));
|
||||
|
||||
match &*fetched_fallback_routes.read_unchecked() {
|
||||
Some(Ok(routes)) => {
|
||||
@@ -282,13 +272,6 @@ fn FallbackRoutesTable() -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn PageNotFound(route: Vec<String>) -> Element {
|
||||
rsx! {
|
||||
p { "Page not found"}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_sort_indicator(
|
||||
sort_column: Signal<String>,
|
||||
sort_direction: Signal<SortDirection>,
|
||||
@@ -304,53 +287,6 @@ fn get_sort_indicator(
|
||||
}
|
||||
}
|
||||
|
||||
fn create_peer_table(
|
||||
peers: &[mycelium::peer_manager::PeerStats],
|
||||
sort_column: Signal<String>,
|
||||
sort_direction: Signal<SortDirection>,
|
||||
mut sort_peers: impl FnMut(String) + Clone + 'static,
|
||||
) -> Element {
|
||||
rsx! {
|
||||
table {
|
||||
thead {
|
||||
tr {
|
||||
// th { "Endpoint" }
|
||||
th {
|
||||
onclick: {
|
||||
let mut sort_peers = sort_peers.clone();
|
||||
move |_| {
|
||||
sort_peers("Endpoint".to_string());
|
||||
}
|
||||
},
|
||||
"Endpoint {get_sort_indicator(sort_column, sort_direction, \"Endpoint\".to_string())}"
|
||||
}
|
||||
th {
|
||||
onclick: move |_| {
|
||||
sort_peers("Type".to_string());
|
||||
},
|
||||
"Type {get_sort_indicator(sort_column, sort_direction, \"Type\".to_string())}"
|
||||
}
|
||||
// th { "Type" }
|
||||
th { "Connection State" }
|
||||
th { "Tx bytes" }
|
||||
th { "Rx bytes" }
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
for peer in peers {
|
||||
tr {
|
||||
td { "{peer.endpoint}" }
|
||||
td { "{peer.pt}" }
|
||||
td { "{peer.connection_state}" }
|
||||
td { "{peer.tx_bytes}" }
|
||||
td { "{peer.rx_bytes}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_routes(routes: &mut [mycelium_api::Route], column: &str, direction: &SortDirection) {
|
||||
routes.sort_by(|a, b| {
|
||||
let cmp = match column {
|
||||
|
||||
@@ -112,7 +112,7 @@ pub enum ConnectionState {
|
||||
}
|
||||
|
||||
/// Identification and information/statistics for a specific [`Peer`]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PeerStats {
|
||||
/// The endpoint of the [`Peer`].
|
||||
|
||||
Reference in new issue
Block a user