Files
Mycell-UI/src-tauri/src/api/mod.rs
syoul d737231123 P1: sidecar lifecycle and HTTP bridge
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
2026-04-25 22:45:52 +02:00

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