2026-03-12 00:19:30 +00:00
|
|
|
use super::RpcHandler;
|
2026-03-17 00:03:08 +00:00
|
|
|
use crate::mesh;
|
2026-03-12 00:19:30 +00:00
|
|
|
use anyhow::Result;
|
|
|
|
|
use tracing::info;
|
|
|
|
|
|
|
|
|
|
impl RpcHandler {
|
2026-03-17 00:03:08 +00:00
|
|
|
/// mesh.status — Get mesh radio status, device info, and peer count.
|
2026-03-12 00:19:30 +00:00
|
|
|
pub(super) async fn handle_mesh_status(&self) -> Result<serde_json::Value> {
|
2026-03-17 00:03:08 +00:00
|
|
|
let service = self.mesh_service.read().await;
|
|
|
|
|
if let Some(svc) = service.as_ref() {
|
|
|
|
|
let status = svc.status().await;
|
|
|
|
|
Ok(serde_json::to_value(status)?)
|
|
|
|
|
} else {
|
|
|
|
|
// No service running — return basic config + device detection
|
|
|
|
|
let config = mesh::load_config(&self.config.data_dir).await?;
|
|
|
|
|
let devices = mesh::detect_devices().await;
|
|
|
|
|
Ok(serde_json::json!({
|
|
|
|
|
"enabled": config.enabled,
|
|
|
|
|
"device_connected": false,
|
|
|
|
|
"device_type": "unknown",
|
|
|
|
|
"device_path": config.device_path,
|
|
|
|
|
"channel_name": config.channel_name.unwrap_or_else(|| "archipelago".to_string()),
|
|
|
|
|
"detected_devices": devices,
|
|
|
|
|
"peer_count": 0,
|
|
|
|
|
"messages_sent": 0,
|
|
|
|
|
"messages_received": 0,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-12 00:19:30 +00:00
|
|
|
|
2026-03-17 00:03:08 +00:00
|
|
|
/// mesh.peers — List discovered mesh peers.
|
|
|
|
|
pub(super) async fn handle_mesh_peers(&self) -> Result<serde_json::Value> {
|
|
|
|
|
let service = self.mesh_service.read().await;
|
|
|
|
|
if let Some(svc) = service.as_ref() {
|
|
|
|
|
let peers = svc.peers().await;
|
|
|
|
|
Ok(serde_json::json!({
|
|
|
|
|
"peers": peers,
|
|
|
|
|
"count": peers.len(),
|
|
|
|
|
}))
|
|
|
|
|
} else {
|
|
|
|
|
Ok(serde_json::json!({
|
|
|
|
|
"peers": [],
|
|
|
|
|
"count": 0,
|
|
|
|
|
}))
|
|
|
|
|
}
|
2026-03-12 00:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-17 00:03:08 +00:00
|
|
|
/// mesh.messages — Get recent mesh message history.
|
|
|
|
|
pub(super) async fn handle_mesh_messages(
|
2026-03-12 00:19:30 +00:00
|
|
|
&self,
|
|
|
|
|
params: Option<serde_json::Value>,
|
|
|
|
|
) -> Result<serde_json::Value> {
|
2026-03-17 00:03:08 +00:00
|
|
|
let limit = params
|
2026-03-12 00:19:30 +00:00
|
|
|
.as_ref()
|
2026-03-17 00:03:08 +00:00
|
|
|
.and_then(|p| p.get("limit"))
|
|
|
|
|
.and_then(|v| v.as_u64())
|
|
|
|
|
.map(|n| n as usize);
|
|
|
|
|
|
|
|
|
|
let service = self.mesh_service.read().await;
|
|
|
|
|
if let Some(svc) = service.as_ref() {
|
|
|
|
|
let messages = svc.messages(limit).await;
|
|
|
|
|
Ok(serde_json::json!({
|
|
|
|
|
"messages": messages,
|
|
|
|
|
"count": messages.len(),
|
|
|
|
|
}))
|
|
|
|
|
} else {
|
|
|
|
|
Ok(serde_json::json!({
|
|
|
|
|
"messages": [],
|
|
|
|
|
"count": 0,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// mesh.send — Send an encrypted message to a mesh peer.
|
|
|
|
|
pub(super) async fn handle_mesh_send(
|
|
|
|
|
&self,
|
|
|
|
|
params: Option<serde_json::Value>,
|
|
|
|
|
) -> Result<serde_json::Value> {
|
|
|
|
|
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
|
|
|
|
|
|
|
|
|
let contact_id = params
|
|
|
|
|
.get("contact_id")
|
|
|
|
|
.and_then(|v| v.as_u64())
|
|
|
|
|
.ok_or_else(|| anyhow::anyhow!("Missing contact_id"))? as u32;
|
2026-03-12 00:19:30 +00:00
|
|
|
|
2026-03-17 00:03:08 +00:00
|
|
|
let message = params
|
|
|
|
|
.get("message")
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.ok_or_else(|| anyhow::anyhow!("Missing message"))?;
|
2026-03-12 00:19:30 +00:00
|
|
|
|
2026-03-17 00:03:08 +00:00
|
|
|
if message.is_empty() {
|
|
|
|
|
anyhow::bail!("Message cannot be empty");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let service = self.mesh_service.read().await;
|
|
|
|
|
let svc = service
|
|
|
|
|
.as_ref()
|
|
|
|
|
.ok_or_else(|| anyhow::anyhow!("Mesh service not running. Enable mesh first."))?;
|
|
|
|
|
|
|
|
|
|
let msg = svc.send_message(contact_id, message).await?;
|
|
|
|
|
info!(contact_id, encrypted = msg.encrypted, "Sent mesh message");
|
2026-03-12 00:19:30 +00:00
|
|
|
|
|
|
|
|
Ok(serde_json::json!({
|
2026-03-17 00:03:08 +00:00
|
|
|
"sent": true,
|
|
|
|
|
"message_id": msg.id,
|
|
|
|
|
"encrypted": msg.encrypted,
|
2026-03-12 00:19:30 +00:00
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// mesh.broadcast — Broadcast our node identity over mesh.
|
|
|
|
|
pub(super) async fn handle_mesh_broadcast(&self) -> Result<serde_json::Value> {
|
2026-03-17 00:03:08 +00:00
|
|
|
let service = self.mesh_service.read().await;
|
|
|
|
|
let svc = service
|
|
|
|
|
.as_ref()
|
|
|
|
|
.ok_or_else(|| anyhow::anyhow!("Mesh service not running. Enable mesh first."))?;
|
2026-03-12 00:19:30 +00:00
|
|
|
|
2026-03-17 00:03:08 +00:00
|
|
|
svc.broadcast_identity().await?;
|
2026-03-12 00:19:30 +00:00
|
|
|
info!("Broadcast identity over mesh");
|
2026-03-17 00:03:08 +00:00
|
|
|
|
2026-03-12 00:19:30 +00:00
|
|
|
Ok(serde_json::json!({ "broadcast": true }))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// mesh.configure — Enable/disable mesh and set device path.
|
|
|
|
|
pub(super) async fn handle_mesh_configure(
|
|
|
|
|
&self,
|
|
|
|
|
params: Option<serde_json::Value>,
|
|
|
|
|
) -> Result<serde_json::Value> {
|
|
|
|
|
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
|
|
|
|
|
|
|
|
|
let mut config = mesh::load_config(&self.config.data_dir).await?;
|
|
|
|
|
|
|
|
|
|
if let Some(enabled) = params.get("enabled").and_then(|v| v.as_bool()) {
|
|
|
|
|
config.enabled = enabled;
|
|
|
|
|
}
|
|
|
|
|
if let Some(device) = params.get("device_path").and_then(|v| v.as_str()) {
|
|
|
|
|
config.device_path = Some(device.to_string());
|
|
|
|
|
}
|
|
|
|
|
if let Some(channel) = params.get("channel_name").and_then(|v| v.as_str()) {
|
|
|
|
|
config.channel_name = Some(channel.to_string());
|
|
|
|
|
}
|
|
|
|
|
if let Some(broadcast) = params.get("broadcast_identity").and_then(|v| v.as_bool()) {
|
|
|
|
|
config.broadcast_identity = broadcast;
|
|
|
|
|
}
|
2026-03-17 00:03:08 +00:00
|
|
|
if let Some(name) = params.get("advert_name").and_then(|v| v.as_str()) {
|
|
|
|
|
config.advert_name = Some(name.to_string());
|
|
|
|
|
}
|
2026-03-12 00:19:30 +00:00
|
|
|
|
|
|
|
|
mesh::save_config(&self.config.data_dir, &config).await?;
|
|
|
|
|
|
2026-03-17 00:03:08 +00:00
|
|
|
// If we have a running service, update its config
|
|
|
|
|
let mut service = self.mesh_service.write().await;
|
|
|
|
|
if let Some(svc) = service.as_mut() {
|
|
|
|
|
svc.configure(config.clone()).await?;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-12 00:19:30 +00:00
|
|
|
info!("Mesh config updated");
|
|
|
|
|
Ok(serde_json::json!({
|
|
|
|
|
"configured": true,
|
|
|
|
|
"enabled": config.enabled,
|
|
|
|
|
"device_path": config.device_path,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|