use crate::{ApiResponse, CreateTunnelRequest, TunnelResponse};
use anyhow::{Context, Result};
pub struct GoutClient {
inner: reqwest::Client,
base: String,
api_key: String,
}
impl GoutClient {
pub fn new(server_addr: &str, api_key: &str) -> Self {
Self {
inner: reqwest::Client::new(),
base: format!("http://{server_addr}"),
api_key: api_key.to_string(),
}
}
pub async fn create_tunnel(
&self,
tunnel_type: crate::TunnelType,
local_port: u16,
) -> Result<TunnelResponse> {
let resp = self
.inner
.post(format!("{}/api/v1/tunnels", self.base))
.header("X-Api-Key", &self.api_key)
.json(&CreateTunnelRequest {
tunnel_type,
local_port: Some(local_port),
})
.send()
.await
.context("REST create tunnel failed")?;
if !resp.status().is_success() {
let api_resp: ApiResponse<TunnelResponse> = resp
.json()
.await
.context("parse error response")?;
anyhow::bail!("server error: {}", api_resp.error.unwrap_or_default());
}
let api_resp: ApiResponse<TunnelResponse> = resp
.json()
.await
.context("parse success response")?;
api_resp.data.context("no tunnel data in response")
}
pub async fn list_tunnels(&self) -> Result<Vec<crate::TunnelListEntry>> {
let resp = self
.inner
.get(format!("{}/api/v1/tunnels", self.base))
.header("X-Api-Key", &self.api_key)
.send()
.await
.context("REST list tunnels failed")?;
let api_resp: ApiResponse<Vec<crate::TunnelListEntry>> = resp
.json()
.await
.context("parse list tunnels response")?;
Ok(api_resp.data.unwrap_or_default())
}
pub async fn delete_tunnel(&self, token: u64) -> Result<()> {
let resp = self
.inner
.delete(format!("{}/api/v1/tunnels/{}", self.base, token))
.header("X-Api-Key", &self.api_key)
.send()
.await
.context("REST delete tunnel failed")?;
if !resp.status().is_success() {
anyhow::bail!("delete tunnel failed: {}", resp.status());
}
Ok(())
}
pub fn server_addr(&self) -> &str {
&self.base[7..]
}
pub fn api_key(&self) -> &str {
&self.api_key
}
}