P2: peers CRUD and aggregated stats
Backend - api/peers.rs: list/add/remove + aggregate() that derives totals, per-state counts, and tx/rx sums in one pass over the peer list - poller.rs spawns a 3s tokio loop that emits peers://updated and stats://updated; cancelled via abort() on stop_daemon - DELETE peer URL-encodes the endpoint (the path includes ://) with a small inline percent-encoder to avoid a url crate dep - Tauri commands: peers_list, peer_add (with empty-string guard), peer_remove, peers_stats Frontend - peers store subscribes to the two events and refreshes after add/remove for immediate UI feedback - Peers view renders endpoint, type, color-coded state badge, and formatBytes-formatted rx/tx; the four stat cards re-use a reusable Stat component - AddPeerDialog uses radix-vue's Dialog primitive with regex validation for tcp:// and quic:// schemes
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
use crate::api::admin::NodeInfo;
|
||||
use crate::api::peers::{AggregatedStats, PeerInfo};
|
||||
use crate::api::MyceliumClient;
|
||||
use crate::error::{AppError, AppResult};
|
||||
use crate::sidecar::SidecarConfig;
|
||||
use crate::state::AppState;
|
||||
@@ -13,8 +15,7 @@ pub struct DaemonStatus {
|
||||
pub config_path: Option<String>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn daemon_status(state: State<'_, AppState>) -> DaemonStatus {
|
||||
fn snapshot(state: &AppState) -> DaemonStatus {
|
||||
let sc = &state.sidecar;
|
||||
DaemonStatus {
|
||||
running: sc.is_running(),
|
||||
@@ -24,6 +25,15 @@ pub fn daemon_status(state: State<'_, AppState>) -> DaemonStatus {
|
||||
}
|
||||
}
|
||||
|
||||
fn require_client(state: &AppState) -> AppResult<MyceliumClient> {
|
||||
state.sidecar.client().ok_or(AppError::DaemonNotRunning)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn daemon_status(state: State<'_, AppState>) -> DaemonStatus {
|
||||
snapshot(&state)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn start_daemon(
|
||||
app: AppHandle,
|
||||
@@ -32,18 +42,22 @@ pub async fn start_daemon(
|
||||
) -> AppResult<DaemonStatus> {
|
||||
let cfg = config.unwrap_or_default();
|
||||
state.sidecar.start(&app, &cfg).await?;
|
||||
Ok(daemon_status(state))
|
||||
state
|
||||
.poller
|
||||
.start(app.clone(), std::sync::Arc::clone(&state.sidecar));
|
||||
Ok(snapshot(&state))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn stop_daemon(state: State<'_, AppState>) -> AppResult<DaemonStatus> {
|
||||
state.poller.stop();
|
||||
state.sidecar.stop().await;
|
||||
Ok(daemon_status(state))
|
||||
Ok(snapshot(&state))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn node_info(state: State<'_, AppState>) -> AppResult<NodeInfo> {
|
||||
let client = state.sidecar.client().ok_or(AppError::DaemonNotRunning)?;
|
||||
let client = require_client(&state)?;
|
||||
client.node_info().await
|
||||
}
|
||||
|
||||
@@ -51,3 +65,29 @@ pub async fn node_info(state: State<'_, AppState>) -> AppResult<NodeInfo> {
|
||||
pub fn sidecar_logs(state: State<'_, AppState>) -> Vec<String> {
|
||||
state.sidecar.logs_snapshot()
|
||||
}
|
||||
|
||||
// ─── Peers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn peers_list(state: State<'_, AppState>) -> AppResult<Vec<PeerInfo>> {
|
||||
require_client(&state)?.list_peers().await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn peer_add(state: State<'_, AppState>, endpoint: String) -> AppResult<()> {
|
||||
if endpoint.trim().is_empty() {
|
||||
return Err(AppError::BadInput("endpoint must not be empty".into()));
|
||||
}
|
||||
require_client(&state)?.add_peer(endpoint.trim()).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn peer_remove(state: State<'_, AppState>, endpoint: String) -> AppResult<()> {
|
||||
require_client(&state)?.remove_peer(&endpoint).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn peers_stats(state: State<'_, AppState>) -> AppResult<AggregatedStats> {
|
||||
let peers = require_client(&state)?.list_peers().await?;
|
||||
Ok(crate::api::peers::aggregate(&peers))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user