connection.rs (3479B)
1 use radroots_nostr::prelude::radroots_nostr_parse_pubkey; 2 use serde::{Deserialize, Serialize}; 3 use url::Url; 4 5 use crate::transport::jsonrpc::RpcError; 6 7 #[derive(Clone, Debug, Serialize)] 8 pub enum Nip46ConnectMode { 9 Bunker, 10 Nostrconnect, 11 } 12 13 #[derive(Clone, Debug)] 14 pub struct Nip46ConnectInfo { 15 pub mode: Nip46ConnectMode, 16 pub relays: Vec<String>, 17 pub remote_signer_pubkey: Option<String>, 18 pub client_pubkey: Option<String>, 19 pub secret: Option<String>, 20 pub perms: Vec<String>, 21 pub name: Option<String>, 22 pub url: Option<String>, 23 pub image: Option<String>, 24 } 25 26 #[derive(Debug, Deserialize)] 27 #[serde(untagged)] 28 enum RelayParam { 29 One(String), 30 Many(Vec<String>), 31 } 32 33 #[derive(Debug, Deserialize)] 34 struct Nip46ConnectQuery { 35 relay: Option<RelayParam>, 36 secret: Option<String>, 37 perms: Option<String>, 38 name: Option<String>, 39 url: Option<String>, 40 image: Option<String>, 41 } 42 43 pub fn parse_connect_url(raw: &str) -> Result<Nip46ConnectInfo, RpcError> { 44 let url = Url::parse(raw).map_err(|e| RpcError::InvalidParams(e.to_string()))?; 45 match url.scheme() { 46 "bunker" => parse_bunker_url(&url), 47 "nostrconnect" => parse_nostrconnect_url(&url), 48 _ => Err(RpcError::InvalidParams("unsupported scheme".to_string())), 49 } 50 } 51 52 fn parse_bunker_url(url: &Url) -> Result<Nip46ConnectInfo, RpcError> { 53 let remote_signer_pubkey = url.host_str().map(|host| host.to_string()); 54 let query: Nip46ConnectQuery = serde_qs::from_str(url.query().unwrap_or_default()) 55 .map_err(|e| RpcError::InvalidParams(e.to_string()))?; 56 let relays = relay_list(query.relay); 57 let perms = parse_perms(query.perms); 58 59 Ok(Nip46ConnectInfo { 60 mode: Nip46ConnectMode::Bunker, 61 relays, 62 remote_signer_pubkey, 63 client_pubkey: None, 64 secret: query.secret, 65 perms, 66 name: query.name, 67 url: query.url, 68 image: query.image, 69 }) 70 } 71 72 fn parse_nostrconnect_url(url: &Url) -> Result<Nip46ConnectInfo, RpcError> { 73 let client_pubkey = url 74 .host_str() 75 .map(|host| host.to_string()) 76 .ok_or_else(|| RpcError::InvalidParams("missing client pubkey".to_string()))?; 77 radroots_nostr_parse_pubkey(&client_pubkey) 78 .map_err(|e| RpcError::InvalidParams(format!("invalid client pubkey: {e}")))?; 79 let query: Nip46ConnectQuery = serde_qs::from_str(url.query().unwrap_or_default()) 80 .map_err(|e| RpcError::InvalidParams(e.to_string()))?; 81 let relays = relay_list(query.relay); 82 let perms = parse_perms(query.perms); 83 84 Ok(Nip46ConnectInfo { 85 mode: Nip46ConnectMode::Nostrconnect, 86 relays, 87 remote_signer_pubkey: None, 88 client_pubkey: Some(client_pubkey), 89 secret: query.secret, 90 perms, 91 name: query.name, 92 url: query.url, 93 image: query.image, 94 }) 95 } 96 97 fn parse_perms(perms: Option<String>) -> Vec<String> { 98 perms 99 .unwrap_or_default() 100 .split(',') 101 .map(|entry| entry.trim().to_string()) 102 .filter(|entry| !entry.is_empty()) 103 .collect() 104 } 105 106 fn relay_list(relay: Option<RelayParam>) -> Vec<String> { 107 let relays = match relay { 108 Some(RelayParam::One(value)) => vec![value], 109 Some(RelayParam::Many(values)) => values, 110 None => Vec::new(), 111 }; 112 relays 113 .into_iter() 114 .map(|value| value.trim().to_string()) 115 .filter(|value| !value.is_empty()) 116 .collect() 117 }