diff options
Diffstat (limited to 'src/config.rs')
| -rw-r--r-- | src/config.rs | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..ceff44d --- /dev/null +++ b/src/config.rs | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | use anyhow::{Context, Result}; | ||
| 2 | use nostr::{FromBech32, PublicKey, ToBech32}; | ||
| 3 | use serde::Deserialize; | ||
| 4 | use std::path::PathBuf; | ||
| 5 | |||
| 6 | #[derive(Debug, Deserialize)] | ||
| 7 | pub struct AppConfig { | ||
| 8 | pub discovery: DiscoveryConfig, | ||
| 9 | pub servers: ServersConfig, | ||
| 10 | pub storage: StorageConfig, | ||
| 11 | pub signing: Option<SigningConfig>, | ||
| 12 | } | ||
| 13 | |||
| 14 | #[derive(Debug, Deserialize)] | ||
| 15 | pub struct DiscoveryConfig { | ||
| 16 | pub index_relays: Vec<String>, | ||
| 17 | #[serde(default = "default_poll_interval")] | ||
| 18 | pub poll_interval_secs: u64, | ||
| 19 | } | ||
| 20 | |||
| 21 | fn default_poll_interval() -> u64 { | ||
| 22 | 300 | ||
| 23 | } | ||
| 24 | |||
| 25 | #[derive(Debug, Deserialize)] | ||
| 26 | pub struct ServersConfig { | ||
| 27 | pub known: Vec<String>, | ||
| 28 | } | ||
| 29 | |||
| 30 | #[derive(Debug, Deserialize)] | ||
| 31 | pub struct StorageConfig { | ||
| 32 | #[serde(default = "default_mirror_dir")] | ||
| 33 | pub mirror_dir: PathBuf, | ||
| 34 | #[serde(default = "default_database")] | ||
| 35 | pub database: PathBuf, | ||
| 36 | } | ||
| 37 | |||
| 38 | fn default_mirror_dir() -> PathBuf { | ||
| 39 | dirs::data_dir() | ||
| 40 | .unwrap_or_else(|| PathBuf::from(".")) | ||
| 41 | .join("grasp-mirror") | ||
| 42 | .join("repos") | ||
| 43 | } | ||
| 44 | |||
| 45 | fn default_database() -> PathBuf { | ||
| 46 | dirs::data_dir() | ||
| 47 | .unwrap_or_else(|| PathBuf::from(".")) | ||
| 48 | .join("grasp-mirror") | ||
| 49 | .join("mirror.db") | ||
| 50 | } | ||
| 51 | |||
| 52 | #[derive(Debug, Deserialize)] | ||
| 53 | pub struct SigningConfig { | ||
| 54 | pub key_file: PathBuf, | ||
| 55 | } | ||
| 56 | |||
| 57 | pub struct ResolvedConfig { | ||
| 58 | pub discovery: DiscoveryConfig, | ||
| 59 | pub servers: ServersConfig, | ||
| 60 | pub storage: StorageConfig, | ||
| 61 | pub signing: Option<SigningConfig>, | ||
| 62 | pub npubs: Vec<PublicKey>, | ||
| 63 | } | ||
| 64 | |||
| 65 | impl ResolvedConfig { | ||
| 66 | pub fn load(config_path: &PathBuf) -> Result<Self> { | ||
| 67 | let _ = dotenvy::dotenv(); | ||
| 68 | |||
| 69 | let config_str = std::fs::read_to_string(config_path) | ||
| 70 | .with_context(|| format!("failed to read config from {:?}", config_path))?; | ||
| 71 | let app: AppConfig = toml::from_str(&config_str).context("failed to parse config.toml")?; | ||
| 72 | |||
| 73 | let npubs = Self::load_npubs()?; | ||
| 74 | |||
| 75 | std::fs::create_dir_all(&app.storage.mirror_dir) | ||
| 76 | .context("failed to create mirror directory")?; | ||
| 77 | if let Some(parent) = app.storage.database.parent() { | ||
| 78 | std::fs::create_dir_all(parent).context("failed to create database directory")?; | ||
| 79 | } | ||
| 80 | |||
| 81 | Ok(Self { | ||
| 82 | discovery: app.discovery, | ||
| 83 | servers: app.servers, | ||
| 84 | storage: app.storage, | ||
| 85 | signing: app.signing, | ||
| 86 | npubs, | ||
| 87 | }) | ||
| 88 | } | ||
| 89 | |||
| 90 | fn load_npubs() -> Result<Vec<PublicKey>> { | ||
| 91 | let raw = std::env::var("MIRROR_NPUBS").unwrap_or_default(); | ||
| 92 | let mut npubs = Vec::new(); | ||
| 93 | |||
| 94 | for entry in raw.split(',').map(|s| s.trim()).filter(|s| !s.is_empty()) { | ||
| 95 | let pk = if entry.starts_with("npub1") { | ||
| 96 | PublicKey::from_bech32(entry) | ||
| 97 | .with_context(|| format!("invalid npub: {}", entry))? | ||
| 98 | } else { | ||
| 99 | let bytes = hex::decode(entry) | ||
| 100 | .with_context(|| format!("invalid hex pubkey: {}", entry))?; | ||
| 101 | PublicKey::from_slice(&bytes) | ||
| 102 | .with_context(|| format!("invalid pubkey bytes: {}", entry))? | ||
| 103 | }; | ||
| 104 | npubs.push(pk); | ||
| 105 | } | ||
| 106 | |||
| 107 | if npubs.is_empty() { | ||
| 108 | tracing::warn!("MIRROR_NPUBS is empty — no pubkeys to mirror"); | ||
| 109 | } | ||
| 110 | |||
| 111 | Ok(npubs) | ||
| 112 | } | ||
| 113 | |||
| 114 | pub fn relay_urls(&self) -> Vec<String> { | ||
| 115 | let mut relays = self.discovery.index_relays.clone(); | ||
| 116 | for server in &self.servers.known { | ||
| 117 | let relay = format!( | ||
| 118 | "wss://{}", | ||
| 119 | server | ||
| 120 | .trim_start_matches("https://") | ||
| 121 | .trim_start_matches("http://") | ||
| 122 | ); | ||
| 123 | if !relays.contains(&relay) { | ||
| 124 | relays.push(relay); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | relays | ||
| 128 | } | ||
| 129 | } | ||