commit 8339260d6b70db00d407d9473011bb4e80a21b1c
parent 59cf13debba2a7040a8851078480f74225d9f08a
Author: triesap <137732411+triesap@users.noreply.github.com>
Date: Sat, 23 Aug 2025 18:59:59 +0000
Edit `relays` rpc methods file tree, update/fix nostr utils usage.
Diffstat:
11 files changed, 224 insertions(+), 134 deletions(-)
diff --git a/crates/radrootsd/src/rpc/error.rs b/crates/radrootsd/src/rpc/error.rs
@@ -9,12 +9,20 @@ pub enum RpcError {
NoRelays,
#[error("invalid params: {0}")]
InvalidParams(String),
+ #[error("method not found: {0}")]
+ MethodNotFound(String),
#[error("{0}")]
Other(String),
}
impl From<RpcError> for ErrorObjectOwned {
fn from(err: RpcError) -> Self {
- ErrorObject::owned(-32000, err.to_string(), None::<()>)
+ match err {
+ RpcError::InvalidParams(msg) => ErrorObject::owned(-32602, msg, None::<()>),
+ RpcError::MethodNotFound(name) => {
+ ErrorObject::owned(-32601, format!("method not found: {name}"), None::<()>)
+ }
+ other => ErrorObject::owned(-32000, other.to_string(), None::<()>),
+ }
}
}
diff --git a/crates/radrootsd/src/rpc/events/profile/list.rs b/crates/radrootsd/src/rpc/events/profile/list.rs
@@ -4,7 +4,7 @@ use serde_json::{Value as JsonValue, json};
use std::time::Duration;
use crate::{radrootsd::Radrootsd, rpc::RpcError};
-use radroots_nostr::prelude::{fetch_latest_metadata_for_author, npub_string};
+use radroots_nostr::prelude::{fetch_metadata_for_author, npub_string};
pub fn register(m: &mut RpcModule<Radrootsd>) -> Result<()> {
m.register_async_method("events.profile.list", |_params, ctx, _| async move {
@@ -14,7 +14,7 @@ pub fn register(m: &mut RpcModule<Radrootsd>) -> Result<()> {
let me_pk = ctx.pubkey;
- let latest = fetch_latest_metadata_for_author(&ctx.client, me_pk, Duration::from_secs(10))
+ let latest = fetch_metadata_for_author(&ctx.client, me_pk, Duration::from_secs(10))
.await
.map_err(|e| RpcError::Other(format!("metadata fetch failed: {e}")))?;
diff --git a/crates/radrootsd/src/rpc/relays.rs b/crates/radrootsd/src/rpc/relays.rs
@@ -1,117 +0,0 @@
-use anyhow::Result;
-use jsonrpsee::RpcModule;
-use serde::Deserialize;
-use serde_json::{Value as JsonValue, json};
-
-use crate::radrootsd::Radrootsd;
-use crate::rpc::RpcError;
-
-use radroots_nostr::prelude::{add_relay, connect, fetch_nip11, remove_relay};
-
-#[derive(Debug, Deserialize)]
-struct AddParams {
- url: String,
-}
-
-#[derive(Debug, Deserialize)]
-struct RemoveParams {
- url: String,
-}
-
-#[derive(Debug, Deserialize)]
-struct StatusParams {
- #[serde(default)]
- include_nip11: bool,
-}
-
-pub fn module(radrootsd: Radrootsd) -> Result<RpcModule<Radrootsd>> {
- let mut m = RpcModule::new(radrootsd);
-
- m.register_async_method("relays.add", |params, ctx, _| async move {
- let AddParams { url } = params
- .parse()
- .map_err(|e| RpcError::InvalidParams(e.to_string()))?;
- add_relay(&ctx.client, &url)
- .await
- .map_err(|e| RpcError::AddRelay(url.clone(), e.to_string()))?;
-
- Ok::<JsonValue, RpcError>(json!({ "added": url }))
- })?;
-
- m.register_async_method("relays.remove", |params, ctx, _| async move {
- let RemoveParams { url } = params
- .parse()
- .map_err(|e| RpcError::InvalidParams(e.to_string()))?;
-
- remove_relay(&ctx.client, &url)
- .await
- .map_err(|e| RpcError::Other(format!("failed to remove relay {url}: {e}")))?;
-
- Ok::<JsonValue, RpcError>(json!({ "removed": url }))
- })?;
-
- m.register_async_method("relays.list", |_p, ctx, _| async move {
- let relays = ctx.client.relays().await;
- Ok::<JsonValue, RpcError>(json!(
- relays.keys().map(|u| u.to_string()).collect::<Vec<_>>()
- ))
- })?;
-
- m.register_async_method("relays.status", |params, ctx, _| async move {
- let StatusParams { include_nip11 } = params.parse().unwrap_or(StatusParams {
- include_nip11: false,
- });
-
- let relays = ctx.client.relays().await;
-
- let mut out = Vec::with_capacity(relays.len());
-
- for (relay_url, relay) in relays {
- let url_str = relay_url.to_string();
- let parsed = reqwest::Url::parse(&url_str).ok();
-
- let scheme = parsed.as_ref().map(|u| u.scheme().to_string());
- let host = parsed
- .as_ref()
- .and_then(|u| u.host_str())
- .map(|s| s.to_string());
- let port = parsed.as_ref().and_then(|u| u.port());
- let onion = host
- .as_deref()
- .map(|h| h.ends_with(".onion"))
- .unwrap_or(false);
-
- let mut row = json!({
- "url": url_str,
- "status": format!("{}", relay.status()),
- "scheme": scheme,
- "host": host,
- "port": port,
- "onion": onion
- });
-
- if include_nip11 {
- if let Some(doc) = fetch_nip11(row["url"].as_str().unwrap()).await {
- row["nip11"] = json!(doc);
- }
- }
-
- out.push(row);
- }
-
- Ok::<JsonValue, RpcError>(json!(out))
- })?;
-
- m.register_async_method("relays.connect", |_p, ctx, _| async move {
- let relays = ctx.client.relays().await;
- if relays.is_empty() {
- return Err(RpcError::NoRelays);
- }
- let client = ctx.client.clone();
- tokio::spawn(async move { connect(&client).await });
-
- Ok::<JsonValue, RpcError>(json!({ "connecting": relays.len() }))
- })?;
-
- Ok(m)
-}
diff --git a/crates/radrootsd/src/rpc/relays/add.rs b/crates/radrootsd/src/rpc/relays/add.rs
@@ -0,0 +1,28 @@
+use anyhow::Result;
+use jsonrpsee::RpcModule;
+use radroots_nostr::prelude::add_relay;
+use serde::Deserialize;
+use serde_json::{Value as JsonValue, json};
+
+use crate::radrootsd::Radrootsd;
+use crate::rpc::RpcError;
+
+#[derive(Debug, Deserialize)]
+struct AddParams {
+ url: String,
+}
+
+pub fn register(m: &mut RpcModule<Radrootsd>) -> Result<()> {
+ m.register_async_method("relays.add", |params, ctx, _| async move {
+ let AddParams { url } = params
+ .parse()
+ .map_err(|e| RpcError::InvalidParams(e.to_string()))?;
+
+ add_relay(&ctx.client, &url)
+ .await
+ .map_err(|e| RpcError::AddRelay(url.clone(), e.to_string()))?;
+
+ Ok::<JsonValue, RpcError>(json!({ "added": url }))
+ })?;
+ Ok(())
+}
diff --git a/crates/radrootsd/src/rpc/relays/connect.rs b/crates/radrootsd/src/rpc/relays/connect.rs
@@ -0,0 +1,45 @@
+use anyhow::Result;
+use jsonrpsee::RpcModule;
+use serde_json::{Value as JsonValue, json};
+
+use crate::radrootsd::Radrootsd;
+use crate::rpc::RpcError;
+
+use nostr_sdk::RelayStatus;
+use radroots_nostr::prelude::connect;
+
+pub fn register(m: &mut RpcModule<Radrootsd>) -> Result<()> {
+ m.register_async_method("relays.connect", |_p, ctx, _| async move {
+ let relays = ctx.client.relays().await;
+ if relays.is_empty() {
+ return Err(RpcError::NoRelays);
+ }
+
+ let mut connected = 0usize;
+ let mut connecting = 0usize;
+ let mut disconnected = 0usize;
+
+ for (_, r) in &relays {
+ match r.status() {
+ RelayStatus::Connected => connected += 1,
+ RelayStatus::Connecting => connecting += 1,
+ _ => disconnected += 1,
+ }
+ }
+
+ // Idempotent: only spawn if we have anything not connected/connecting
+ let need_connect = disconnected > 0;
+ if need_connect {
+ let client = ctx.client.clone();
+ tokio::spawn(async move { connect(&client).await });
+ }
+
+ Ok::<JsonValue, RpcError>(json!({
+ "connected": connected,
+ "connecting": connecting,
+ "disconnected": disconnected,
+ "spawned_connect": need_connect
+ }))
+ })?;
+ Ok(())
+}
diff --git a/crates/radrootsd/src/rpc/relays/list.rs b/crates/radrootsd/src/rpc/relays/list.rs
@@ -0,0 +1,16 @@
+use anyhow::Result;
+use jsonrpsee::RpcModule;
+use serde_json::{Value as JsonValue, json};
+
+use crate::radrootsd::Radrootsd;
+use crate::rpc::RpcError;
+
+pub fn register(m: &mut RpcModule<Radrootsd>) -> Result<()> {
+ m.register_async_method("relays.list", |_p, ctx, _| async move {
+ let relays = ctx.client.relays().await;
+ Ok::<JsonValue, RpcError>(json!(
+ relays.keys().map(|u| u.to_string()).collect::<Vec<_>>()
+ ))
+ })?;
+ Ok(())
+}
diff --git a/crates/radrootsd/src/rpc/relays/mod.rs b/crates/radrootsd/src/rpc/relays/mod.rs
@@ -0,0 +1,22 @@
+use anyhow::Result;
+use jsonrpsee::RpcModule;
+
+use crate::radrootsd::Radrootsd;
+
+pub mod add;
+pub mod connect;
+pub mod list;
+pub mod remove;
+pub mod status;
+
+pub fn module(radrootsd: Radrootsd) -> Result<RpcModule<Radrootsd>> {
+ let mut m = RpcModule::new(radrootsd);
+
+ add::register(&mut m)?;
+ remove::register(&mut m)?;
+ list::register(&mut m)?;
+ status::register(&mut m)?;
+ connect::register(&mut m)?;
+
+ Ok(m)
+}
diff --git a/crates/radrootsd/src/rpc/relays/remove.rs b/crates/radrootsd/src/rpc/relays/remove.rs
@@ -0,0 +1,28 @@
+use anyhow::Result;
+use jsonrpsee::RpcModule;
+use radroots_nostr::prelude::remove_relay;
+use serde::Deserialize;
+use serde_json::{Value as JsonValue, json};
+
+use crate::radrootsd::Radrootsd;
+use crate::rpc::RpcError;
+
+#[derive(Debug, Deserialize)]
+struct RemoveParams {
+ url: String,
+}
+
+pub fn register(m: &mut RpcModule<Radrootsd>) -> Result<()> {
+ m.register_async_method("relays.remove", |params, ctx, _| async move {
+ let RemoveParams { url } = params
+ .parse()
+ .map_err(|e| RpcError::InvalidParams(e.to_string()))?;
+
+ remove_relay(&ctx.client, &url)
+ .await
+ .map_err(|e| RpcError::Other(format!("failed to remove relay {url}: {e}")))?;
+
+ Ok::<JsonValue, RpcError>(json!({ "removed": url }))
+ })?;
+ Ok(())
+}
diff --git a/crates/radrootsd/src/rpc/relays/status.rs b/crates/radrootsd/src/rpc/relays/status.rs
@@ -0,0 +1,58 @@
+use anyhow::Result;
+use jsonrpsee::RpcModule;
+use serde::Deserialize;
+use serde_json::{Map as JsonMap, Value as JsonValue, json};
+
+use crate::radrootsd::Radrootsd;
+use crate::rpc::RpcError;
+use radroots_nostr::prelude::fetch_nip11;
+
+#[derive(Debug, Deserialize)]
+struct StatusParams {
+ #[serde(default)]
+ include_nip11: bool,
+}
+
+pub fn register(m: &mut RpcModule<Radrootsd>) -> Result<()> {
+ m.register_async_method("relays.status", |params, ctx, _| async move {
+ let StatusParams { include_nip11 } = params
+ .parse()
+ .map_err(|e| RpcError::InvalidParams(e.to_string()))?;
+
+ let relays = ctx.client.relays().await;
+ let mut out = Vec::with_capacity(relays.len());
+
+ for (relay_url, relay) in relays {
+ let url_str = relay_url.to_string();
+ let status_str = format!("{}", relay.status());
+ let parsed = reqwest::Url::parse(&url_str).ok();
+
+ // Build with locals; only insert present fields.
+ let mut row = JsonMap::new();
+ row.insert("url".into(), json!(url_str));
+ row.insert("status".into(), json!(status_str));
+
+ if let Some(u) = &parsed {
+ row.insert("scheme".into(), json!(u.scheme()));
+ if let Some(h) = u.host_str() {
+ row.insert("host".into(), json!(h));
+ row.insert("onion".into(), json!(h.ends_with(".onion")));
+ }
+ if let Some(p) = u.port() {
+ row.insert("port".into(), json!(p));
+ }
+ }
+
+ if include_nip11 {
+ if let Some(doc) = fetch_nip11(row["url"].as_str().unwrap()).await {
+ row.insert("nip11".into(), json!(doc));
+ }
+ }
+
+ out.push(JsonValue::Object(row));
+ }
+
+ Ok::<JsonValue, RpcError>(json!(out))
+ })?;
+ Ok(())
+}
diff --git a/crates/radrootsd/src/rpc/system.rs b/crates/radrootsd/src/rpc/system.rs
@@ -19,7 +19,22 @@ pub fn module(radrootsd: Radrootsd) -> Result<RpcModule<Radrootsd>> {
})?;
m.register_method("system.help", |_p, _ctx, _| {
- vec!["system.ping", "system.get_info", "system.help"]
+ vec![
+ /* %% radrootsd-methods %% */
+ "system.get_info",
+ "system.help",
+ "system.ping",
+ "events.note.list",
+ "events.note.publish",
+ "events.profile.list",
+ "events.profile.publish",
+ "relays.add",
+ "relays.connect",
+ "relays.list",
+ "relays.remove",
+ "relays.status",
+ /* %% radrootsd-methods %% */
+ ]
})?;
Ok(m)
diff --git a/crates/radrootsd/src/utils.rs b/crates/radrootsd/src/utils.rs
@@ -1,13 +0,0 @@
-pub fn ws_to_http(ws: &str) -> Option<String> {
- let mut u = reqwest::Url::parse(ws).ok()?;
- let scheme = u.scheme().to_owned();
-
- let new_scheme = match scheme.as_str() {
- "wss" => "https",
- "ws" => "http",
- other => other,
- };
-
- u.set_scheme(new_scheme).ok()?;
- Some(u.into())
-}