mirror of
https://github.com/YGGverse/Yoda.git
synced 2025-01-15 17:20:08 +00:00
implement identity remove action
This commit is contained in:
parent
00afa70d5d
commit
29083f50d9
@ -86,7 +86,7 @@ impl Gemini {
|
|||||||
.gemini
|
.gemini
|
||||||
.auth
|
.auth
|
||||||
.database
|
.database
|
||||||
.records(None)
|
.records_scope(None)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|this| this.profile_identity_gemini_id == identity.id)
|
.filter(|this| this.profile_identity_gemini_id == identity.id)
|
||||||
@ -100,7 +100,7 @@ impl Gemini {
|
|||||||
.gemini
|
.gemini
|
||||||
.auth
|
.auth
|
||||||
.database
|
.database
|
||||||
.records(Some(auth_url.as_str()))
|
.records_scope(Some(auth_url.as_str()))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|this| this.profile_identity_gemini_id == identity.id)
|
.filter(|this| this.profile_identity_gemini_id == identity.id)
|
||||||
@ -162,7 +162,8 @@ impl Gemini {
|
|||||||
}
|
}
|
||||||
// Remove all identity auths for `auth_uri`
|
// Remove all identity auths for `auth_uri`
|
||||||
None => {
|
None => {
|
||||||
if let Err(reason) = profile.identity.gemini.auth.remove(auth_url.as_str())
|
if let Err(reason) =
|
||||||
|
profile.identity.gemini.auth.remove_scope(auth_url.as_str())
|
||||||
{
|
{
|
||||||
todo!("{}", reason.to_string())
|
todo!("{}", reason.to_string())
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
mod drop;
|
||||||
mod file;
|
mod file;
|
||||||
pub mod list;
|
pub mod list;
|
||||||
mod name;
|
mod name;
|
||||||
mod save;
|
mod save;
|
||||||
|
|
||||||
|
use drop::Drop;
|
||||||
use file::File;
|
use file::File;
|
||||||
use list::{item::value::Value, List};
|
use list::{item::value::Value, List};
|
||||||
use name::Name;
|
use name::Name;
|
||||||
@ -15,6 +17,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
pub struct Form {
|
pub struct Form {
|
||||||
// pub action: Rc<Action>,
|
// pub action: Rc<Action>,
|
||||||
|
// pub drop: Rc<Drop>,
|
||||||
pub file: Rc<File>,
|
pub file: Rc<File>,
|
||||||
pub list: Rc<List>,
|
pub list: Rc<List>,
|
||||||
pub name: Rc<Name>,
|
pub name: Rc<Name>,
|
||||||
@ -28,6 +31,7 @@ impl Form {
|
|||||||
/// Create new `Self`
|
/// Create new `Self`
|
||||||
pub fn new(profile: Rc<Profile>, action: Rc<Action>) -> Self {
|
pub fn new(profile: Rc<Profile>, action: Rc<Action>) -> Self {
|
||||||
// Init components
|
// Init components
|
||||||
|
let drop = Rc::new(Drop::new(profile.clone(), action.clone()));
|
||||||
let file = Rc::new(File::new(action.clone()));
|
let file = Rc::new(File::new(action.clone()));
|
||||||
let list = Rc::new(List::new());
|
let list = Rc::new(List::new());
|
||||||
let name = Rc::new(Name::new(action.clone()));
|
let name = Rc::new(Name::new(action.clone()));
|
||||||
@ -39,10 +43,12 @@ impl Form {
|
|||||||
gobject.append(&list.gobject);
|
gobject.append(&list.gobject);
|
||||||
gobject.append(&name.gobject);
|
gobject.append(&name.gobject);
|
||||||
gobject.append(&file.gobject);
|
gobject.append(&file.gobject);
|
||||||
|
gobject.append(&drop.gobject);
|
||||||
gobject.append(&save.gobject);
|
gobject.append(&save.gobject);
|
||||||
|
|
||||||
// Connect events
|
// Connect events
|
||||||
list.on_select({
|
list.on_select({
|
||||||
|
let drop = drop.clone();
|
||||||
let file = file.clone();
|
let file = file.clone();
|
||||||
let name = name.clone();
|
let name = name.clone();
|
||||||
let save = save.clone();
|
let save = save.clone();
|
||||||
@ -54,12 +60,14 @@ impl Form {
|
|||||||
// Change file choose button visibility
|
// Change file choose button visibility
|
||||||
file.update(matches!(item, Value::ImportPem));
|
file.update(matches!(item, Value::ImportPem));
|
||||||
|
|
||||||
// Change export button visibility by update it holder value
|
// Change other components visibility by update it holder value
|
||||||
match item {
|
match item {
|
||||||
Value::ProfileIdentityGeminiId(value) => {
|
Value::ProfileIdentityGeminiId(value) => {
|
||||||
|
drop.update(Some(value));
|
||||||
save.update(Some(value));
|
save.update(Some(value));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
drop.update(None);
|
||||||
save.update(None);
|
save.update(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,11 +80,12 @@ impl Form {
|
|||||||
// Return activated `Self`
|
// Return activated `Self`
|
||||||
Self {
|
Self {
|
||||||
// action,
|
// action,
|
||||||
gobject,
|
// drop,
|
||||||
file,
|
file,
|
||||||
list,
|
list,
|
||||||
name,
|
name,
|
||||||
// save,
|
// save,
|
||||||
|
gobject,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,128 @@
|
|||||||
|
use super::Action;
|
||||||
|
use crate::profile::Profile;
|
||||||
|
use adw::{
|
||||||
|
prelude::{AdwDialogExt, AlertDialogExt, AlertDialogExtManual},
|
||||||
|
AlertDialog, ResponseAppearance,
|
||||||
|
};
|
||||||
|
use gtk::{
|
||||||
|
prelude::{ButtonExt, WidgetExt},
|
||||||
|
Button,
|
||||||
|
};
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
|
||||||
|
const LABEL: &str = "Delete";
|
||||||
|
const TOOLTIP_TEXT: &str = "Drop selected identity from database";
|
||||||
|
const MARGIN: i32 = 8;
|
||||||
|
|
||||||
|
const HEADING: &str = "Delete";
|
||||||
|
const BODY: &str = "Permanently delete selected identity from database?";
|
||||||
|
const RESPONSE_CANCEL: (&str, &str) = ("cancel", "Cancel");
|
||||||
|
const RESPONSE_CONFIRM: (&str, &str) = ("confirm", "Confirm");
|
||||||
|
|
||||||
|
pub struct Drop {
|
||||||
|
profile_identity_gemini_id: Rc<RefCell<Option<i64>>>,
|
||||||
|
pub gobject: Button,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop {
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
/// Create new `Self`
|
||||||
|
pub fn new(profile: Rc<Profile>, action: Rc<Action>) -> Self {
|
||||||
|
// Init selected option holder
|
||||||
|
let profile_identity_gemini_id = Rc::new(RefCell::new(None::<i64>));
|
||||||
|
|
||||||
|
// Init `GObject`
|
||||||
|
let gobject = Button::builder()
|
||||||
|
.label(LABEL)
|
||||||
|
.margin_top(MARGIN)
|
||||||
|
.tooltip_text(TOOLTIP_TEXT)
|
||||||
|
.visible(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Init events
|
||||||
|
gobject.connect_clicked({
|
||||||
|
let action = action.clone();
|
||||||
|
let gobject = gobject.clone();
|
||||||
|
let profile_identity_gemini_id = profile_identity_gemini_id.clone();
|
||||||
|
move |_| {
|
||||||
|
// Get selected identity from holder
|
||||||
|
match profile_identity_gemini_id.borrow().as_ref() {
|
||||||
|
Some(profile_identity_gemini_id) => {
|
||||||
|
// Init main `GObject`
|
||||||
|
let dialog = AlertDialog::builder()
|
||||||
|
.heading(HEADING)
|
||||||
|
.body(BODY)
|
||||||
|
.close_response(RESPONSE_CANCEL.0)
|
||||||
|
.default_response(RESPONSE_CANCEL.0)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Set response variants
|
||||||
|
dialog.add_responses(&[RESPONSE_CANCEL, RESPONSE_CONFIRM]);
|
||||||
|
|
||||||
|
// Decorate default response preset
|
||||||
|
dialog.set_response_appearance(
|
||||||
|
RESPONSE_CONFIRM.0,
|
||||||
|
ResponseAppearance::Suggested,
|
||||||
|
);
|
||||||
|
|
||||||
|
dialog.set_response_appearance(
|
||||||
|
RESPONSE_CANCEL.0,
|
||||||
|
ResponseAppearance::Destructive,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Connect confirmation event
|
||||||
|
dialog.connect_response(Some(RESPONSE_CONFIRM.0), {
|
||||||
|
let action = action.clone();
|
||||||
|
let profile = profile.clone();
|
||||||
|
let gobject = gobject.clone();
|
||||||
|
let profile_identity_gemini_id = profile_identity_gemini_id.clone();
|
||||||
|
move |_, _| {
|
||||||
|
match profile.identity.gemini.delete(profile_identity_gemini_id) {
|
||||||
|
Ok(_) => {
|
||||||
|
gobject.set_css_classes(&["success"]);
|
||||||
|
gobject.set_label("Identity successfully deleted")
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
gobject.set_css_classes(&["error"]);
|
||||||
|
gobject.set_label(&e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
action.update.activate()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show dialog
|
||||||
|
dialog.present(Some(&gobject))
|
||||||
|
}
|
||||||
|
None => todo!(), // unexpected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return activated `Self`
|
||||||
|
Self {
|
||||||
|
profile_identity_gemini_id,
|
||||||
|
gobject,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
|
||||||
|
/// Update `profile_identity_gemini_id` holder,
|
||||||
|
/// toggle visibility depending on given value
|
||||||
|
pub fn update(&self, profile_identity_gemini_id: Option<i64>) {
|
||||||
|
self.gobject.set_visible(match profile_identity_gemini_id {
|
||||||
|
Some(value) => {
|
||||||
|
self.profile_identity_gemini_id.replace(Some(value));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.profile_identity_gemini_id.replace(None);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -68,6 +68,20 @@ impl Gemini {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete record from database including children dependencies, update memory index
|
||||||
|
pub fn delete(&self, profile_identity_gemini_id: i64) -> Result<(), Error> {
|
||||||
|
match self.auth.remove_ref(profile_identity_gemini_id) {
|
||||||
|
Ok(_) => match self.database.delete(profile_identity_gemini_id) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.index()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(reason) => Err(Error::Database(reason)),
|
||||||
|
},
|
||||||
|
Err(e) => Err(Error::Auth(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate new certificate and insert record to DB, update memory index
|
/// Generate new certificate and insert record to DB, update memory index
|
||||||
/// * return new `profile_identity_gemini_id` on success
|
/// * return new `profile_identity_gemini_id` on success
|
||||||
pub fn make(&self, time: Option<(DateTime, DateTime)>, name: &str) -> Result<i64, Error> {
|
pub fn make(&self, time: Option<(DateTime, DateTime)>, name: &str) -> Result<i64, Error> {
|
||||||
|
@ -43,7 +43,7 @@ impl Auth {
|
|||||||
/// * return last insert `profile_identity_gemini_auth_id` on success
|
/// * return last insert `profile_identity_gemini_auth_id` on success
|
||||||
pub fn apply(&self, profile_identity_gemini_id: i64, scope: &str) -> Result<i64, Error> {
|
pub fn apply(&self, profile_identity_gemini_id: i64, scope: &str) -> Result<i64, Error> {
|
||||||
// Cleanup records match `scope` (unauthorize)
|
// Cleanup records match `scope` (unauthorize)
|
||||||
self.remove(scope)?;
|
self.remove_scope(scope)?;
|
||||||
|
|
||||||
// Create new record (auth)
|
// Create new record (auth)
|
||||||
let profile_identity_gemini_auth_id =
|
let profile_identity_gemini_auth_id =
|
||||||
@ -60,8 +60,24 @@ impl Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Remove all records match request (unauthorize)
|
/// Remove all records match request (unauthorize)
|
||||||
pub fn remove(&self, scope: &str) -> Result<(), Error> {
|
pub fn remove_scope(&self, scope: &str) -> Result<(), Error> {
|
||||||
match self.database.records(Some(scope)) {
|
match self.database.records_scope(Some(scope)) {
|
||||||
|
Ok(records) => {
|
||||||
|
for record in records {
|
||||||
|
if let Err(reason) = self.database.delete(record.id) {
|
||||||
|
return Err(Error::Database(reason));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(reason) => return Err(Error::Database(reason)),
|
||||||
|
}
|
||||||
|
self.index()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove all records match `profile_identity_gemini_id` foreign reference key
|
||||||
|
pub fn remove_ref(&self, profile_identity_gemini_id: i64) -> Result<(), Error> {
|
||||||
|
match self.database.records_ref(profile_identity_gemini_id) {
|
||||||
Ok(records) => {
|
Ok(records) => {
|
||||||
for record in records {
|
for record in records {
|
||||||
if let Err(reason) = self.database.delete(record.id) {
|
if let Err(reason) = self.database.delete(record.id) {
|
||||||
@ -83,7 +99,7 @@ impl Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build new index
|
// Build new index
|
||||||
match self.database.records(None) {
|
match self.database.records_scope(None) {
|
||||||
Ok(records) => {
|
Ok(records) => {
|
||||||
for record in records {
|
for record in records {
|
||||||
if let Err(reason) = self
|
if let Err(reason) = self
|
||||||
|
@ -60,10 +60,17 @@ impl Database {
|
|||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
/// Get records from database match current `profile_id` optionally filtered by `scope`
|
/// Get records from database match current `profile_id` optionally filtered by `scope`
|
||||||
pub fn records(&self, scope: Option<&str>) -> Result<Vec<Table>, Error> {
|
pub fn records_scope(&self, scope: Option<&str>) -> Result<Vec<Table>, Error> {
|
||||||
let readable = self.connection.read().unwrap(); // @TODO
|
let readable = self.connection.read().unwrap(); // @TODO
|
||||||
let tx = readable.unchecked_transaction()?;
|
let tx = readable.unchecked_transaction()?;
|
||||||
select(&tx, scope)
|
select_scope(&tx, scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get records from database match current `profile_id` optionally filtered by `scope`
|
||||||
|
pub fn records_ref(&self, profile_identity_gemini_id: i64) -> Result<Vec<Table>, Error> {
|
||||||
|
let readable = self.connection.read().unwrap(); // @TODO
|
||||||
|
let tx = readable.unchecked_transaction()?;
|
||||||
|
select_ref(&tx, profile_identity_gemini_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +112,7 @@ pub fn delete(tx: &Transaction, id: i64) -> Result<usize, Error> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(tx: &Transaction, scope: Option<&str>) -> Result<Vec<Table>, Error> {
|
pub fn select_scope(tx: &Transaction, scope: Option<&str>) -> Result<Vec<Table>, Error> {
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`,
|
"SELECT `id`,
|
||||||
`profile_identity_gemini_id`,
|
`profile_identity_gemini_id`,
|
||||||
@ -133,6 +140,34 @@ pub fn select(tx: &Transaction, scope: Option<&str>) -> Result<Vec<Table>, Error
|
|||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn select_ref(tx: &Transaction, profile_identity_gemini_id: i64) -> Result<Vec<Table>, Error> {
|
||||||
|
let mut stmt = tx.prepare(
|
||||||
|
"SELECT `id`,
|
||||||
|
`profile_identity_gemini_id`,
|
||||||
|
`scope`
|
||||||
|
|
||||||
|
FROM `profile_identity_gemini_auth`
|
||||||
|
WHERE `profile_identity_gemini_id` = ?",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let result = stmt.query_map([profile_identity_gemini_id], |row| {
|
||||||
|
Ok(Table {
|
||||||
|
id: row.get(0)?,
|
||||||
|
profile_identity_gemini_id: row.get(1)?,
|
||||||
|
scope: row.get(2)?,
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut records = Vec::new();
|
||||||
|
|
||||||
|
for record in result {
|
||||||
|
let table = record?;
|
||||||
|
records.push(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(records)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
||||||
tx.last_insert_rowid()
|
tx.last_insert_rowid()
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,22 @@ impl Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get single record match `id`
|
/// Get single record match `id`
|
||||||
pub fn record(&self, id: i64) -> Result<Option<Table>, Error> {
|
pub fn record(&self, id: i64) -> Result<Option<Table>, Error> {
|
||||||
let readable = self.connection.read().unwrap();
|
let readable = self.connection.read().unwrap();
|
||||||
@ -94,6 +110,10 @@ pub fn insert(tx: &Transaction, profile_identity_id: i64, pem: &str) -> Result<u
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn delete(tx: &Transaction, id: i64) -> Result<usize, Error> {
|
||||||
|
tx.execute("DELETE FROM `profile_identity_gemini` WHERE `id` = ?", [id])
|
||||||
|
}
|
||||||
|
|
||||||
pub fn select(tx: &Transaction, profile_identity_id: i64) -> Result<Vec<Table>, Error> {
|
pub fn select(tx: &Transaction, profile_identity_id: i64) -> Result<Vec<Table>, Error> {
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`,
|
"SELECT `id`,
|
||||||
|
@ -11,14 +11,14 @@ pub enum Error {
|
|||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Auth(reason) => write!(f, "Could not create auth: {reason}"),
|
Self::Auth(e) => write!(f, "Could not create auth: {e}"),
|
||||||
Self::Certificate(reason) => {
|
Self::Certificate(e) => {
|
||||||
write!(f, "Could not create certificate: {reason}")
|
write!(f, "Could not create certificate: {e}")
|
||||||
}
|
}
|
||||||
Self::Database(reason) => {
|
Self::Database(e) => {
|
||||||
write!(f, "Database error: {reason}")
|
write!(f, "Database error: {e}")
|
||||||
}
|
}
|
||||||
Self::Memory(reason) => write!(f, "Memory error: {reason}"),
|
Self::Memory(e) => write!(f, "Memory error: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user