Skip to main content

unifly_api/session/
vpn.rs

1use serde_json::{Value, json};
2use tracing::debug;
3
4use crate::error::Error;
5use crate::model::IpsecSa;
6use crate::session::client::SessionClient;
7
8impl SessionClient {
9    pub async fn list_ipsec_sa(&self) -> Result<Vec<IpsecSa>, Error> {
10        let url = self.site_url("stat/ipsec-sa");
11        debug!("listing ipsec security associations");
12        match self.get(url).await {
13            Ok(sas) => Ok(sas),
14            Err(error) if error.is_not_found() => Ok(Vec::new()),
15            Err(error) => Err(error),
16        }
17    }
18
19    /// Suggest available OpenVPN ports via the v2 API.
20    ///
21    /// `GET /v2/api/site/{site}/network/port-suggest?service=openvpn`
22    pub async fn get_openvpn_port_suggestions(&self) -> Result<Value, Error> {
23        let mut url = self.site_url_v2("network/port-suggest");
24        url.query_pairs_mut().append_pair("service", "openvpn");
25        debug!("fetching OpenVPN port suggestions (v2)");
26        self.get_raw(url).await
27    }
28
29    /// List VPN client connections via the v2 API.
30    ///
31    /// `GET /v2/api/site/{site}/vpn/connections`
32    pub async fn list_vpn_client_connections(&self) -> Result<Vec<Value>, Error> {
33        let url = self.site_url_v2("vpn/connections");
34        debug!("listing VPN client connections (v2)");
35        let value = self.get_raw(url).await?;
36        Ok(value
37            .get("connections")
38            .and_then(Value::as_array)
39            .cloned()
40            .or_else(|| value.as_array().cloned())
41            .unwrap_or_default())
42    }
43
44    /// Restart a VPN client connection via the v2 API.
45    ///
46    /// `POST /v2/api/site/{site}/vpn/{connection_id}/restart`
47    pub async fn restart_vpn_client_connection(&self, connection_id: &str) -> Result<Value, Error> {
48        let path = format!("v2/api/site/{}/vpn/{connection_id}/restart", self.site());
49        debug!(connection_id, "restarting VPN client connection (v2)");
50        self.raw_post(&path, &json!({})).await
51    }
52
53    /// List magic site-to-site VPN configs via the v2 API.
54    ///
55    /// `GET /v2/api/site/{site}/magicsitetositevpn/configs`
56    pub async fn list_magic_site_to_site_vpn_configs(&self) -> Result<Vec<Value>, Error> {
57        let url = self.site_url_v2("magicsitetositevpn/configs");
58        debug!("listing magic site-to-site VPN configs (v2)");
59        let value = self.get_raw(url).await?;
60        Ok(value.as_array().cloned().unwrap_or_default())
61    }
62
63    /// Download an OpenVPN client configuration via the v2 API.
64    ///
65    /// `GET /v2/api/site/{site}/vpn/openvpn/{server_id}/configuration`
66    pub async fn download_openvpn_configuration(&self, server_id: &str) -> Result<Vec<u8>, Error> {
67        let url = self.site_url_v2(&format!("vpn/openvpn/{server_id}/configuration"));
68        debug!(server_id, "downloading OpenVPN configuration (v2)");
69        let resp = self
70            .http()
71            .get(url)
72            .send()
73            .await
74            .map_err(Error::Transport)?;
75        if !resp.status().is_success() {
76            return Err(Error::SessionApi {
77                message: format!(
78                    "OpenVPN configuration download failed: HTTP {}",
79                    resp.status()
80                ),
81            });
82        }
83        let bytes = resp.bytes().await.map_err(Error::Transport)?;
84        Ok(bytes.to_vec())
85    }
86}