Dorian e3aa95a103 fix: prevent tokio runtime deadlock in credential issue/verify
The credential issuance and verification handlers used
Handle::block_on() directly inside the tokio runtime, causing a
deadlock. Wrapped with block_in_place() to properly yield the
runtime thread.

Also completed full feature verification across all 25 test groups
(~175 checks) on live server.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 07:43:12 +00:00

169 lines
5.7 KiB
Rust

use super::RpcHandler;
use crate::network::router;
use anyhow::Result;
impl RpcHandler {
/// Discover UPnP router on the local network.
pub(super) async fn handle_router_discover(&self) -> Result<serde_json::Value> {
let info = router::discover_router().await?;
Ok(serde_json::json!({
"discovered": info.discovered,
"device_name": info.device_name,
"wan_ip": info.wan_ip,
"upnp_available": info.upnp_available,
}))
}
/// List all configured port forwards.
pub(super) async fn handle_router_list_forwards(&self) -> Result<serde_json::Value> {
let forwards = router::list_forwards(&self.config.data_dir).await?;
let items: Vec<serde_json::Value> = forwards
.into_iter()
.map(|f| {
serde_json::json!({
"id": f.id,
"service_name": f.service_name,
"internal_port": f.internal_port,
"external_port": f.external_port,
"protocol": f.protocol,
"enabled": f.enabled,
"created_at": f.created_at,
})
})
.collect();
Ok(serde_json::json!({ "forwards": items }))
}
/// Add a port forward.
pub(super) async fn handle_router_add_forward(
&self,
params: Option<serde_json::Value>,
) -> Result<serde_json::Value> {
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
let service_name = params
.get("service_name")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing service_name"))?;
let internal_port = params
.get("internal_port")
.and_then(|v| v.as_u64())
.ok_or_else(|| anyhow::anyhow!("Missing internal_port"))? as u16;
let external_port = params
.get("external_port")
.and_then(|v| v.as_u64())
.ok_or_else(|| anyhow::anyhow!("Missing external_port"))? as u16;
let protocol = params
.get("protocol")
.and_then(|v| v.as_str())
.unwrap_or("TCP");
let forward = router::add_forward(
&self.config.data_dir,
service_name,
internal_port,
external_port,
protocol,
)
.await?;
Ok(serde_json::json!({
"id": forward.id,
"service_name": forward.service_name,
"external_port": forward.external_port,
}))
}
/// Remove a port forward.
pub(super) async fn handle_router_remove_forward(
&self,
params: Option<serde_json::Value>,
) -> Result<serde_json::Value> {
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
let id = params
.get("id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing id"))?;
router::remove_forward(&self.config.data_dir, id).await?;
Ok(serde_json::json!({ "ok": true }))
}
/// Run network diagnostics.
pub(super) async fn handle_network_diagnostics(&self) -> Result<serde_json::Value> {
let diag = router::run_diagnostics().await?;
Ok(serde_json::json!({
"wan_ip": diag.wan_ip,
"nat_type": diag.nat_type,
"upnp_available": diag.upnp_available,
"tor_connected": diag.tor_connected,
"dns_working": diag.dns_working,
"recommendations": diag.recommendations,
}))
}
/// Detect the type of router at a given gateway address.
pub(super) async fn handle_router_detect(
&self,
params: Option<serde_json::Value>,
) -> Result<serde_json::Value> {
let gateway = params
.as_ref()
.and_then(|p| p.get("gateway"))
.and_then(|v| v.as_str())
.unwrap_or("192.168.1.1");
let router_type = router::detect_router_type(gateway).await;
Ok(serde_json::json!({
"gateway": gateway,
"router_type": router_type,
}))
}
/// Get router info and capabilities.
pub(super) async fn handle_router_info(&self) -> Result<serde_json::Value> {
router::get_router_info(&self.config.data_dir).await
}
/// Configure router API access.
pub(super) async fn handle_router_configure(
&self,
params: Option<serde_json::Value>,
) -> Result<serde_json::Value> {
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
let router_type_str = params
.get("router_type")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let address = params
.get("address")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing address"))?;
let api_key = params.get("api_key").and_then(|v| v.as_str());
let username = params.get("username").and_then(|v| v.as_str());
let password = params.get("password").and_then(|v| v.as_str());
let router_type = match router_type_str {
"openwrt" => router::RouterType::OpenWrt,
"pfsense" => router::RouterType::PfSense,
"opnsense" => router::RouterType::OPNsense,
"upnp" => router::RouterType::UPnP,
_ => router::RouterType::Unknown,
};
let config = router::configure_router(
&self.config.data_dir,
router_type,
address,
api_key,
username,
password,
)
.await?;
Ok(serde_json::json!({
"configured": config.configured,
"router_type": config.router_type,
}))
}
}