upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/config.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/config.rs')
-rw-r--r--src/config.rs129
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 @@
1use anyhow::{Context, Result};
2use nostr::{FromBech32, PublicKey, ToBech32};
3use serde::Deserialize;
4use std::path::PathBuf;
5
6#[derive(Debug, Deserialize)]
7pub 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)]
15pub struct DiscoveryConfig {
16 pub index_relays: Vec<String>,
17 #[serde(default = "default_poll_interval")]
18 pub poll_interval_secs: u64,
19}
20
21fn default_poll_interval() -> u64 {
22 300
23}
24
25#[derive(Debug, Deserialize)]
26pub struct ServersConfig {
27 pub known: Vec<String>,
28}
29
30#[derive(Debug, Deserialize)]
31pub struct StorageConfig {
32 #[serde(default = "default_mirror_dir")]
33 pub mirror_dir: PathBuf,
34 #[serde(default = "default_database")]
35 pub database: PathBuf,
36}
37
38fn default_mirror_dir() -> PathBuf {
39 dirs::data_dir()
40 .unwrap_or_else(|| PathBuf::from("."))
41 .join("grasp-mirror")
42 .join("repos")
43}
44
45fn 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)]
53pub struct SigningConfig {
54 pub key_file: PathBuf,
55}
56
57pub 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
65impl 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}