From 5751463942cc91f1f1ffaf6e2ac633d7a0085f25 Mon Sep 17 00:00:00 2001 From: Ellie Huxtable Date: Tue, 13 Apr 2021 19:14:07 +0100 Subject: Add history sync, resolves #13 (#31) * Add encryption * Add login and register command * Add count endpoint * Write initial sync push * Add single sync command Confirmed working for one client only * Automatically sync on a configurable frequency * Add key command, key arg to login * Only load session if it exists * Use sync and history timestamps for download * Bind other key code Seems like some systems have this code for up arrow? I'm not sure why, and it's not an easy one to google. * Simplify upload * Try and fix download sync loop * Change sync order to avoid uploading what we just downloaded * Multiline import fix * Fix time parsing * Fix importing history with no time * Add hostname to sync * Use hostname to filter sync * Fixes * Add binding * Stuff from yesterday * Set cursor modes * Make clippy happy * Bump version --- src/local/api_client.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/local/api_client.rs (limited to 'src/local/api_client.rs') diff --git a/src/local/api_client.rs b/src/local/api_client.rs new file mode 100644 index 00000000..434c07ba --- /dev/null +++ b/src/local/api_client.rs @@ -0,0 +1,94 @@ +use chrono::Utc; +use eyre::Result; +use reqwest::header::AUTHORIZATION; + +use crate::api::{AddHistoryRequest, CountResponse, ListHistoryResponse}; +use crate::local::encryption::{decrypt, load_key}; +use crate::local::history::History; +use crate::settings::Settings; +use crate::utils::hash_str; + +pub struct Client<'a> { + settings: &'a Settings, +} + +impl<'a> Client<'a> { + pub const fn new(settings: &'a Settings) -> Self { + Client { settings } + } + + pub fn count(&self) -> Result { + let url = format!("{}/sync/count", self.settings.local.sync_address); + let client = reqwest::blocking::Client::new(); + + let resp = client + .get(url) + .header( + AUTHORIZATION, + format!("Token {}", self.settings.local.session_token), + ) + .send()?; + + let count = resp.json::()?; + + Ok(count.count) + } + + pub fn get_history( + &self, + sync_ts: chrono::DateTime, + history_ts: chrono::DateTime, + host: Option, + ) -> Result> { + let key = load_key(self.settings)?; + + let host = match host { + None => hash_str(&format!("{}:{}", whoami::hostname(), whoami::username())), + Some(h) => h, + }; + + // this allows for syncing between users on the same machine + let url = format!( + "{}/sync/history?sync_ts={}&history_ts={}&host={}", + self.settings.local.sync_address, + sync_ts.to_rfc3339(), + history_ts.to_rfc3339(), + host, + ); + let client = reqwest::blocking::Client::new(); + + let resp = client + .get(url) + .header( + AUTHORIZATION, + format!("Token {}", self.settings.local.session_token), + ) + .send()?; + + let history = resp.json::()?; + let history = history + .history + .iter() + .map(|h| serde_json::from_str(h).expect("invalid base64")) + .map(|h| decrypt(&h, &key).expect("failed to decrypt history! check your key")) + .collect(); + + Ok(history) + } + + pub fn post_history(&self, history: &[AddHistoryRequest]) -> Result<()> { + let client = reqwest::blocking::Client::new(); + + let url = format!("{}/history", self.settings.local.sync_address); + client + .post(url) + .json(history) + .header( + AUTHORIZATION, + format!("Token {}", self.settings.local.session_token), + ) + .send()?; + + Ok(()) + } +} -- cgit v1.3.1