radicle: Explorer and preferred seed configuration
Allows users to configure the default node explorer to use. Allows users to configure their preferred seeds.
This commit is contained in:
parent
3ad97fde0b
commit
59a08f21a4
|
|
@ -239,7 +239,7 @@ pub fn announce_refs(mut node: Node, rid: Id) -> Result<(), Error> {
|
||||||
match node.announce_refs(rid) {
|
match node.announce_refs(rid) {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(e) if e.is_connection_err() => Ok(()),
|
Err(e) if e.is_connection_err() => Ok(()),
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => Err(e.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ use serde_json::Value;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use tower::ServiceExt;
|
use tower::ServiceExt;
|
||||||
|
|
||||||
use radicle::cli;
|
|
||||||
use radicle::cob::issue::Issues;
|
use radicle::cob::issue::Issues;
|
||||||
use radicle::cob::patch::{MergeTarget, Patches};
|
use radicle::cob::patch::{MergeTarget, Patches};
|
||||||
use radicle::crypto::ssh::keystore::MemorySigner;
|
use radicle::crypto::ssh::keystore::MemorySigner;
|
||||||
|
|
@ -69,10 +68,7 @@ pub fn profile(home: &Path, seed: [u8; 32]) -> radicle::Profile {
|
||||||
storage,
|
storage,
|
||||||
keystore,
|
keystore,
|
||||||
public_key: keypair.pk.into(),
|
public_key: keypair.pk.into(),
|
||||||
config: profile::Config {
|
config: profile::Config::new(alias),
|
||||||
node: node::Config::new(alias),
|
|
||||||
cli: cli::Config::default(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ impl Environment {
|
||||||
let config = profile::Config {
|
let config = profile::Config {
|
||||||
node: node::Config::new(alias.clone()),
|
node: node::Config::new(alias.clone()),
|
||||||
cli: cli::Config { hints: false },
|
cli: cli::Config { hints: false },
|
||||||
|
public_explorer: profile::Explorer::default(),
|
||||||
};
|
};
|
||||||
config.write(&home.config()).unwrap();
|
config.write(&home.config()).unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,34 @@ use crate::node::{Address, Alias, NodeId};
|
||||||
/// Target number of peers to maintain connections to.
|
/// Target number of peers to maintain connections to.
|
||||||
pub const TARGET_OUTBOUND_PEERS: usize = 8;
|
pub const TARGET_OUTBOUND_PEERS: usize = 8;
|
||||||
|
|
||||||
|
/// Configured public seeds.
|
||||||
|
pub mod seeds {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use super::{ConnectAddress, PeerAddr};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
/// The radicle public community seed node.
|
||||||
|
pub static RADICLE_COMMUNITY_NODE: Lazy<ConnectAddress> = Lazy::new(|| {
|
||||||
|
// SAFETY: `ConnectAddress` is known at compile time.
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
PeerAddr::from_str(
|
||||||
|
"z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.into()
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The radicle team node.
|
||||||
|
pub static RADICLE_TEAM_NODE: Lazy<ConnectAddress> = Lazy::new(|| {
|
||||||
|
// SAFETY: `ConnectAddress` is known at compile time.
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
PeerAddr::from_str("z6MksmpU5b1dS7oaqF2bHXhQi1DWy2hB7Mh9CuN7y1DN6QSz@seed.radicle.xyz:8776")
|
||||||
|
.unwrap()
|
||||||
|
.into()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Peer-to-peer network.
|
/// Peer-to-peer network.
|
||||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
|
@ -24,22 +52,13 @@ pub enum Network {
|
||||||
impl Network {
|
impl Network {
|
||||||
/// Bootstrap nodes for this network.
|
/// Bootstrap nodes for this network.
|
||||||
pub fn bootstrap(&self) -> Vec<(Alias, ConnectAddress)> {
|
pub fn bootstrap(&self) -> Vec<(Alias, ConnectAddress)> {
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Main => [
|
Self::Main => [
|
||||||
(
|
("seed.radicle.garden", seeds::RADICLE_COMMUNITY_NODE.clone()),
|
||||||
"seed.radicle.garden",
|
("seed.radicle.xyz", seeds::RADICLE_TEAM_NODE.clone()),
|
||||||
"z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"seed.radicle.xyz",
|
|
||||||
"z6MksmpU5b1dS7oaqF2bHXhQi1DWy2hB7Mh9CuN7y1DN6QSz@seed.radicle.xyz:8776",
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
// SAFETY: These are valid addresses.
|
.map(|(a, s)| (Alias::new(a), s))
|
||||||
.map(|(a, s)| (Alias::new(a), PeerAddr::from_str(s).unwrap().into()))
|
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|
||||||
Self::Test => vec![],
|
Self::Test => vec![],
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ use crate::crypto::ssh::{keystore, Keystore, Passphrase};
|
||||||
use crate::crypto::{PublicKey, Signer};
|
use crate::crypto::{PublicKey, Signer};
|
||||||
use crate::node::{address, routing, tracking, Alias, AliasStore};
|
use crate::node::{address, routing, tracking, Alias, AliasStore};
|
||||||
use crate::prelude::Did;
|
use crate::prelude::Did;
|
||||||
use crate::prelude::NodeId;
|
use crate::prelude::{Id, NodeId};
|
||||||
use crate::storage::git::transport;
|
use crate::storage::git::transport;
|
||||||
use crate::storage::git::Storage;
|
use crate::storage::git::Storage;
|
||||||
use crate::{cli, git, node};
|
use crate::{cli, git, node};
|
||||||
|
|
@ -80,6 +80,55 @@ pub mod env {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ExplorerUrlError {
|
||||||
|
#[error("invalid explorer URL {0:?}: unknown protocol")]
|
||||||
|
UnknownProtocol(String),
|
||||||
|
#[error("invalid explorer URL {0:?}: missing `$host` component")]
|
||||||
|
MissingHost(String),
|
||||||
|
#[error("invalid explorer URL {0:?}: missing `$rid` component")]
|
||||||
|
MissingRid(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A public explorer, eg. `https://app.radicle.xyz`.
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct Explorer(String);
|
||||||
|
|
||||||
|
impl Default for Explorer {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(String::from("https://app.radicle.xyz/nodes/$host/$rid"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Explorer {
|
||||||
|
/// Get the explorer URL, filling in the host and RID.
|
||||||
|
pub fn url(&self, host: &str, rid: &Id) -> String {
|
||||||
|
self.0
|
||||||
|
.replace("$host", host)
|
||||||
|
.replace("$rid", rid.urn().as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Explorer {
|
||||||
|
type Err = ExplorerUrlError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let url = s.to_owned();
|
||||||
|
|
||||||
|
if !url.starts_with("http://") && !url.starts_with("https://") {
|
||||||
|
return Err(ExplorerUrlError::UnknownProtocol(url));
|
||||||
|
}
|
||||||
|
if !url.contains("$host") {
|
||||||
|
return Err(ExplorerUrlError::MissingHost(url));
|
||||||
|
}
|
||||||
|
if !url.contains("$rid") {
|
||||||
|
return Err(ExplorerUrlError::MissingRid(url));
|
||||||
|
}
|
||||||
|
Ok(Explorer(url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
|
@ -119,15 +168,29 @@ pub struct Config {
|
||||||
/// CLI configuration.
|
/// CLI configuration.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub cli: cli::Config,
|
pub cli: cli::Config,
|
||||||
|
/// Public explorer. This is used for generating links.
|
||||||
|
#[serde(default)]
|
||||||
|
pub public_explorer: Explorer,
|
||||||
|
/// Preferred seeds. These seeds will be used for explorer links
|
||||||
|
/// and in other situations when a seed needs to be chosen.
|
||||||
|
#[serde(default)]
|
||||||
|
pub preferred_seeds: Vec<node::config::ConnectAddress>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
/// Initialize a new configuration. Fails if the path already exists.
|
/// Create a new, default configuration.
|
||||||
pub fn init(alias: Alias, path: &Path) -> io::Result<Self> {
|
pub fn new(alias: Alias) -> Self {
|
||||||
let cfg = Self {
|
Self {
|
||||||
node: node::Config::new(alias),
|
node: node::Config::new(alias),
|
||||||
cli: cli::Config::default(),
|
cli: cli::Config::default(),
|
||||||
};
|
public_explorer: Explorer::default(),
|
||||||
|
preferred_seeds: vec![node::config::seeds::RADICLE_COMMUNITY_NODE.clone()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize a new configuration. Fails if the path already exists.
|
||||||
|
pub fn init(alias: Alias, path: &Path) -> io::Result<Self> {
|
||||||
|
let cfg = Config::new(alias);
|
||||||
cfg.write(path)?;
|
cfg.write(path)?;
|
||||||
|
|
||||||
Ok(cfg)
|
Ok(cfg)
|
||||||
|
|
@ -146,10 +209,7 @@ impl Config {
|
||||||
let Ok(alias) = Alias::from_str(&user) else {
|
let Ok(alias) = Alias::from_str(&user) else {
|
||||||
return Err(ConfigError::Io(path.to_owned(), e));
|
return Err(ConfigError::Io(path.to_owned(), e));
|
||||||
};
|
};
|
||||||
Ok(Config {
|
Ok(Config::new(alias))
|
||||||
node: node::Config::new(alias),
|
|
||||||
cli: cli::Config::default(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue