Backend - api/routes.rs models the Babel-style route shape; metric uses an untagged enum to round-trip both numeric hop counts and the literal "infinite" string the daemon emits for poisoned routes - routes_snapshot() runs the three GETs concurrently with try_join so the snapshot is internally consistent - poller spawns a second 5s loop emitting routes://updated; both loops are owned by the Poller and aborted together on stop_daemon Frontend - routes store mirrors the snapshot shape; tabbed view (radix-vue) with selected, fallback and queried lists - RouteTable component shared by selected/fallback; metric column is colour-coded (0 green, low neutral, high yellow, infinite red) - Queried subnets show a live `expires in 12s` countdown driven by a 1Hz tick ref instead of mutating the store
102 lines
3.3 KiB
Rust
102 lines
3.3 KiB
Rust
use crate::api::admin::NodeInfo;
|
|
use crate::api::peers::{AggregatedStats, PeerInfo};
|
|
use crate::api::routes::RoutesSnapshot;
|
|
use crate::api::MyceliumClient;
|
|
use crate::error::{AppError, AppResult};
|
|
use crate::sidecar::SidecarConfig;
|
|
use crate::state::AppState;
|
|
use serde::Serialize;
|
|
use tauri::{AppHandle, State};
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct DaemonStatus {
|
|
pub running: bool,
|
|
pub api_url: Option<String>,
|
|
pub key_path: Option<String>,
|
|
pub config_path: Option<String>,
|
|
}
|
|
|
|
fn snapshot(state: &AppState) -> DaemonStatus {
|
|
let sc = &state.sidecar;
|
|
DaemonStatus {
|
|
running: sc.is_running(),
|
|
api_url: sc.client().map(|c| c.base_url().to_string()),
|
|
key_path: sc.key_path().map(|p| p.display().to_string()),
|
|
config_path: sc.config_path().map(|p| p.display().to_string()),
|
|
}
|
|
}
|
|
|
|
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,
|
|
state: State<'_, AppState>,
|
|
config: Option<SidecarConfig>,
|
|
) -> AppResult<DaemonStatus> {
|
|
let cfg = config.unwrap_or_default();
|
|
state.sidecar.start(&app, &cfg).await?;
|
|
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(snapshot(&state))
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn node_info(state: State<'_, AppState>) -> AppResult<NodeInfo> {
|
|
let client = require_client(&state)?;
|
|
client.node_info().await
|
|
}
|
|
|
|
#[tauri::command]
|
|
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))
|
|
}
|
|
|
|
// ─── Routes ──────────────────────────────────────────────────────────────────
|
|
|
|
#[tauri::command]
|
|
pub async fn routes_snapshot(state: State<'_, AppState>) -> AppResult<RoutesSnapshot> {
|
|
require_client(&state)?.routes_snapshot().await
|
|
}
|