summaryrefslogtreecommitdiffstats
path: root/src/local/api_client.rs
blob: 1b64a29533fce70aa197c295c90ea9057bc1026f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use chrono::Utc;
use eyre::Result;
use reqwest::header::{HeaderMap, AUTHORIZATION};
use reqwest::Url;
use sodiumoxide::crypto::secretbox;

use crate::api::{AddHistoryRequest, CountResponse, SyncHistoryResponse};
use crate::local::encryption::decrypt;
use crate::local::history::History;
use crate::utils::hash_str;

pub struct Client<'a> {
    sync_addr: &'a str,
    token: &'a str,
    key: secretbox::Key,
    client: reqwest::Client,
}

impl<'a> Client<'a> {
    pub fn new(sync_addr: &'a str, token: &'a str, key: secretbox::Key) -> Self {
        Client {
            sync_addr,
            token,
            key,
            client: reqwest::Client::new(),
        }
    }

    pub async fn count(&self) -> Result<i64> {
        let url = format!("{}/sync/count", self.sync_addr);
        let url = Url::parse(url.as_str())?;
        let token = format!("Token {}", self.token);
        let token = token.parse()?;

        let mut headers = HeaderMap::new();
        headers.insert(AUTHORIZATION, token);

        let resp = self.client.get(url).headers(headers).send().await?;

        let count = resp.json::<CountResponse>().await?;

        Ok(count.count)
    }

    pub async fn get_history(
        &self,
        sync_ts: chrono::DateTime<Utc>,
        history_ts: chrono::DateTime<Utc>,
        host: Option<String>,
    ) -> Result<Vec<History>> {
        let host = match host {
            None => hash_str(&format!("{}:{}", whoami::hostname(), whoami::username())),
            Some(h) => h,
        };

        let url = format!(
            "{}/sync/history?sync_ts={}&history_ts={}&host={}",
            self.sync_addr,
            urlencoding::encode(sync_ts.to_rfc3339().as_str()),
            urlencoding::encode(history_ts.to_rfc3339().as_str()),
            host,
        );

        let resp = self
            .client
            .get(url)
            .header(AUTHORIZATION, format!("Token {}", self.token))
            .send()
            .await?;

        let history = resp.json::<SyncHistoryResponse>().await?;
        let history = history
            .history
            .iter()
            .map(|h| serde_json::from_str(h).expect("invalid base64"))
            .map(|h| decrypt(&h, &self.key).expect("failed to decrypt history! check your key"))
            .collect();

        Ok(history)
    }

    pub async fn post_history(&self, history: &[AddHistoryRequest]) -> Result<()> {
        let url = format!("{}/history", self.sync_addr);
        let url = Url::parse(url.as_str())?;

        self.client
            .post(url)
            .json(history)
            .header(AUTHORIZATION, format!("Token {}", self.token))
            .send()
            .await?;

        Ok(())
    }
}