1use crate::{ApiResponse, CreateTunnelRequest, TunnelResponse};
7use anyhow::{Context, Result};
8
9pub struct GoutClient {
27 inner: reqwest::Client,
28 base: String,
29 api_key: String,
30}
31
32impl GoutClient {
33 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 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 if !resp.status().is_success() {
74 let api_resp: ApiResponse<TunnelResponse> = resp
75 .json()
76 .await
77 .context("parse error response")?;
78 anyhow::bail!("server error: {}", api_resp.error.unwrap_or_default());
79 }
80
81 let api_resp: ApiResponse<TunnelResponse> = resp
82 .json()
83 .await
84 .context("parse success response")?;
85 api_resp.data.context("no tunnel data in response")
86 }
87
88 pub async fn list_tunnels(&self) -> Result<Vec<crate::TunnelListEntry>> {
93 let resp = self
94 .inner
95 .get(format!("{}/api/v1/tunnels", self.base))
96 .header("X-Api-Key", &self.api_key)
97 .send()
98 .await
99 .context("REST list tunnels failed")?;
100
101 let api_resp: ApiResponse<Vec<crate::TunnelListEntry>> = resp
102 .json()
103 .await
104 .context("parse list tunnels response")?;
105 Ok(api_resp.data.unwrap_or_default())
106 }
107
108 pub async fn delete_tunnel(&self, token: u64) -> Result<()> {
112 let resp = self
113 .inner
114 .delete(format!("{}/api/v1/tunnels/{}", self.base, token))
115 .header("X-Api-Key", &self.api_key)
116 .send()
117 .await
118 .context("REST delete tunnel failed")?;
119
120 if !resp.status().is_success() {
121 anyhow::bail!("delete tunnel failed: {}", resp.status());
122 }
123 Ok(())
124 }
125
126 pub fn server_addr(&self) -> &str {
128 &self.base[7..]
129 }
130
131 pub fn api_key(&self) -> &str {
133 &self.api_key
134 }
135}