diff --git a/src/db/person.rs b/src/db/person.rs index 8e520d26..cbbd7180 100644 --- a/src/db/person.rs +++ b/src/db/person.rs @@ -1,8 +1,5 @@ -use crate::error::Error; -use crate::globals::GLOBALS; use nostr_types::PublicKeyHex; use serde::{Deserialize, Serialize}; -use tokio::task::spawn_blocking; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DbPerson { @@ -16,142 +13,3 @@ pub struct DbPerson { pub metadata_at: Option, pub followed: u8, } - -impl DbPerson { - pub async fn fetch(criteria: Option<&str>) -> Result, Error> { - let sql = - "SELECT pubkey, name, about, picture, dns_id, dns_id_valid, dns_id_last_checked, metadata_at, followed FROM person".to_owned(); - let sql = match criteria { - None => sql, - Some(crit) => format!("{} WHERE {}", sql, crit), - }; - - let output: Result, Error> = spawn_blocking(move || { - let maybe_db = GLOBALS.db.blocking_lock(); - let db = maybe_db.as_ref().unwrap(); - - let mut stmt = db.prepare(&sql)?; - let rows = stmt.query_map([], |row| { - Ok(DbPerson { - pubkey: PublicKeyHex(row.get(0)?), - name: row.get(1)?, - about: row.get(2)?, - picture: row.get(3)?, - dns_id: row.get(4)?, - dns_id_valid: row.get(5)?, - dns_id_last_checked: row.get(6)?, - metadata_at: row.get(7)?, - followed: row.get(8)?, - }) - })?; - - let mut output: Vec = Vec::new(); - for row in rows { - output.push(row?); - } - Ok(output) - }) - .await?; - - output - } - - pub async fn fetch_one(pubkey: PublicKeyHex) -> Result, Error> { - let people = DbPerson::fetch(Some(&format!("pubkey='{}'", pubkey))).await?; - - if people.is_empty() { - Ok(None) - } else { - Ok(Some(people[0].clone())) - } - } - - pub async fn insert(person: DbPerson) -> Result<(), Error> { - let sql = - "INSERT OR IGNORE INTO person (pubkey, name, about, picture, dns_id, dns_id_valid, dns_id_last_checked, metadata_at, followed) \ - VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)"; - - spawn_blocking(move || { - let maybe_db = GLOBALS.db.blocking_lock(); - let db = maybe_db.as_ref().unwrap(); - - let mut stmt = db.prepare(sql)?; - stmt.execute(( - &person.pubkey.0, - &person.name, - &person.about, - &person.picture, - &person.dns_id, - &person.dns_id_valid, - &person.dns_id_last_checked, - &person.metadata_at, - &person.followed, - ))?; - Ok::<(), Error>(()) - }) - .await??; - - Ok(()) - } - - pub async fn upsert_valid_nip05( - pubkey: PublicKeyHex, - dns_id: String, - dns_id_last_checked: u64, - ) -> Result<(), Error> { - let sql = "INSERT INTO person (pubkey, dns_id, dns_id_valid, dns_id_last_checked, followed) \ - values (?, ?, 1, ?, 1) \ - ON CONFLICT(pubkey) DO UPDATE SET dns_id=?, dns_id_valid=1, dns_id_last_checked=?, followed=1"; - - spawn_blocking(move || { - let maybe_db = GLOBALS.db.blocking_lock(); - let db = maybe_db.as_ref().unwrap(); - - let mut stmt = db.prepare(sql)?; - stmt.execute(( - &pubkey.0, - &dns_id, - &dns_id_last_checked, - &dns_id, - &dns_id_last_checked, - ))?; - Ok::<(), Error>(()) - }) - .await??; - - Ok(()) - } - - pub async fn follow(pubkey: PublicKeyHex) -> Result<(), Error> { - let sql = "INSERT INTO PERSON (pubkey, followed) values (?, 1) \ - ON CONFLICT(pubkey) DO UPDATE SET followed=1"; - - spawn_blocking(move || { - let maybe_db = GLOBALS.db.blocking_lock(); - let db = maybe_db.as_ref().unwrap(); - - let mut stmt = db.prepare(sql)?; - stmt.execute((&pubkey.0,))?; - Ok::<(), Error>(()) - }) - .await??; - - Ok(()) - } - - /* - pub async fn delete(criteria: &str) -> Result<(), Error> { - let sql = format!("DELETE FROM person WHERE {}", criteria); - - spawn_blocking(move || { - let maybe_db = GLOBALS.db.blocking_lock(); - let db = maybe_db.as_ref().unwrap(); - db.execute(&sql, [])?; - Ok::<(), Error>(()) - }) - .await??; - - Ok(()) - } - */ -} diff --git a/src/overlord/mod.rs b/src/overlord/mod.rs index 269e7c39..130bb25d 100644 --- a/src/overlord/mod.rs +++ b/src/overlord/mod.rs @@ -2,7 +2,7 @@ mod minion; mod relay_picker; use crate::comms::BusMessage; -use crate::db::{DbEvent, DbPerson, DbPersonRelay, DbRelay}; +use crate::db::{DbEvent, DbPersonRelay, DbRelay}; use crate::error::Error; use crate::globals::{Globals, GLOBALS}; use crate::people::People; @@ -588,12 +588,24 @@ impl Overlord { }; // Save person - DbPerson::upsert_valid_nip05( - (*pubkey).into(), - dns_id.clone(), - Unixtime::now().unwrap().0 as u64, - ) - .await?; + GLOBALS + .people + .write() + .await + .upsert_valid_nip05( + (*pubkey).into(), + dns_id.clone(), + Unixtime::now().unwrap().0 as u64, + ) + .await?; + + // Mark as followed + GLOBALS + .people + .write() + .await + .follow((*pubkey).into()) + .await?; info!("Followed {}", &dns_id); @@ -627,7 +639,7 @@ impl Overlord { async fn follow_bech32(bech32: String, relay: String) -> Result<(), Error> { let pk = PublicKey::try_from_bech32_string(&bech32)?; let pkhex: PublicKeyHex = pk.into(); - DbPerson::follow(pkhex.clone()).await?; + GLOBALS.people.write().await.follow(pkhex.clone()).await?; debug!("Followed {}", &pkhex); @@ -655,7 +667,7 @@ impl Overlord { async fn follow_hexkey(hexkey: String, relay: String) -> Result<(), Error> { let pk = PublicKey::try_from_hex_string(&hexkey)?; let pkhex: PublicKeyHex = pk.into(); - DbPerson::follow(pkhex.clone()).await?; + GLOBALS.people.write().await.follow(pkhex.clone()).await?; debug!("Followed {}", &pkhex); diff --git a/src/people/mod.rs b/src/people/mod.rs index c79fef74..bf89d521 100644 --- a/src/people/mod.rs +++ b/src/people/mod.rs @@ -35,7 +35,8 @@ impl People { } // Try loading from the database - let maybe_dbperson = DbPerson::fetch_one(pubkey.into()).await?; + let maybe_dbperson = Self::fetch_one(pubkey.into()).await?; + if let Some(dbperson) = maybe_dbperson { // Insert into the map self.people.insert(pubkey, dbperson); @@ -55,7 +56,7 @@ impl People { // Insert into the map self.people.insert(pubkey, dbperson.clone()); // Insert into the database - DbPerson::insert(dbperson).await?; + Self::insert(dbperson).await?; } Ok(()) @@ -189,7 +190,7 @@ impl People { pub async fn sync(&mut self) -> Result<(), Error> { for pubkey in self.deferred_load.iter() { if !self.people.contains_key(pubkey) { - if let Some(person) = DbPerson::fetch_one((*pubkey).into()).await? { + if let Some(person) = Self::fetch_one((*pubkey).into()).await? { let _ = self.people.insert(*pubkey, person); } } @@ -212,4 +213,161 @@ impl People { Ok(()) } + + pub async fn follow(&mut self, pubkeyhex: PublicKeyHex) -> Result<(), Error> { + // Follow in database + let sql = "INSERT INTO PERSON (pubkey, followed) values (?, 1) \ + ON CONFLICT(pubkey) DO UPDATE SET followed=1"; + let pubkeyhex2 = pubkeyhex.clone(); + task::spawn_blocking(move || { + let maybe_db = GLOBALS.db.blocking_lock(); + let db = maybe_db.as_ref().unwrap(); + let mut stmt = db.prepare(sql)?; + stmt.execute((&pubkeyhex2.0,))?; + Ok::<(), Error>(()) + }) + .await??; + + // Make sure memory matches + let pubkey: PublicKey = PublicKey::try_from_hex_string(&pubkeyhex.0)?; + if let Some(dbperson) = self.people.get_mut(&pubkey) { + dbperson.followed = 1; + } else { + // load + if let Some(person) = Self::fetch_one(pubkeyhex).await? { + self.people.insert(pubkey, person); + } + } + + Ok(()) + } + + pub async fn upsert_valid_nip05( + &mut self, + pubkeyhex: PublicKeyHex, + dns_id: String, + dns_id_last_checked: u64, + ) -> Result<(), Error> { + // Update memory + let pubkey: PublicKey = PublicKey::try_from_hex_string(&pubkeyhex.0)?; + if let Some(dbperson) = self.people.get_mut(&pubkey) { + dbperson.dns_id = Some(dns_id.clone()); + dbperson.dns_id_last_checked = Some(dns_id_last_checked); + } + + // Update in database + let sql = "INSERT INTO person (pubkey, dns_id, dns_id_valid, dns_id_last_checked) \ + values (?, ?, 1, ?) \ + ON CONFLICT(pubkey) DO UPDATE SET dns_id=?, dns_id_valid=1, dns_id_last_checked=?"; + + task::spawn_blocking(move || { + let maybe_db = GLOBALS.db.blocking_lock(); + let db = maybe_db.as_ref().unwrap(); + + let mut stmt = db.prepare(sql)?; + stmt.execute(( + &pubkeyhex.0, + &dns_id, + &dns_id_last_checked, + &dns_id, + &dns_id_last_checked, + ))?; + Ok::<(), Error>(()) + }) + .await??; + + Ok(()) + } + + async fn fetch(criteria: Option<&str>) -> Result, Error> { + let sql = + "SELECT pubkey, name, about, picture, dns_id, dns_id_valid, dns_id_last_checked, metadata_at, followed FROM person".to_owned(); + let sql = match criteria { + None => sql, + Some(crit) => format!("{} WHERE {}", sql, crit), + }; + + let output: Result, Error> = task::spawn_blocking(move || { + let maybe_db = GLOBALS.db.blocking_lock(); + let db = maybe_db.as_ref().unwrap(); + + let mut stmt = db.prepare(&sql)?; + let rows = stmt.query_map([], |row| { + Ok(DbPerson { + pubkey: PublicKeyHex(row.get(0)?), + name: row.get(1)?, + about: row.get(2)?, + picture: row.get(3)?, + dns_id: row.get(4)?, + dns_id_valid: row.get(5)?, + dns_id_last_checked: row.get(6)?, + metadata_at: row.get(7)?, + followed: row.get(8)?, + }) + })?; + + let mut output: Vec = Vec::new(); + for row in rows { + output.push(row?); + } + Ok(output) + }) + .await?; + + output + } + + async fn fetch_one(pubkey: PublicKeyHex) -> Result, Error> { + let people = Self::fetch(Some(&format!("pubkey='{}'", pubkey))).await?; + + if people.is_empty() { + Ok(None) + } else { + Ok(Some(people[0].clone())) + } + } + + async fn insert(person: DbPerson) -> Result<(), Error> { + let sql = + "INSERT OR IGNORE INTO person (pubkey, name, about, picture, dns_id, dns_id_valid, dns_id_last_checked, metadata_at, followed) \ + VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)"; + + task::spawn_blocking(move || { + let maybe_db = GLOBALS.db.blocking_lock(); + let db = maybe_db.as_ref().unwrap(); + + let mut stmt = db.prepare(sql)?; + stmt.execute(( + &person.pubkey.0, + &person.name, + &person.about, + &person.picture, + &person.dns_id, + &person.dns_id_valid, + &person.dns_id_last_checked, + &person.metadata_at, + &person.followed, + ))?; + Ok::<(), Error>(()) + }) + .await??; + + Ok(()) + } + + /* + pub async fn delete(criteria: &str) -> Result<(), Error> { + let sql = format!("DELETE FROM person WHERE {}", criteria); + + task::spawn_blocking(move || { + let maybe_db = GLOBALS.db.blocking_lock(); + let db = maybe_db.as_ref().unwrap(); + db.execute(&sql, [])?; + Ok::<(), Error>(()) + }) + .await??; + + Ok(()) + } + */ }