Skip to main content

gout_api/
client.rs

1//! `GoutClient` — 使用普通 `api-key` 与 goutd 通信。
2//!
3//! 负责隧道的 CRUD 操作(创建、列出、删除)。
4//! 数据通道的握手和转发由 [`data_channel`](crate::data_channel) 模块处理。
5
6use crate::{CreateTunnelRequest, TunnelResponse};
7use anyhow::{Context, Result};
8
9/// 隧道操作客户端。
10///
11/// 使用服务端分配的普通 `api-key`(非 admin key)进行认证。
12/// 客户端实例持有 HTTP 连接池,建议复用。
13///
14/// # 示例
15///
16/// ```no_run
17/// # async fn doc() {
18/// use gout_api::client::GoutClient;
19/// use gout_api::TunnelType;
20///
21/// let gout = GoutClient::new("server.example.com:8080", "sk-your-key");
22/// let tunnel = gout.create_tunnel(TunnelType::Tcp, 4000).await.unwrap();
23/// gout.delete_tunnel(tunnel.token).await.unwrap();
24/// # }
25/// ```
26pub struct GoutClient {
27    inner: reqwest::Client,
28    base: String,
29    api_key: String,
30}
31
32impl GoutClient {
33    /// 创建一个新的 `GoutClient`。
34    ///
35    /// # 参数
36    ///
37    /// * `server_addr` — 服务端地址,`host:port` 格式,如 `"example.com:8080"`
38    /// * `api_key` — 普通隧道 API key
39    pub fn new(server_addr: &str, api_key: &str) -> Self {
40        Self {
41            inner: reqwest::Client::new(),
42            base: format!("http://{server_addr}"),
43            api_key: api_key.to_string(),
44        }
45    }
46
47    /// 创建一个新隧道。
48    ///
49    /// 返回 [`TunnelResponse`],调用方据此建立数据通道连接。
50    /// 数据通道的握手和 pipe 由 [`data_channel`](crate::data_channel) 模块提供。
51    ///
52    /// # 参数
53    ///
54    /// * `tunnel_type` — 隧道协议类型(TCP / UDP / HTTP)
55    /// * `local_port` — 本地服务端口号
56    pub async fn create_tunnel(
57        &self,
58        tunnel_type: crate::TunnelType,
59        local_port: u16,
60    ) -> Result<TunnelResponse> {
61        let resp = self
62            .inner
63            .post(format!("{}/api/v1/tunnels", self.base))
64            .header("X-Api-Key", &self.api_key)
65            .json(&CreateTunnelRequest {
66                tunnel_type,
67                local_port: Some(local_port),
68            })
69            .send()
70            .await
71            .context("REST create tunnel failed")?;
72
73        crate::parse_api_response(resp).await
74    }
75
76    /// 列出所有活跃隧道。
77    ///
78    /// 返回当前服务端上状态为 "waiting" 或 "active" 的隧道列表。
79    /// 已关闭或已过期的隧道不会出现在列表中。
80    pub async fn list_tunnels(&self) -> Result<Vec<crate::TunnelListEntry>> {
81        let resp = self
82            .inner
83            .get(format!("{}/api/v1/tunnels", self.base))
84            .header("X-Api-Key", &self.api_key)
85            .send()
86            .await
87            .context("REST list tunnels failed")?;
88
89        crate::parse_api_response(resp).await
90    }
91
92    /// 删除指定 token 的隧道。
93    ///
94    /// 服务端会关闭对应的公网端口监听并清理所有相关资源。
95    pub async fn delete_tunnel(&self, token: u64) -> Result<()> {
96        let resp = self
97            .inner
98            .delete(format!("{}/api/v1/tunnels/{}", self.base, token))
99            .header("X-Api-Key", &self.api_key)
100            .send()
101            .await
102            .context("REST delete tunnel failed")?;
103
104        if !resp.status().is_success() {
105            anyhow::bail!("delete tunnel failed: {}", resp.status());
106        }
107        Ok(())
108    }
109
110    /// 获取服务端地址(不含 `http://` 前缀)。
111    pub fn server_addr(&self) -> &str {
112        &self.base[7..]
113    }
114
115    /// 获取当前客户端使用的 API key。
116    pub fn api_key(&self) -> &str {
117        &self.api_key
118    }
119}