mixin_sdk_rs/
request.rs

1use once_cell::sync::Lazy;
2use reqwest::{
3    Client,
4    header::{AUTHORIZATION, CONTENT_TYPE, HeaderMap, HeaderValue, USER_AGENT},
5};
6use serde::Deserialize;
7use std::sync::Mutex;
8use std::time::Duration;
9use uuid::Uuid;
10
11use crate::error::Error;
12
13pub const DEFAULT_API_HOST: &str = "https://api.mixin.one";
14pub const DEFAULT_BLAZE_HOST: &str = "blaze.mixin.one";
15pub const DEFAULT_USER_AGENT: &str = "Bot-API-Rust-Client";
16
17pub static HTTP_CLIENT: Lazy<Client> = Lazy::new(|| {
18    Client::builder()
19        .timeout(Duration::from_secs(10))
20        .build()
21        .expect("Failed to create HTTP client")
22});
23
24static HTTP_URI: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(DEFAULT_API_HOST.to_string()));
25static BLAZE_URI: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(DEFAULT_BLAZE_HOST.to_string()));
26static USER_AGENT_STR: Lazy<Mutex<String>> =
27    Lazy::new(|| Mutex::new(DEFAULT_USER_AGENT.to_string()));
28static UID: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));
29static SID: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));
30static PRIVATE_KEY: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));
31
32#[derive(Debug, Deserialize, Default)]
33pub struct ApiResponse<T> {
34    pub data: Option<T>,
35    pub error: Option<ApiError>,
36}
37
38#[derive(Debug, Deserialize, Clone, Default)]
39pub struct ApiError {
40    pub status: i32,
41    pub code: i32,
42    pub description: String,
43}
44
45impl std::fmt::Display for ApiError {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        write!(
48            f,
49            "API Error (Status: {}, Code: {}): {}",
50            self.status, self.code, self.description
51        )
52    }
53}
54
55impl std::error::Error for ApiError {}
56
57pub fn with_api_key(user_id: String, session_id: String, private_key: String) {
58    *UID.lock().unwrap() = user_id;
59    *SID.lock().unwrap() = session_id;
60    *PRIVATE_KEY.lock().unwrap() = private_key;
61}
62
63pub fn set_base_uri(base: String) {
64    *HTTP_URI.lock().unwrap() = base;
65}
66
67pub fn set_blaze_uri(blaze: String) {
68    *BLAZE_URI.lock().unwrap() = blaze;
69}
70
71pub fn set_user_agent(ua: String) {
72    *USER_AGENT_STR.lock().unwrap() = ua;
73}
74
75pub async fn request(
76    method: &str,
77    path: &str,
78    body: &[u8],
79    access_token: &str,
80) -> Result<Vec<u8>, Error> {
81    request_with_id(method, path, body, access_token, Uuid::new_v4().to_string()).await
82}
83
84pub async fn request_with_id(
85    method: &str,
86    path: &str,
87    body: &[u8],
88    access_token: &str,
89    request_id: String,
90) -> Result<Vec<u8>, Error> {
91    let uri = format!("{}{}", *HTTP_URI.lock().unwrap(), path);
92
93    let mut headers = HeaderMap::new();
94    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
95    headers.insert(
96        AUTHORIZATION,
97        HeaderValue::from_str(&format!("Bearer {}", access_token))?,
98    );
99    headers.insert("X-Request-Id", HeaderValue::from_str(&request_id)?);
100    headers.insert(
101        USER_AGENT,
102        HeaderValue::from_str(&USER_AGENT_STR.lock().unwrap())?,
103    );
104
105    let response = HTTP_CLIENT
106        .request(reqwest::Method::from_bytes(method.as_bytes())?, &uri)
107        .headers(headers)
108        .body(body.to_vec())
109        .send()
110        .await?;
111
112    if response.status().is_server_error() {
113        let error = ApiError {
114            status: response.status().as_u16() as i32,
115            code: 0,
116            description: "Server error".to_string(),
117        };
118        return Err(error.into());
119    }
120
121    let body = response.bytes().await?;
122    Ok(body.to_vec())
123}
124
125pub async fn simple_request(method: &str, path: &str, body: &[u8]) -> Result<Vec<u8>, Error> {
126    let uri = format!("{}{}", *HTTP_URI.lock().unwrap(), path);
127
128    let mut headers = HeaderMap::new();
129    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
130    let response = HTTP_CLIENT
131        .request(reqwest::Method::from_bytes(method.as_bytes())?, &uri)
132        .headers(headers)
133        .body(body.to_vec())
134        .send()
135        .await?;
136
137    if response.status().is_server_error() {
138        let error = ApiError {
139            status: response.status().as_u16() as i32,
140            code: 0,
141            description: "Server error".to_string(),
142        };
143        return Err(error.into());
144    }
145
146    let body = response.bytes().await?;
147    Ok(body.to_vec())
148}