radrootsd

JSON-RPC bridge for Radroots event publishing
git clone https://radroots.dev/git/radrootsd.git
Log | Files | Refs | README | LICENSE

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 }