diff --git a/src/app/browser/window/tab/item/identity/gemini.rs b/src/app/browser/window/tab/item/identity/gemini.rs index 0d2668b1..b457118b 100644 --- a/src/app/browser/window/tab/item/identity/gemini.rs +++ b/src/app/browser/window/tab/item/identity/gemini.rs @@ -57,18 +57,18 @@ impl Gemini { } // Init events - widget.connect_response({ - let profile = profile.clone(); - move |value| { - match value { - Some(id) => { - // Activate selected identity ID - } - None => { - // Create and select new identity - } - } // @TODO handle result + widget.on_apply(move |response| match response { + // Apply selected identity for `auth_uri` + Some(profile_identity_gemini_id) => { + profile + .identity + .gemini + .auth + .apply(profile_identity_gemini_id, auth_uri.to_string().as_str()) + .unwrap(); //@TODO handle errors } + // Create new certificate, then apply it to the new identity for `auth_uri` + None => {} }); // Return activated `Self` diff --git a/src/app/browser/window/tab/item/identity/gemini/widget.rs b/src/app/browser/window/tab/item/identity/gemini/widget.rs index 889aae7c..22aebdd1 100644 --- a/src/app/browser/window/tab/item/identity/gemini/widget.rs +++ b/src/app/browser/window/tab/item/identity/gemini/widget.rs @@ -62,15 +62,12 @@ impl Widget { // Actions - /// Wrapper for default [response](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/signal.AlertDialog.response.html) signal + /// Callback wrapper for `apply` [response](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/signal.AlertDialog.response.html) /// * return `profile_identity_gemini_id` or new record request on `None` - pub fn connect_response(&self, callback: impl Fn(Option) + 'static) { - self.gobject.connect_response(None, move |_, response| { - if response == RESPONSE_APPLY.0 { - callback(None) - } else { - callback(None) - } // @TODO + pub fn on_apply(&self, callback: impl Fn(Option) + 'static) { + self.gobject.connect_response(Some(RESPONSE_APPLY.0), { + let form = self.form.clone(); + move |_, _| callback(form.list.selected()) }); } diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/form.rs b/src/app/browser/window/tab/item/identity/gemini/widget/form.rs index 53fbc877..252f41cd 100644 --- a/src/app/browser/window/tab/item/identity/gemini/widget/form.rs +++ b/src/app/browser/window/tab/item/identity/gemini/widget/form.rs @@ -32,7 +32,7 @@ impl Form { gobject.append(&name.gobject); // Connect events - list.connect_selected_notify(move |key| name.gobject.set_visible(key.is_none())); + list.on_select(move |key| name.gobject.set_visible(key.is_none())); // Return activated `Self` Self { diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/form/list.rs b/src/app/browser/window/tab/item/identity/gemini/widget/form/list.rs index 882a7346..298c2b19 100644 --- a/src/app/browser/window/tab/item/identity/gemini/widget/form/list.rs +++ b/src/app/browser/window/tab/item/identity/gemini/widget/form/list.rs @@ -59,8 +59,8 @@ impl List { // Events /// Run callback function on `connect_selected_notify` event - /// * return formatted `profile_identity_gemini_id` match selected - pub fn connect_selected_notify(&self, callback: impl Fn(Option) + 'static) { + /// * return formatted `profile_identity_gemini_id` match selected item + pub fn on_select(&self, callback: impl Fn(Option) + 'static) { self.gobject.connect_selected_notify(move |list| { callback( list.selected_item() @@ -70,4 +70,15 @@ impl List { ) }); } + + // Getters + + /// Get formatted `profile_identity_gemini_id` **option** match selected item + pub fn selected(&self) -> Option { + self.gobject + .selected_item() + .and_downcast::() + .unwrap() + .profile_identity_gemini_id_option() + } } diff --git a/src/profile/identity/gemini.rs b/src/profile/identity/gemini.rs index d4e8297c..0b0da865 100644 --- a/src/profile/identity/gemini.rs +++ b/src/profile/identity/gemini.rs @@ -18,6 +18,7 @@ pub struct Gemini { pub auth: Rc, pub database: Rc, pub memory: Rc, + profile_identity_id: Rc, } impl Gemini { @@ -33,7 +34,7 @@ impl Gemini { Ok(auth) => Rc::new(auth), Err(_) => return Err(Error::AuthInit), // @TODO }; - let database = Rc::new(Database::new(connection, profile_identity_id)); + let database = Rc::new(Database::new(connection, profile_identity_id.clone())); let memory = Rc::new(Memory::new()); // Init `Self` @@ -41,6 +42,7 @@ impl Gemini { auth, database, memory, + profile_identity_id, }; // Build initial index @@ -69,8 +71,6 @@ impl Gemini { }; Ok(()) // @TODO } - - // @TODO create new identity API } // Tools diff --git a/src/profile/identity/gemini/auth.rs b/src/profile/identity/gemini/auth.rs index e237fd07..69796cdf 100644 --- a/src/profile/identity/gemini/auth.rs +++ b/src/profile/identity/gemini/auth.rs @@ -1,3 +1,5 @@ +//! Controller for children `database` and `memory` components + mod database; mod error; mod memory; @@ -35,6 +37,45 @@ impl Auth { // Actions + /// Apply `profile_identity_gemini_id` certificate as the auth for `url` + /// * deactivate active auth by remove previous records from `Self` database + /// * reindex `Self` memory index on success + /// * return last insert `profile_identity_gemini_auth_id` on success + pub fn apply(&self, profile_identity_gemini_id: i64, url: &str) -> Result { + // Get all records match request + match self.database.records(Some(url)) { + Ok(records) => { + // Cleanup records match `profile_identity_gemini_id` (unauth) + for record in records { + if record.profile_identity_gemini_id == profile_identity_gemini_id { + if self.database.delete(record.id).is_err() { + return Err(Error::DatabaseRecordDelete(record.id)); + } + } + } + + // Create new record (auth) + let profile_identity_gemini_auth_id = + match self.database.add(profile_identity_gemini_id, url) { + Ok(id) => id, + Err(_) => { + return Err(Error::DatabaseRecordCreate( + profile_identity_gemini_id, + url.to_string(), + )) + } + }; + + // Reindex + self.index()?; + + // Done + Ok(profile_identity_gemini_auth_id) + } + Err(_) => return Err(Error::DatabaseRecordsRead(url.to_string())), + } + } + /// Create new `Memory` index from `Database` for `Self` pub fn index(&self) -> Result<(), Error> { // Clear previous records @@ -44,14 +85,12 @@ impl Auth { match self.database.records(None) { Ok(records) => { for record in records { - if record.is_active { - if self - .memory - .add(record.url, record.profile_identity_gemini_id) - .is_err() - { - return Err(Error::MemoryIndex); - } + if self + .memory + .add(record.url, record.profile_identity_gemini_id) + .is_err() + { + return Err(Error::MemoryIndex); } } } diff --git a/src/profile/identity/gemini/auth/database.rs b/src/profile/identity/gemini/auth/database.rs index 3739cb22..2455ac13 100644 --- a/src/profile/identity/gemini/auth/database.rs +++ b/src/profile/identity/gemini/auth/database.rs @@ -2,9 +2,8 @@ use sqlite::{Connection, Error, Transaction}; use std::{rc::Rc, sync::RwLock}; pub struct Table { - //pub id: i64, + pub id: i64, pub profile_identity_gemini_id: i64, - pub is_active: bool, pub url: String, } @@ -21,6 +20,43 @@ impl Database { Self { connection } } + // Actions + + /// Create new record in database + pub fn add(&self, profile_identity_gemini_id: i64, url: &str) -> Result { + // Begin new transaction + let mut writable = self.connection.write().unwrap(); // @TODO + let tx = writable.transaction()?; + + // Create new record + insert(&tx, profile_identity_gemini_id, url)?; + + // Hold insert ID for result + let id = last_insert_id(&tx); + + // Done + match tx.commit() { + Ok(_) => Ok(id), + Err(reason) => Err(reason), + } + } + + /// Delete record with given `id` from database + pub fn delete(&self, id: i64) -> Result<(), Error> { + // Begin new transaction + let mut writable = self.connection.write().unwrap(); // @TODO + let tx = writable.transaction()?; + + // Create new record + delete(&tx, id)?; + + // Done + match tx.commit() { + Ok(_) => Ok(()), + Err(reason) => Err(reason), + } + } + // Getters /// Get records from database match current `profile_id` optionally filtered by `url` @@ -39,14 +75,12 @@ pub fn init(tx: &Transaction) -> Result { ( `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `profile_identity_gemini_id` INTEGER NOT NULL, - `is_active` INTEGER NOT NULL, `url` VARCHAR(1024) NOT NULL, FOREIGN KEY (`profile_identity_gemini_id`) REFERENCES `profile_identity_gemini`(`id`), UNIQUE ( `profile_identity_gemini_id`, - `is_active`, `url` ) )", @@ -54,11 +88,31 @@ pub fn init(tx: &Transaction) -> Result { ) } +pub fn insert( + tx: &Transaction, + profile_identity_gemini_id: i64, + url: &str, +) -> Result { + tx.execute( + "INSERT INTO `profile_identity_gemini_auth` ( + `profile_identity_gemini_id`, + `url` + ) VALUES (?, ?)", + (profile_identity_gemini_id, url), + ) +} + +pub fn delete(tx: &Transaction, id: i64) -> Result { + tx.execute( + "DELETE FROM `profile_identity_gemini_auth` WHERE `id` = ?", + [id], + ) +} + pub fn select(tx: &Transaction, url: Option<&str>) -> Result, Error> { let mut stmt = tx.prepare( "SELECT `id`, `profile_identity_gemini_id`, - `is_active`, `url` FROM `profile_identity_gemini_auth` @@ -67,10 +121,9 @@ pub fn select(tx: &Transaction, url: Option<&str>) -> Result, Error> let result = stmt.query_map([url.unwrap_or("%")], |row| { Ok(Table { - //id: row.get(0)?, + id: row.get(0)?, profile_identity_gemini_id: row.get(1)?, - is_active: row.get(2)?, - url: row.get(3)?, + url: row.get(2)?, }) })?; @@ -83,3 +136,7 @@ pub fn select(tx: &Transaction, url: Option<&str>) -> Result, Error> Ok(records) } + +pub fn last_insert_id(tx: &Transaction) -> i64 { + tx.last_insert_rowid() +} diff --git a/src/profile/identity/gemini/auth/error.rs b/src/profile/identity/gemini/auth/error.rs index 971fe84a..73199a89 100644 --- a/src/profile/identity/gemini/auth/error.rs +++ b/src/profile/identity/gemini/auth/error.rs @@ -1,5 +1,8 @@ #[derive(Debug)] pub enum Error { DatabaseIndex, + DatabaseRecordCreate(i64, String), + DatabaseRecordDelete(i64), + DatabaseRecordsRead(String), MemoryIndex, } diff --git a/src/profile/identity/gemini/database.rs b/src/profile/identity/gemini/database.rs index 6bc45945..35869b84 100644 --- a/src/profile/identity/gemini/database.rs +++ b/src/profile/identity/gemini/database.rs @@ -27,6 +27,27 @@ impl Database { } } + // Actions + + /// Create new record in database + pub fn add(&self, pem: &str, name: Option<&str>) -> Result { + // Begin new transaction + let mut writable = self.connection.write().unwrap(); // @TODO + let tx = writable.transaction()?; + + // Create new record + insert(&tx, *self.profile_identity_id, pem, name)?; + + // Hold insert ID for result + let id = last_insert_id(&tx); + + // Done + match tx.commit() { + Ok(_) => Ok(id), + Err(reason) => Err(reason), + } + } + /// Get all records match current `profile_identity_id` pub fn records(&self) -> Result, Error> { let readable = self.connection.read().unwrap(); // @TODO @@ -56,6 +77,22 @@ pub fn init(tx: &Transaction) -> Result { ) } +pub fn insert( + tx: &Transaction, + profile_identity_id: i64, + pem: &str, + name: Option<&str>, +) -> Result { + tx.execute( + "INSERT INTO `profile_identity_gemini` ( + `profile_identity_id`, + `pem`, + `name` + ) VALUES (?, ?, ?)", + (profile_identity_id, pem, name), + ) +} + pub fn select(tx: &Transaction, profile_identity_id: i64) -> Result, Error> { let mut stmt = tx.prepare( "SELECT `id`, @@ -84,3 +121,7 @@ pub fn select(tx: &Transaction, profile_identity_id: i64) -> Result, Ok(records) } + +pub fn last_insert_id(tx: &Transaction) -> i64 { + tx.last_insert_rowid() +}