1use std::collections::HashMap;
7
8use serde::{Deserialize, Serialize};
9
10use crate::error::Result;
11use crate::DakeraClient;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct OpsStats {
20 pub version: String,
21 pub total_vectors: u64,
22 pub namespace_count: u64,
23 pub uptime_seconds: u64,
24 pub timestamp: u64,
25 pub state: String,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct ClusterStatus {
31 pub cluster_id: String,
32 pub state: String,
33 pub node_count: u32,
34 pub total_vectors: u64,
35 pub namespace_count: u64,
36 pub version: String,
37 pub timestamp: u64,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct NodeInfo {
43 pub node_id: String,
44 pub address: String,
45 pub role: String,
46 pub status: String,
47 pub version: String,
48 pub uptime_seconds: u64,
49 pub vector_count: u64,
50 pub memory_bytes: u64,
51 #[serde(default)]
52 pub cpu_percent: f32,
53 #[serde(default)]
54 pub memory_percent: f32,
55 pub last_heartbeat: u64,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct NodeListResponse {
61 pub nodes: Vec<NodeInfo>,
62 pub total: u32,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct IndexStats {
72 pub index_type: String,
73 pub is_built: bool,
74 pub size_bytes: u64,
75 pub indexed_vectors: u64,
76 #[serde(skip_serializing_if = "Option::is_none")]
77 pub last_rebuild: Option<u64>,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct NamespaceAdminInfo {
83 pub name: String,
84 pub vector_count: u64,
85 #[serde(skip_serializing_if = "Option::is_none")]
86 pub dimension: Option<usize>,
87 pub index_type: String,
88 pub storage_bytes: u64,
89 pub document_count: u64,
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub created_at: Option<u64>,
92 #[serde(skip_serializing_if = "Option::is_none")]
93 pub updated_at: Option<u64>,
94 pub index_stats: IndexStats,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct NamespaceListResponse {
100 pub namespaces: Vec<NamespaceAdminInfo>,
101 pub total: u64,
102 pub total_vectors: u64,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct OptimizeRequest {
108 #[serde(default)]
109 pub force: bool,
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub target_index_type: Option<String>,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct OptimizeResponse {
117 pub success: bool,
118 #[serde(skip_serializing_if = "Option::is_none")]
119 pub job_id: Option<String>,
120 pub message: String,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct IndexStatsResponse {
130 pub namespaces: HashMap<String, IndexStats>,
131 pub total_indexed_vectors: u64,
132 pub total_size_bytes: u64,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct RebuildIndexRequest {
138 #[serde(skip_serializing_if = "Option::is_none")]
139 pub namespace: Option<String>,
140 #[serde(skip_serializing_if = "Option::is_none")]
141 pub index_type: Option<String>,
142 #[serde(default)]
143 pub force: bool,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct RebuildIndexResponse {
149 pub success: bool,
150 pub job_id: String,
151 pub message: String,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct CacheStats {
161 pub enabled: bool,
162 pub cache_type: String,
163 pub entries: u64,
164 pub size_bytes: u64,
165 pub hits: u64,
166 pub misses: u64,
167 pub hit_rate: f64,
168 pub evictions: u64,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct ClearCacheRequest {
174 #[serde(skip_serializing_if = "Option::is_none")]
175 pub namespace: Option<String>,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct ClearCacheResponse {
181 pub success: bool,
182 pub entries_cleared: u64,
183 pub message: String,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct RuntimeConfig {
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub max_vectors_per_namespace: Option<u64>,
195 pub default_index_type: String,
196 pub cache_enabled: bool,
197 pub cache_max_size_bytes: u64,
198 pub rate_limit_enabled: bool,
199 pub rate_limit_rps: u32,
200 pub query_timeout_ms: u64,
201 #[serde(default = "default_true")]
203 pub autopilot_enabled: bool,
204 #[serde(default = "default_dedup_threshold")]
206 pub autopilot_dedup_threshold: f32,
207 #[serde(default = "default_dedup_interval")]
209 pub autopilot_dedup_interval_hours: u64,
210 #[serde(default = "default_consolidation_interval")]
212 pub autopilot_consolidation_interval_hours: u64,
213}
214
215fn default_true() -> bool {
216 true
217}
218fn default_dedup_threshold() -> f32 {
219 0.93
220}
221fn default_dedup_interval() -> u64 {
222 6
223}
224fn default_consolidation_interval() -> u64 {
225 12
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct UpdateConfigResponse {
231 pub success: bool,
232 pub config: RuntimeConfig,
233 pub message: String,
234 #[serde(default, skip_serializing_if = "Vec::is_empty")]
235 pub warnings: Vec<String>,
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize)]
244pub struct QuotaConfig {
245 #[serde(skip_serializing_if = "Option::is_none")]
246 pub max_vectors: Option<u64>,
247 #[serde(skip_serializing_if = "Option::is_none")]
248 pub max_storage_bytes: Option<u64>,
249 #[serde(skip_serializing_if = "Option::is_none")]
250 pub max_queries_per_minute: Option<u64>,
251 #[serde(skip_serializing_if = "Option::is_none")]
252 pub max_writes_per_minute: Option<u64>,
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct QuotaUsage {
258 #[serde(default)]
259 pub current_vectors: u64,
260 #[serde(default)]
261 pub current_storage_bytes: u64,
262 #[serde(default)]
263 pub queries_this_minute: u64,
264 #[serde(default)]
265 pub writes_this_minute: u64,
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct QuotaStatus {
271 pub namespace: String,
272 pub config: QuotaConfig,
273 pub usage: QuotaUsage,
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct QuotaListResponse {
279 pub quotas: Vec<QuotaStatus>,
280 pub total: u64,
281 #[serde(skip_serializing_if = "Option::is_none")]
282 pub default_config: Option<QuotaConfig>,
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct SlowQueryEntry {
292 pub id: String,
293 pub timestamp: u64,
294 pub namespace: String,
295 pub query_type: String,
296 pub duration_ms: f64,
297 #[serde(default)]
298 pub parameters: Option<serde_json::Value>,
299 #[serde(default)]
300 pub results_count: u64,
301 #[serde(default)]
302 pub vectors_scanned: u64,
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize)]
307pub struct SlowQueryListResponse {
308 pub queries: Vec<SlowQueryEntry>,
309 pub total: u64,
310 pub threshold_ms: f64,
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize)]
319pub struct BackupInfo {
320 pub backup_id: String,
321 pub name: String,
322 pub backup_type: String,
323 pub status: String,
324 pub namespaces: Vec<String>,
325 pub vector_count: u64,
326 pub size_bytes: u64,
327 pub created_at: u64,
328 #[serde(skip_serializing_if = "Option::is_none")]
329 pub completed_at: Option<u64>,
330 #[serde(skip_serializing_if = "Option::is_none")]
331 pub duration_seconds: Option<u64>,
332 #[serde(skip_serializing_if = "Option::is_none")]
333 pub storage_path: Option<String>,
334 #[serde(skip_serializing_if = "Option::is_none")]
335 pub error: Option<String>,
336 pub encrypted: bool,
337 #[serde(skip_serializing_if = "Option::is_none")]
338 pub compression: Option<String>,
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct BackupListResponse {
344 pub backups: Vec<BackupInfo>,
345 pub total: u64,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
350pub struct CreateBackupRequest {
351 pub name: String,
352 #[serde(skip_serializing_if = "Option::is_none")]
353 pub backup_type: Option<String>,
354 #[serde(skip_serializing_if = "Option::is_none")]
355 pub namespaces: Option<Vec<String>>,
356 #[serde(skip_serializing_if = "Option::is_none")]
357 pub encrypt: Option<bool>,
358 #[serde(skip_serializing_if = "Option::is_none")]
359 pub compression: Option<String>,
360}
361
362#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct CreateBackupResponse {
365 pub backup: BackupInfo,
366 #[serde(skip_serializing_if = "Option::is_none")]
367 pub estimated_completion: Option<u64>,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
372pub struct RestoreBackupRequest {
373 pub backup_id: String,
374 #[serde(skip_serializing_if = "Option::is_none")]
375 pub target_namespaces: Option<Vec<String>>,
376 #[serde(skip_serializing_if = "Option::is_none")]
377 pub overwrite: Option<bool>,
378 #[serde(skip_serializing_if = "Option::is_none")]
379 pub point_in_time: Option<u64>,
380}
381
382#[derive(Debug, Clone, Serialize, Deserialize)]
384pub struct RestoreBackupResponse {
385 pub restore_id: String,
386 pub status: String,
387 pub backup_id: String,
388 pub namespaces: Vec<String>,
389 pub started_at: u64,
390 #[serde(skip_serializing_if = "Option::is_none")]
391 pub estimated_completion: Option<u64>,
392 #[serde(skip_serializing_if = "Option::is_none")]
393 pub progress_percent: Option<u8>,
394 #[serde(skip_serializing_if = "Option::is_none")]
395 pub vectors_restored: Option<u64>,
396 #[serde(skip_serializing_if = "Option::is_none")]
397 pub completed_at: Option<u64>,
398 #[serde(skip_serializing_if = "Option::is_none")]
399 pub duration_seconds: Option<u64>,
400 #[serde(skip_serializing_if = "Option::is_none")]
401 pub error: Option<String>,
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize)]
410pub struct AutoPilotConfig {
411 pub enabled: bool,
412 pub dedup_threshold: f32,
413 pub dedup_interval_hours: u64,
414 pub consolidation_interval_hours: u64,
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize)]
419pub struct DedupResultSnapshot {
420 pub namespaces_processed: usize,
421 pub memories_scanned: usize,
422 pub duplicates_removed: usize,
423}
424
425#[derive(Debug, Clone, Serialize, Deserialize)]
427pub struct ConsolidationResultSnapshot {
428 pub namespaces_processed: usize,
429 pub memories_scanned: usize,
430 pub clusters_merged: usize,
431 pub memories_consolidated: usize,
432}
433
434#[derive(Debug, Clone, Serialize, Deserialize)]
436pub struct AutoPilotStatusResponse {
437 pub config: AutoPilotConfig,
438 #[serde(skip_serializing_if = "Option::is_none")]
439 pub last_dedup_at: Option<u64>,
440 #[serde(skip_serializing_if = "Option::is_none")]
441 pub last_consolidation_at: Option<u64>,
442 #[serde(skip_serializing_if = "Option::is_none")]
443 pub last_dedup: Option<DedupResultSnapshot>,
444 #[serde(skip_serializing_if = "Option::is_none")]
445 pub last_consolidation: Option<ConsolidationResultSnapshot>,
446 pub total_dedup_removed: u64,
447 pub total_consolidated: u64,
448}
449
450#[derive(Debug, Clone, Serialize, Deserialize, Default)]
452pub struct AutoPilotConfigRequest {
453 #[serde(skip_serializing_if = "Option::is_none")]
454 pub enabled: Option<bool>,
455 #[serde(skip_serializing_if = "Option::is_none")]
456 pub dedup_threshold: Option<f32>,
457 #[serde(skip_serializing_if = "Option::is_none")]
458 pub dedup_interval_hours: Option<u64>,
459 #[serde(skip_serializing_if = "Option::is_none")]
460 pub consolidation_interval_hours: Option<u64>,
461}
462
463#[derive(Debug, Clone, Serialize, Deserialize)]
465pub struct AutoPilotConfigResponse {
466 pub success: bool,
467 pub config: AutoPilotConfig,
468 pub message: String,
469}
470
471#[derive(Debug, Clone, Serialize, Deserialize)]
473#[serde(rename_all = "lowercase")]
474pub enum AutoPilotTriggerAction {
475 Dedup,
476 Consolidate,
477 All,
478}
479
480#[derive(Debug, Clone, Serialize, Deserialize)]
482pub struct AutoPilotTriggerRequest {
483 pub action: AutoPilotTriggerAction,
484}
485
486#[derive(Debug, Clone, Serialize, Deserialize)]
488pub struct AutoPilotDedupResult {
489 pub namespaces_processed: usize,
490 pub memories_scanned: usize,
491 pub duplicates_removed: usize,
492}
493
494#[derive(Debug, Clone, Serialize, Deserialize)]
496pub struct AutoPilotConsolidationResult {
497 pub namespaces_processed: usize,
498 pub memories_scanned: usize,
499 pub clusters_merged: usize,
500 pub memories_consolidated: usize,
501}
502
503#[derive(Debug, Clone, Serialize, Deserialize)]
505pub struct AutoPilotTriggerResponse {
506 pub success: bool,
507 pub action: AutoPilotTriggerAction,
508 #[serde(skip_serializing_if = "Option::is_none")]
509 pub dedup: Option<AutoPilotDedupResult>,
510 #[serde(skip_serializing_if = "Option::is_none")]
511 pub consolidation: Option<AutoPilotConsolidationResult>,
512 pub message: String,
513}
514
515#[derive(Debug, Clone, Serialize, Deserialize)]
521pub struct DecayConfigResponse {
522 pub strategy: String,
524 pub half_life_hours: f64,
526 pub min_importance: f32,
528}
529
530#[derive(Debug, Clone, Serialize, Deserialize, Default)]
532pub struct DecayConfigUpdateRequest {
533 #[serde(skip_serializing_if = "Option::is_none")]
535 pub strategy: Option<String>,
536 #[serde(skip_serializing_if = "Option::is_none")]
538 pub half_life_hours: Option<f64>,
539 #[serde(skip_serializing_if = "Option::is_none")]
541 pub min_importance: Option<f32>,
542}
543
544#[derive(Debug, Clone, Serialize, Deserialize)]
546pub struct DecayConfigUpdateResponse {
547 pub success: bool,
548 pub config: DecayConfigResponse,
549 pub message: String,
550}
551
552#[derive(Debug, Clone, Serialize, Deserialize)]
554pub struct LastDecayCycleStats {
555 pub namespaces_processed: usize,
556 pub memories_processed: usize,
557 pub memories_decayed: usize,
558 pub memories_deleted: usize,
559}
560
561#[derive(Debug, Clone, Serialize, Deserialize)]
563pub struct DecayStatsResponse {
564 pub total_decayed: u64,
566 pub total_deleted: u64,
568 #[serde(skip_serializing_if = "Option::is_none")]
570 pub last_run_at: Option<u64>,
571 pub cycles_run: u64,
573 #[serde(skip_serializing_if = "Option::is_none")]
575 pub last_cycle: Option<LastDecayCycleStats>,
576}
577
578#[derive(Debug, Clone, Serialize, Deserialize)]
584pub struct TtlCleanupRequest {
585 #[serde(skip_serializing_if = "Option::is_none")]
586 pub namespace: Option<String>,
587}
588
589#[derive(Debug, Clone, Serialize, Deserialize)]
591pub struct TtlCleanupResponse {
592 pub success: bool,
593 pub vectors_removed: u64,
594 pub namespaces_cleaned: Vec<String>,
595 pub message: String,
596}
597
598#[derive(Debug, Clone, Serialize, Deserialize)]
600pub struct TtlStats {
601 pub namespace: String,
602 pub vectors_with_ttl: u64,
603 pub expiring_within_hour: u64,
604 pub expiring_within_day: u64,
605 pub expired_pending_cleanup: u64,
606}
607
608#[derive(Debug, Clone, Serialize, Deserialize)]
610pub struct TtlStatsResponse {
611 pub namespaces: Vec<TtlStats>,
612 pub total_with_ttl: u64,
613 pub total_expired: u64,
614}
615
616impl DakeraClient {
621 pub async fn ops_stats(&self) -> Result<OpsStats> {
629 let url = format!("{}/v1/ops/stats", self.base_url);
630 let response = self.client.get(&url).send().await?;
631 self.handle_response(response).await
632 }
633
634 pub async fn ops_metrics(&self) -> Result<String> {
639 let url = format!("{}/v1/ops/metrics", self.base_url);
640 let response = self.client.get(&url).send().await?;
641 self.handle_text_response(response).await
642 }
643
644 pub async fn cluster_status(&self) -> Result<ClusterStatus> {
646 let url = format!("{}/admin/cluster/status", self.base_url);
647 let response = self.client.get(&url).send().await?;
648 self.handle_response(response).await
649 }
650
651 pub async fn cluster_nodes(&self) -> Result<NodeListResponse> {
653 let url = format!("{}/admin/cluster/nodes", self.base_url);
654 let response = self.client.get(&url).send().await?;
655 self.handle_response(response).await
656 }
657
658 pub async fn list_namespaces_admin(&self) -> Result<NamespaceListResponse> {
664 let url = format!("{}/admin/namespaces", self.base_url);
665 let response = self.client.get(&url).send().await?;
666 self.handle_response(response).await
667 }
668
669 pub async fn delete_namespace_admin(&self, namespace: &str) -> Result<serde_json::Value> {
671 let url = format!("{}/admin/namespaces/{}", self.base_url, namespace);
672 let response = self.client.delete(&url).send().await?;
673 self.handle_response(response).await
674 }
675
676 pub async fn optimize_namespace(
678 &self,
679 namespace: &str,
680 request: OptimizeRequest,
681 ) -> Result<OptimizeResponse> {
682 let url = format!("{}/admin/namespaces/{}/optimize", self.base_url, namespace);
683 let response = self.client.post(&url).json(&request).send().await?;
684 self.handle_response(response).await
685 }
686
687 pub async fn index_stats(&self) -> Result<IndexStatsResponse> {
693 let url = format!("{}/admin/indexes/stats", self.base_url);
694 let response = self.client.get(&url).send().await?;
695 self.handle_response(response).await
696 }
697
698 pub async fn rebuild_indexes(
700 &self,
701 request: RebuildIndexRequest,
702 ) -> Result<RebuildIndexResponse> {
703 let url = format!("{}/admin/indexes/rebuild", self.base_url);
704 let response = self.client.post(&url).json(&request).send().await?;
705 self.handle_response(response).await
706 }
707
708 pub async fn cache_stats(&self) -> Result<CacheStats> {
714 let url = format!("{}/admin/cache/stats", self.base_url);
715 let response = self.client.get(&url).send().await?;
716 self.handle_response(response).await
717 }
718
719 pub async fn cache_clear(&self, namespace: Option<&str>) -> Result<ClearCacheResponse> {
721 let url = format!("{}/admin/cache/clear", self.base_url);
722 let request = ClearCacheRequest {
723 namespace: namespace.map(|s| s.to_string()),
724 };
725 let response = self.client.post(&url).json(&request).send().await?;
726 self.handle_response(response).await
727 }
728
729 pub async fn get_config(&self) -> Result<RuntimeConfig> {
735 let url = format!("{}/admin/config", self.base_url);
736 let response = self.client.get(&url).send().await?;
737 self.handle_response(response).await
738 }
739
740 pub async fn update_config(
742 &self,
743 updates: HashMap<String, serde_json::Value>,
744 ) -> Result<UpdateConfigResponse> {
745 let url = format!("{}/admin/config", self.base_url);
746 let response = self.client.put(&url).json(&updates).send().await?;
747 self.handle_response(response).await
748 }
749
750 pub async fn get_quotas(&self) -> Result<QuotaListResponse> {
756 let url = format!("{}/admin/quotas", self.base_url);
757 let response = self.client.get(&url).send().await?;
758 self.handle_response(response).await
759 }
760
761 pub async fn get_quota(&self, namespace: &str) -> Result<QuotaStatus> {
763 let url = format!("{}/admin/quotas/{}", self.base_url, namespace);
764 let response = self.client.get(&url).send().await?;
765 self.handle_response(response).await
766 }
767
768 pub async fn set_quota(
770 &self,
771 namespace: &str,
772 config: QuotaConfig,
773 ) -> Result<serde_json::Value> {
774 let url = format!("{}/admin/quotas/{}", self.base_url, namespace);
775 let request = serde_json::json!({ "config": config });
776 let response = self.client.put(&url).json(&request).send().await?;
777 self.handle_response(response).await
778 }
779
780 pub async fn delete_quota(&self, namespace: &str) -> Result<serde_json::Value> {
782 let url = format!("{}/admin/quotas/{}", self.base_url, namespace);
783 let response = self.client.delete(&url).send().await?;
784 self.handle_response(response).await
785 }
786
787 pub async fn update_quotas(&self, config: Option<QuotaConfig>) -> Result<serde_json::Value> {
789 let url = format!("{}/admin/quotas/default", self.base_url);
790 let request = serde_json::json!({ "config": config });
791 let response = self.client.put(&url).json(&request).send().await?;
792 self.handle_response(response).await
793 }
794
795 pub async fn slow_queries(
801 &self,
802 limit: Option<usize>,
803 namespace: Option<&str>,
804 query_type: Option<&str>,
805 ) -> Result<SlowQueryListResponse> {
806 let mut url = format!("{}/admin/slow-queries", self.base_url);
807 let mut params = Vec::new();
808 if let Some(l) = limit {
809 params.push(format!("limit={}", l));
810 }
811 if let Some(ns) = namespace {
812 params.push(format!("namespace={}", ns));
813 }
814 if let Some(qt) = query_type {
815 params.push(format!("query_type={}", qt));
816 }
817 if !params.is_empty() {
818 url.push('?');
819 url.push_str(¶ms.join("&"));
820 }
821 let response = self.client.get(&url).send().await?;
822 self.handle_response(response).await
823 }
824
825 pub async fn slow_query_summary(&self) -> Result<serde_json::Value> {
827 let url = format!("{}/admin/slow-queries/summary", self.base_url);
828 let response = self.client.get(&url).send().await?;
829 self.handle_response(response).await
830 }
831
832 pub async fn clear_slow_queries(&self) -> Result<serde_json::Value> {
834 let url = format!("{}/admin/slow-queries", self.base_url);
835 let response = self.client.delete(&url).send().await?;
836 self.handle_response(response).await
837 }
838
839 pub async fn create_backup(
845 &self,
846 request: CreateBackupRequest,
847 ) -> Result<CreateBackupResponse> {
848 let url = format!("{}/admin/backups", self.base_url);
849 let response = self.client.post(&url).json(&request).send().await?;
850 self.handle_response(response).await
851 }
852
853 pub async fn list_backups(&self) -> Result<BackupListResponse> {
855 let url = format!("{}/admin/backups", self.base_url);
856 let response = self.client.get(&url).send().await?;
857 self.handle_response(response).await
858 }
859
860 pub async fn get_backup(&self, backup_id: &str) -> Result<BackupInfo> {
862 let url = format!("{}/admin/backups/{}", self.base_url, backup_id);
863 let response = self.client.get(&url).send().await?;
864 self.handle_response(response).await
865 }
866
867 pub async fn restore_backup(
869 &self,
870 request: RestoreBackupRequest,
871 ) -> Result<RestoreBackupResponse> {
872 let url = format!("{}/admin/backups/restore", self.base_url);
873 let response = self.client.post(&url).json(&request).send().await?;
874 self.handle_response(response).await
875 }
876
877 pub async fn delete_backup(&self, backup_id: &str) -> Result<serde_json::Value> {
879 let url = format!("{}/admin/backups/{}", self.base_url, backup_id);
880 let response = self.client.delete(&url).send().await?;
881 self.handle_response(response).await
882 }
883
884 pub async fn ttl_cleanup(&self, namespace: Option<&str>) -> Result<TtlCleanupResponse> {
890 let url = format!("{}/admin/ttl/cleanup", self.base_url);
891 let request = TtlCleanupRequest {
892 namespace: namespace.map(|s| s.to_string()),
893 };
894 let response = self.client.post(&url).json(&request).send().await?;
895 self.handle_response(response).await
896 }
897
898 pub async fn ttl_stats(&self) -> Result<TtlStatsResponse> {
900 let url = format!("{}/admin/ttl/stats", self.base_url);
901 let response = self.client.get(&url).send().await?;
902 self.handle_response(response).await
903 }
904
905 pub async fn autopilot_status(&self) -> Result<AutoPilotStatusResponse> {
911 let url = format!("{}/admin/autopilot/status", self.base_url);
912 let response = self.client.get(&url).send().await?;
913 self.handle_response(response).await
914 }
915
916 pub async fn autopilot_update_config(
920 &self,
921 request: AutoPilotConfigRequest,
922 ) -> Result<AutoPilotConfigResponse> {
923 let url = format!("{}/admin/autopilot/config", self.base_url);
924 let response = self.client.put(&url).json(&request).send().await?;
925 self.handle_response(response).await
926 }
927
928 pub async fn autopilot_trigger(
933 &self,
934 action: AutoPilotTriggerAction,
935 ) -> Result<AutoPilotTriggerResponse> {
936 let url = format!("{}/admin/autopilot/trigger", self.base_url);
937 let request = AutoPilotTriggerRequest { action };
938 let response = self.client.post(&url).json(&request).send().await?;
939 self.handle_response(response).await
940 }
941
942 pub async fn decay_config(&self) -> Result<DecayConfigResponse> {
951 let url = format!("{}/admin/decay/config", self.base_url);
952 let response = self.client.get(&url).send().await?;
953 self.handle_response(response).await
954 }
955
956 pub async fn decay_update_config(
962 &self,
963 request: DecayConfigUpdateRequest,
964 ) -> Result<DecayConfigUpdateResponse> {
965 let url = format!("{}/admin/decay/config", self.base_url);
966 let response = self.client.put(&url).json(&request).send().await?;
967 self.handle_response(response).await
968 }
969
970 pub async fn decay_stats(&self) -> Result<DecayStatsResponse> {
975 let url = format!("{}/admin/decay/stats", self.base_url);
976 let response = self.client.get(&url).send().await?;
977 self.handle_response(response).await
978 }
979}