1use crate::{
2 docker_host::{DockerHost, DockerHostList},
3 error::{Error, InvalidReferenceError, Result, TotpResult},
4 event::Event,
5 maintenance::{Maintenance, MaintenanceList, MaintenanceMonitor, MaintenanceStatusPage},
6 monitor::{Monitor, MonitorList},
7 notification::{Notification, NotificationList},
8 response::LoginResponse,
9 status_page::{PublicGroupList, StatusPage, StatusPageList},
10 tag::{Tag, TagDefinition},
11 util::ResultLogger,
12 Config,
13};
14use futures_util::FutureExt;
15use itertools::Itertools;
16use log::{debug, trace, warn};
17use native_tls::{Certificate, TlsConnector};
18use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
19use rust_socketio::{
20 asynchronous::{Client as SocketIO, ClientBuilder},
21 Event as SocketIOEvent, Payload,
22};
23use serde::de::DeserializeOwned;
24use serde_json::{json, Value};
25use std::{
26 collections::{HashMap, HashSet},
27 fs, mem,
28 str::FromStr,
29 sync::{Arc, Weak},
30 time::Duration,
31};
32use tap::prelude::*;
33use tokio::{runtime::Handle, sync::Mutex};
34use totp_rs::{Rfc6238, TOTP};
35
36struct Ready {
37 pub monitor_list: bool,
38 pub notification_list: bool,
39 pub maintenance_list: bool,
40 pub status_page_list: bool,
41 pub docker_host_list: bool,
42}
43
44impl Ready {
45 pub fn new() -> Self {
46 Self {
47 monitor_list: false,
48 notification_list: false,
49 maintenance_list: false,
50 status_page_list: false,
51 docker_host_list: false,
52 }
53 }
54
55 pub fn reset(&mut self) {
56 *self = Ready::new()
57 }
58
59 pub fn is_ready(&self) -> bool {
60 self.monitor_list
61 && self.notification_list
62 && self.maintenance_list
63 && self.status_page_list
64 && self.docker_host_list
65 }
66}
67
68struct Worker {
69 config: Arc<Config>,
70 #[allow(dead_code)]
71 socket_io: Arc<Mutex<Option<SocketIO>>>,
72 monitors: Arc<Mutex<MonitorList>>,
73 notifications: Arc<Mutex<NotificationList>>,
74 docker_hosts: Arc<Mutex<DockerHostList>>,
75 maintenances: Arc<Mutex<MaintenanceList>>,
76 status_pages: Arc<Mutex<StatusPageList>>,
77 is_connected: Arc<Mutex<bool>>,
78 is_ready: Arc<Mutex<Ready>>,
79 is_logged_in: Arc<Mutex<bool>>,
80 auth_token: Arc<Mutex<Option<String>>>,
81 reqwest: Arc<Mutex<reqwest::Client>>,
82 custom_cert: Option<(String, Certificate)>,
83}
84
85impl Worker {
86 fn new(config: Config) -> Result<Arc<Self>> {
87 let custom_cert = config
88 .tls
89 .cert
90 .as_ref()
91 .map(|file| -> Result<(String, Certificate)> {
92 fs::read(file)
93 .map_err(|e| Error::InvalidTlsCert(file.clone(), e.to_string()))
94 .and_then(|content| {
95 Certificate::from_pem(&content)
96 .map_err(|e| Error::InvalidTlsCert(file.clone(), e.to_string()))
97 })
98 .map(|cert| (file.clone(), cert))
99 })
100 .transpose()?;
101
102 let mut reqwest_builder = reqwest::Client::builder()
103 .danger_accept_invalid_certs(!config.tls.verify)
104 .default_headers(HeaderMap::from_iter(
105 config
106 .headers
107 .iter()
108 .filter_map(|header| header.split_once("="))
109 .filter_map(|(key, value)| {
110 match (
111 HeaderName::from_bytes(key.as_bytes()),
112 HeaderValue::from_bytes(value.as_bytes()),
113 ) {
114 (Ok(key), Ok(value)) => Some((key, value)),
115 _ => None,
116 }
117 }),
118 ));
119
120 if let Some((file, cert)) = &custom_cert {
121 reqwest_builder = reqwest_builder.add_root_certificate(
122 reqwest::Certificate::from_der(
123 &cert
124 .to_der()
125 .map_err(|e| Error::InvalidTlsCert(file.clone(), e.to_string()))?,
126 )
127 .map_err(|e| Error::InvalidTlsCert(file.clone(), e.to_string()))?,
128 );
129 }
130
131 Ok(Arc::new(Worker {
132 config: Arc::new(config.clone()),
133 socket_io: Arc::new(Mutex::new(None)),
134 monitors: Default::default(),
135 notifications: Default::default(),
136 maintenances: Default::default(),
137 status_pages: Default::default(),
138 docker_hosts: Default::default(),
139 is_connected: Arc::new(Mutex::new(false)),
140 is_ready: Arc::new(Mutex::new(Ready::new())),
141 is_logged_in: Arc::new(Mutex::new(false)),
142 auth_token: Arc::new(Mutex::new(config.auth_token)),
143 reqwest: Arc::new(Mutex::new(reqwest_builder.build().unwrap())),
144 custom_cert: custom_cert,
145 }))
146 }
147
148 fn get_mfa_token(self: &Arc<Self>) -> TotpResult<Option<String>> {
149 Ok(match &self.config.mfa_secret {
150 Some(secret) => {
151 let totp = match secret {
152 url if url.starts_with("otpauth://") => TOTP::from_url(url)?,
153 secret => TOTP::from_rfc6238(Rfc6238::with_defaults(
154 totp_rs::Secret::Encoded(secret.clone()).to_bytes()?,
155 )?)?,
156 };
157 Some(totp.generate_current()?)
158 }
159 None => self.config.mfa_token.clone(),
160 })
161 }
162
163 async fn on_monitor_list(self: &Arc<Self>, monitor_list: MonitorList) -> Result<()> {
164 *self.monitors.lock().await = monitor_list;
165 self.is_ready.lock().await.monitor_list = true;
166
167 Ok(())
168 }
169
170 async fn on_notification_list(
171 self: &Arc<Self>,
172 notification_list: NotificationList,
173 ) -> Result<()> {
174 *self.notifications.lock().await = notification_list;
175 self.is_ready.lock().await.notification_list = true;
176
177 Ok(())
178 }
179
180 async fn on_maintenance_list(
181 self: &Arc<Self>,
182 maintenance_list: MaintenanceList,
183 ) -> Result<()> {
184 *self.maintenances.lock().await = maintenance_list;
185 self.is_ready.lock().await.maintenance_list = true;
186
187 Ok(())
188 }
189
190 async fn on_status_page_list(self: &Arc<Self>, status_page_list: StatusPageList) -> Result<()> {
191 *self.status_pages.lock().await = status_page_list;
192 self.is_ready.lock().await.status_page_list = true;
193
194 Ok(())
195 }
196
197 async fn on_docker_host_list(self: &Arc<Self>, docker_host_list: DockerHostList) -> Result<()> {
198 *self.docker_hosts.lock().await = docker_host_list;
199 self.is_ready.lock().await.docker_host_list = true;
200
201 Ok(())
202 }
203
204 async fn on_info(self: &Arc<Self>) -> Result<()> {
205 *self.is_connected.lock().await = true;
206 let logged_in = *self.is_logged_in.lock().await;
207
208 if !logged_in {
209 let auth_token = self.auth_token.lock().await.clone();
210
211 if let Some(auth_token) = auth_token {
213 if self.login_by_token(auth_token).await.is_ok() {
214 return Ok(());
215 }
216 }
217
218 if let (Some(username), Some(password)) = (&self.config.username, &self.config.password)
219 {
220 let mfa_token = self.get_mfa_token()?;
221 self.login(username, password, mfa_token).await?;
222 }
223 }
224
225 Ok(())
226 }
227
228 async fn on_login_required(self: &Arc<Self>) -> Result<()> {
229 Ok(())
230 }
231
232 async fn on_auto_login(self: &Arc<Self>) -> Result<()> {
233 debug!("Logged in using AutoLogin!");
234 *self.is_logged_in.lock().await = true;
235 Ok(())
236 }
237
238 async fn on_delete_monitor_from_list(self: &Arc<Self>, monitor_id: i32) -> Result<()> {
239 self.monitors.lock().await.remove(&monitor_id.to_string());
240 Ok(())
241 }
242
243 async fn on_update_monitor_into_list(self: &Arc<Self>, monitors: MonitorList) -> Result<()> {
244 self.monitors.lock().await.extend(monitors);
245 Ok(())
246 }
247
248 async fn on_event(self: &Arc<Self>, event: Event, payload: Value) -> Result<()> {
249 match event {
250 Event::MonitorList => {
251 self.on_monitor_list(
252 serde_json::from_value(payload)
253 .log_error(module_path!(), |_| "Failed to deserialize MonitorList")
254 .unwrap(),
255 )
256 .await?
257 }
258 Event::NotificationList => {
259 self.on_notification_list(
260 serde_json::from_value(payload)
261 .log_error(module_path!(), |_| "Failed to deserialize NotificationList")
262 .unwrap(),
263 )
264 .await?
265 }
266 Event::MaintenanceList => {
267 self.on_maintenance_list(
268 serde_json::from_value(payload)
269 .log_error(module_path!(), |_| "Failed to deserialize MaintenanceList")
270 .unwrap(),
271 )
272 .await?
273 }
274 Event::StatusPageList => {
275 self.on_status_page_list(
276 serde_json::from_value(payload)
277 .log_error(module_path!(), |_| "Failed to deserialize StatusPageList")
278 .unwrap(),
279 )
280 .await?
281 }
282 Event::DockerHostList => {
283 self.on_docker_host_list(
284 serde_json::from_value(payload)
285 .log_error(module_path!(), |_| "Failed to deserialize DockerHostList")
286 .unwrap(),
287 )
288 .await?
289 }
290 Event::Info => self.on_info().await?,
291 Event::AutoLogin => self.on_auto_login().await?,
292 Event::LoginRequired => self.on_login_required().await?,
293 Event::UpdateMonitorIntoList => {
294 self.on_update_monitor_into_list(
295 serde_json::from_value(payload)
296 .log_error(module_path!(), |_| {
297 "Failed to deserialize UpdateMonitorIntoList"
298 })
299 .unwrap(),
300 )
301 .await?
302 }
303 Event::DeleteMonitorFromList => {
304 self.on_delete_monitor_from_list(payload.as_i64().unwrap().try_into().unwrap())
305 .await?
306 }
307 _ => {}
308 }
309
310 Ok(())
311 }
312
313 fn extract_response<T: DeserializeOwned>(
314 response: Vec<Value>,
315 result_ptr: impl AsRef<str>,
316 verify: bool,
317 ) -> Result<T> {
318 let json = json!(response);
319
320 if verify
321 && !json
322 .pointer("/0/0/ok")
323 .ok_or_else(|| {
324 Error::InvalidResponse(response.clone(), result_ptr.as_ref().to_owned())
325 })?
326 .as_bool()
327 .unwrap_or_default()
328 {
329 let error_msg = json
330 .pointer("/0/0/msg")
331 .unwrap_or_else(|| &json!(null))
332 .as_str()
333 .unwrap_or_else(|| "Unknown error");
334
335 return Err(Error::ServerError(error_msg.to_owned()));
336 }
337
338 json.pointer(&format!("/0/0{}", result_ptr.as_ref()))
339 .and_then(|value| serde_json::from_value(value.to_owned()).ok())
340 .ok_or_else(|| Error::InvalidResponse(response, result_ptr.as_ref().to_owned()))
341 }
342
343 async fn call<A, T>(
344 self: &Arc<Self>,
345 method: impl Into<String>,
346 args: A,
347 result_ptr: impl Into<String>,
348 verify: bool,
349 ) -> Result<T>
350 where
351 A: IntoIterator<Item = Value> + Send + Clone,
352 T: DeserializeOwned + Send + 'static,
353 {
354 let method = method.into();
355 let result_ptr: String = result_ptr.into();
356
357 let method_ref = method.clone();
358 let args: A = args.clone();
359 let result_ptr = result_ptr.clone();
360 let (tx, mut rx) = tokio::sync::mpsc::channel::<Result<T>>(1);
361
362 let lock = self.socket_io.lock().await;
363 let socket_io = match &*lock {
364 Some(socket_io) => socket_io,
365 None => Err(Error::Disconnected)?,
366 };
367
368 socket_io
369 .emit_with_ack(
370 method.clone(),
371 Payload::Text(args.into_iter().collect_vec()),
372 Duration::from_secs_f64(self.config.call_timeout),
373 move |message: Payload, _: SocketIO| {
374 debug!("call {} -> {:?}", method_ref, &message);
375 let tx = tx.clone();
376 let result_ptr = result_ptr.clone();
377 async move {
378 _ = match message {
379 Payload::Text(response) => {
380 tx.send(Self::extract_response(response, result_ptr, verify))
381 .await
382 }
383 _ => tx.send(Err(Error::UnsupportedResponse)).await,
384 }
385 }
386 .boxed()
387 },
388 )
389 .await
390 .map_err(|e| Error::CommunicationError(e.to_string()))?;
391
392 let result =
393 tokio::time::timeout(Duration::from_secs_f64(self.config.call_timeout), rx.recv())
394 .await
395 .map_err(|_| Error::CallTimeout(method.clone()))?
396 .ok_or_else(|| Error::CallTimeout(method))?;
397
398 result
399 }
400
401 pub async fn login(
402 self: &Arc<Self>,
403 username: impl AsRef<str>,
404 password: impl AsRef<str>,
405 token: Option<String>,
406 ) -> Result<()> {
407 let result: Result<LoginResponse> = self
408 .call(
409 "login",
410 vec![serde_json::to_value(HashMap::from([
411 ("username", json!(username.as_ref())),
412 ("password", json!(password.as_ref())),
413 ("token", json!(token)),
414 ]))
415 .unwrap()],
416 "",
417 false,
418 )
419 .await;
420
421 match result {
422 Ok(LoginResponse::TokenRequired { .. }) => Err(Error::TokenRequired),
423 Ok(LoginResponse::Normal {
424 ok: true,
425 token: Some(auth_token),
426 ..
427 }) => {
428 debug!("Logged in as {}!", username.as_ref());
429 *self.is_logged_in.lock().await = true;
430 *self.auth_token.lock().await = Some(auth_token);
431 Ok(())
432 }
433 Ok(LoginResponse::Normal {
434 ok: false,
435 msg: Some(msg),
436 ..
437 }) => Err(Error::LoginError(msg)),
438 Err(e) => {
439 *self.is_logged_in.lock().await = false;
440 Err(e)
441 }
442 _ => {
443 *self.is_logged_in.lock().await = false;
444 Err(Error::LoginError("Unexpect login response".to_owned()))
445 }
446 }
447 .log_warn(std::module_path!(), |e| e.to_string())
448 }
449
450 pub async fn login_by_token(self: &Arc<Self>, auth_token: impl AsRef<str>) -> Result<()> {
451 let result: Result<LoginResponse> = self
452 .call("loginByToken", vec![json!(auth_token.as_ref())], "", false)
453 .await;
454
455 match result {
456 Ok(LoginResponse::TokenRequired { .. }) => Err(Error::TokenRequired),
457 Ok(LoginResponse::Normal { ok: true, .. }) => {
458 debug!("Logged in using auth_token!");
459 *self.is_logged_in.lock().await = true;
460 Ok(())
461 }
462 Ok(LoginResponse::Normal {
463 ok: false,
464 msg: Some(msg),
465 ..
466 }) => Err(Error::LoginError(msg)),
467 Err(e) => {
468 *self.is_logged_in.lock().await = false;
469 Err(e)
470 }
471 _ => {
472 *self.is_logged_in.lock().await = false;
473 Err(Error::LoginError("Unexpect login response".to_owned()))
474 }
475 }
476 .log_warn(std::module_path!(), |e| e.to_string())
477 }
478
479 async fn get_tags(self: &Arc<Self>) -> Result<Vec<TagDefinition>> {
480 self.call("getTags", vec![], "/tags", true).await
481 }
482
483 pub async fn add_tag(self: &Arc<Self>, tag: &mut TagDefinition) -> Result<()> {
484 *tag = self
485 .call(
486 "addTag",
487 vec![serde_json::to_value(tag.clone()).unwrap()],
488 "/tag",
489 true,
490 )
491 .await?;
492
493 Ok(())
494 }
495
496 pub async fn edit_tag(self: &Arc<Self>, tag: &mut TagDefinition) -> Result<()> {
497 *tag = self
498 .call(
499 "editTag",
500 vec![serde_json::to_value(tag.clone()).unwrap()],
501 "/tag",
502 true,
503 )
504 .await?;
505
506 Ok(())
507 }
508
509 pub async fn delete_tag(self: &Arc<Self>, tag_id: i32) -> Result<()> {
510 let _: bool = self
511 .call("deleteTag", vec![json!(tag_id)], "/ok", true)
512 .await?;
513
514 Ok(())
515 }
516
517 pub async fn add_notification(self: &Arc<Self>, notification: &mut Notification) -> Result<()> {
518 self.edit_notification(notification).await
519 }
520
521 pub async fn edit_notification(
522 self: &Arc<Self>,
523 notification: &mut Notification,
524 ) -> Result<()> {
525 let json = serde_json::to_value(notification.clone()).unwrap();
526 let config_json =
527 serde_json::to_value(notification.config.clone().unwrap_or_else(|| json!({}))).unwrap();
528
529 let merge = serde_merge::omerge(config_json, &json).unwrap();
530
531 notification.id = Some(
532 self.call(
533 "addNotification",
534 vec![merge, notification.id.into()],
535 "/id",
536 true,
537 )
538 .await?,
539 );
540
541 Ok(())
542 }
543
544 pub async fn delete_notification(self: &Arc<Self>, notification_id: i32) -> Result<()> {
545 let _: bool = self
546 .call(
547 "deleteNotification",
548 vec![json!(notification_id)],
549 "/ok",
550 true,
551 )
552 .await?;
553
554 Ok(())
555 }
556
557 pub async fn add_monitor_tag(
558 self: &Arc<Self>,
559 monitor_id: i32,
560 tag_id: i32,
561 value: Option<String>,
562 ) -> Result<()> {
563 let _: bool = self
564 .call(
565 "addMonitorTag",
566 vec![
567 json!(tag_id),
568 json!(monitor_id),
569 json!(value.unwrap_or_default()),
570 ],
571 "/ok",
572 true,
573 )
574 .await?;
575
576 Ok(())
577 }
578
579 pub async fn edit_monitor_tag(
580 self: &Arc<Self>,
581 monitor_id: i32,
582 tag_id: i32,
583 value: Option<String>,
584 ) -> Result<()> {
585 let _: bool = self
586 .call(
587 "editMonitorTag",
588 vec![
589 json!(tag_id),
590 json!(monitor_id),
591 json!(value.unwrap_or_default()),
592 ],
593 "/ok",
594 true,
595 )
596 .await?;
597
598 Ok(())
599 }
600
601 pub async fn delete_monitor_tag(
602 self: &Arc<Self>,
603 monitor_id: i32,
604 tag_id: i32,
605 value: Option<String>,
606 ) -> Result<()> {
607 let _: bool = self
608 .call(
609 "deleteMonitorTag",
610 vec![
611 json!(tag_id),
612 json!(monitor_id),
613 json!(value.unwrap_or_default()),
614 ],
615 "/ok",
616 true,
617 )
618 .await?;
619
620 Ok(())
621 }
622
623 pub async fn delete_monitor(self: &Arc<Self>, monitor_id: i32) -> Result<()> {
624 let _: bool = self
625 .call("deleteMonitor", vec![json!(monitor_id)], "/ok", true)
626 .await?;
627
628 Ok(())
629 }
630
631 async fn update_monitor_tags(self: &Arc<Self>, monitor_id: i32, tags: &Vec<Tag>) -> Result<()> {
632 let new_tags = tags
633 .iter()
634 .filter_map(|tag| tag.tag_id.and_then(|id| Some((id, tag))))
635 .collect::<HashMap<_, _>>();
636
637 if let Some(monitor) = self.monitors.lock().await.get(&monitor_id.to_string()) {
638 let current_tags = monitor
639 .common()
640 .tags()
641 .iter()
642 .filter_map(|tag| tag.tag_id.and_then(|id| Some((id, tag))))
643 .collect::<HashMap<_, _>>();
644
645 let duplicates = monitor
646 .common()
647 .tags()
648 .iter()
649 .duplicates_by(|tag| tag.tag_id)
650 .filter_map(|tag| tag.tag_id.as_ref().map(|id| (id, tag)))
651 .collect::<HashMap<_, _>>();
652
653 let to_delete = current_tags
654 .iter()
655 .filter(|(id, _)| !new_tags.contains_key(*id) && !duplicates.contains_key(*id))
656 .collect_vec();
657
658 let to_create = new_tags
659 .iter()
660 .filter(|(id, _)| !current_tags.contains_key(*id))
661 .collect_vec();
662
663 let to_update = current_tags
664 .keys()
665 .filter_map(|id| match (current_tags.get(id), new_tags.get(id)) {
666 (Some(current), Some(new)) => Some((id, current, new)),
667 _ => None,
668 })
669 .collect_vec();
670
671 for (tag_id, tag) in duplicates {
672 self.delete_monitor_tag(monitor_id, *tag_id, tag.value.clone())
673 .await?;
674 }
675
676 for (tag_id, tag) in to_delete {
677 self.delete_monitor_tag(monitor_id, *tag_id, tag.value.clone())
678 .await?;
679 }
680
681 for (tag_id, tag) in to_create {
682 self.add_monitor_tag(monitor_id, *tag_id, tag.value.clone())
683 .await?
684 }
685
686 for (tag_id, current, new) in to_update {
687 if current.value != new.value {
688 self.edit_monitor_tag(monitor_id, *tag_id, new.value.clone())
689 .await?;
690 }
691 }
692 } else {
693 for tag in tags {
694 if let Some(tag_id) = tag.tag_id {
695 self.add_monitor_tag(monitor_id, tag_id, tag.value.clone())
696 .await?;
697 }
698 }
699 }
700
701 Ok(())
702 }
703
704 async fn verify_monitor(self: &Arc<Self>, monitor: &Monitor) -> Result<()> {
705 if let Some(referenced_parent) = monitor.common().parent() {
706 if !self
707 .monitors
708 .lock()
709 .await
710 .values()
711 .any(|m| m.common().id().is_some_and(|id| &id == referenced_parent))
712 {
713 return Err(Error::InvalidReference(
714 InvalidReferenceError::InvalidParent(referenced_parent.to_string()),
715 ));
716 }
717 }
718
719 if let Some(referenced_notifications) = monitor.common().notification_id_list() {
720 let available_notifications = self
721 .notifications
722 .lock()
723 .await
724 .iter()
725 .filter_map(|n| n.id)
726 .collect::<HashSet<_>>();
727
728 for (notification_id, _) in referenced_notifications {
729 if let Some(id) = notification_id.parse::<i32>().ok() {
730 if !available_notifications.contains(&id) {
731 return Err(Error::InvalidReference(
732 InvalidReferenceError::InvalidNotification(notification_id.to_owned()),
733 ));
734 }
735 } else {
736 return Err(Error::InvalidReference(
737 InvalidReferenceError::InvalidNotification(notification_id.to_owned()),
738 ));
739 }
740 }
741 }
742
743 if let Monitor::Docker {
744 value: docker_monitor,
745 } = monitor
746 {
747 if let Some(referenced_docker_host) = &docker_monitor.docker_host {
748 if !self
749 .docker_hosts
750 .lock()
751 .await
752 .iter()
753 .any(|dh| dh.id.is_some_and(|id| &id == referenced_docker_host))
754 {
755 return Err(Error::InvalidReference(
756 InvalidReferenceError::InvalidDockerHost(
757 referenced_docker_host.to_string(),
758 ),
759 ));
760 }
761 }
762 }
763
764 Ok(())
765 }
766
767 pub async fn add_monitor(self: &Arc<Self>, monitor: &mut Monitor, verify: bool) -> Result<()> {
768 if verify {
769 self.verify_monitor(monitor).await?;
770 }
771
772 let tags = mem::take(monitor.common_mut().tags_mut());
773 let notifications = mem::take(monitor.common_mut().notification_id_list_mut());
774
775 #[cfg(feature = "private-api")]
776 let parent_name = mem::take(monitor.common_mut().parent_name_mut());
777 #[cfg(feature = "private-api")]
778 let create_paused = mem::take(monitor.common_mut().create_paused_mut());
779 #[cfg(feature = "private-api")]
780 let notification_names = mem::take(monitor.common_mut().notification_names_mut());
781 #[cfg(feature = "private-api")]
782 let docker_host_name = match monitor {
783 Monitor::Docker {
784 value: docker_monitor,
785 } => mem::take(&mut docker_monitor.docker_host_name),
786 _ => None,
787 };
788 #[cfg(feature = "private-api")]
789 let tag_names = mem::take(monitor.common_mut().tag_names_mut());
790
791 let id: i32 = self
792 .clone()
793 .call(
794 "add",
795 vec![serde_json::to_value(&monitor).unwrap()],
796 "/monitorID",
797 true,
798 )
799 .await?;
800
801 *monitor.common_mut().id_mut() = Some(id);
802 *monitor.common_mut().notification_id_list_mut() = notifications;
803 *monitor.common_mut().tags_mut() = tags;
804
805 #[cfg(feature = "private-api")]
806 {
807 *monitor.common_mut().parent_name_mut() = parent_name;
808 *monitor.common_mut().create_paused_mut() = create_paused;
809 *monitor.common_mut().notification_names_mut() = notification_names;
810 if let Monitor::Docker {
811 value: docker_monitor,
812 } = monitor
813 {
814 docker_monitor.docker_host_name = docker_host_name;
815 }
816 *monitor.common_mut().tag_names_mut() = tag_names;
817 }
818
819 self.edit_monitor(monitor, false).await?;
820
821 self.monitors
822 .lock()
823 .await
824 .insert(id.to_string(), monitor.clone());
825
826 #[cfg(feature = "private-api")]
827 if create_paused == Some(true) {
828 self.pause_monitor(id).await?;
829 }
830
831 Ok(())
832 }
833
834 pub async fn get_monitor(self: &Arc<Self>, monitor_id: i32) -> Result<Monitor> {
835 self.call(
836 "getMonitor",
837 vec![serde_json::to_value(monitor_id.clone()).unwrap()],
838 "/monitor",
839 true,
840 )
841 .await
842 .map_err(|e| match e {
843 Error::ServerError(msg) if msg.contains("Cannot read properties of null") => {
844 Error::IdNotFound("Monitor".to_owned(), monitor_id)
845 }
846 _ => e,
847 })
848 }
849
850 pub async fn edit_monitor(self: &Arc<Self>, monitor: &mut Monitor, verify: bool) -> Result<()> {
851 if verify {
852 self.verify_monitor(monitor).await?;
853 }
854
855 let tags = mem::take(monitor.common_mut().tags_mut());
856
857 #[cfg(feature = "private-api")]
858 let create_paused = mem::take(monitor.common_mut().create_paused_mut());
859
860 let mut monitor_json = serde_json::to_value(&monitor).unwrap();
861
862 if let Some(monitor_json) = monitor_json.as_object_mut() {
864 if !monitor_json.contains_key("url") {
865 monitor_json.insert("url".to_owned(), json!("https://"));
866 }
867 }
868
869 let id: i32 = self
870 .call("editMonitor", vec![monitor_json], "/monitorID", true)
871 .await?;
872
873 self.update_monitor_tags(id, &tags).await?;
874
875 *monitor.common_mut().tags_mut() = tags;
876
877 #[cfg(feature = "private-api")]
878 {
879 *monitor.common_mut().create_paused_mut() = create_paused;
880 }
881
882 Ok(())
883 }
884
885 pub async fn pause_monitor(self: &Arc<Self>, monitor_id: i32) -> Result<()> {
886 let _: bool = self
887 .call(
888 "pauseMonitor",
889 vec![serde_json::to_value(monitor_id).unwrap()],
890 "/ok",
891 true,
892 )
893 .await?;
894
895 Ok(())
896 }
897
898 pub async fn resume_monitor(self: &Arc<Self>, monitor_id: i32) -> Result<()> {
899 let _: bool = self
900 .call(
901 "resumeMonitor",
902 vec![serde_json::to_value(monitor_id).unwrap()],
903 "/ok",
904 true,
905 )
906 .await?;
907
908 Ok(())
909 }
910
911 async fn get_maintenance_monitors(
912 self: &Arc<Self>,
913 maintenance_id: i32,
914 ) -> Result<Vec<MaintenanceMonitor>> {
915 self.call(
916 "getMonitorMaintenance",
917 vec![serde_json::to_value(maintenance_id).unwrap()],
918 "/monitors",
919 true,
920 )
921 .await
922 }
923
924 async fn set_maintenance_monitors(
925 self: &Arc<Self>,
926 maintenance_id: i32,
927 monitors: &Vec<MaintenanceMonitor>,
928 ) -> Result<()> {
929 let _: bool = self
930 .call(
931 "addMonitorMaintenance",
932 vec![
933 serde_json::to_value(maintenance_id).unwrap(),
934 serde_json::to_value(monitors).unwrap(),
935 ],
936 "/ok",
937 true,
938 )
939 .await?;
940
941 Ok(())
942 }
943
944 async fn get_maintenance_status_pages(
945 self: &Arc<Self>,
946 maintenance_id: i32,
947 ) -> Result<Vec<MaintenanceStatusPage>> {
948 self.call(
949 "getMaintenanceStatusPage",
950 vec![serde_json::to_value(maintenance_id).unwrap()],
951 "/statusPages",
952 true,
953 )
954 .await
955 }
956
957 async fn set_maintenance_status_pages(
958 self: &Arc<Self>,
959 maintenance_id: i32,
960 status_pages: &Vec<MaintenanceStatusPage>,
961 ) -> Result<()> {
962 let _: bool = self
963 .call(
964 "addMaintenanceStatusPage",
965 vec![
966 serde_json::to_value(maintenance_id).unwrap(),
967 serde_json::to_value(status_pages).unwrap(),
968 ],
969 "/ok",
970 true,
971 )
972 .await?;
973
974 Ok(())
975 }
976
977 pub async fn delete_maintenance(self: &Arc<Self>, maintenance_id: i32) -> Result<()> {
978 let _: bool = self
979 .call(
980 "deleteMaintenance",
981 vec![json!(maintenance_id)],
982 "/ok",
983 true,
984 )
985 .await?;
986
987 Ok(())
988 }
989
990 pub async fn add_maintenance(self: &Arc<Self>, maintenance: &mut Maintenance) -> Result<()> {
991 let id = self
992 .call(
993 "addMaintenance",
994 vec![serde_json::to_value(maintenance.clone()).unwrap()],
995 "/maintenanceID",
996 true,
997 )
998 .await?;
999
1000 maintenance.common_mut().id = Some(id);
1001 if let Some(monitors) = &maintenance.common().monitors {
1002 self.set_maintenance_monitors(id, monitors).await?;
1003 }
1004 if let Some(status_pages) = &maintenance.common().status_pages {
1005 self.set_maintenance_status_pages(id, status_pages).await?;
1006 }
1007
1008 Ok(())
1009 }
1010
1011 pub async fn get_maintenance(self: &Arc<Self>, maintenance_id: i32) -> Result<Maintenance> {
1012 let mut maintenance: Maintenance = self
1013 .call(
1014 "getMaintenance",
1015 vec![serde_json::to_value(maintenance_id.clone()).unwrap()],
1016 "/maintenance",
1017 true,
1018 )
1019 .await
1020 .map_err(|e| match e {
1021 Error::ServerError(msg) if msg.contains("Cannot read properties of null") => {
1022 Error::IdNotFound("Maintenance".to_owned(), maintenance_id)
1023 }
1024 _ => e,
1025 })?;
1026
1027 maintenance.common_mut().monitors =
1028 Some(self.get_maintenance_monitors(maintenance_id).await?);
1029 maintenance.common_mut().status_pages =
1030 Some(self.get_maintenance_status_pages(maintenance_id).await?);
1031
1032 Ok(maintenance)
1033 }
1034
1035 pub async fn edit_maintenance(self: &Arc<Self>, maintenance: &mut Maintenance) -> Result<()> {
1036 let id = self
1037 .call(
1038 "addMaintenance",
1039 vec![serde_json::to_value(maintenance.clone()).unwrap()],
1040 "/maintenanceID",
1041 true,
1042 )
1043 .await?;
1044
1045 maintenance.common_mut().id = Some(id);
1046 if let Some(monitors) = &maintenance.common().monitors {
1047 self.set_maintenance_monitors(id, monitors).await?;
1048 }
1049 if let Some(status_pages) = &maintenance.common().status_pages {
1050 self.set_maintenance_status_pages(id, status_pages).await?;
1051 }
1052
1053 Ok(())
1054 }
1055
1056 pub async fn pause_maintenance(self: &Arc<Self>, maintenance_id: i32) -> Result<()> {
1057 let _: bool = self
1058 .call(
1059 "pauseMaintenance",
1060 vec![serde_json::to_value(maintenance_id).unwrap()],
1061 "/ok",
1062 true,
1063 )
1064 .await?;
1065
1066 Ok(())
1067 }
1068
1069 pub async fn resume_maintenance(self: &Arc<Self>, maintenance_id: i32) -> Result<()> {
1070 let _: bool = self
1071 .call(
1072 "resumeMaintenance",
1073 vec![serde_json::to_value(maintenance_id).unwrap()],
1074 "/ok",
1075 true,
1076 )
1077 .await?;
1078
1079 Ok(())
1080 }
1081
1082 async fn get_public_group_list(self: &Arc<Self>, slug: &str) -> Result<PublicGroupList> {
1083 let response: Value = self
1084 .reqwest
1085 .lock()
1086 .await
1087 .get(
1088 self.config
1089 .url
1090 .join(&format!("api/status-page/{}", slug))
1091 .map_err(|e| Error::InvalidUrl(e.to_string()))?,
1092 )
1093 .send()
1094 .await?
1095 .json()
1096 .await?;
1097
1098 let monitor_list = response
1099 .clone()
1100 .pointer("/publicGroupList")
1101 .ok_or_else(|| {
1102 Error::InvalidResponse(vec![response.clone()], "/publicGroupList".to_owned())
1103 })?
1104 .clone();
1105
1106 Ok(serde_json::from_value(monitor_list)
1107 .log_warn(std::module_path!(), |e| e.to_string())
1108 .map_err(|_| Error::UnsupportedResponse)?)
1109 }
1110
1111 pub async fn delete_status_page(self: &Arc<Self>, slug: &str) -> Result<()> {
1112 let _: bool = self
1113 .call("deleteStatusPage", vec![json!(slug)], "/ok", true)
1114 .await?;
1115
1116 Ok(())
1117 }
1118
1119 pub async fn add_status_page(self: &Arc<Self>, status_page: &mut StatusPage) -> Result<()> {
1120 let ok: bool = self
1121 .call(
1122 "addStatusPage",
1123 vec![
1124 serde_json::to_value(status_page.title.clone()).unwrap(),
1125 serde_json::to_value(status_page.slug.clone()).unwrap(),
1126 ],
1127 "/ok",
1128 true,
1129 )
1130 .await?;
1131
1132 if !ok {
1133 return Err(Error::ServerError("Unable to add status page".to_owned()));
1134 }
1135
1136 self.edit_status_page(status_page).await?;
1137
1138 Ok(())
1139 }
1140
1141 pub async fn get_status_page(self: &Arc<Self>, slug: &str) -> Result<StatusPage> {
1142 let mut status_page: StatusPage = self
1143 .call(
1144 "getStatusPage",
1145 vec![serde_json::to_value(slug).unwrap()],
1146 "/config",
1147 true,
1148 )
1149 .await
1150 .map_err(|e| match e {
1151 Error::ServerError(msg) if msg.contains("Cannot read properties of null") => {
1152 Error::SlugNotFound("StatusPage".to_owned(), slug.to_owned())
1153 }
1154 _ => e,
1155 })?;
1156
1157 status_page.public_group_list = Some(
1158 self.get_public_group_list(&status_page.slug.clone().unwrap_or_default())
1159 .await?,
1160 );
1161
1162 Ok(status_page)
1163 }
1164
1165 pub async fn edit_status_page(self: &Arc<Self>, status_page: &mut StatusPage) -> Result<()> {
1166 let mut config = serde_json::to_value(status_page.clone()).unwrap();
1167 config
1168 .as_object_mut()
1169 .unwrap()
1170 .insert("logo".to_owned(), status_page.icon.clone().into());
1171
1172 let _: bool = self
1173 .call(
1174 "saveStatusPage",
1175 vec![
1176 serde_json::to_value(status_page.slug.clone()).unwrap(),
1177 serde_json::to_value(config).unwrap(),
1178 serde_json::to_value(status_page.icon.clone()).unwrap(),
1179 serde_json::to_value(status_page.public_group_list.clone()).unwrap(),
1180 ],
1181 "/ok",
1182 true,
1183 )
1184 .await?;
1185
1186 Ok(())
1187 }
1188
1189 pub async fn add_docker_host(self: &Arc<Self>, docker_host: &mut DockerHost) -> Result<()> {
1190 self.edit_docker_host(docker_host).await
1191 }
1192
1193 pub async fn edit_docker_host(self: &Arc<Self>, docker_host: &mut DockerHost) -> Result<()> {
1194 docker_host.id = self
1195 .call(
1196 "addDockerHost",
1197 vec![
1198 serde_json::to_value(docker_host.clone()).unwrap(),
1199 serde_json::to_value(docker_host.id.clone()).unwrap(),
1200 ],
1201 "/id",
1202 true,
1203 )
1204 .await?;
1205
1206 Ok(())
1207 }
1208
1209 pub async fn delete_docker_host(self: &Arc<Self>, docker_host_id: i32) -> Result<()> {
1210 let _: bool = self
1211 .call(
1212 "deleteDockerHost",
1213 vec![serde_json::to_value(docker_host_id).unwrap()],
1214 "/ok",
1215 true,
1216 )
1217 .await?;
1218
1219 Ok(())
1220 }
1221
1222 pub async fn test_docker_host(self: &Arc<Self>, docker_host: &DockerHost) -> Result<String> {
1223 let msg: String = self
1224 .call(
1225 "testDockerHost",
1226 vec![serde_json::to_value(docker_host).unwrap()],
1227 "/msg",
1228 true,
1229 )
1230 .await?;
1231
1232 Ok(msg)
1233 }
1234
1235 pub async fn get_database_size(self: &Arc<Self>) -> Result<u64> {
1236 let size: u64 = self.call("getDatabaseSize", vec![], "/size", true).await?;
1237 Ok(size)
1238 }
1239
1240 pub async fn shrink_database(self: &Arc<Self>) -> Result<()> {
1241 let _: bool = self.call("shrinkDatabase", vec![], "/ok", true).await?;
1242 Ok(())
1243 }
1244
1245 pub async fn connect(self: &Arc<Self>) -> Result<()> {
1246 let mut tls_config = TlsConnector::builder();
1247
1248 tls_config.danger_accept_invalid_certs(!self.config.tls.verify);
1249
1250 if let Some((_, cert)) = &self.custom_cert {
1251 tls_config.add_root_certificate(cert.clone());
1252 }
1253
1254 self.is_ready.lock().await.reset();
1255 *self.is_logged_in.lock().await = false;
1256 *self.socket_io.lock().await = None;
1257
1258 let mut builder = ClientBuilder::new(
1259 self.config
1260 .url
1261 .join("socket.io/")
1262 .map_err(|e| Error::InvalidUrl(e.to_string()))?,
1263 )
1264 .tls_config(tls_config.build().map_err(|e| {
1265 Error::InvalidTlsCert(
1266 self.custom_cert
1267 .as_ref()
1268 .map(|(file, _)| file.to_owned())
1269 .unwrap_or_default(),
1270 e.to_string(),
1271 )
1272 })?)
1273 .transport_type(rust_socketio::TransportType::Websocket);
1274
1275 for (key, value) in self
1276 .config
1277 .headers
1278 .iter()
1279 .filter_map(|header| header.split_once("="))
1280 {
1281 builder = builder.opening_header(key, value);
1282 }
1283
1284 let handle = Handle::current();
1285 let self_ref = Arc::downgrade(self);
1286 let client = builder
1287 .on_any(move |event, payload, _| {
1288 let handle = handle.clone();
1289 let self_ref: Weak<Worker> = self_ref.clone();
1290 trace!("Client::on_any({:?}, {:?})", &event, &payload);
1291 async move {
1292 if let Some(arc) = self_ref.upgrade() {
1293 match (event, payload) {
1294 (SocketIOEvent::Message, Payload::Text(params)) => {
1295 if let Ok(e) = Event::from_str(
1296 ¶ms[0]
1297 .as_str()
1298 .log_warn(std::module_path!(), || {
1299 "Error while deserializing Event..."
1300 })
1301 .unwrap_or(""),
1302 ) {
1303 handle.clone().spawn(async move {
1304 _ = arc.on_event(e, json!(null)).await.log_warn(
1305 std::module_path!(),
1306 |e| {
1307 format!(
1308 "Error while handling message event: {}",
1309 e.to_string()
1310 )
1311 },
1312 );
1313 });
1314 }
1315 }
1316 (event, Payload::Text(params)) => {
1317 if let Ok(e) = Event::from_str(&String::from(event)) {
1318 handle.clone().spawn(async move {
1319 _ = arc
1320 .on_event(e.clone(), params.into_iter().next().unwrap())
1321 .await
1322 .log_warn(std::module_path!(), |err| {
1323 format!(
1324 "Error while handling '{:?}' event: {}",
1325 e,
1326 err.to_string()
1327 )
1328 });
1329 });
1330 }
1331 }
1332 _ => {}
1333 }
1334 }
1335 }
1336 .boxed()
1337 })
1338 .connect()
1339 .await
1340 .log_error(std::module_path!(), |_| "Error during connect")
1341 .ok();
1342
1343 debug!("Waiting for connection");
1344
1345 debug!("Connection opened!");
1346 *self.socket_io.lock().await = client;
1347
1348 for i in 0..10 {
1349 if self.is_ready().await {
1350 debug!("Connected!");
1351 return Ok(());
1352 }
1353
1354 debug!("Waiting for Kuma to get ready...");
1355 tokio::time::sleep(Duration::from_millis(200 * i)).await;
1356 }
1357
1358 warn!("Timeout while waiting for Kuma to get ready...");
1359 match *self.is_connected.lock().await {
1360 true => Err(Error::NotAuthenticated),
1361 false => Err(Error::ConnectionTimeout),
1362 }
1363 }
1364
1365 pub async fn disconnect(self: &Arc<Self>) -> Result<()> {
1366 let self_ref = self.to_owned();
1367 tokio::spawn(async move {
1368 let socket_io = self_ref.socket_io.lock().await;
1369 if let Some(socket_io) = &*socket_io {
1370 _ = socket_io.disconnect().await;
1371 }
1372 drop(socket_io);
1373 *self_ref.socket_io.lock().await = None;
1374 debug!("Connection closed!");
1375 })
1376 .await
1377 .pipe(|result| {
1378 return match result {
1379 Ok(_) => Ok(()),
1380 Err(e) if e.is_cancelled() => Ok(()),
1381 Err(e) => Err(Error::CommunicationError(format!(
1382 "Error while disconnecting: {}",
1383 e.to_string()
1384 ))),
1385 };
1386 })
1387 .log_error(std::module_path!(), |e| e.to_string())?;
1388
1389 Ok(())
1390 }
1391
1392 pub async fn is_ready(self: &Arc<Self>) -> bool {
1393 self.is_ready.lock().await.is_ready()
1394 }
1395}
1396
1397pub struct Client {
1477 worker: Arc<Worker>,
1478}
1479
1480impl Client {
1481 pub async fn connect(config: Config) -> Result<Client> {
1482 let worker = Worker::new(config)?;
1483 match worker.connect().await {
1484 Ok(_) => Ok(Self { worker }),
1485 Err(e) => {
1486 _ = worker
1487 .disconnect()
1488 .await
1489 .log_error(std::module_path!(), |e| e.to_string());
1490
1491 Err(e)
1492 }
1493 }
1494 }
1495
1496 pub async fn get_monitors(&self) -> Result<MonitorList> {
1498 match self.worker.is_ready().await {
1499 true => Ok(self.worker.monitors.lock().await.clone()),
1500 false => Err(Error::NotReady),
1501 }
1502 }
1503
1504 pub async fn get_monitor(&self, monitor_id: i32) -> Result<Monitor> {
1506 self.worker.get_monitor(monitor_id).await
1507 }
1508
1509 pub async fn add_monitor<T: Into<Monitor>>(&self, monitor: T) -> Result<Monitor> {
1511 let mut monitor = monitor.into();
1512 self.worker.add_monitor(&mut monitor, true).await?;
1513 Ok(monitor)
1514 }
1515
1516 pub async fn edit_monitor<T: Into<Monitor>>(&self, monitor: T) -> Result<Monitor> {
1518 let mut monitor = monitor.into();
1519 self.worker.edit_monitor(&mut monitor, true).await?;
1520 Ok(monitor)
1521 }
1522
1523 pub async fn delete_monitor(&self, monitor_id: i32) -> Result<()> {
1525 self.worker.delete_monitor(monitor_id).await
1526 }
1527
1528 pub async fn pause_monitor(&self, monitor_id: i32) -> Result<()> {
1530 self.worker.pause_monitor(monitor_id).await
1531 }
1532
1533 pub async fn resume_monitor(&self, monitor_id: i32) -> Result<()> {
1535 self.worker.resume_monitor(monitor_id).await
1536 }
1537
1538 pub async fn get_tags(&self) -> Result<Vec<TagDefinition>> {
1540 self.worker.get_tags().await
1541 }
1542
1543 pub async fn get_tag(&self, tag_id: i32) -> Result<TagDefinition> {
1545 self.worker.get_tags().await.and_then(|tags| {
1546 tags.into_iter()
1547 .find(|tag| tag.tag_id == Some(tag_id))
1548 .ok_or_else(|| Error::IdNotFound("Tag".to_owned(), tag_id))
1549 })
1550 }
1551
1552 pub async fn add_tag(&self, mut tag: TagDefinition) -> Result<TagDefinition> {
1554 self.worker.add_tag(&mut tag).await?;
1555 Ok(tag)
1556 }
1557
1558 pub async fn edit_tag(&self, mut tag: TagDefinition) -> Result<TagDefinition> {
1560 self.worker.edit_tag(&mut tag).await?;
1561 Ok(tag)
1562 }
1563
1564 pub async fn delete_tag(&self, tag_id: i32) -> Result<()> {
1566 self.worker.delete_tag(tag_id).await
1567 }
1568
1569 pub async fn get_notifications(&self) -> Result<NotificationList> {
1571 match self.worker.is_ready().await {
1572 true => Ok(self.worker.notifications.lock().await.clone()),
1573 false => Err(Error::NotReady),
1574 }
1575 }
1576
1577 pub async fn get_notification(&self, notification_id: i32) -> Result<Notification> {
1579 self.get_notifications().await.and_then(|notifications| {
1580 notifications
1581 .into_iter()
1582 .find(|notification| notification.id == Some(notification_id))
1583 .ok_or_else(|| Error::IdNotFound("Notification".to_owned(), notification_id))
1584 })
1585 }
1586
1587 pub async fn add_notification(&self, mut notification: Notification) -> Result<Notification> {
1589 self.worker.add_notification(&mut notification).await?;
1590 Ok(notification)
1591 }
1592
1593 pub async fn edit_notification(&self, mut notification: Notification) -> Result<Notification> {
1595 self.worker.edit_notification(&mut notification).await?;
1596 Ok(notification)
1597 }
1598
1599 pub async fn delete_notification(&self, notification_id: i32) -> Result<()> {
1601 self.worker.delete_notification(notification_id).await
1602 }
1603
1604 pub async fn get_maintenances(&self) -> Result<MaintenanceList> {
1606 match self.worker.is_ready().await {
1607 true => Ok(self.worker.maintenances.lock().await.clone()),
1608 false => Err(Error::NotReady),
1609 }
1610 }
1611
1612 pub async fn get_maintenance(&self, maintenance_id: i32) -> Result<Maintenance> {
1614 self.worker.get_maintenance(maintenance_id).await
1615 }
1616
1617 pub async fn add_maintenance(&self, mut maintenance: Maintenance) -> Result<Maintenance> {
1619 self.worker.add_maintenance(&mut maintenance).await?;
1620 Ok(maintenance)
1621 }
1622
1623 pub async fn edit_maintenance(&self, mut maintenance: Maintenance) -> Result<Maintenance> {
1625 self.worker.edit_maintenance(&mut maintenance).await?;
1626 Ok(maintenance)
1627 }
1628
1629 pub async fn delete_maintenance(&self, maintenance_id: i32) -> Result<()> {
1631 self.worker.delete_maintenance(maintenance_id).await
1632 }
1633
1634 pub async fn pause_maintenance(&self, maintenance_id: i32) -> Result<()> {
1636 self.worker.pause_maintenance(maintenance_id).await
1637 }
1638
1639 pub async fn resume_maintenance(&self, maintenance_id: i32) -> Result<()> {
1641 self.worker.resume_maintenance(maintenance_id).await
1642 }
1643
1644 pub async fn get_status_pages(&self) -> Result<StatusPageList> {
1646 match self.worker.is_ready().await {
1647 true => Ok(self.worker.status_pages.lock().await.clone()),
1648 false => Err(Error::NotReady),
1649 }
1650 }
1651
1652 pub async fn get_status_page<T: AsRef<str>>(&self, slug: T) -> Result<StatusPage> {
1654 self.worker.get_status_page(slug.as_ref()).await
1655 }
1656
1657 pub async fn add_status_page(&self, mut status_page: StatusPage) -> Result<StatusPage> {
1659 self.worker.add_status_page(&mut status_page).await?;
1660 Ok(status_page)
1661 }
1662
1663 pub async fn edit_status_page(&self, mut status_page: StatusPage) -> Result<StatusPage> {
1665 self.worker.edit_status_page(&mut status_page).await?;
1666 Ok(status_page)
1667 }
1668
1669 pub async fn delete_status_page<T: AsRef<str>>(&self, slug: T) -> Result<()> {
1671 self.worker.delete_status_page(slug.as_ref()).await
1672 }
1673
1674 pub async fn get_docker_hosts(&self) -> Result<DockerHostList> {
1676 match self.worker.is_ready().await {
1677 true => Ok(self.worker.docker_hosts.lock().await.clone()),
1678 false => Err(Error::NotReady),
1679 }
1680 }
1681
1682 pub async fn get_docker_host(&self, docker_host_id: i32) -> Result<DockerHost> {
1684 self.get_docker_hosts().await.and_then(|docker_host| {
1685 docker_host
1686 .into_iter()
1687 .find(|docker_host| docker_host.id == Some(docker_host_id))
1688 .ok_or_else(|| Error::IdNotFound("Docker Host".to_owned(), docker_host_id))
1689 })
1690 }
1691
1692 pub async fn add_docker_host(&self, mut docker_host: DockerHost) -> Result<DockerHost> {
1694 self.worker.add_docker_host(&mut docker_host).await?;
1695 Ok(docker_host)
1696 }
1697
1698 pub async fn edit_docker_host(&self, mut docker_host: DockerHost) -> Result<DockerHost> {
1700 self.worker.edit_docker_host(&mut docker_host).await?;
1701 Ok(docker_host)
1702 }
1703
1704 pub async fn delete_docker_host(&self, docker_host_id: i32) -> Result<()> {
1706 self.worker.delete_docker_host(docker_host_id).await
1707 }
1708
1709 pub async fn test_docker_host<T: std::borrow::Borrow<DockerHost>>(
1711 &self,
1712 docker_host: T,
1713 ) -> Result<String> {
1714 self.worker.test_docker_host(docker_host.borrow()).await
1715 }
1716
1717 pub async fn get_database_size(&self) -> Result<u64> {
1719 self.worker.get_database_size().await
1720 }
1721
1722 pub async fn shrink_database(&self) -> Result<()> {
1724 self.worker.shrink_database().await
1725 }
1726
1727 pub async fn disconnect(&self) -> Result<()> {
1729 self.worker.disconnect().await
1730 }
1731
1732 pub async fn get_auth_token(&self) -> Option<String> {
1734 self.worker.auth_token.lock().await.clone()
1735 }
1736}
1737
1738impl Drop for Client {
1739 fn drop(&mut self) {
1740 let worker = self.worker.clone();
1741 tokio::spawn(async move {
1742 _ = worker
1743 .disconnect()
1744 .await
1745 .log_error(std::module_path!(), |e| e.to_string());
1746 });
1747 }
1748}