diff --git a/kordophone-db/src/repository.rs b/kordophone-db/src/repository.rs index dee4497..d630810 100644 --- a/kordophone-db/src/repository.rs +++ b/kordophone-db/src/repository.rs @@ -84,11 +84,14 @@ impl<'a> Repository<'a> { Ok(None) } - pub fn all_conversations(&mut self) -> Result> { + pub fn all_conversations(&mut self, limit: i32, offset: i32) -> Result> { use crate::schema::conversations::dsl::*; use crate::schema::participants::dsl::*; let db_conversations = conversations + .order(schema::conversations::date.desc()) + .offset(offset as i64) + .limit(limit as i64) .load::(self.connection)?; let mut result = Vec::new(); diff --git a/kordophone/src/api/auth.rs b/kordophone/src/api/auth.rs index d192c25..4d8f65c 100644 --- a/kordophone/src/api/auth.rs +++ b/kordophone/src/api/auth.rs @@ -5,8 +5,8 @@ use async_trait::async_trait; #[async_trait] pub trait AuthenticationStore { async fn get_credentials(&mut self) -> Option; - async fn get_token(&mut self) -> Option; - async fn set_token(&mut self, token: JwtToken); + async fn get_token(&mut self) -> Option; + async fn set_token(&mut self, token: String); } pub struct InMemoryAuthenticationStore { @@ -35,11 +35,11 @@ impl AuthenticationStore for InMemoryAuthenticationStore { self.credentials.clone() } - async fn get_token(&mut self) -> Option { - self.token.clone() + async fn get_token(&mut self) -> Option { + self.token.clone().map(|token| token.to_string()) } - async fn set_token(&mut self, token: JwtToken) { - self.token = Some(token); + async fn set_token(&mut self, token: String) { + self.token = Some(JwtToken::new(&token).unwrap()); } } diff --git a/kordophone/src/api/http_client.rs b/kordophone/src/api/http_client.rs index bfdf0ec..c89b74f 100644 --- a/kordophone/src/api/http_client.rs +++ b/kordophone/src/api/http_client.rs @@ -92,6 +92,7 @@ impl From for Error { trait AuthBuilder { fn with_auth(self, token: &Option) -> Self; + fn with_auth_string(self, token: &Option) -> Self; } impl AuthBuilder for hyper::http::request::Builder { @@ -100,6 +101,12 @@ impl AuthBuilder for hyper::http::request::Builder { self.header("Authorization", token.to_header_value()) } else { self } } + + fn with_auth_string(self, token: &Option) -> Self { + if let Some(token) = &token { + self.header("Authorization", format!("Bearer: {}", token)) + } else { self } + } } #[cfg(test)] @@ -196,7 +203,7 @@ impl APIInterface for HTTPAPIClient { let token = JwtToken::new(&token.jwt).map_err(|e| Error::DecodeError(e.to_string()))?; log::debug!("Saving token: {:?}", token); - self.auth_store.set_token(token.clone()).await; + self.auth_store.set_token(token.to_string()).await; Ok(token) } @@ -261,8 +268,7 @@ impl APIInterface for HTTPAPIClient { match &auth { Some(token) => { - let header_value = token.to_header_value().to_str().unwrap().parse().unwrap(); // ugh - request.headers_mut().insert("Authorization", header_value); + request.headers_mut().insert("Authorization", format!("Bearer: {}", token).parse().unwrap()); } None => { log::warn!(target: "websocket", "Proceeding without auth token."); @@ -276,14 +282,14 @@ impl APIInterface for HTTPAPIClient { log::debug!("Websocket connected: {:?}", response.status()); Ok(WebsocketEventSocket::new(socket)) } - Err(e) => match e { + Err(e) => match &e { Error::ClientError(ce) => match ce.as_str() { "HTTP error: 401 Unauthorized" | "Unauthorized" => { // Try to authenticate if let Some(credentials) = &self.auth_store.get_credentials().await { log::warn!("Websocket connection failed, attempting to authenticate"); let new_token = self.authenticate(credentials.clone()).await?; - self.auth_store.set_token(new_token).await; + self.auth_store.set_token(new_token.to_string()).await; // try again on the next attempt. return Err(Error::Unauthorized); @@ -292,7 +298,7 @@ impl APIInterface for HTTPAPIClient { return Err(Error::ClientError("Unauthorized, no credentials provided".into())); } } - _ => Err(Error::Unauthorized) + _ => Err(e) } _ => Err(e) @@ -355,12 +361,12 @@ impl HTTPAPIClient { let uri = self.uri_for_endpoint(endpoint, None); log::debug!("Requesting {:?} {:?}", method, uri); - let build_request = move |auth: &Option| { + let build_request = move |auth: &Option| { let body = body_fn(); Request::builder() .method(&method) .uri(&uri) - .with_auth(auth) + .with_auth_string(auth) .body(body) .expect("Unable to build request") }; @@ -384,7 +390,7 @@ impl HTTPAPIClient { log::debug!("Renewing token using credentials: u: {:?}", credentials.username); let new_token = self.authenticate(credentials.clone()).await?; - let request = build_request(&Some(new_token)); + let request = build_request(&Some(new_token.to_string())); response = self.client.request(request).await?; } else { return Err(Error::ClientError("Unauthorized, no credentials provided".into())); diff --git a/kordophone/src/model/jwt.rs b/kordophone/src/model/jwt.rs index 9521dac..f458b5f 100644 --- a/kordophone/src/model/jwt.rs +++ b/kordophone/src/model/jwt.rs @@ -137,4 +137,8 @@ impl JwtToken { pub fn to_header_value(&self) -> HeaderValue { format!("Bearer {}", self.token).parse().unwrap() } + + pub fn to_string(&self) -> String { + self.token.clone() + } } diff --git a/kordophoned/include/net.buzzert.kordophonecd.Server.xml b/kordophoned/include/net.buzzert.kordophonecd.Server.xml index 7623714..8198155 100644 --- a/kordophoned/include/net.buzzert.kordophonecd.Server.xml +++ b/kordophoned/include/net.buzzert.kordophonecd.Server.xml @@ -11,6 +11,9 @@ + + + ), /// Returns all known conversations from the database. - GetAllConversations(Reply>), + /// Parameters: + /// - limit: The maximum number of conversations to return. (-1 for no limit) + /// - offset: The offset into the conversation list to start returning conversations from. + GetAllConversations(i32, i32, Reply>), /// Returns all known settings from the database. GetAllSettings(Reply), diff --git a/kordophoned/src/daemon/mod.rs b/kordophoned/src/daemon/mod.rs index 4a6e656..5bd4e75 100644 --- a/kordophoned/src/daemon/mod.rs +++ b/kordophoned/src/daemon/mod.rs @@ -175,8 +175,8 @@ impl Daemon { reply.send(()).unwrap(); }, - Event::GetAllConversations(reply) => { - let conversations = self.get_conversations().await; + Event::GetAllConversations(limit, offset, reply) => { + let conversations = self.get_conversations_limit_offset(limit, offset).await; reply.send(conversations).unwrap(); }, @@ -226,7 +226,11 @@ impl Daemon { } async fn get_conversations(&mut self) -> Vec { - self.database.lock().await.with_repository(|r| r.all_conversations().unwrap()).await + self.database.lock().await.with_repository(|r| r.all_conversations(i32::MAX, 0).unwrap()).await + } + + async fn get_conversations_limit_offset(&mut self, limit: i32, offset: i32) -> Vec { + self.database.lock().await.with_repository(|r| r.all_conversations(limit, offset).unwrap()).await } async fn get_messages(&mut self, conversation_id: String, last_message_id: Option) -> Vec { diff --git a/kordophoned/src/daemon/settings.rs b/kordophoned/src/daemon/settings.rs index 4d78d93..265c988 100644 --- a/kordophoned/src/daemon/settings.rs +++ b/kordophoned/src/daemon/settings.rs @@ -17,14 +17,21 @@ pub struct Settings { impl Settings { pub fn from_db(db_settings: &mut DbSettings) -> Result { - let server_url: Option = db_settings.get(keys::SERVER_URL)?; - let username: Option = db_settings.get(keys::USERNAME)?; - let token: Option = db_settings.get(keys::TOKEN)?; - Ok(Self { + let server_url = db_settings.get(keys::SERVER_URL)?; + let username = db_settings.get(keys::USERNAME)?; + let token = db_settings.get(keys::TOKEN)?; + + // Create the settings struct with the results + let settings = Self { server_url, username, token, - }) + }; + + // Load bearing + log::debug!("Loaded settings: {:?}", settings); + + Ok(settings) } pub fn save(&self, db_settings: &mut DbSettings) -> Result<()> { diff --git a/kordophoned/src/daemon/update_monitor.rs b/kordophoned/src/daemon/update_monitor.rs index 06edec7..ed65766 100644 --- a/kordophoned/src/daemon/update_monitor.rs +++ b/kordophoned/src/daemon/update_monitor.rs @@ -42,11 +42,13 @@ impl UpdateMonitor { match update { UpdateEvent::ConversationChanged(conversation) => { log::info!(target: target::UPDATES, "Conversation changed: {:?}", conversation); - log::info!(target: target::UPDATES, "Syncing new messages for conversation id: {}", conversation.guid); - self.send_event(|r| Event::SyncConversation(conversation.guid, r)).await - .unwrap_or_else(|e| { - log::error!("Failed to send daemon event: {}", e); - }); + if conversation.unread_count > 0 { + log::info!(target: target::UPDATES, "Syncing new messages for conversation id: {}", conversation.guid); + self.send_event(|r| Event::SyncConversation(conversation.guid, r)).await + .unwrap_or_else(|e| { + log::error!("Failed to send daemon event: {}", e); + }); + } } UpdateEvent::MessageReceived(conversation, message) => { diff --git a/kordophoned/src/dbus/server_impl.rs b/kordophoned/src/dbus/server_impl.rs index 2abc10d..93b3cb8 100644 --- a/kordophoned/src/dbus/server_impl.rs +++ b/kordophoned/src/dbus/server_impl.rs @@ -51,8 +51,8 @@ impl DbusRepository for ServerImpl { self.send_event_sync(Event::GetVersion) } - fn get_conversations(&mut self) -> Result, dbus::MethodErr> { - self.send_event_sync(Event::GetAllConversations) + fn get_conversations(&mut self, limit: i32, offset: i32) -> Result, dbus::MethodErr> { + self.send_event_sync(|r| Event::GetAllConversations(limit, offset, r)) .map(|conversations| { conversations.into_iter().map(|conv| { let mut map = arg::PropMap::new(); diff --git a/kpcli/src/daemon/mod.rs b/kpcli/src/daemon/mod.rs index 9dc9af6..00c7d88 100644 --- a/kpcli/src/daemon/mod.rs +++ b/kpcli/src/daemon/mod.rs @@ -111,7 +111,7 @@ impl DaemonCli { } pub async fn print_conversations(&mut self) -> Result<()> { - let conversations = KordophoneRepository::get_conversations(&self.proxy())?; + let conversations = KordophoneRepository::get_conversations(&self.proxy(), 100, 0)?; println!("Number of conversations: {}", conversations.len()); for conversation in conversations { diff --git a/kpcli/src/db/mod.rs b/kpcli/src/db/mod.rs index 15b58ea..e5dc513 100644 --- a/kpcli/src/db/mod.rs +++ b/kpcli/src/db/mod.rs @@ -111,7 +111,7 @@ impl DbClient { pub async fn print_conversations(&mut self) -> Result<()> { let all_conversations = self.database.with_repository(|repository| { - repository.all_conversations() + repository.all_conversations(i32::MAX, 0) }).await?; println!("{} Conversations: ", all_conversations.len());