From c5742e66f4bd0ea50f4406c50dbe3628d988418c Mon Sep 17 00:00:00 2001 From: antifallobst Date: Sun, 18 Jun 2023 12:47:15 +0200 Subject: [PATCH] fix (accounts): caching clients to fix the 'Session IDs were already set' bug --- src/accounts.rs | 125 ++++++++++++++++++++++++++++++------------------ src/app/mod.rs | 14 ++++-- src/main.rs | 6 +-- 3 files changed, 92 insertions(+), 53 deletions(-) diff --git a/src/accounts.rs b/src/accounts.rs index 038ab03..7e6f97f 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -1,5 +1,5 @@ +use std::borrow::{Borrow, BorrowMut}; use std::fs; -use std::path::Path; use matrix_sdk::{Client, config::SyncSettings, ruma::{user_id, @@ -8,9 +8,8 @@ use matrix_sdk::{Client, Session}; use anyhow::{Error, Result}; use serde::{Deserialize, Serialize}; -use crate::app; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Account { homeserver: String, id: u32, @@ -20,29 +19,48 @@ pub struct Account { } #[derive(Debug, Serialize, Deserialize)] -pub struct AccountManager { - selected_account_index: u32, +struct AccountsData { + current_account: u32, accounts: Vec, } -impl AccountManager { - pub fn new() -> Self { - let json_file: &Path = Path::new("userdata/accounts.json"); - return if json_file.exists() { - let serialized = fs::read_to_string(json_file).expect("ailed to read json"); - serde_json::from_str(&serialized).expect("failed to deserialize json") - } else { - Self { - selected_account_index: 0, +pub struct AccountsManager { + current_account: u32, + num_accounts: u32, + accounts: Vec, + clients: Vec>, +} + +impl AccountsManager { + pub fn new(config:Option) -> Self { + return match config { + Some(s) => { + let accounts_data:AccountsData = serde_json::from_str(&s).expect("failed to deserialize json"); + let mut clients = Vec::new(); + clients.resize(accounts_data.accounts.len(), None); + Self { + current_account: accounts_data.current_account, + num_accounts: accounts_data.accounts.len() as u32, + accounts: accounts_data.accounts, + clients, + } + }, + None => Self { + current_account: 0, + num_accounts: 0, accounts: Vec::new(), - } + clients: Vec::new(), + }, } } - pub async fn add(&mut self, homeserver: &str, username: &str, password: &str) -> Result { + pub async fn add(&mut self, homeserver: &str, username: &str, password: &str) -> Result { + let id = self.num_accounts; + self.num_accounts += 1; + let client = Client::builder() .homeserver_url(homeserver) - .sled_store("userdata/data", Some("supersecure"))? + .sled_store(format!("userdata/{id}"), Some("supersecure"))? .build() .await?; @@ -54,62 +72,77 @@ impl AccountManager { let session = client.session().expect("failed to get session"); - println!("logged in as {username} device ID: {}", session.device_id.as_str()); + println!("logged in as '{username}' device ID: {}", session.device_id.as_str()); let account = Account { homeserver: homeserver.to_string(), - id: self.accounts.len() as u32, + id, session, sync_token: None }; - self.logout(&client).await?; - self.selected_account_index = account.id; + self.logout().await?; + self.current_account = id; self.accounts.push(account); + self.clients.push(Some(client)); self.save()?; - Ok(client) + Ok(id) } - pub async fn login(&mut self, account_id:u32) -> Result { - let account = match self.get(account_id) { - None => return Err(Error::msg("Invalid account ID")), - Some(a) => a, + pub async fn login(&mut self, account_id:u32) -> Result<()> { + self.logout().await?; // log out the current account + + let account = if account_id >= self.num_accounts { + return Err(Error::msg("Invalid account ID")); + } else { + self.accounts.get(account_id as usize).expect("Account lookup failed") }; - let client = Client::builder() - .homeserver_url(&account.homeserver) - .sled_store("userdata/data", Some("supersecure"))? - .build() - .await?; - client.restore_login(account.session.clone()).await?; + if self.clients.get(account_id as usize).expect("client lookup failed").is_none() { + let client = Client::builder() + .homeserver_url(&account.homeserver) + .sled_store(format!("userdata/{account_id}"), Some("supersecure")) ? + .build() + .await?; + client.restore_login(account.session.clone()).await?; - println!("restored account: {} device ID: {}", &account.session.user_id, &account.session.device_id); + println!("restored account: '{}' device ID: {}", &account.session.user_id, &account.session.device_id); + } else { + println!("using cached client for account: '{}' device ID: {}", &account.session.user_id, &account.session.device_id); + }; - self.logout(&client).await?; - self.selected_account_index = account_id; + self.current_account = account_id; - Ok(client) - } - - pub async fn logout(&mut self, client: &Client) -> Result<()> { - // idk, do some matrix related stuff in here or something like that Ok(()) } - pub fn get(&self, id: u32) -> Option<&Account> { - return self.accounts.get(id as usize); - } + pub async fn logout(&mut self) -> Result<()> { + // idk, do some matrix related stuff in here or something like that + if self.clients.get(self.current_account as usize).is_none() { + return Ok(()); + } + let client = match self.clients.get(self.current_account as usize).unwrap() { + None => return Ok(()), + Some(c) => c, + }; - pub fn current(&self) -> Option<&Account> { - return self.accounts.get(self.selected_account_index as usize); + println!("logged out {}", client.session().unwrap().user_id); + client.logout().await?; + + Ok(()) } pub fn save(&self) -> Result<()>{ - let serialized = serde_json::to_string(self as &AccountManager)?; + let accounts_data = AccountsData { + current_account: self.current_account, + accounts: self.accounts.clone(), + }; + + let serialized = serde_json::to_string(&accounts_data)?; fs::write("userdata/accounts.json", serialized)?; Ok(()) diff --git a/src/app/mod.rs b/src/app/mod.rs index 7f99ad4..712e792 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,18 +1,26 @@ +use std::path::Path; use crate::accounts; use matrix_sdk::{Client}; use accounts::Account; -use crate::accounts::AccountManager; +use accounts::AccountsManager; pub struct App { - pub account_manager: accounts::AccountManager, + pub accounts_manager: accounts::AccountsManager, pub client: Option, } impl App { pub fn new() -> Self { + let path:&std::path::Path = Path::new("userdata/accounts.json"); + let config = if path.exists() { + Some(std::fs::read_to_string(path).expect("failed to read accounts config")) + } else { + None + }; + Self { - account_manager: AccountManager::new(), + accounts_manager: AccountsManager::new(config), client: None } } diff --git a/src/main.rs b/src/main.rs index 00ba119..85e4cad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,11 +11,9 @@ async fn main() -> anyhow::Result<()> { let mut app = app::App::new(); - let client = app.account_manager.add("https://nerdcult.net", "test", "abcd1234").await?; - app.client = Some(client); + let account = app.accounts_manager.add("https://nerdcult.net", "test", "abcd1234").await?; - // let client = app.account_manager.login(0).await?; - // app.client = Some(client); + app.accounts_manager.login(0).await?; Ok(()) }