hrobot/api/storagebox/mod.rs
1//! Storagebox structs and implementation.
2use crate::{error::Error, AsyncRobot};
3
4use super::{
5 wrapper::{Empty, List, Single},
6 UnauthenticatedRequest,
7};
8
9mod models;
10pub use models::*;
11use serde::Serialize;
12
13fn list_storageboxes() -> UnauthenticatedRequest<List<StorageBoxReference>> {
14 UnauthenticatedRequest::from("https://robot-ws.your-server.de/storagebox")
15}
16
17fn get_storagebox(storagebox: StorageBoxId) -> UnauthenticatedRequest<Single<StorageBox>> {
18 UnauthenticatedRequest::from(&format!(
19 "https://robot-ws.your-server.de/storagebox/{storagebox}"
20 ))
21}
22
23fn reset_password(storagebox: StorageBoxId) -> UnauthenticatedRequest<Single<String>> {
24 UnauthenticatedRequest::from(&format!(
25 "https://robot-ws.your-server.de/storagebox/{storagebox}/password"
26 ))
27 .with_method("POST")
28}
29
30fn rename_storagebox(
31 storagebox: StorageBoxId,
32 name: &str,
33) -> Result<UnauthenticatedRequest<Single<StorageBox>>, serde_html_form::ser::Error> {
34 #[derive(Serialize)]
35 struct RenameBox<'a> {
36 storagebox_name: &'a str,
37 }
38 UnauthenticatedRequest::from(&format!(
39 "https://robot-ws.your-server.de/storagebox/{storagebox}"
40 ))
41 .with_method("POST")
42 .with_body(RenameBox {
43 storagebox_name: name,
44 })
45}
46
47fn toggle_service(
48 storagebox: StorageBoxId,
49 service: &str,
50 enabled: bool,
51) -> UnauthenticatedRequest<Single<StorageBox>> {
52 UnauthenticatedRequest::from(&format!(
53 "https://robot-ws.your-server.de/storagebox/{storagebox}"
54 ))
55 .with_method("POST")
56 .with_serialized_body(format!("{service}={enabled}"))
57}
58
59fn configure_accessibility(
60 storagebox: StorageBoxId,
61 accessibility: Accessibility,
62) -> Result<UnauthenticatedRequest<Single<StorageBox>>, serde_html_form::ser::Error> {
63 UnauthenticatedRequest::from(&format!(
64 "https://robot-ws.your-server.de/storagebox/{storagebox}"
65 ))
66 .with_method("POST")
67 .with_body(accessibility)
68}
69
70fn list_snapshots(storagebox: StorageBoxId) -> UnauthenticatedRequest<List<Snapshot>> {
71 UnauthenticatedRequest::from(&format!(
72 "https://robot-ws.your-server.de/storagebox/{storagebox}/snapshot"
73 ))
74}
75
76fn create_snapshot(storagebox: StorageBoxId) -> UnauthenticatedRequest<Single<CreatedSnapshot>> {
77 UnauthenticatedRequest::from(&format!(
78 "https://robot-ws.your-server.de/storagebox/{storagebox}/snapshot"
79 ))
80 .with_method("POST")
81}
82
83fn delete_snapshot(storagebox: StorageBoxId, snapshot_name: &str) -> UnauthenticatedRequest<Empty> {
84 UnauthenticatedRequest::from(&format!(
85 "https://robot-ws.your-server.de/storagebox/{storagebox}/snapshot/{snapshot_name}"
86 ))
87 .with_method("DELETE")
88}
89
90fn revert_to_snapshot(
91 storagebox: StorageBoxId,
92 snapshot_name: &str,
93) -> UnauthenticatedRequest<Empty> {
94 UnauthenticatedRequest::from(&format!(
95 "https://robot-ws.your-server.de/storagebox/{storagebox}/snapshot/{snapshot_name}"
96 ))
97 .with_method("POST")
98 .with_serialized_body("revert=true".to_string())
99}
100
101fn change_snapshot_comment(
102 storagebox: StorageBoxId,
103 snapshot_name: &str,
104 comment: &str,
105) -> Result<UnauthenticatedRequest<Empty>, serde_html_form::ser::Error> {
106 #[derive(Serialize)]
107 struct ChangeComment<'a> {
108 comment: &'a str,
109 }
110 UnauthenticatedRequest::from(&format!(
111 "https://robot-ws.your-server.de/storagebox/{storagebox}/snapshot/{snapshot_name}/comment"
112 ))
113 .with_method("POST")
114 .with_body(ChangeComment { comment })
115}
116
117fn get_snapshot_plan(storagebox: StorageBoxId) -> UnauthenticatedRequest<Single<SnapshotPlan>> {
118 UnauthenticatedRequest::from(&format!(
119 "https://robot-ws.your-server.de/storagebox/{storagebox}/snapshotplan"
120 ))
121}
122
123fn update_snapshot_plan(
124 storagebox: StorageBoxId,
125 plan: SnapshotPlan,
126) -> Result<UnauthenticatedRequest<Single<SnapshotPlan>>, serde_html_form::ser::Error> {
127 UnauthenticatedRequest::from(&format!(
128 "https://robot-ws.your-server.de/storagebox/{storagebox}/snapshotplan"
129 ))
130 .with_method("POST")
131 .with_body(plan)
132}
133
134fn list_subaccounts(storagebox: StorageBoxId) -> UnauthenticatedRequest<List<Subaccount>> {
135 UnauthenticatedRequest::from(&format!(
136 "https://robot-ws.your-server.de/storagebox/{storagebox}/subaccount"
137 ))
138}
139
140#[derive(Serialize)]
141struct SubaccountConfig<'a> {
142 #[serde(skip_serializing_if = "Option::is_none")]
143 homedirectory: Option<&'a str>,
144
145 #[serde(skip_serializing_if = "Option::is_none")]
146 samba: Option<bool>,
147
148 #[serde(skip_serializing_if = "Option::is_none")]
149 ssh: Option<bool>,
150
151 #[serde(skip_serializing_if = "Option::is_none")]
152 webdav: Option<bool>,
153
154 #[serde(skip_serializing_if = "Option::is_none")]
155 readonly: Option<Permission>,
156
157 #[serde(skip_serializing_if = "Option::is_none")]
158 comment: Option<&'a str>,
159}
160
161fn create_subaccount(
162 storagebox: StorageBoxId,
163 home_directory: &str,
164 accessibility: Accessibility,
165 read_only: Permission,
166 comment: Option<&str>,
167) -> Result<UnauthenticatedRequest<Single<CreatedSubaccount>>, serde_html_form::ser::Error> {
168 UnauthenticatedRequest::from(&format!(
169 "https://robot-ws.your-server.de/storagebox/{storagebox}/subaccount"
170 ))
171 .with_method("POST")
172 .with_body(SubaccountConfig {
173 homedirectory: Some(home_directory),
174 samba: Some(accessibility.samba),
175 ssh: Some(accessibility.ssh),
176 webdav: Some(accessibility.webdav),
177 readonly: Some(read_only),
178 comment,
179 })
180}
181
182fn update_subaccount(
183 storagebox: StorageBoxId,
184 subaccount: &SubaccountId,
185 home_directory: Option<&str>,
186 accessibility: Option<&Accessibility>,
187 read_only: Option<Permission>,
188 comment: Option<&str>,
189) -> Result<UnauthenticatedRequest<Empty>, serde_html_form::ser::Error> {
190 UnauthenticatedRequest::from(&format!(
191 "https://robot-ws.your-server.de/storagebox/{storagebox}/subaccount/{subaccount}"
192 ))
193 .with_method("PUT")
194 .with_body(SubaccountConfig {
195 homedirectory: home_directory,
196 samba: accessibility.map(|a| a.samba),
197 ssh: accessibility.map(|a| a.ssh),
198 webdav: accessibility.map(|a| a.webdav),
199 readonly: read_only,
200 comment,
201 })
202}
203
204fn delete_subaccount(
205 storagebox: StorageBoxId,
206 subaccount: SubaccountId,
207) -> UnauthenticatedRequest<Empty> {
208 UnauthenticatedRequest::from(&format!(
209 "https://robot-ws.your-server.de/storagebox/{storagebox}/subaccount/{subaccount}"
210 ))
211 .with_method("DELETE")
212}
213
214fn reset_subaccount_password(
215 storagebox: StorageBoxId,
216 subaccount: &SubaccountId,
217) -> UnauthenticatedRequest<Single<String>> {
218 UnauthenticatedRequest::from(&format!(
219 "https://robot-ws.your-server.de/storagebox/{storagebox}/subaccount/{subaccount}/password"
220 ))
221 .with_method("POST")
222}
223
224impl AsyncRobot {
225 /// List all storageboxes associated with this account.
226 ///
227 /// Note that this function returns a truncated version of the [`StorageBox`] which
228 /// does not contain disk usage and service accessibility information.
229 ///
230 /// # Example
231 /// ```rust,no_run
232 /// # #[tokio::main]
233 /// # async fn main() {
234 /// # let _ = dotenvy::dotenv().ok();
235 /// let robot = hrobot::AsyncRobot::default();
236 /// robot.list_storageboxes().await.unwrap();
237 /// # }
238 /// ```
239 pub async fn list_storageboxes(&self) -> Result<Vec<StorageBoxReference>, Error> {
240 Ok(self.go(list_storageboxes()).await?.0)
241 }
242
243 /// Get a single storagebox.
244 ///
245 /// # Example
246 /// ```rust,no_run
247 /// # use hrobot::api::storagebox::StorageBoxId;
248 /// # #[tokio::main]
249 /// # async fn main() {
250 /// # let _ = dotenvy::dotenv().ok();
251 /// let robot = hrobot::AsyncRobot::default();
252 /// robot.get_storagebox(StorageBoxId(1234)).await.unwrap();
253 /// # }
254 /// ```
255 pub async fn get_storagebox(&self, id: StorageBoxId) -> Result<StorageBox, Error> {
256 Ok(self.go(get_storagebox(id)).await?.0)
257 }
258
259 /// Rename storagebox.
260 ///
261 /// # Example
262 /// ```rust,no_run
263 /// # use hrobot::api::storagebox::StorageBoxId;
264 /// # #[tokio::main]
265 /// # async fn main() {
266 /// # let _ = dotenvy::dotenv().ok();
267 /// let robot = hrobot::AsyncRobot::default();
268 /// robot.rename_storagebox(StorageBoxId(1234), "my-backups").await.unwrap();
269 /// # }
270 /// ```
271 pub async fn rename_storagebox(
272 &self,
273 id: StorageBoxId,
274 name: &str,
275 ) -> Result<StorageBox, Error> {
276 Ok(self.go(rename_storagebox(id, name)?).await?.0)
277 }
278
279 /// Configure storagebox accessibility.
280 ///
281 /// # Example
282 /// ```rust,no_run
283 /// # use hrobot::api::storagebox::{StorageBoxId, Accessibility};
284 /// # #[tokio::main]
285 /// # async fn main() {
286 /// # let _ = dotenvy::dotenv().ok();
287 /// let robot = hrobot::AsyncRobot::default();
288 /// robot.configure_storagebox_accessibility(StorageBoxId(1234), Accessibility {
289 /// webdav: false,
290 /// samba: false,
291 /// ssh: false,
292 /// external_reachability: false,
293 /// }).await.unwrap();
294 /// # }
295 /// ```
296 pub async fn configure_storagebox_accessibility(
297 &self,
298 id: StorageBoxId,
299 accessibility: Accessibility,
300 ) -> Result<StorageBox, Error> {
301 Ok(self
302 .go(configure_accessibility(id, accessibility)?)
303 .await?
304 .0)
305 }
306
307 /// Enable Samba (SMB) access to the storagebox.
308 ///
309 /// # Example
310 /// ```rust,no_run
311 /// # use hrobot::api::storagebox::StorageBoxId;
312 /// # #[tokio::main]
313 /// # async fn main() {
314 /// # let _ = dotenvy::dotenv().ok();
315 /// let robot = hrobot::AsyncRobot::default();
316 /// robot.enable_storagebox_samba(StorageBoxId(1234)).await.unwrap();
317 /// # }
318 /// ```
319 pub async fn enable_storagebox_samba(&self, id: StorageBoxId) -> Result<StorageBox, Error> {
320 Ok(self.go(toggle_service(id, "samba", true)).await?.0)
321 }
322
323 /// Disable Samba (SMB) access to the storagebox.
324 ///
325 /// # Example
326 /// ```rust,no_run
327 /// # use hrobot::api::storagebox::StorageBoxId;
328 /// # #[tokio::main]
329 /// # async fn main() {
330 /// # let _ = dotenvy::dotenv().ok();
331 /// let robot = hrobot::AsyncRobot::default();
332 /// robot.disable_storagebox_samba(StorageBoxId(1234)).await.unwrap();
333 /// # }
334 /// ```
335 pub async fn disable_storagebox_samba(&self, id: StorageBoxId) -> Result<StorageBox, Error> {
336 Ok(self.go(toggle_service(id, "samba", false)).await?.0)
337 }
338
339 /// Enable WebDAV access to the storagebox.
340 ///
341 /// # Example
342 /// ```rust,no_run
343 /// # use hrobot::api::storagebox::StorageBoxId;
344 /// # #[tokio::main]
345 /// # async fn main() {
346 /// # let _ = dotenvy::dotenv().ok();
347 /// let robot = hrobot::AsyncRobot::default();
348 /// robot.enable_storagebox_webdav(StorageBoxId(1234)).await.unwrap();
349 /// # }
350 /// ```
351 pub async fn enable_storagebox_webdav(&self, id: StorageBoxId) -> Result<StorageBox, Error> {
352 Ok(self.go(toggle_service(id, "webdav", true)).await?.0)
353 }
354
355 /// Disable WebDAV access to the storagebox.
356 ///
357 /// # Example
358 /// ```rust,no_run
359 /// # use hrobot::api::storagebox::StorageBoxId;
360 /// # #[tokio::main]
361 /// # async fn main() {
362 /// # let _ = dotenvy::dotenv().ok();
363 /// let robot = hrobot::AsyncRobot::default();
364 /// robot.disable_storagebox_webdav(StorageBoxId(1234)).await.unwrap();
365 /// # }
366 /// ```
367 pub async fn disable_storagebox_webdav(&self, id: StorageBoxId) -> Result<StorageBox, Error> {
368 Ok(self.go(toggle_service(id, "webdav", false)).await?.0)
369 }
370
371 /// Enable SSH access to the storagebox.
372 ///
373 /// # Example
374 /// ```rust,no_run
375 /// # use hrobot::api::storagebox::StorageBoxId;
376 /// # #[tokio::main]
377 /// # async fn main() {
378 /// # let _ = dotenvy::dotenv().ok();
379 /// let robot = hrobot::AsyncRobot::default();
380 /// robot.enable_storagebox_ssh(StorageBoxId(1234)).await.unwrap();
381 /// # }
382 /// ```
383 pub async fn enable_storagebox_ssh(&self, id: StorageBoxId) -> Result<StorageBox, Error> {
384 Ok(self.go(toggle_service(id, "ssh", true)).await?.0)
385 }
386
387 /// Disable SSH access to the storagebox.
388 ///
389 /// # Example
390 /// ```rust,no_run
391 /// # use hrobot::api::storagebox::StorageBoxId;
392 /// # #[tokio::main]
393 /// # async fn main() {
394 /// # let _ = dotenvy::dotenv().ok();
395 /// let robot = hrobot::AsyncRobot::default();
396 /// robot.disable_storagebox_ssh(StorageBoxId(1234)).await.unwrap();
397 /// # }
398 /// ```
399 pub async fn disable_storagebox_ssh(&self, id: StorageBoxId) -> Result<StorageBox, Error> {
400 Ok(self.go(toggle_service(id, "ssh", false)).await?.0)
401 }
402
403 /// Enable external reachability for the storagebox.
404 ///
405 /// External reachability means that the enabled services are reachable
406 /// outside of Hetzner's networks. Without this enabled, you won't be able
407 /// to log into the storagebox from anything other than Hetzner Cloud
408 /// or Hetzner's Dedicated Servers.
409 ///
410 /// # Example
411 /// ```rust,no_run
412 /// # use hrobot::api::storagebox::StorageBoxId;
413 /// # #[tokio::main]
414 /// # async fn main() {
415 /// # let _ = dotenvy::dotenv().ok();
416 /// let robot = hrobot::AsyncRobot::default();
417 /// robot.enable_storagebox_external_reachability(StorageBoxId(1234)).await.unwrap();
418 /// # }
419 /// ```
420 pub async fn enable_storagebox_external_reachability(
421 &self,
422 id: StorageBoxId,
423 ) -> Result<StorageBox, Error> {
424 Ok(self
425 .go(toggle_service(id, "external_reachability", true))
426 .await?
427 .0)
428 }
429
430 /// Disable external reachability for to the storagebox.
431 ///
432 /// External reachability means that the enabled services are reachable
433 /// outside of Hetzner's networks. Without this enabled, you won't be able
434 /// to log into the storagebox from anything other than Hetzner Cloud
435 /// or Hetzner's Dedicated Servers.
436 ///
437 /// # Example
438 /// ```rust,no_run
439 /// # use hrobot::api::storagebox::StorageBoxId;
440 /// # #[tokio::main]
441 /// # async fn main() {
442 /// # let _ = dotenvy::dotenv().ok();
443 /// let robot = hrobot::AsyncRobot::default();
444 /// robot.disable_storagebox_external_reachability(StorageBoxId(1234)).await.unwrap();
445 /// # }
446 /// ```
447 pub async fn disable_storagebox_external_reachability(
448 &self,
449 id: StorageBoxId,
450 ) -> Result<StorageBox, Error> {
451 Ok(self
452 .go(toggle_service(id, "external_reachability", false))
453 .await?
454 .0)
455 }
456
457 /// Enable snapshot directory visibility for the storagebox.
458 ///
459 /// When enabled, mounts a directory containing storage box snapshots
460 /// in /.zfs or /home/.zfs depending on access method.
461 ///
462 /// Read more at: <https://docs.hetzner.com/robot/storage-box/snapshots/>
463 ///
464 /// # Example
465 /// ```rust,no_run
466 /// # use hrobot::api::storagebox::StorageBoxId;
467 /// # #[tokio::main]
468 /// # async fn main() {
469 /// # let _ = dotenvy::dotenv().ok();
470 /// let robot = hrobot::AsyncRobot::default();
471 /// robot.enable_storagebox_snapshot_directory(StorageBoxId(1234)).await.unwrap();
472 /// # }
473 /// ```
474 pub async fn enable_storagebox_snapshot_directory(
475 &self,
476 id: StorageBoxId,
477 ) -> Result<StorageBox, Error> {
478 Ok(self.go(toggle_service(id, "zfs", true)).await?.0)
479 }
480
481 /// Disable snapshot directory visibility for the storagebox.
482 ///
483 /// When enabled, mounts a directory containing storage box snapshots
484 /// in /.zfs or /home/.zfs depending on access method.
485 ///
486 /// Read more at: <https://docs.hetzner.com/robot/storage-box/snapshots/>
487 ///
488 /// # Example
489 /// ```rust,no_run
490 /// # use hrobot::api::storagebox::StorageBoxId;
491 /// # #[tokio::main]
492 /// # async fn main() {
493 /// # let _ = dotenvy::dotenv().ok();
494 /// let robot = hrobot::AsyncRobot::default();
495 /// robot.disable_storagebox_snapshot_directory(StorageBoxId(1234)).await.unwrap();
496 /// # }
497 /// ```
498 pub async fn disable_storagebox_snapshot_directory(
499 &self,
500 id: StorageBoxId,
501 ) -> Result<StorageBox, Error> {
502 Ok(self.go(toggle_service(id, "zfs", false)).await?.0)
503 }
504
505 /// Reset storagebox password, returning the new password.
506 ///
507 /// # Example
508 /// ```rust,no_run
509 /// # use hrobot::api::storagebox::StorageBoxId;
510 /// # #[tokio::main]
511 /// # async fn main() {
512 /// # let _ = dotenvy::dotenv().ok();
513 /// let robot = hrobot::AsyncRobot::default();
514 /// robot.reset_storagebox_password(StorageBoxId(1234)).await.unwrap();
515 /// # }
516 /// ```
517 pub async fn reset_storagebox_password(&self, id: StorageBoxId) -> Result<String, Error> {
518 Ok(self.go(reset_password(id)).await?.0)
519 }
520
521 /// List snapshots for storagebox.
522 ///
523 /// # Example
524 /// ```rust,no_run
525 /// # use hrobot::api::storagebox::StorageBoxId;
526 /// # #[tokio::main]
527 /// # async fn main() {
528 /// # let _ = dotenvy::dotenv().ok();
529 /// let robot = hrobot::AsyncRobot::default();
530 /// robot.list_snapshots(StorageBoxId(1234)).await.unwrap();
531 /// # }
532 /// ```
533 pub async fn list_snapshots(&self, id: StorageBoxId) -> Result<Vec<Snapshot>, Error> {
534 Ok(self.go(list_snapshots(id)).await?.0)
535 }
536
537 /// Create a new snapshot of the storagebox.
538 ///
539 /// # Example
540 /// ```rust,no_run
541 /// # use hrobot::api::storagebox::StorageBoxId;
542 /// # #[tokio::main]
543 /// # async fn main() {
544 /// # let _ = dotenvy::dotenv().ok();
545 /// let robot = hrobot::AsyncRobot::default();
546 /// robot.create_snapshot(StorageBoxId(1234)).await.unwrap();
547 /// # }
548 /// ```
549 pub async fn create_snapshot(&self, id: StorageBoxId) -> Result<CreatedSnapshot, Error> {
550 Ok(self.go(create_snapshot(id)).await?.0)
551 }
552
553 /// Delete a snapshot of the storagebox.
554 ///
555 /// Snapshots are named after the timestamp at which they are created
556 /// with an implicit timezone of UTC. The safest way to target a snapshot
557 /// for deletion, is to first retrieve it, and use its name from there.
558 ///
559 /// If you otherwise know the timestamp, but not the name of the snapshot,
560 /// you can format it as "YYYY-MM-DDThh-mm-ss", with an assumed UTC timezone.
561 ///
562 /// # Example
563 /// ```rust,no_run
564 /// # use hrobot::api::storagebox::StorageBoxId;
565 /// # #[tokio::main]
566 /// # async fn main() {
567 /// # let _ = dotenvy::dotenv().ok();
568 /// let robot = hrobot::AsyncRobot::default();
569 /// robot.delete_snapshot(
570 /// StorageBoxId(1234),
571 /// "2015-12-21T13-13-03"
572 /// ).await.unwrap();
573 /// # }
574 /// ```
575 pub async fn delete_snapshot(
576 &self,
577 id: StorageBoxId,
578 snapshot_name: &str,
579 ) -> Result<(), Error> {
580 self.go(delete_snapshot(id, snapshot_name))
581 .await?
582 .throw_away();
583 Ok(())
584 }
585
586 /// Revert storagebox to a snapshot.
587 ///
588 /// # Example
589 /// ```rust,no_run
590 /// # use hrobot::api::storagebox::StorageBoxId;
591 /// # #[tokio::main]
592 /// # async fn main() {
593 /// # let _ = dotenvy::dotenv().ok();
594 /// let robot = hrobot::AsyncRobot::default();
595 /// robot.revert_to_snapshot(
596 /// StorageBoxId(1234),
597 /// "2015-12-21T13-13-03"
598 /// ).await.unwrap();
599 /// # }
600 /// ```
601 pub async fn revert_to_snapshot(
602 &self,
603 id: StorageBoxId,
604 snapshot_name: &str,
605 ) -> Result<(), Error> {
606 self.go(revert_to_snapshot(id, snapshot_name))
607 .await?
608 .throw_away();
609 Ok(())
610 }
611
612 /// Change snapshot comment.
613 ///
614 /// # Example
615 /// ```rust,no_run
616 /// # use hrobot::api::storagebox::StorageBoxId;
617 /// # #[tokio::main]
618 /// # async fn main() {
619 /// # let _ = dotenvy::dotenv().ok();
620 /// let robot = hrobot::AsyncRobot::default();
621 /// robot.change_snapshot_comment(
622 /// StorageBoxId(1234),
623 /// "2015-12-21T13-13-03",
624 /// "Last backup before upgrade to 2.0"
625 /// ).await.unwrap();
626 /// # }
627 /// ```
628 pub async fn change_snapshot_comment(
629 &self,
630 id: StorageBoxId,
631 snapshot_name: &str,
632 comment: &str,
633 ) -> Result<(), Error> {
634 self.go(change_snapshot_comment(id, snapshot_name, comment)?)
635 .await?
636 .throw_away();
637 Ok(())
638 }
639
640 /// Update snapshot plan for storagebox
641 ///
642 /// # Example
643 /// ```rust,no_run
644 /// # use hrobot::api::storagebox::StorageBoxId;
645 /// # #[tokio::main]
646 /// # async fn main() {
647 /// # let _ = dotenvy::dotenv().ok();
648 /// let robot = hrobot::AsyncRobot::default();
649 /// robot.get_snapshot_plan(StorageBoxId(1234)).await.unwrap();
650 /// # }
651 /// ```
652 pub async fn get_snapshot_plan(&self, id: StorageBoxId) -> Result<SnapshotPlan, Error> {
653 Ok(self.go(get_snapshot_plan(id)).await?.0)
654 }
655
656 /// Update snapshot plan.
657 ///
658 /// # Example
659 /// ```rust,no_run
660 /// # use hrobot::api::storagebox::{StorageBoxId, SnapshotPlan};
661 /// # use hrobot::time::Weekday;
662 /// # #[tokio::main]
663 /// # async fn main() {
664 /// # let _ = dotenvy::dotenv().ok();
665 /// let robot = hrobot::AsyncRobot::default();
666 /// robot.update_snapshot_plan(
667 /// StorageBoxId(1234),
668 /// SnapshotPlan::weekly(Weekday::Monday, 10, 0)
669 /// ).await.unwrap();
670 /// # }
671 /// ```
672 pub async fn update_snapshot_plan(
673 &self,
674 id: StorageBoxId,
675 plan: SnapshotPlan,
676 ) -> Result<SnapshotPlan, Error> {
677 Ok(self.go(update_snapshot_plan(id, plan)?).await?.0)
678 }
679
680 /// List sub-accounts for storagebox.
681 ///
682 /// # Example
683 /// ```rust,no_run
684 /// # use hrobot::api::storagebox::StorageBoxId;
685 /// # #[tokio::main]
686 /// # async fn main() {
687 /// # let _ = dotenvy::dotenv().ok();
688 /// let robot = hrobot::AsyncRobot::default();
689 /// robot.list_subaccounts(StorageBoxId(1234)).await.unwrap();
690 /// # }
691 /// ```
692 pub async fn list_subaccounts(&self, id: StorageBoxId) -> Result<Vec<Subaccount>, Error> {
693 Ok(self.go(list_subaccounts(id)).await?.0)
694 }
695
696 /// Create sub-account.
697 ///
698 /// # Example
699 /// ```rust,no_run
700 /// # use hrobot::api::storagebox::{StorageBoxId, SubaccountId, Permission, Accessibility};
701 /// # #[tokio::main]
702 /// # async fn main() {
703 /// # let _ = dotenvy::dotenv().ok();
704 /// let robot = hrobot::AsyncRobot::default();
705 /// robot.create_subaccount(
706 /// StorageBoxId(1234),
707 /// "/home/test-user",
708 /// Accessibility::default(), // default disables all access.
709 /// Permission::ReadOnly,
710 /// None
711 /// ).await.unwrap();
712 /// # }
713 /// ```
714 pub async fn create_subaccount(
715 &self,
716 storagebox: StorageBoxId,
717 home_directory: &str,
718 accessibility: Accessibility,
719 permissions: Permission,
720 comment: Option<&str>,
721 ) -> Result<CreatedSubaccount, Error> {
722 Ok(self
723 .go(create_subaccount(
724 storagebox,
725 home_directory,
726 accessibility,
727 permissions,
728 comment,
729 )?)
730 .await?
731 .0)
732 }
733
734 /// Change home directory of storagebox sub-account
735 ///
736 /// # Example
737 /// ```rust,no_run
738 /// # use hrobot::api::storagebox::{StorageBoxId, SubaccountId, Accessibility};
739 /// # #[tokio::main]
740 /// # async fn main() {
741 /// # let _ = dotenvy::dotenv().ok();
742 /// let robot = hrobot::AsyncRobot::default();
743 /// robot.set_subaccount_home_directory(
744 /// StorageBoxId(1234),
745 /// &SubaccountId("u1234-sub1".to_string()),
746 /// "/homedirs/sub1"
747 /// ).await.unwrap();
748 /// # }
749 /// ```
750 pub async fn set_subaccount_home_directory(
751 &self,
752 storagebox: StorageBoxId,
753 subaccount: &SubaccountId,
754 home_directory: &str,
755 ) -> Result<(), Error> {
756 self.go(update_subaccount(
757 storagebox,
758 subaccount,
759 Some(home_directory),
760 None,
761 None,
762 None,
763 )?)
764 .await?
765 .throw_away();
766 Ok(())
767 }
768
769 /// Change sub-account comment/description.
770 ///
771 /// # Example
772 /// ```rust,no_run
773 /// # use hrobot::api::storagebox::{StorageBoxId, SubaccountId, Permission};
774 /// # #[tokio::main]
775 /// # async fn main() {
776 /// # let _ = dotenvy::dotenv().ok();
777 /// let robot = hrobot::AsyncRobot::default();
778 /// robot.update_subaccount(
779 /// StorageBoxId(1234),
780 /// &SubaccountId("u1234-sub1".to_string()),
781 /// "/new/home/dir",
782 /// None, // Keep old accessibility options
783 /// Some(Permission::ReadWrite),
784 /// Some("Sub-account used for accessing backups")
785 /// ).await.unwrap();
786 /// # }
787 /// ```
788 pub async fn update_subaccount(
789 &self,
790 storagebox: StorageBoxId,
791 subaccount: &SubaccountId,
792 home_directory: &str,
793 accessibility: Option<&Accessibility>,
794 permissions: Option<Permission>,
795 comment: Option<&str>,
796 ) -> Result<(), Error> {
797 self.go(update_subaccount(
798 storagebox,
799 subaccount,
800 Some(home_directory),
801 accessibility,
802 permissions,
803 comment,
804 )?)
805 .await?
806 .throw_away();
807 Ok(())
808 }
809
810 /// Reset sub-account password.
811 ///
812 /// # Example
813 /// ```rust,no_run
814 /// # use hrobot::api::storagebox::{StorageBoxId, SubaccountId};
815 /// # #[tokio::main]
816 /// # async fn main() {
817 /// # let _ = dotenvy::dotenv().ok();
818 /// let robot = hrobot::AsyncRobot::default();
819 /// let password = robot.reset_subaccount_password(
820 /// StorageBoxId(1234),
821 /// &SubaccountId("u1234-sub1".to_string()),
822 /// ).await.unwrap();
823 ///
824 /// println!("new password: {password}");
825 /// # }
826 /// ```
827 pub async fn reset_subaccount_password(
828 &self,
829 storagebox: StorageBoxId,
830 subaccount: &SubaccountId,
831 ) -> Result<String, Error> {
832 Ok(self
833 .go(reset_subaccount_password(storagebox, subaccount))
834 .await?
835 .0)
836 }
837
838 /// Delete sub-account.
839 ///
840 /// # Example
841 /// ```rust,no_run
842 /// # use hrobot::api::storagebox::{StorageBoxId, SubaccountId};
843 /// # #[tokio::main]
844 /// # async fn main() {
845 /// # let _ = dotenvy::dotenv().ok();
846 /// let robot = hrobot::AsyncRobot::default();
847 /// robot.delete_subaccount(
848 /// StorageBoxId(1234),
849 /// SubaccountId("u1234-sub1".to_string()),
850 /// ).await.unwrap();
851 /// # }
852 /// ```
853 pub async fn delete_subaccount(
854 &self,
855 storagebox: StorageBoxId,
856 subaccount: SubaccountId,
857 ) -> Result<(), Error> {
858 self.go(delete_subaccount(storagebox, subaccount))
859 .await?
860 .throw_away();
861 Ok(())
862 }
863}