license_api/
lib.rs

1use reqwest::StatusCode;
2use std::collections::HashMap;
3use std::error::Error;
4use models::{ErrorResponse, LoginRequest, LoginResponse, MeResponse};
5use traits::Authenticator;
6
7pub mod hwid;
8pub mod models;
9pub mod traits;
10
11pub const NOT_LINKED: &str = "not_linked";
12
13pub struct BasicAuthenticator {
14    pub base_url: String,
15    pub client: reqwest::Client,
16}
17
18impl BasicAuthenticator {
19    pub fn new(base_url: impl Into<String>) -> Self {
20        BasicAuthenticator {
21            base_url: base_url.into(),
22            client: reqwest::Client::new(),
23        }
24    }
25}
26
27#[async_trait::async_trait]
28impl Authenticator for BasicAuthenticator {
29    async fn login(
30        &self,
31        creds: &LoginRequest,
32    ) -> Result<LoginResponse, Box<dyn Error + Send + Sync>> {
33        let url = format!("{}/license-api-connector/login", self.base_url.trim_end_matches('/'));
34
35        let resp = self.client.post(&url).form(&creds).send().await?;
36
37        match resp.status() {
38            StatusCode::OK | StatusCode::CREATED => {
39                let lr = resp.json::<LoginResponse>().await?;
40                Ok(lr)
41            }
42            StatusCode::UNAUTHORIZED | StatusCode::BAD_REQUEST => {
43                let err = resp.json::<ErrorResponse>().await?;
44                Err(format!("{}", err.detail).into())
45            }
46            other => Err(format!("unexpected response status: {}", other).into()),
47        }
48    }
49
50    async fn me(&self, access_token: &str) -> Result<MeResponse, Box<dyn Error + Send + Sync>> {
51        let url = format!("{}/users/me", self.base_url.trim_end_matches('/'));
52
53        let resp = self
54            .client
55            .get(&url)
56            .bearer_auth(access_token)
57            .send()
58            .await?;
59
60        match resp.status() {
61            StatusCode::OK => {
62                let mr = resp.json::<MeResponse>().await?;
63                Ok(mr)
64            }
65            StatusCode::UNAUTHORIZED | StatusCode::BAD_REQUEST => {
66                let err = resp.json::<ErrorResponse>().await?;
67                Err(format!("{}", err.detail).into())
68            }
69            other => Err(format!("unexpected response status: {}", other).into()),
70        }
71    }
72
73    async fn link_hwid(
74        &self,
75        hwid: &str,
76        access_token: &str,
77    ) -> Result<String, Box<dyn Error + Send + Sync>> {
78        let url = format!("{}/users/hwid", self.base_url.trim_end_matches('/'));
79
80        let mut map = HashMap::new();
81        map.insert("value", hwid);
82
83        let resp = self
84            .client
85            .patch(&url)
86            .json(&map)
87            .bearer_auth(access_token)
88            .send()
89            .await?;
90
91        match resp.status() {
92            StatusCode::OK => Ok("hwid successfully linked".to_string().into()),
93            StatusCode::UNAUTHORIZED | StatusCode::BAD_REQUEST => {
94                let err = resp.json::<ErrorResponse>().await?;
95                Err(format!("link failed: {}", err.detail).into())
96            }
97            other => Err(format!("unexpected response status: {}", other).into()),
98        }
99    }
100}