diff --git a/cli/src/main.rs b/cli/src/main.rs index c6693bf..b8cad0c 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -15,7 +15,7 @@ async fn main() { let args = Cli::from_args(); let config = load_config(); - let sv = init_client(&config).await; + let (sv, mut cached) = init_all(&config).await; if let Some(cmd) = args.commands { match cmd { @@ -234,8 +234,10 @@ async fn main() { } } - // sv.save_session(&config.sessions_file) - // .expect("Couldn't save the session"); + // todo: maybe save these again ONLY if modified + cached + .save(&config.cached_stuff_path) + .expect("Couldn't save cached stuff"); std::fs::write(&config.session_file, sv.session.serialize()).unwrap(); save_config(&config).expect("Couldn't save the config"); } diff --git a/cli/src/utils.rs b/cli/src/utils.rs index e0c88c9..0dede98 100644 --- a/cli/src/utils.rs +++ b/cli/src/utils.rs @@ -1,15 +1,18 @@ use rpassword::read_password; use serde::{Deserialize, Serialize}; use std::io::{stdin, stdout, Write}; +use std::time::{Duration, SystemTime}; use crate::error::MyFriendlyError; use socialvoid::session::SessionHolder; +use socialvoid_types::ServerInformation; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Config { pub session_file: String, pub base_path: String, pub config_path: String, + pub cached_stuff_path: String, } pub fn load_config() -> Config { @@ -33,6 +36,7 @@ pub fn load_config() -> Config { }, Err(_err) => Config { session_file: format!("{}/session", base_path), + cached_stuff_path: format!("{}/cached_stuff", base_path), base_path, config_path, }, @@ -65,14 +69,54 @@ pub fn prompt_password(prompt: &str) -> String { read_password().expect("Couldn't read the password") } -pub async fn init_client(config: &Config) -> socialvoid::Client { - match std::fs::read(&config.session_file) { - Ok(bytes) => match socialvoid::new(SessionHolder::deserialize(bytes)).await { - Ok(client) => client, - Err(_) => panic!( +pub async fn init_all(config: &Config) -> (socialvoid::Client, CachedStuff) { + //TODO: only send errors from here and let the main.rs handle situations for panics + + //load cached stuff + let mut cached: CachedStuff = match std::fs::read_to_string(&config.cached_stuff_path) { + Ok(cached_stuff_str) => { + serde_json::from_str(&cached_stuff_str).unwrap_or_else(|_| CachedStuff::default()) + } + Err(_err) => CachedStuff::default(), + }; + + if let Ok(cache_last_updated) = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH + Duration::from_secs(cached.last_updated_on)) + { + if cache_last_updated.as_secs() > cached.update_after { + cached.update().await; + match cached.save(&config.cached_stuff_path) { + Ok(_) => {} + Err(err) => println!("Error while saving cache.\n{}", err), + } + } + } else { + cached.update().await; + match cached.save(&config.cached_stuff_path) { + Ok(_) => {} + Err(err) => println!("Error while saving cache.\n{}", err), + } + } + + //load sessions + let client = match std::fs::read(&config.session_file) { + Ok(bytes) => { + match socialvoid::new( + SessionHolder::deserialize(bytes), + cached.rpc_url.clone(), + cached + .server_info + .as_ref() + .map(|server_info| server_info.cdn_server.clone()), + ) + .await + { + Ok(client) => client, + Err(_) => panic!( "The session file may be corrupt. try deleting it to have a new session created." - ), - }, + ), + } + } Err(err) => { println!( "There was a problem while reading the session file.\n{}", @@ -89,5 +133,52 @@ pub async fn init_client(config: &Config) -> socialvoid::Client { ), } } + }; + + (client, cached) +} + +#[derive(Serialize, Deserialize)] +pub struct CachedStuff { + /// unix timestamp when cache updated last time + last_updated_on: u64, + /// update cache if `update_after` seconds have passed after updating + /// it the last time (since `last_updated_on`). + update_after: u64, + + pub rpc_url: Option, //only updated by the user + + pub server_info: Option, +} + +impl Default for CachedStuff { + fn default() -> Self { + Self { + last_updated_on: 0, + update_after: 86400, // 1 day + rpc_url: Some("http://socialvoid.qlg1.com:5601/".to_string()), + server_info: None, + } + } +} + +impl CachedStuff { + async fn update(&mut self) { + let sv = socialvoid::new_with_host(self.rpc_url.clone()); + if let Ok(server_info) = sv.help.get_server_information().await { + self.server_info = Some(server_info); + + self.last_updated_on = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(); + } else { + println!("There was a problem while updating the cache."); + } + } + + pub fn save(&self, path: &str) -> Result<(), std::io::Error> { + std::fs::write(path, serde_json::to_string(&self).unwrap())?; + Ok(()) } } diff --git a/client/src/lib.rs b/client/src/lib.rs index c6092b8..2c79024 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -63,12 +63,49 @@ async fn make_cdn_client_from( )) } -/// Create a client with user defined session +/// Creates a new client with the given rpc url, default cdn url and no sessions +pub fn new_with_host(rpc_url: Option) -> Client { + let rpc_client = if let Some(rpc_url) = rpc_url { + Arc::new(socialvoid_rawclient::with_host(&rpc_url)) + } else { + Arc::new(socialvoid_rawclient::new()) + }; + let cdn_client = Arc::new(socialvoid_rawclient::CdnClient::new()); + let client_info = Arc::new(ClientInfo::generate()); + let session_holder = Arc::new(Mutex::new(SessionHolder::new(Arc::clone(&client_info)))); + let (session, network, account, timeline, help) = init_methods( + Arc::clone(&rpc_client), + Arc::clone(&cdn_client), + Arc::clone(&session_holder), + ); + Client { + session, + cdn_client, + help, + timeline, + network, + account, + } +} + +/// Create a client with user defined session, (optional)rpc server url and (optional)cdn server url /// And CDN as given in the server information /// TODO: maybe verify the session and return an error if session is invalid -pub async fn new(session: SessionHolder) -> Result { - let rpc_client = Arc::new(socialvoid_rawclient::new()); - let cdn_client = Arc::new(make_cdn_client_from(Arc::clone(&rpc_client)).await?); +pub async fn new( + session: SessionHolder, + rpc_url: Option, + cdn_url: Option, +) -> Result { + let rpc_client = if let Some(rpc_url) = rpc_url { + Arc::new(socialvoid_rawclient::with_host(&rpc_url)) + } else { + Arc::new(socialvoid_rawclient::new()) + }; + let cdn_client = if let Some(cdn_url) = cdn_url { + Arc::new(socialvoid_rawclient::CdnClient::with_cdn_url(cdn_url)) + } else { + Arc::new(make_cdn_client_from(Arc::clone(&rpc_client)).await?) + }; let session_holder = Arc::new(Mutex::new(session)); let (session, network, account, timeline, help) = init_methods( Arc::clone(&rpc_client), diff --git a/jsonrpc2-client/src/lib.rs b/jsonrpc2-client/src/lib.rs index c8ad7b3..c884a23 100644 --- a/jsonrpc2-client/src/lib.rs +++ b/jsonrpc2-client/src/lib.rs @@ -37,10 +37,10 @@ impl Client { ) -> Result { let request = RawRequest::new(Some(generate_id()), method.to_string(), Some(params)); - // dbg!( - // "Request: {}", - // serde_json::to_string_pretty(&request).unwrap() - // ); + println!( + "Request: {}", + serde_json::to_string_pretty(&request).unwrap() + ); //TODO: maybe check the response better as well?? let resp: serde_json::Value = self @@ -52,7 +52,11 @@ impl Client { .json() .await?; - // dbg!("{}", serde_json::to_string_pretty(&resp).unwrap()); + println!( + "Response to `{}`: {}", + method, + serde_json::to_string_pretty(&resp).unwrap() + ); let resp: RawResponse = serde_json::value::from_value(resp).unwrap(); resp.result() } @@ -116,7 +120,7 @@ impl std::convert::From for RpcError { fn from(error: reqwest::Error) -> Self { RpcError { code: -1, - message: Some(format!("Reqwest error: {}", error)), + message: Some(format!("Reqwest error: {:?}", error)), data: None, } }