1
0
Fork 0

fix (accounts): caching clients to fix the 'Session IDs were already set' bug

This commit is contained in:
antifallobst 2023-06-18 12:47:15 +02:00
parent a3cb8b8e20
commit c5742e66f4
3 changed files with 92 additions and 53 deletions

View File

@ -1,5 +1,5 @@
use std::borrow::{Borrow, BorrowMut};
use std::fs; use std::fs;
use std::path::Path;
use matrix_sdk::{Client, use matrix_sdk::{Client,
config::SyncSettings, config::SyncSettings,
ruma::{user_id, ruma::{user_id,
@ -8,9 +8,8 @@ use matrix_sdk::{Client,
Session}; Session};
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::app;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Account { pub struct Account {
homeserver: String, homeserver: String,
id: u32, id: u32,
@ -20,29 +19,48 @@ pub struct Account {
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct AccountManager { struct AccountsData {
selected_account_index: u32, current_account: u32,
accounts: Vec<Account>, accounts: Vec<Account>,
} }
impl AccountManager { pub struct AccountsManager {
pub fn new() -> Self { current_account: u32,
let json_file: &Path = Path::new("userdata/accounts.json"); num_accounts: u32,
return if json_file.exists() { accounts: Vec<Account>,
let serialized = fs::read_to_string(json_file).expect("ailed to read json"); clients: Vec<Option<Client>>,
serde_json::from_str(&serialized).expect("failed to deserialize json") }
} else {
Self { impl AccountsManager {
selected_account_index: 0, pub fn new(config:Option<String>) -> 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(), accounts: Vec::new(),
} clients: Vec::new(),
},
} }
} }
pub async fn add(&mut self, homeserver: &str, username: &str, password: &str) -> Result<Client> { pub async fn add(&mut self, homeserver: &str, username: &str, password: &str) -> Result<u32> {
let id = self.num_accounts;
self.num_accounts += 1;
let client = Client::builder() let client = Client::builder()
.homeserver_url(homeserver) .homeserver_url(homeserver)
.sled_store("userdata/data", Some("supersecure"))? .sled_store(format!("userdata/{id}"), Some("supersecure"))?
.build() .build()
.await?; .await?;
@ -54,62 +72,77 @@ impl AccountManager {
let session = client.session().expect("failed to get session"); 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 { let account = Account {
homeserver: homeserver.to_string(), homeserver: homeserver.to_string(),
id: self.accounts.len() as u32, id,
session, session,
sync_token: None sync_token: None
}; };
self.logout(&client).await?; self.logout().await?;
self.selected_account_index = account.id; self.current_account = id;
self.accounts.push(account); self.accounts.push(account);
self.clients.push(Some(client));
self.save()?; self.save()?;
Ok(client) Ok(id)
} }
pub async fn login(&mut self, account_id:u32) -> Result<Client> { pub async fn login(&mut self, account_id:u32) -> Result<()> {
let account = match self.get(account_id) { self.logout().await?; // log out the current account
None => return Err(Error::msg("Invalid account ID")),
Some(a) => a, 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.current_account = account_id;
self.selected_account_index = 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(()) Ok(())
} }
pub fn get(&self, id: u32) -> Option<&Account> { pub async fn logout(&mut self) -> Result<()> {
return self.accounts.get(id as usize); // 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> { println!("logged out {}", client.session().unwrap().user_id);
return self.accounts.get(self.selected_account_index as usize); client.logout().await?;
Ok(())
} }
pub fn save(&self) -> Result<()>{ 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)?; fs::write("userdata/accounts.json", serialized)?;
Ok(()) Ok(())

View File

@ -1,18 +1,26 @@
use std::path::Path;
use crate::accounts; use crate::accounts;
use matrix_sdk::{Client}; use matrix_sdk::{Client};
use accounts::Account; use accounts::Account;
use crate::accounts::AccountManager; use accounts::AccountsManager;
pub struct App { pub struct App {
pub account_manager: accounts::AccountManager, pub accounts_manager: accounts::AccountsManager,
pub client: Option<Client>, pub client: Option<Client>,
} }
impl App { impl App {
pub fn new() -> Self { 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 { Self {
account_manager: AccountManager::new(), accounts_manager: AccountsManager::new(config),
client: None client: None
} }
} }

View File

@ -11,11 +11,9 @@ async fn main() -> anyhow::Result<()> {
let mut app = app::App::new(); let mut app = app::App::new();
let client = app.account_manager.add("https://nerdcult.net", "test", "abcd1234").await?; let account = app.accounts_manager.add("https://nerdcult.net", "test", "abcd1234").await?;
app.client = Some(client);
// let client = app.account_manager.login(0).await?; app.accounts_manager.login(0).await?;
// app.client = Some(client);
Ok(()) Ok(())
} }