license_api/
auth.rs

1use futures_util::SinkExt;
2use http::Uri;
3use reqwest::Client;
4use serde::{Deserialize, Serialize};
5use std::error::Error;
6use std::process;
7use std::str::FromStr;
8use tokio::time::Duration;
9use tokio::time::sleep;
10use tokio_websockets::{ClientBuilder, Message};
11
12#[derive(Serialize, Deserialize)]
13pub struct LoginRequest {
14    pub username: String,
15    pub password: String,
16    pub hwid: String,
17}
18
19pub struct LicenseAPI {
20    url: String,
21    client: Client,
22    token: String,
23}
24
25impl LicenseAPI {
26    pub fn new(url: impl Into<String>) -> Self {
27        LicenseAPI {
28            url: url.into(),
29            client: Client::new(),
30            token: String::new(),
31        }
32    }
33
34    pub async fn login(
35        &mut self,
36        creds: &LoginRequest,
37    ) -> Result<bool, Box<dyn Error + Send + Sync>> {
38        let login_resp = self
39            .client
40            .post(&format!("{}/auth/login", self.url.trim_end_matches('/')))
41            .json(creds)
42            .send()
43            .await?
44            .error_for_status()?;
45
46        self.token = login_resp
47            .json::<serde_json::Value>()
48            .await?
49            .get("access_token")
50            .and_then(|v| v.as_str().map(String::from))
51            .ok_or("missing access_token in response")?;
52
53        Ok(true)
54    }
55
56    pub async fn connect_to_websocket(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
57        if self.token.is_empty() {
58            return Err("You must login before connecting to the WebSocket.".into());
59        }
60
61        let base_ws = self
62            .url
63            .trim_end_matches('/')
64            .replace("http://", "ws://")
65            .replace("https://", "wss://");
66        let url_str = format!("{}/ws/notify?token={}", base_ws, self.token);
67        let uri = Uri::from_str(&url_str)?;
68
69        let (mut client, _) = ClientBuilder::from_uri(uri).connect().await?;
70
71        tokio::spawn(async move {
72            loop {
73                sleep(Duration::from_secs(30)).await;
74
75                if let Err(_e) = client.send(Message::ping(Vec::new())).await {
76                    eprintln!("Check your internet connection: Exiting...");
77                    process::exit(1);
78                }
79            }
80        });
81
82        Ok(())
83    }
84}