Backend - sidecar.rs supervises the bundled `mycelium` binary launched via pkexec; locates it in resource_dir or CARGO_MANIFEST_DIR/binaries matching $TAURI_ENV_TARGET_TRIPLE - ephemeral port via portpicker, key + config persisted in app_data_dir, kill_on_drop with explicit start_kill on stop - health-check loop calls /api/v1/admin until 2xx (timeout 20s); emits sidecar://ready and sidecar://exited - 500-line ring buffer of stdout/stderr surfaced via sidecar_logs command for the upcoming Settings page - elevation::is_auth_failure(126|127) maps pkexec cancel to a dedicated AppError variant - AppError uses thiserror, Serialize impl renders messages as plain strings for the JS side Frontend - typed `api` wrapper around invoke() in src/lib/api.ts - node store (Pinia) bootstraps on mount, listens on sidecar://ready and sidecar://exited - StartupOverlay covers the whole window for idle/starting/error phases; sidebar status dot + start/stop button - Status view renders subnet, pubkey, api endpoint and key path with one-click clipboard copy
69 lines
1.8 KiB
Rust
69 lines
1.8 KiB
Rust
pub mod admin;
|
|
|
|
use crate::error::{AppError, AppResult};
|
|
use reqwest::{Client, Response};
|
|
use serde::de::DeserializeOwned;
|
|
use std::time::Duration;
|
|
|
|
/// Thin REST client for the mycelium daemon's HTTP API.
|
|
///
|
|
/// The base URL is set after the sidecar reports ready; clients are cheap
|
|
/// to clone (the inner `reqwest::Client` keeps a shared connection pool).
|
|
#[derive(Debug, Clone)]
|
|
pub struct MyceliumClient {
|
|
base: String,
|
|
http: Client,
|
|
}
|
|
|
|
impl MyceliumClient {
|
|
pub fn new(base: impl Into<String>) -> Self {
|
|
let http = Client::builder()
|
|
.timeout(Duration::from_secs(10))
|
|
.build()
|
|
.expect("reqwest client build");
|
|
Self {
|
|
base: base.into(),
|
|
http,
|
|
}
|
|
}
|
|
|
|
pub fn base_url(&self) -> &str {
|
|
&self.base
|
|
}
|
|
|
|
pub(crate) fn url(&self, path: &str) -> String {
|
|
format!("{}/api/v1{}", self.base, path)
|
|
}
|
|
|
|
pub(crate) async fn parse<T: DeserializeOwned>(resp: Response) -> AppResult<T> {
|
|
let status = resp.status();
|
|
if status.is_success() {
|
|
resp.json::<T>().await.map_err(AppError::from)
|
|
} else {
|
|
let body = resp.text().await.unwrap_or_default();
|
|
Err(AppError::DaemonStatus {
|
|
status: status.as_u16(),
|
|
body,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)] // wired in P2 (peers add/remove)
|
|
pub(crate) async fn check_status(resp: Response) -> AppResult<()> {
|
|
let status = resp.status();
|
|
if status.is_success() {
|
|
Ok(())
|
|
} else {
|
|
let body = resp.text().await.unwrap_or_default();
|
|
Err(AppError::DaemonStatus {
|
|
status: status.as_u16(),
|
|
body,
|
|
})
|
|
}
|
|
}
|
|
|
|
pub(crate) fn http(&self) -> &Client {
|
|
&self.http
|
|
}
|
|
}
|