clone

a self-hosted sync node for lofi apps: jazz sync over iroh, ticket-gated, with no static ip or cloud needed

release/0.5.0
lofi-node / native / iroh-js / src / relay.rs
relay.rs 5.3K
1use std::{str::FromStr, sync::Arc};23use napi::bindgen_prelude::*;4use napi_derive::napi;56/// Config for a single relay server.7#[derive(Debug, Clone)]8#[napi(object)]9pub struct RelayConfig {10 pub url: String,11 pub quic_port: Option<u16>,12 pub auth_token: Option<String>,13}1415impl TryFrom<RelayConfig> for iroh::RelayConfig {16 type Error = anyhow::Error;1718 fn try_from(value: RelayConfig) -> anyhow::Result<Self> {19 let url = iroh::RelayUrl::from_str(&value.url)?;20 let quic = value.quic_port.map(iroh_relay::RelayQuicConfig::new);21 let mut config = iroh::RelayConfig::new(url, quic);22 if let Some(token) = value.auth_token {23 config = config.with_auth_token(token);24 }25 Ok(config)26 }27}2829impl From<&iroh::RelayConfig> for RelayConfig {30 fn from(value: &iroh::RelayConfig) -> Self {31 Self {32 url: value.url.to_string(),33 quic_port: value.quic.as_ref().map(|q| q.port),34 auth_token: value.auth_token.clone(),35 }36 }37}3839/// A collection of relay servers an endpoint should consider.40#[derive(Debug, Clone)]41#[napi]42pub struct RelayMap(pub(crate) iroh::RelayMap);4344impl From<iroh::RelayMap> for RelayMap {45 fn from(map: iroh::RelayMap) -> Self {46 Self(map)47 }48}4950#[napi]51impl RelayMap {52 /// Create an empty relay map.53 #[napi(factory)]54 pub fn empty() -> Self {55 Self(iroh::RelayMap::empty())56 }5758 /// Build a relay map from a list of relay URLs.59 #[napi(factory)]60 pub fn from_urls(urls: Vec<String>) -> Result<Self> {61 let map = iroh::RelayMap::try_from_iter(urls.iter().map(|s| s.as_str()))62 .map_err(anyhow::Error::from)?;63 Ok(Self(map))64 }6566 /// Insert a relay (replacing any prior entry for the same URL).67 #[napi]68 pub fn insert(&self, config: RelayConfig) -> Result<()> {69 let config: iroh::RelayConfig = config.try_into()?;70 let url = config.url.clone();71 self.0.insert(url, Arc::new(config));72 Ok(())73 }7475 /// Remove the entry for the given relay URL. Returns true if removed.76 #[napi]77 pub fn remove(&self, url: String) -> Result<bool> {78 let url = iroh::RelayUrl::from_str(&url).map_err(anyhow::Error::from)?;79 Ok(self.0.remove(&url).is_some())80 }8182 /// Check whether the given relay URL is in the map.83 #[napi]84 pub fn contains(&self, url: String) -> Result<bool> {85 let url = iroh::RelayUrl::from_str(&url).map_err(anyhow::Error::from)?;86 Ok(self.0.contains(&url))87 }8889 /// Look up the configuration for the given relay URL.90 #[napi]91 pub fn get(&self, url: String) -> Result<Option<RelayConfig>> {92 let url = iroh::RelayUrl::from_str(&url).map_err(anyhow::Error::from)?;93 Ok(self.0.get(&url).map(|c| (c.as_ref()).into()))94 }9596 /// All relay URLs currently in the map.97 #[napi]98 pub fn urls(&self) -> Vec<String> {99 self.0100 .urls::<Vec<_>>()101 .into_iter()102 .map(|u| u.to_string())103 .collect()104 }105106 /// Number of relays in the map.107 #[napi]108 pub fn len(&self) -> u32 {109 self.0.len() as _110 }111112 /// True if the map has no relays.113 #[napi]114 pub fn is_empty(&self) -> bool {115 self.0.is_empty()116 }117118 /// Clean string representation.119 #[napi]120 pub fn to_string(&self) -> String {121 let urls: Vec<String> = self122 .0123 .urls::<Vec<_>>()124 .into_iter()125 .map(|u| u.to_string())126 .collect();127 format!("RelayMap([{}])", urls.join(", "))128 }129}130131/// Configuration for which relay servers an endpoint uses.132#[derive(Debug, Clone)]133#[napi]134pub struct RelayMode(pub(crate) iroh::RelayMode);135136impl From<iroh::RelayMode> for RelayMode {137 fn from(mode: iroh::RelayMode) -> Self {138 Self(mode)139 }140}141142#[napi]143impl RelayMode {144 /// No relays.145 #[napi(factory)]146 pub fn disabled() -> Self {147 Self(iroh::RelayMode::Disabled)148 }149150 /// Use the n0 production relay map.151 #[napi(factory)]152 pub fn default_mode() -> Self {153 Self(iroh::RelayMode::Default)154 }155156 /// Use the n0 staging relay map.157 #[napi(factory)]158 pub fn staging() -> Self {159 Self(iroh::RelayMode::Staging)160 }161162 /// Use a custom relay map.163 #[napi(factory)]164 pub fn custom(map: &RelayMap) -> Self {165 Self(iroh::RelayMode::Custom(map.0.clone()))166 }167168 /// Build a custom relay mode from a list of relay URLs.169 #[napi(factory)]170 pub fn custom_from_urls(urls: Vec<String>) -> Result<Self> {171 let urls: Vec<iroh::RelayUrl> = urls172 .into_iter()173 .map(|s| iroh::RelayUrl::from_str(&s).map_err(anyhow::Error::from))174 .collect::<anyhow::Result<Vec<_>>>()?;175 Ok(Self(iroh::RelayMode::custom(urls)))176 }177178 /// The relay map this mode resolves to.179 #[napi]180 pub fn relay_map(&self) -> RelayMap {181 self.0.relay_map().into()182 }183184 /// Clean string representation.185 #[napi]186 pub fn to_string(&self) -> String {187 match &self.0 {188 iroh::RelayMode::Disabled => "disabled".to_string(),189 iroh::RelayMode::Default => "default".to_string(),190 iroh::RelayMode::Staging => "staging".to_string(),191 iroh::RelayMode::Custom(map) => format!("custom({} relays)", map.len()),192 }193 }194}