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}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct ClusterStatus {
30 pub cluster_id: String,
31 pub state: String,
32 pub node_count: u32,
33 pub total_vectors: u64,
34 pub namespace_count: u64,
35 pub version: String,
36 pub timestamp: u64,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct NodeInfo {
42 pub node_id: String,
43 pub address: String,
44 pub role: String,
45 pub status: String,
46 pub version: String,
47 pub uptime_seconds: u64,
48 pub vector_count: u64,
49 pub memory_bytes: u64,
50 #[serde(default)]
51 pub cpu_percent: f32,
52 #[serde(default)]
53 pub memory_percent: f32,
54 pub last_heartbeat: u64,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct NodeListResponse {
60 pub nodes: Vec<NodeInfo>,
61 pub total: u32,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct IndexStats {
71 pub index_type: String,
72 pub is_built: bool,
73 pub size_bytes: u64,
74 pub indexed_vectors: u64,
75 #[serde(skip_serializing_if = "Option::is_none")]
76 pub last_rebuild: Option<u64>,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct NamespaceAdminInfo {
82 pub name: String,
83 pub vector_count: u64,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub dimension: Option<usize>,
86 pub index_type: String,
87 pub storage_bytes: u64,
88 pub document_count: u64,
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub created_at: Option<u64>,
91 #[serde(skip_serializing_if = "Option::is_none")]
92 pub updated_at: Option<u64>,
93 pub index_stats: IndexStats,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct NamespaceListResponse {
99 pub namespaces: Vec<NamespaceAdminInfo>,
100 pub total: u64,
101 pub total_vectors: u64,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct OptimizeRequest {
107 #[serde(default)]
108 pub force: bool,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub target_index_type: Option<String>,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct OptimizeResponse {
116 pub success: bool,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub job_id: Option<String>,
119 pub message: String,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct IndexStatsResponse {
129 pub namespaces: HashMap<String, IndexStats>,
130 pub total_indexed_vectors: u64,
131 pub total_size_bytes: u64,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct RebuildIndexRequest {
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub namespace: Option<String>,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub index_type: Option<String>,
141 #[serde(default)]
142 pub force: bool,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct RebuildIndexResponse {
148 pub success: bool,
149 pub job_id: String,
150 pub message: String,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct CacheStats {
160 pub enabled: bool,
161 pub cache_type: String,
162 pub entries: u64,
163 pub size_bytes: u64,
164 pub hits: u64,
165 pub misses: u64,
166 pub hit_rate: f64,
167 pub evictions: u64,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct ClearCacheRequest {
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub namespace: Option<String>,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct ClearCacheResponse {
180 pub success: bool,
181 pub entries_cleared: u64,
182 pub message: String,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct RuntimeConfig {
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub max_vectors_per_namespace: Option<u64>,
194 pub default_index_type: String,
195 pub cache_enabled: bool,
196 pub cache_max_size_bytes: u64,
197 pub rate_limit_enabled: bool,
198 pub rate_limit_rps: u32,
199 pub query_timeout_ms: u64,
200 #[serde(default = "default_true")]
202 pub autopilot_enabled: bool,
203 #[serde(default = "default_dedup_threshold")]
205 pub autopilot_dedup_threshold: f32,
206 #[serde(default = "default_dedup_interval")]
208 pub autopilot_dedup_interval_hours: u64,
209 #[serde(default = "default_consolidation_interval")]
211 pub autopilot_consolidation_interval_hours: u64,
212}
213
214fn default_true() -> bool {
215 true
216}
217fn default_dedup_threshold() -> f32 {
218 0.93
219}
220fn default_dedup_interval() -> u64 {
221 6
222}
223fn default_consolidation_interval() -> u64 {
224 12
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct UpdateConfigResponse {
230 pub success: bool,
231 pub config: RuntimeConfig,
232 pub message: String,
233 #[serde(default, skip_serializing_if = "Vec::is_empty")]
234 pub warnings: Vec<String>,
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct QuotaConfig {
244 #[serde(skip_serializing_if = "Option::is_none")]
245 pub max_vectors: Option<u64>,
246 #[serde(skip_serializing_if = "Option::is_none")]
247 pub max_storage_bytes: Option<u64>,
248 #[serde(skip_serializing_if = "Option::is_none")]
249 pub max_queries_per_minute: Option<u64>,
250 #[serde(skip_serializing_if = "Option::is_none")]
251 pub max_writes_per_minute: Option<u64>,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct QuotaUsage {
257 #[serde(default)]
258 pub current_vectors: u64,
259 #[serde(default)]
260 pub current_storage_bytes: u64,
261 #[serde(default)]
262 pub queries_this_minute: u64,
263 #[serde(default)]
264 pub writes_this_minute: u64,
265}
266
267#[derive(Debug, Clone, Serialize, Deserialize)]
269pub struct QuotaStatus {
270 pub namespace: String,
271 pub config: QuotaConfig,
272 pub usage: QuotaUsage,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct QuotaListResponse {
278 pub quotas: Vec<QuotaStatus>,
279 pub total: u64,
280 #[serde(skip_serializing_if = "Option::is_none")]
281 pub default_config: Option<QuotaConfig>,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct SlowQueryEntry {
291 pub id: String,
292 pub timestamp: u64,
293 pub namespace: String,
294 pub query_type: String,
295 pub duration_ms: f64,
296 #[serde(default)]
297 pub parameters: Option<serde_json::Value>,
298 #[serde(default)]
299 pub results_count: u64,
300 #[serde(default)]
301 pub vectors_scanned: u64,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct SlowQueryListResponse {
307 pub queries: Vec<SlowQueryEntry>,
308 pub total: u64,
309 pub threshold_ms: f64,
310}
311
312#[derive(Debug, Clone, Serialize, Deserialize)]
318pub struct BackupInfo {
319 pub backup_id: String,
320 pub name: String,
321 pub backup_type: String,
322 pub status: String,
323 pub namespaces: Vec<String>,
324 pub vector_count: u64,
325 pub size_bytes: u64,
326 pub created_at: u64,
327 #[serde(skip_serializing_if = "Option::is_none")]
328 pub completed_at: Option<u64>,
329 #[serde(skip_serializing_if = "Option::is_none")]
330 pub duration_seconds: Option<u64>,
331 #[serde(skip_serializing_if = "Option::is_none")]
332 pub storage_path: Option<String>,
333 #[serde(skip_serializing_if = "Option::is_none")]
334 pub error: Option<String>,
335 pub encrypted: bool,
336 #[serde(skip_serializing_if = "Option::is_none")]
337 pub compression: Option<String>,
338}
339
340#[derive(Debug, Clone, Serialize, Deserialize)]
342pub struct BackupListResponse {
343 pub backups: Vec<BackupInfo>,
344 pub total: u64,
345}
346
347#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct CreateBackupRequest {
350 pub name: String,
351 #[serde(skip_serializing_if = "Option::is_none")]
352 pub backup_type: Option<String>,
353 #[serde(skip_serializing_if = "Option::is_none")]
354 pub namespaces: Option<Vec<String>>,
355 #[serde(skip_serializing_if = "Option::is_none")]
356 pub encrypt: Option<bool>,
357 #[serde(skip_serializing_if = "Option::is_none")]
358 pub compression: Option<String>,
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize)]
363pub struct CreateBackupResponse {
364 pub backup: BackupInfo,
365 #[serde(skip_serializing_if = "Option::is_none")]
366 pub estimated_completion: Option<u64>,
367}
368
369#[derive(Debug, Clone, Serialize, Deserialize)]
371pub struct RestoreBackupRequest {
372 pub backup_id: String,
373 #[serde(skip_serializing_if = "Option::is_none")]
374 pub target_namespaces: Option<Vec<String>>,
375 #[serde(skip_serializing_if = "Option::is_none")]
376 pub overwrite: Option<bool>,
377 #[serde(skip_serializing_if = "Option::is_none")]
378 pub point_in_time: Option<u64>,
379}
380
381#[derive(Debug, Clone, Serialize, Deserialize)]
383pub struct RestoreBackupResponse {
384 pub restore_id: String,
385 pub status: String,
386 pub backup_id: String,
387 pub namespaces: Vec<String>,
388 pub started_at: u64,
389 #[serde(skip_serializing_if = "Option::is_none")]
390 pub estimated_completion: Option<u64>,
391 #[serde(skip_serializing_if = "Option::is_none")]
392 pub progress_percent: Option<u8>,
393 #[serde(skip_serializing_if = "Option::is_none")]
394 pub vectors_restored: Option<u64>,
395 #[serde(skip_serializing_if = "Option::is_none")]
396 pub completed_at: Option<u64>,
397 #[serde(skip_serializing_if = "Option::is_none")]
398 pub duration_seconds: Option<u64>,
399 #[serde(skip_serializing_if = "Option::is_none")]
400 pub error: Option<String>,
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize)]
409pub struct AutoPilotConfig {
410 pub enabled: bool,
411 pub dedup_threshold: f32,
412 pub dedup_interval_hours: u64,
413 pub consolidation_interval_hours: u64,
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize)]
418pub struct DedupResultSnapshot {
419 pub namespaces_processed: usize,
420 pub memories_scanned: usize,
421 pub duplicates_removed: usize,
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize)]
426pub struct ConsolidationResultSnapshot {
427 pub namespaces_processed: usize,
428 pub memories_scanned: usize,
429 pub clusters_merged: usize,
430 pub memories_consolidated: usize,
431}
432
433#[derive(Debug, Clone, Serialize, Deserialize)]
435pub struct AutoPilotStatusResponse {
436 pub config: AutoPilotConfig,
437 #[serde(skip_serializing_if = "Option::is_none")]
438 pub last_dedup_at: Option<u64>,
439 #[serde(skip_serializing_if = "Option::is_none")]
440 pub last_consolidation_at: Option<u64>,
441 #[serde(skip_serializing_if = "Option::is_none")]
442 pub last_dedup: Option<DedupResultSnapshot>,
443 #[serde(skip_serializing_if = "Option::is_none")]
444 pub last_consolidation: Option<ConsolidationResultSnapshot>,
445 pub total_dedup_removed: u64,
446 pub total_consolidated: u64,
447}
448
449#[derive(Debug, Clone, Serialize, Deserialize, Default)]
451pub struct AutoPilotConfigRequest {
452 #[serde(skip_serializing_if = "Option::is_none")]
453 pub enabled: Option<bool>,
454 #[serde(skip_serializing_if = "Option::is_none")]
455 pub dedup_threshold: Option<f32>,
456 #[serde(skip_serializing_if = "Option::is_none")]
457 pub dedup_interval_hours: Option<u64>,
458 #[serde(skip_serializing_if = "Option::is_none")]
459 pub consolidation_interval_hours: Option<u64>,
460}
461
462#[derive(Debug, Clone, Serialize, Deserialize)]
464pub struct AutoPilotConfigResponse {
465 pub success: bool,
466 pub config: AutoPilotConfig,
467 pub message: String,
468}
469
470#[derive(Debug, Clone, Serialize, Deserialize)]
472#[serde(rename_all = "lowercase")]
473pub enum AutoPilotTriggerAction {
474 Dedup,
475 Consolidate,
476 All,
477}
478
479#[derive(Debug, Clone, Serialize, Deserialize)]
481pub struct AutoPilotTriggerRequest {
482 pub action: AutoPilotTriggerAction,
483}
484
485#[derive(Debug, Clone, Serialize, Deserialize)]
487pub struct AutoPilotDedupResult {
488 pub namespaces_processed: usize,
489 pub memories_scanned: usize,
490 pub duplicates_removed: usize,
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize)]
495pub struct AutoPilotConsolidationResult {
496 pub namespaces_processed: usize,
497 pub memories_scanned: usize,
498 pub clusters_merged: usize,
499 pub memories_consolidated: usize,
500}
501
502#[derive(Debug, Clone, Serialize, Deserialize)]
504pub struct AutoPilotTriggerResponse {
505 pub success: bool,
506 pub action: AutoPilotTriggerAction,
507 #[serde(skip_serializing_if = "Option::is_none")]
508 pub dedup: Option<AutoPilotDedupResult>,
509 #[serde(skip_serializing_if = "Option::is_none")]
510 pub consolidation: Option<AutoPilotConsolidationResult>,
511 pub message: String,
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize)]
520pub struct DecayConfigResponse {
521 pub strategy: String,
523 pub half_life_hours: f64,
525 pub min_importance: f32,
527}
528
529#[derive(Debug, Clone, Serialize, Deserialize, Default)]
531pub struct DecayConfigUpdateRequest {
532 #[serde(skip_serializing_if = "Option::is_none")]
534 pub strategy: Option<String>,
535 #[serde(skip_serializing_if = "Option::is_none")]
537 pub half_life_hours: Option<f64>,
538 #[serde(skip_serializing_if = "Option::is_none")]
540 pub min_importance: Option<f32>,
541}
542
543#[derive(Debug, Clone, Serialize, Deserialize)]
545pub struct DecayConfigUpdateResponse {
546 pub success: bool,
547 pub config: DecayConfigResponse,
548 pub message: String,
549}
550
551#[derive(Debug, Clone, Serialize, Deserialize)]
553pub struct LastDecayCycleStats {
554 pub namespaces_processed: usize,
555 pub memories_processed: usize,
556 pub memories_decayed: usize,
557 pub memories_deleted: usize,
558}
559
560#[derive(Debug, Clone, Serialize, Deserialize)]
562pub struct DecayStatsResponse {
563 pub total_decayed: u64,
565 pub total_deleted: u64,
567 #[serde(skip_serializing_if = "Option::is_none")]
569 pub last_run_at: Option<u64>,
570 pub cycles_run: u64,
572 #[serde(skip_serializing_if = "Option::is_none")]
574 pub last_cycle: Option<LastDecayCycleStats>,
575}
576
577#[derive(Debug, Clone, Serialize, Deserialize)]
583pub struct TtlCleanupRequest {
584 #[serde(skip_serializing_if = "Option::is_none")]
585 pub namespace: Option<String>,
586}
587
588#[derive(Debug, Clone, Serialize, Deserialize)]
590pub struct TtlCleanupResponse {
591 pub success: bool,
592 pub vectors_removed: u64,
593 pub namespaces_cleaned: Vec<String>,
594 pub message: String,
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize)]
599pub struct TtlStats {
600 pub namespace: String,
601 pub vectors_with_ttl: u64,
602 pub expiring_within_hour: u64,
603 pub expiring_within_day: u64,
604 pub expired_pending_cleanup: u64,
605}
606
607#[derive(Debug, Clone, Serialize, Deserialize)]
609pub struct TtlStatsResponse {
610 pub namespaces: Vec<TtlStats>,
611 pub total_with_ttl: u64,
612 pub total_expired: u64,
613}
614
615impl DakeraClient {
620 pub async fn ops_stats(&self) -> Result<OpsStats> {
628 let url = format!("{}/v1/ops/stats", self.base_url);
629 let response = self.client.get(&url).send().await?;
630 self.handle_response(response).await
631 }
632
633 pub async fn cluster_status(&self) -> Result<ClusterStatus> {
635 let url = format!("{}/admin/cluster/status", self.base_url);
636 let response = self.client.get(&url).send().await?;
637 self.handle_response(response).await
638 }
639
640 pub async fn cluster_nodes(&self) -> Result<NodeListResponse> {
642 let url = format!("{}/admin/cluster/nodes", self.base_url);
643 let response = self.client.get(&url).send().await?;
644 self.handle_response(response).await
645 }
646
647 pub async fn list_namespaces_admin(&self) -> Result<NamespaceListResponse> {
653 let url = format!("{}/admin/namespaces", self.base_url);
654 let response = self.client.get(&url).send().await?;
655 self.handle_response(response).await
656 }
657
658 pub async fn delete_namespace_admin(&self, namespace: &str) -> Result<serde_json::Value> {
660 let url = format!("{}/admin/namespaces/{}", self.base_url, namespace);
661 let response = self.client.delete(&url).send().await?;
662 self.handle_response(response).await
663 }
664
665 pub async fn optimize_namespace(
667 &self,
668 namespace: &str,
669 request: OptimizeRequest,
670 ) -> Result<OptimizeResponse> {
671 let url = format!("{}/admin/namespaces/{}/optimize", self.base_url, namespace);
672 let response = self.client.post(&url).json(&request).send().await?;
673 self.handle_response(response).await
674 }
675
676 pub async fn index_stats(&self) -> Result<IndexStatsResponse> {
682 let url = format!("{}/admin/indexes/stats", self.base_url);
683 let response = self.client.get(&url).send().await?;
684 self.handle_response(response).await
685 }
686
687 pub async fn rebuild_indexes(
689 &self,
690 request: RebuildIndexRequest,
691 ) -> Result<RebuildIndexResponse> {
692 let url = format!("{}/admin/indexes/rebuild", self.base_url);
693 let response = self.client.post(&url).json(&request).send().await?;
694 self.handle_response(response).await
695 }
696
697 pub async fn cache_stats(&self) -> Result<CacheStats> {
703 let url = format!("{}/admin/cache/stats", self.base_url);
704 let response = self.client.get(&url).send().await?;
705 self.handle_response(response).await
706 }
707
708 pub async fn cache_clear(&self, namespace: Option<&str>) -> Result<ClearCacheResponse> {
710 let url = format!("{}/admin/cache/clear", self.base_url);
711 let request = ClearCacheRequest {
712 namespace: namespace.map(|s| s.to_string()),
713 };
714 let response = self.client.post(&url).json(&request).send().await?;
715 self.handle_response(response).await
716 }
717
718 pub async fn get_config(&self) -> Result<RuntimeConfig> {
724 let url = format!("{}/admin/config", self.base_url);
725 let response = self.client.get(&url).send().await?;
726 self.handle_response(response).await
727 }
728
729 pub async fn update_config(
731 &self,
732 updates: HashMap<String, serde_json::Value>,
733 ) -> Result<UpdateConfigResponse> {
734 let url = format!("{}/admin/config", self.base_url);
735 let response = self.client.put(&url).json(&updates).send().await?;
736 self.handle_response(response).await
737 }
738
739 pub async fn get_quotas(&self) -> Result<QuotaListResponse> {
745 let url = format!("{}/admin/quotas", self.base_url);
746 let response = self.client.get(&url).send().await?;
747 self.handle_response(response).await
748 }
749
750 pub async fn get_quota(&self, namespace: &str) -> Result<QuotaStatus> {
752 let url = format!("{}/admin/quotas/{}", self.base_url, namespace);
753 let response = self.client.get(&url).send().await?;
754 self.handle_response(response).await
755 }
756
757 pub async fn set_quota(
759 &self,
760 namespace: &str,
761 config: QuotaConfig,
762 ) -> Result<serde_json::Value> {
763 let url = format!("{}/admin/quotas/{}", self.base_url, namespace);
764 let request = serde_json::json!({ "config": config });
765 let response = self.client.put(&url).json(&request).send().await?;
766 self.handle_response(response).await
767 }
768
769 pub async fn delete_quota(&self, namespace: &str) -> Result<serde_json::Value> {
771 let url = format!("{}/admin/quotas/{}", self.base_url, namespace);
772 let response = self.client.delete(&url).send().await?;
773 self.handle_response(response).await
774 }
775
776 pub async fn update_quotas(&self, config: Option<QuotaConfig>) -> Result<serde_json::Value> {
778 let url = format!("{}/admin/quotas/default", self.base_url);
779 let request = serde_json::json!({ "config": config });
780 let response = self.client.put(&url).json(&request).send().await?;
781 self.handle_response(response).await
782 }
783
784 pub async fn slow_queries(
790 &self,
791 limit: Option<usize>,
792 namespace: Option<&str>,
793 query_type: Option<&str>,
794 ) -> Result<SlowQueryListResponse> {
795 let mut url = format!("{}/admin/slow-queries", self.base_url);
796 let mut params = Vec::new();
797 if let Some(l) = limit {
798 params.push(format!("limit={}", l));
799 }
800 if let Some(ns) = namespace {
801 params.push(format!("namespace={}", ns));
802 }
803 if let Some(qt) = query_type {
804 params.push(format!("query_type={}", qt));
805 }
806 if !params.is_empty() {
807 url.push('?');
808 url.push_str(¶ms.join("&"));
809 }
810 let response = self.client.get(&url).send().await?;
811 self.handle_response(response).await
812 }
813
814 pub async fn slow_query_summary(&self) -> Result<serde_json::Value> {
816 let url = format!("{}/admin/slow-queries/summary", self.base_url);
817 let response = self.client.get(&url).send().await?;
818 self.handle_response(response).await
819 }
820
821 pub async fn clear_slow_queries(&self) -> Result<serde_json::Value> {
823 let url = format!("{}/admin/slow-queries", self.base_url);
824 let response = self.client.delete(&url).send().await?;
825 self.handle_response(response).await
826 }
827
828 pub async fn create_backup(
834 &self,
835 request: CreateBackupRequest,
836 ) -> Result<CreateBackupResponse> {
837 let url = format!("{}/admin/backups", self.base_url);
838 let response = self.client.post(&url).json(&request).send().await?;
839 self.handle_response(response).await
840 }
841
842 pub async fn list_backups(&self) -> Result<BackupListResponse> {
844 let url = format!("{}/admin/backups", self.base_url);
845 let response = self.client.get(&url).send().await?;
846 self.handle_response(response).await
847 }
848
849 pub async fn get_backup(&self, backup_id: &str) -> Result<BackupInfo> {
851 let url = format!("{}/admin/backups/{}", self.base_url, backup_id);
852 let response = self.client.get(&url).send().await?;
853 self.handle_response(response).await
854 }
855
856 pub async fn restore_backup(
858 &self,
859 request: RestoreBackupRequest,
860 ) -> Result<RestoreBackupResponse> {
861 let url = format!("{}/admin/backups/restore", self.base_url);
862 let response = self.client.post(&url).json(&request).send().await?;
863 self.handle_response(response).await
864 }
865
866 pub async fn delete_backup(&self, backup_id: &str) -> Result<serde_json::Value> {
868 let url = format!("{}/admin/backups/{}", self.base_url, backup_id);
869 let response = self.client.delete(&url).send().await?;
870 self.handle_response(response).await
871 }
872
873 pub async fn ttl_cleanup(&self, namespace: Option<&str>) -> Result<TtlCleanupResponse> {
879 let url = format!("{}/admin/ttl/cleanup", self.base_url);
880 let request = TtlCleanupRequest {
881 namespace: namespace.map(|s| s.to_string()),
882 };
883 let response = self.client.post(&url).json(&request).send().await?;
884 self.handle_response(response).await
885 }
886
887 pub async fn ttl_stats(&self) -> Result<TtlStatsResponse> {
889 let url = format!("{}/admin/ttl/stats", self.base_url);
890 let response = self.client.get(&url).send().await?;
891 self.handle_response(response).await
892 }
893
894 pub async fn autopilot_status(&self) -> Result<AutoPilotStatusResponse> {
900 let url = format!("{}/admin/autopilot/status", 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_update_config(
909 &self,
910 request: AutoPilotConfigRequest,
911 ) -> Result<AutoPilotConfigResponse> {
912 let url = format!("{}/admin/autopilot/config", self.base_url);
913 let response = self.client.put(&url).json(&request).send().await?;
914 self.handle_response(response).await
915 }
916
917 pub async fn autopilot_trigger(
922 &self,
923 action: AutoPilotTriggerAction,
924 ) -> Result<AutoPilotTriggerResponse> {
925 let url = format!("{}/admin/autopilot/trigger", self.base_url);
926 let request = AutoPilotTriggerRequest { action };
927 let response = self.client.post(&url).json(&request).send().await?;
928 self.handle_response(response).await
929 }
930
931 pub async fn decay_config(&self) -> Result<DecayConfigResponse> {
940 let url = format!("{}/admin/decay/config", self.base_url);
941 let response = self.client.get(&url).send().await?;
942 self.handle_response(response).await
943 }
944
945 pub async fn decay_update_config(
951 &self,
952 request: DecayConfigUpdateRequest,
953 ) -> Result<DecayConfigUpdateResponse> {
954 let url = format!("{}/admin/decay/config", self.base_url);
955 let response = self.client.put(&url).json(&request).send().await?;
956 self.handle_response(response).await
957 }
958
959 pub async fn decay_stats(&self) -> Result<DecayStatsResponse> {
964 let url = format!("{}/admin/decay/stats", self.base_url);
965 let response = self.client.get(&url).send().await?;
966 self.handle_response(response).await
967 }
968}