nimbuspulse_client/
lib.rs

1use anyhow::{bail, Ok, Result};
2use serde::Serialize;
3use types::dcs_runtime::DcsRuntime;
4pub use types::instance::{Instance, InstanceStatus, Terrain};
5pub use types::region::Region;
6pub use types::system_resources::{
7    CpuMetric, CpuMetricData, RamMetric, RamMetricData, ServerResources,
8};
9pub use types::system_resources_periode::SystemResourcesPeriod;
10
11use uuid::Uuid;
12
13mod types;
14
15#[derive(Debug, Serialize)]
16#[cfg_attr(test, derive(ts_rs::TS))]
17#[cfg_attr(test, ts(export, export_to = "../../javascript/lib/types/"))]
18pub struct CreateInstanceRequest {
19    pub product_id: Uuid,
20    pub region: Region,
21    pub settings: DcsSettingsPayload,
22    pub active_mods: Vec<String>,
23    pub wanted_terrains: Vec<Terrain>,
24}
25
26#[derive(Debug, Serialize)]
27#[cfg_attr(test, derive(ts_rs::TS))]
28#[cfg_attr(test, ts(export, export_to = "../../javascript/lib/types/"))]
29pub struct DcsSettingsPayload {
30    pub initial_server_name: String,
31    pub initial_server_password: String,
32    pub initial_max_players: u32,
33    pub enable_io: bool,
34    pub enable_os: bool,
35    pub enable_lfs: bool,
36    pub initial_use_voice_chat: bool,
37}
38
39#[derive(Debug, Clone)]
40pub struct Client {
41    api_key: String,
42    reqwest_client: reqwest::Client,
43}
44
45impl Client {
46    const BASE_URL: &'static str = "https://coordinator.nimbuspulse.com";
47
48    pub fn new(api_key: impl Into<String>) -> Self {
49        Self {
50            reqwest_client: reqwest::Client::new(),
51            api_key: api_key.into(),
52        }
53    }
54
55    pub fn set_api_key(&mut self, api_key: impl Into<String>) {
56        self.api_key = api_key.into();
57    }
58
59    pub async fn create_server(
60        &self,
61        region: Region,
62        name: impl Into<String>,
63        password: Option<impl Into<String>>,
64        max_players: u32,
65        plan: Uuid,
66        active_mods: Vec<impl Into<String>>,
67        terrains: Vec<Terrain>,
68        use_voice_chat: bool,
69        enable_io: bool,
70        enable_os: bool,
71        enable_lfs: bool,
72    ) -> Result<Instance> {
73        let payload = CreateInstanceRequest {
74            product_id: plan,
75            region,
76            settings: DcsSettingsPayload {
77                initial_server_name: name.into(),
78                initial_server_password: password.map(|p| p.into()).unwrap_or_default(),
79                initial_max_players: max_players,
80                initial_use_voice_chat: use_voice_chat,
81                enable_io,
82                enable_os,
83                enable_lfs,
84            },
85            active_mods: active_mods.into_iter().map(|m| m.into()).collect(),
86            wanted_terrains: terrains,
87        };
88
89        let response = self
90            .reqwest_client
91            .post(format!("{}/game_servers", Self::BASE_URL))
92            .bearer_auth(self.api_key.clone())
93            .json(&payload)
94            .send()
95            .await?;
96
97        if !response.status().is_success() {
98            bail!(format!(
99                "Failed to create server: {:?}",
100                response.text().await?
101            ));
102        }
103
104        Ok(response.json::<Instance>().await?)
105    }
106
107    pub async fn get_runtime(&self, id: &Uuid) -> Result<DcsRuntime> {
108        let response = self
109            .reqwest_client
110            .get(format!("{}/game_servers/{}/runtime", Self::BASE_URL, id))
111            .bearer_auth(self.api_key.clone())
112            .send()
113            .await?;
114
115        if !response.status().is_success() {
116            bail!(format!("Failed to get runtime: {:?}", response));
117        }
118
119        Ok(response.json::<DcsRuntime>().await?)
120    }
121
122    pub async fn get_server_resources(
123        &self,
124        id: &Uuid,
125        period: SystemResourcesPeriod,
126    ) -> Result<ServerResources> {
127        let response = self
128            .reqwest_client
129            .get(format!(
130                "{}/game_servers/{}/resources?period={}",
131                Self::BASE_URL,
132                id,
133                period
134            ))
135            .bearer_auth(self.api_key.clone())
136            .send()
137            .await?;
138
139        if !response.status().is_success() {
140            bail!(format!("Failed to get server resources: {:?}", response));
141        }
142
143        Ok(response.json::<ServerResources>().await?)
144    }
145
146    pub async fn add_missions(&self, id: &Uuid, missions: Vec<String>) -> Result<()> {
147        let response = self
148            .reqwest_client
149            .post(format!(
150                "{}/game_servers/{}/dcs-api/missions",
151                Self::BASE_URL,
152                id
153            ))
154            .bearer_auth(self.api_key.clone())
155            .json(&missions)
156            .send()
157            .await?;
158
159        if !response.status().is_success() {
160            bail!("Failed to add missions: {:?}", response.text().await?);
161        }
162
163        Ok(())
164    }
165
166    pub async fn start_mission(&self, id: &Uuid, mission_idx: u32) -> Result<()> {
167        let response = self
168            .reqwest_client
169            .post(format!(
170                "{}/game_servers/{}/dcs-api/missions/{}/start",
171                Self::BASE_URL,
172                id,
173                mission_idx
174            ))
175            .bearer_auth(self.api_key.clone())
176            .send()
177            .await?;
178
179        if !response.status().is_success() {
180            bail!("Failed to start mission: {:?}", response);
181        }
182
183        Ok(())
184    }
185
186    pub async fn get_servers(&self) -> Result<Vec<Instance>> {
187        let response = self
188            .reqwest_client
189            .get(format!("{}/game_servers", Self::BASE_URL))
190            .bearer_auth(self.api_key.clone())
191            .send()
192            .await?;
193
194        if !response.status().is_success() {
195            bail!(format!("Failed to create server: {:?}", response));
196        }
197
198        Ok(response.json::<Vec<Instance>>().await?)
199    }
200
201    pub async fn start_server(&self, id: &Uuid) -> Result<()> {
202        let response = self
203            .reqwest_client
204            .post(format!("{}/game_servers/{}/start", Self::BASE_URL, id))
205            .bearer_auth(self.api_key.clone())
206            .send()
207            .await?;
208
209        if !response.status().is_success() {
210            bail!(format!("Failed to start server: {:?}", response));
211        }
212
213        Ok(())
214    }
215
216    pub async fn stop_server(&self, id: &Uuid) -> Result<()> {
217        let response = self
218            .reqwest_client
219            .post(format!("{}/game_servers/{}/stop", Self::BASE_URL, id))
220            .bearer_auth(self.api_key.clone())
221            .send()
222            .await?;
223
224        if !response.status().is_success() {
225            bail!(format!("Failed to stop server: {:?}", response));
226        }
227
228        Ok(())
229    }
230
231    pub async fn resume_server(&self, id: &Uuid) -> Result<()> {
232        let response = self
233            .reqwest_client
234            .post(format!(
235                "{}/game_servers/{}/dcs-api/resume",
236                Self::BASE_URL,
237                id
238            ))
239            .bearer_auth(self.api_key.clone())
240            .send()
241            .await?;
242
243        if !response.status().is_success() {
244            bail!("Failed to resume server: {:?}", response);
245        }
246
247        Ok(())
248    }
249
250    pub async fn pause_server(&self, id: &Uuid) -> Result<()> {
251        let response = self
252            .reqwest_client
253            .post(format!(
254                "{}/game_servers/{}/dcs-api/pause",
255                Self::BASE_URL,
256                id
257            ))
258            .bearer_auth(self.api_key.clone())
259            .send()
260            .await?;
261
262        if !response.status().is_success() {
263            bail!("Failed to pause server: {:?}", response);
264        }
265
266        Ok(())
267    }
268
269    pub async fn delete_server(&self, id: &Uuid) -> Result<()> {
270        let response = self
271            .reqwest_client
272            .delete(format!("{}/game_servers/{}", Self::BASE_URL, id))
273            .bearer_auth(self.api_key.clone())
274            .send()
275            .await?;
276
277        if !response.status().is_success() {
278            bail!(format!("Failed to delete server: {:?}", response));
279        }
280
281        Ok(())
282    }
283}