signer-daemon 0.3.2

Signer daemon package.
Documentation
use reqwest::{Client, RequestBuilder, Response, StatusCode, header};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use signer_core::{SignerJWT, SignerJWTClaims, SignerJWTHeader, SignerUser};

/// HTTP 客户端配置
#[derive(Clone)]
pub struct HttpClientConfig {
    /// 基础路径
    pub base_path: String,
    /// 认证信息
    pub auth: Option<HttpClientAuth>,
}

/// HTTP 客户端认证信息
#[derive(Debug, Clone)]
pub struct HttpClientAuth {
    /// 用户信息
    pub user: SignerUser,
    /// 过期时间(分钟)
    pub expire_duration: chrono::Duration,
}

impl HttpClientConfig {
    /// 创建新的配置
    pub fn new(user: SignerUser, base_path: String) -> Self {
        Self {
            base_path,
            auth: Some(HttpClientAuth {
                user,
                expire_duration: chrono::Duration::minutes(5),
            }),
        }
    }

    /// 创建无认证的配置
    pub fn new_no_auth(base_path: String) -> Self {
        Self {
            base_path,
            auth: None,
        }
    }

    /// 设置基础路径
    pub fn with_base_path(&self, base_path: String) -> Self {
        let mut new_config = self.clone();
        new_config.base_path = base_path;
        new_config
    }

    /// 设置过期时间
    pub fn expire(mut self, expire_duration: chrono::Duration) -> Self {
        if let Some(auth) = &mut self.auth {
            auth.expire_duration = expire_duration;
        }
        self
    }
}

/// HTTP 客户端
#[derive(Clone)]
pub struct HttpClient {
    /// 内部 reqwest 客户端
    client: Client,
    /// 配置
    config: HttpClientConfig,
}

impl HttpClient {
    /// 创建新的 HTTP 客户端
    pub fn new(config: HttpClientConfig) -> Self {
        let client = Client::builder()
            .user_agent("SignerDaemon Client v0.3.1")
            .build()
            .expect("创建 HTTP 客户端失败");

        Self { client, config }
    }

    /// 获取认证头
    fn get_auth_header(&self) -> crate::DaemonResult<Option<String>> {
        if let Some(auth) = &self.config.auth {
            let jwt = SignerJWT::new(
                SignerJWTHeader::default(&auth.user),
                SignerJWTClaims::default(
                    &auth.user,
                    self.config.base_path.clone(),
                    uuid::Uuid::new_v4().to_string(),
                )
                .with_expired_duration(auth.expire_duration),
            )
            .encode(&auth.user)
            .map_err(|e| {
                crate::DaemonError::Signer(crate::SignerError::Msg(format!(
                    "编码 JWT 字符串失败: {}",
                    e
                )))
            })?;

            Ok(Some(format!("Bearer {}", jwt)))
        } else {
            Ok(None)
        }
    }

    /// 创建请求构建器
    fn request(&self, method: reqwest::Method, path: &str) -> crate::DaemonResult<RequestBuilder> {
        let url = format!("{}{}", self.config.base_path, path);
        let mut builder = self.client.request(method, url);

        if let Some(auth) = self.get_auth_header()? {
            builder = builder.header(header::AUTHORIZATION, auth);
        }

        Ok(builder)
    }

    /// 发送 GET 请求
    pub async fn get<T: DeserializeOwned>(&self, path: &str) -> crate::DaemonResult<T> {
        let response = self
            .request(reqwest::Method::GET, path)?
            .send()
            .await
            .map_err(|e| {
                crate::DaemonError::Signer(crate::SignerError::Msg(format!(
                    "发送 GET 请求失败: {}",
                    e
                )))
            })?;

        self.process_response(response).await
    }

    /// 发送 GET 请求并返回原始响应
    pub async fn get_raw(&self, path: &str) -> crate::DaemonResult<Response> {
        let response = self
            .request(reqwest::Method::GET, path)?
            .send()
            .await
            .map_err(|e| {
                crate::DaemonError::Signer(crate::SignerError::Msg(format!(
                    "发送 GET 请求失败: {}",
                    e
                )))
            })?;

        Ok(response)
    }

    /// 发送带查询参数的 GET 请求
    pub async fn get_with_query<T: DeserializeOwned, Q: Serialize>(
        &self,
        path: &str,
        query: &Q,
    ) -> crate::DaemonResult<T> {
        let response = self
            .request(reqwest::Method::GET, path)?
            .query(query)
            .send()
            .await
            .map_err(|e| {
                crate::DaemonError::Signer(crate::SignerError::Msg(format!(
                    "发送带查询参数的 GET 请求失败: {}",
                    e
                )))
            })?;

        self.process_response(response).await
    }

    /// 发送 HEAD 请求
    pub async fn head(&self, path: &str) -> crate::DaemonResult<Response> {
        let response = self
            .request(reqwest::Method::HEAD, path)?
            .send()
            .await
            .map_err(|e| {
                crate::DaemonError::Signer(crate::SignerError::Msg(format!(
                    "发送 HEAD 请求失败: {}",
                    e
                )))
            })?;

        Ok(response)
    }

    /// 发送 POST 请求
    pub async fn post<T: DeserializeOwned, B: Serialize>(
        &self,
        path: &str,
        body: &B,
    ) -> crate::DaemonResult<T> {
        let response = self
            .request(reqwest::Method::POST, path)?
            .json(body)
            .send()
            .await
            .map_err(|e| {
                crate::DaemonError::Signer(crate::SignerError::Msg(format!(
                    "发送 POST 请求失败: {}",
                    e
                )))
            })?;

        self.process_response(response).await
    }

    /// 发送 POST 请求并返回原始响应
    pub async fn post_raw<B: Serialize>(
        &self,
        path: &str,
        body: &B,
    ) -> crate::DaemonResult<Response> {
        let response = self
            .request(reqwest::Method::POST, path)?
            .json(body)
            .send()
            .await
            .map_err(|e| {
                crate::DaemonError::Signer(crate::SignerError::Msg(format!(
                    "发送 POST 请求失败: {}",
                    e
                )))
            })?;

        Ok(response)
    }

    /// 发送 PATCH 请求
    pub async fn patch<T: DeserializeOwned, B: Serialize>(
        &self,
        path: &str,
        body: &B,
    ) -> crate::DaemonResult<T> {
        let response = self
            .request(reqwest::Method::PATCH, path)?
            .json(body)
            .send()
            .await
            .map_err(|e| {
                crate::DaemonError::Signer(crate::SignerError::Msg(format!(
                    "发送 PATCH 请求失败: {}",
                    e
                )))
            })?;

        self.process_response(response).await
    }

    /// 发送 POST multipart 请求
    pub async fn post_multipart(
        &self,
        path: &str,
        form: reqwest::multipart::Form,
    ) -> crate::DaemonResult<Response> {
        let response = self
            .request(reqwest::Method::POST, path)?
            .multipart(form)
            .send()
            .await
            .map_err(|e| {
                crate::DaemonError::Signer(crate::SignerError::Msg(format!(
                    "发送 POST multipart 请求失败: {}",
                    e
                )))
            })?;

        Ok(response)
    }

    /// 处理响应
    async fn process_response<T: DeserializeOwned>(
        &self,
        response: Response,
    ) -> crate::DaemonResult<T> {
        if response.status().is_success() {
            let result = response.json::<T>().await.map_err(|e| {
                crate::DaemonError::Signer(crate::SignerError::Msg(format!(
                    "解析响应 JSON 失败: {}",
                    e
                )))
            })?;
            Ok(result)
        } else {
            let status = response.status();
            let body = response
                .text()
                .await
                .unwrap_or_else(|_| "无法读取响应体".to_string());
            Err(crate::DaemonError::Signer(crate::SignerError::Msg(
                format!("HTTP 请求失败: 状态码 {}, 响应体: {}", status, body),
            )))
        }
    }
}

/// 错误类型
#[derive(Debug, thiserror::Error)]
pub enum HttpClientError {
    /// 请求错误
    #[error("请求错误: {0}")]
    RequestError(#[from] reqwest::Error),

    /// 响应错误
    #[error("响应错误: 状态码 {status}, 内容: {content}")]
    ResponseError {
        /// 状态码
        status: StatusCode,
        /// 响应内容
        content: String,
    },
}

/// 通用响应结构
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiResponse<T> {
    /// 响应数据
    pub data: T,
}