1use std::fmt::Debug;
2
3use chrono::Local;
4use config::Config;
5
6pub mod analysis;
7pub mod config;
8pub mod domain;
9pub mod log;
10pub mod prefetch;
11pub mod refresh;
12pub mod traffic;
13pub mod utils;
14
15use anyhow::anyhow;
16use reqwest::header::{HeaderMap, HeaderValue};
17use serde::{de::DeserializeOwned, Deserialize, Serialize};
18use utils::token::{ManageTokenGenerator, SignMethod};
19
20const NOT_FOUND_MSG: &str = "未查询到数据!";
22
23pub const QUERYING: &str = "查询中,请稍候🔎...";
25
26#[derive(Debug, PartialEq, PartialOrd)]
28pub enum SubFunctionEnum {
29 Traffic,
31 Refresh,
33 Log,
35 Domain,
37 Prefetch,
39 AnalysisTop,
41 AnalysisStatus,
43 AnalysisIsp,
45 AnalysisHitmiss,
47 AnalysisCount,
49}
50
51impl SubFunctionEnum {
52 pub fn get_sign_method(&self) -> SignMethod {
53 if [Self::Log, Self::Refresh, Self::Prefetch].contains(self) {
54 return SignMethod::Method2;
55 }
56 SignMethod::Method1
57 }
58
59 pub fn get_host(&self) -> &str {
60 if *self == Self::Domain {
61 return "api.qiniu.com";
62 }
63 "fusion.qiniuapi.com"
64 }
65}
66
67#[derive(Debug, Clone)]
68pub struct Client {
69 config: Config,
70 host: String,
71 sign_method: SignMethod,
72}
73
74#[derive(Debug, Deserialize)]
75pub struct BaseResponse {
76 pub code: i32,
77 pub error: String,
78}
79
80impl Client {
81 pub fn new(config: &Config, sub_func: SubFunctionEnum) -> Self {
82 Self {
83 config: config.to_owned().clone(),
84 host: sub_func.get_host().to_owned(),
85 sign_method: sub_func.get_sign_method(),
86 }
87 }
88 #[allow(clippy::too_many_arguments)]
90 async fn do_request<T: DeserializeOwned + Debug, D: ?Sized + Serialize + std::marker::Sync>(
91 &self,
92 method: &str,
93 url: &str,
94 headers: Option<&reqwest::header::HeaderMap>,
95 content_type: Option<&str>,
96 data: Option<&D>,
97 ) -> Result<T, anyhow::Error> {
98 let client = reqwest::Client::new();
99 let mut body = "".to_string();
100 let body_bytes = match data {
101 Some(d) => {
102 body = serde_json::to_string(d).unwrap();
103 Some(body.as_bytes())
104 }
105 None => None,
106 };
107 let mut header = HeaderMap::new();
108 if headers.is_some() {
109 header = headers.unwrap().clone();
110 }
111 if !header.contains_key("Content-Type") {
113 if let Some(content_type) = content_type {
114 header.insert("Content-Type", HeaderValue::from_str(content_type).unwrap());
115 } else {
116 header.insert(
117 "Content-Type",
118 HeaderValue::from_str("application/json").unwrap(),
119 );
120 }
121 }
122 let config = self.config.clone();
123 let token_generator =
124 ManageTokenGenerator::new(config.cdn.access_key.clone(), config.cdn.secret_key.clone());
125 let sign = match self.sign_method {
126 SignMethod::Method1 => token_generator.generate_v1(url, content_type, body_bytes)?,
127 SignMethod::Method2 => {
128 token_generator.generate_v2(method, url, Some(&header), content_type, body_bytes)?
129 }
130 };
131 let authorization = match self.sign_method {
132 SignMethod::Method1 => format!("QBox {}", sign),
133 SignMethod::Method2 => format!("Qiniu {}", sign),
134 };
135 header.insert(
136 "Authorization",
137 HeaderValue::from_str(&authorization).unwrap(),
138 );
139 let mut builder;
140 match method.to_uppercase().as_str() {
141 "GET" => builder = client.get(url),
142 "POST" => builder = client.post(url),
143 "PUT" => builder = client.put(url),
144 _ => return Err(anyhow!("不支持该方法: {:?}", method)),
145 }
146 let mut start = 0;
147 if config.debug.unwrap_or(false) {
148 println!(
149 "[DEBUG] qiniu request: {} {}\n{:#?}\nbody: {}",
150 method, url, header, body,
151 );
152 start = Local::now().timestamp_millis();
153 }
154 if !body.is_empty() {
155 builder = builder.body(body);
156 }
157 let response = builder.headers(header).send().await?;
158 let status = response.status();
159 if config.debug.unwrap_or(false) {
160 let end = Local::now().timestamp_millis();
161 let elapsed = end - start;
162 let text = response.text().await;
163 if text.is_ok() {
164 let text = text.unwrap();
165 println!("[DEBUG] qiniu response, {elapsed}ms elapsed: \n{}", text);
166 if !status.is_success() {
167 if let Ok(res) = serde_json::from_str::<BaseResponse>(&text) {
168 return Err(anyhow!(
169 "[{}]七牛响应异常,code: {}, error: {}",
170 status.as_u16(),
171 res.code,
172 res.error,
173 ));
174 }
175 return Err(anyhow!("[{}]七牛响应异常", status.as_u16()));
176 }
177 let r: T = serde_json::from_str(&text).unwrap();
178 Ok(r)
179 } else {
180 Err(anyhow!("七牛响应为空"))
181 }
182 } else {
183 if !status.is_success() {
184 if let Ok(res) = response.json::<BaseResponse>().await {
185 return Err(anyhow!(
186 "[{}]七牛响应异常,code: {}, error: {}",
187 status.as_u16(),
188 res.code,
189 res.error,
190 ));
191 }
192 return Err(anyhow!("[{}]七牛响应异常", status.as_u16()));
193 }
194 Ok(response.json().await?)
195 }
196 }
197}