diff --git a/src/db/mod.rs b/src/db/mod.rs index 63795588..ec24cc72 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -244,7 +244,7 @@ fn normalize_urls() -> Result<(), Error> { Ok(()) } -const UPGRADE_SQL: [&str; 34] = [ +const UPGRADE_SQL: [&str; 35] = [ include_str!("sql/schema1.sql"), include_str!("sql/schema2.sql"), include_str!("sql/schema3.sql"), @@ -279,4 +279,5 @@ const UPGRADE_SQL: [&str; 34] = [ include_str!("sql/schema32.sql"), include_str!("sql/schema33.sql"), include_str!("sql/schema34.sql"), + include_str!("sql/schema35.sql"), ]; diff --git a/src/db/relay.rs b/src/db/relay.rs index 36f57497..2eaf4251 100644 --- a/src/db/relay.rs +++ b/src/db/relay.rs @@ -1,6 +1,6 @@ use crate::error::Error; use crate::globals::GLOBALS; -use nostr_types::{Id, RelayUrl}; +use nostr_types::{Id, RelayInformationDocument, RelayUrl}; use tokio::task::spawn_blocking; #[derive(Debug, Clone)] @@ -13,6 +13,8 @@ pub struct DbRelay { pub rank: u64, pub hidden: bool, pub usage_bits: u64, + pub nip11: Option, + pub last_attempt_nip11: Option, } impl DbRelay { @@ -35,6 +37,8 @@ impl DbRelay { rank: 3, hidden: false, usage_bits: 0, + nip11: None, + last_attempt_nip11: None, } } @@ -86,7 +90,8 @@ impl DbRelay { pub async fn fetch(criteria: Option<&str>) -> Result, Error> { let sql = "SELECT url, success_count, failure_count, last_connected_at, \ - last_general_eose_at, rank, hidden, usage_bits FROM relay" + last_general_eose_at, rank, hidden, usage_bits, \ + nip11, last_attempt_nip11 FROM relay" .to_owned(); let sql = match criteria { None => sql, @@ -101,8 +106,12 @@ impl DbRelay { let mut output: Vec = Vec::new(); while let Some(row) = rows.next()? { let s: String = row.get(0)?; + // just skip over invalid relay URLs if let Ok(url) = RelayUrl::try_from_str(&s) { + + let nip11: Option = row.get(8)?; + output.push(DbRelay { url, success_count: row.get(1)?, @@ -112,6 +121,11 @@ impl DbRelay { rank: row.get(5)?, hidden: row.get(6)?, usage_bits: row.get(7)?, + nip11: match nip11 { + None => None, + Some(s) => serde_json::from_str(&s)? + }, + last_attempt_nip11: row.get(9)?, }); } } @@ -134,8 +148,9 @@ impl DbRelay { pub async fn insert(relay: DbRelay) -> Result<(), Error> { let sql = "INSERT OR IGNORE INTO relay (url, success_count, failure_count, \ - last_connected_at, last_general_eose_at, rank, hidden, usage_bits) \ - VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"; + last_connected_at, last_general_eose_at, rank, hidden, usage_bits, \ + nip11, last_attempt_nip11) \ + VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)"; spawn_blocking(move || { let db = GLOBALS.db.blocking_lock(); @@ -150,6 +165,11 @@ impl DbRelay { &relay.rank, &relay.hidden, &relay.usage_bits, + match relay.nip11 { + None => None, + Some(n11) => Some(serde_json::to_string(&n11)?), + }, + &relay.last_attempt_nip11, ))); Ok::<(), Error>(()) }) @@ -161,7 +181,7 @@ impl DbRelay { pub async fn update(relay: DbRelay) -> Result<(), Error> { let sql = "UPDATE relay SET success_count=?, failure_count=?, \ last_connected_at=?, last_general_eose_at=?, \ - rank=?, hidden=?, usage_bits=? \ + rank=?, hidden=?, usage_bits=?, nip11=?, last_attempt_nip11=? \ WHERE url=?"; spawn_blocking(move || { @@ -176,6 +196,11 @@ impl DbRelay { &relay.rank, &relay.hidden, &relay.usage_bits, + match relay.nip11 { + None => None, + Some(n11) => Some(serde_json::to_string(&n11)?), + }, + &relay.last_attempt_nip11, &relay.url.0, ))); Ok::<(), Error>(()) diff --git a/src/db/sql/schema35.sql b/src/db/sql/schema35.sql new file mode 100644 index 00000000..532b8197 --- /dev/null +++ b/src/db/sql/schema35.sql @@ -0,0 +1,2 @@ +ALTER TABLE relay ADD COLUMN nip11 TEXT DEFAULT NULL; +ALTER TABLE relay ADD COLUMN last_attempt_nip11 INTEGER DEFAULT NULL; diff --git a/src/overlord/minion/mod.rs b/src/overlord/minion/mod.rs index 4c74d58b..88fb74c9 100644 --- a/src/overlord/minion/mod.rs +++ b/src/overlord/minion/mod.rs @@ -87,6 +87,8 @@ impl Minion { // Connect to the relay let websocket_stream = { + + // Parse the URI let uri: http::Uri = self.url.0.parse::()?; let mut parts: Parts = uri.into_parts(); parts.scheme = match parts.scheme { @@ -98,6 +100,8 @@ impl Minion { None => Some(Scheme::HTTPS), }; let uri = http::Uri::from_parts(parts)?; + + // Fetch NIP-11 data let request_nip11_future = reqwest::Client::builder() .timeout(std::time::Duration::new(30, 0)) .redirect(reqwest::redirect::Policy::none()) @@ -109,6 +113,7 @@ impl Minion { .header("Accept", "application/nostr+json") .send(); let response = request_nip11_future.await?; + self.dbrelay.last_attempt_nip11 = Some(Unixtime::now().unwrap().0 as u64); let status = response.status(); match Self::text_with_charset(response, "utf-8").await { Ok(text) => { @@ -123,6 +128,7 @@ impl Minion { Ok(nip11) => { tracing::info!("{}: {}", &self.url, nip11); self.nip11 = Some(nip11); + self.dbrelay.nip11 = self.nip11.clone(); } Err(e) => { tracing::warn!( @@ -140,6 +146,13 @@ impl Minion { } } + // Save updated NIP-11 data (even if it failed) + // in memory... + let _ = GLOBALS.all_relays.insert(self.url.clone(), self.dbrelay.clone()); + // and in the database... + DbRelay::update(self.dbrelay.clone()).await?; + + let key: [u8; 16] = rand::random(); let req = http::request::Request::builder().method("GET");