1use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct ClusterInfo {
17 pub name: String,
18 pub version: String,
19 pub node_count: usize,
20 pub leader_id: Option<String>,
21 pub state: ClusterState,
22 pub uptime_seconds: u64,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
27pub enum ClusterState {
28 Healthy,
29 Degraded,
30 Unavailable,
31 Initializing,
32}
33
34impl Default for ClusterInfo {
35 fn default() -> Self {
36 Self {
37 name: "aegis-cluster".to_string(),
38 version: env!("CARGO_PKG_VERSION").to_string(),
39 node_count: 0,
40 leader_id: None,
41 state: ClusterState::Initializing,
42 uptime_seconds: 0,
43 }
44 }
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct NodeInfo {
54 pub id: String,
55 pub address: String,
56 pub role: NodeRole,
57 pub status: NodeStatus,
58 pub version: String,
59 pub uptime_seconds: u64,
60 pub last_heartbeat: u64,
61 pub metrics: NodeMetrics,
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
66pub enum NodeRole {
67 Leader,
68 Follower,
69 Candidate,
70 Learner,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
75pub enum NodeStatus {
76 Online,
77 Offline,
78 Joining,
79 Leaving,
80 Unknown,
81}
82
83#[derive(Debug, Clone, Default, Serialize, Deserialize)]
85pub struct NodeMetrics {
86 pub cpu_usage_percent: f64,
87 pub memory_usage_bytes: u64,
88 pub memory_total_bytes: u64,
89 pub disk_usage_bytes: u64,
90 pub disk_total_bytes: u64,
91 pub connections_active: u64,
92 pub queries_per_second: f64,
93 pub network_bytes_in: u64,
95 pub network_bytes_out: u64,
96 pub network_packets_in: u64,
97 pub network_packets_out: u64,
98 pub latency_p50_ms: f64,
100 pub latency_p90_ms: f64,
101 pub latency_p95_ms: f64,
102 pub latency_p99_ms: f64,
103 pub latency_max_ms: f64,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct DatabaseInfo {
113 pub name: String,
114 pub size_bytes: u64,
115 pub table_count: usize,
116 pub index_count: usize,
117 pub created_at: u64,
118 pub last_modified: u64,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct TableInfo {
124 pub name: String,
125 pub database: String,
126 pub row_count: u64,
127 pub size_bytes: u64,
128 pub columns: Vec<ColumnInfo>,
129 pub indexes: Vec<IndexInfo>,
130 pub created_at: u64,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct ColumnInfo {
136 pub name: String,
137 pub data_type: String,
138 pub nullable: bool,
139 pub primary_key: bool,
140 pub default_value: Option<String>,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct IndexInfo {
146 pub name: String,
147 pub columns: Vec<String>,
148 pub index_type: String,
149 pub unique: bool,
150 pub size_bytes: u64,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct QueryInfo {
160 pub id: String,
161 pub sql: String,
162 pub database: String,
163 pub user: String,
164 pub state: QueryState,
165 pub started_at: u64,
166 pub duration_ms: u64,
167 pub rows_examined: u64,
168 pub rows_returned: u64,
169}
170
171#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
173pub enum QueryState {
174 Running,
175 Finished,
176 Cancelled,
177 Failed,
178}
179
180#[derive(Debug, Clone, Default, Serialize, Deserialize)]
182pub struct QueryStats {
183 pub total_queries: u64,
184 pub queries_per_second: f64,
185 pub avg_duration_ms: f64,
186 pub p50_duration_ms: f64,
187 pub p95_duration_ms: f64,
188 pub p99_duration_ms: f64,
189 pub slow_queries: u64,
190 pub failed_queries: u64,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct ReplicationInfo {
200 pub enabled: bool,
201 pub mode: ReplicationMode,
202 pub lag_ms: u64,
203 pub last_applied_index: u64,
204 pub commit_index: u64,
205 pub replicas: Vec<ReplicaInfo>,
206}
207
208#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
210pub enum ReplicationMode {
211 Synchronous,
212 Asynchronous,
213 SemiSynchronous,
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct ReplicaInfo {
219 pub node_id: String,
220 pub status: ReplicaStatus,
221 pub lag_ms: u64,
222 pub last_applied_index: u64,
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
227pub enum ReplicaStatus {
228 InSync,
229 Lagging,
230 CatchingUp,
231 Offline,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
240pub struct ShardingInfo {
241 pub enabled: bool,
242 pub shard_count: usize,
243 pub replication_factor: usize,
244 pub shards: Vec<ShardInfo>,
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct ShardInfo {
250 pub id: String,
251 pub state: ShardState,
252 pub key_range_start: String,
253 pub key_range_end: String,
254 pub primary_node: String,
255 pub replica_nodes: Vec<String>,
256 pub size_bytes: u64,
257 pub row_count: u64,
258}
259
260#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
262pub enum ShardState {
263 Active,
264 Migrating,
265 Splitting,
266 Merging,
267 Inactive,
268}
269
270#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct ConnectionInfo {
277 pub active: u64,
278 pub idle: u64,
279 pub total: u64,
280 pub max: u64,
281 pub connections: Vec<ConnectionDetails>,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct ConnectionDetails {
287 pub id: String,
288 pub user: String,
289 pub database: String,
290 pub client_address: String,
291 pub state: ConnectionState,
292 pub connected_at: u64,
293 pub last_activity: u64,
294 pub current_query: Option<String>,
295}
296
297#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
299pub enum ConnectionState {
300 Active,
301 Idle,
302 IdleInTransaction,
303 Waiting,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct StorageInfo {
313 pub total_bytes: u64,
314 pub used_bytes: u64,
315 pub available_bytes: u64,
316 pub data_bytes: u64,
317 pub index_bytes: u64,
318 pub wal_bytes: u64,
319 pub temp_bytes: u64,
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct AlertInfo {
329 pub id: String,
330 pub severity: AlertSeverity,
331 pub source: String,
332 pub message: String,
333 pub timestamp: u64,
334 pub acknowledged: bool,
335 pub resolved: bool,
336}
337
338#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
340pub enum AlertSeverity {
341 Info,
342 Warning,
343 Error,
344 Critical,
345}
346
347#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct UserInfo {
354 pub username: String,
355 pub roles: Vec<String>,
356 pub created_at: u64,
357 pub last_login: Option<u64>,
358 pub enabled: bool,
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize)]
367pub struct BackupInfo {
368 pub id: String,
369 pub backup_type: BackupType,
370 pub state: BackupState,
371 pub size_bytes: u64,
372 pub started_at: u64,
373 pub completed_at: Option<u64>,
374 pub duration_seconds: Option<u64>,
375 pub database: Option<String>,
376}
377
378#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
380pub enum BackupType {
381 Full,
382 Incremental,
383 Snapshot,
384}
385
386#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
388pub enum BackupState {
389 InProgress,
390 Completed,
391 Failed,
392 Cancelled,
393}
394
395#[derive(Debug, Clone, Default, Serialize, Deserialize)]
401pub struct DashboardSummary {
402 pub cluster: ClusterSummary,
403 pub performance: PerformanceSummary,
404 pub storage: StorageSummary,
405 pub alerts: AlertSummary,
406}
407
408#[derive(Debug, Clone, Default, Serialize, Deserialize)]
410pub struct ClusterSummary {
411 pub state: String,
412 pub node_count: usize,
413 pub healthy_nodes: usize,
414 pub leader_id: Option<String>,
415 pub version: String,
416}
417
418#[derive(Debug, Clone, Default, Serialize, Deserialize)]
420pub struct PerformanceSummary {
421 pub queries_per_second: f64,
422 pub avg_latency_ms: f64,
423 pub active_connections: u64,
424 pub cpu_usage_percent: f64,
425 pub memory_usage_percent: f64,
426}
427
428#[derive(Debug, Clone, Default, Serialize, Deserialize)]
430pub struct StorageSummary {
431 pub total_bytes: u64,
432 pub used_bytes: u64,
433 pub usage_percent: f64,
434 pub database_count: usize,
435 pub table_count: usize,
436}
437
438#[derive(Debug, Clone, Default, Serialize, Deserialize)]
440pub struct AlertSummary {
441 pub total: usize,
442 pub critical: usize,
443 pub warning: usize,
444 pub unacknowledged: usize,
445}
446
447pub struct AdminService {
453 start_time: std::time::Instant,
454}
455
456impl AdminService {
457 pub fn new() -> Self {
459 Self {
460 start_time: std::time::Instant::now(),
461 }
462 }
463
464 pub fn get_cluster_info(&self) -> ClusterInfo {
466 ClusterInfo {
467 name: "aegis-cluster".to_string(),
468 version: env!("CARGO_PKG_VERSION").to_string(),
469 node_count: 3,
470 leader_id: Some("node-0".to_string()),
471 state: ClusterState::Healthy,
472 uptime_seconds: self.start_time.elapsed().as_secs(),
473 }
474 }
475
476 pub fn get_dashboard_summary(&self) -> DashboardSummary {
478 DashboardSummary {
479 cluster: ClusterSummary {
480 state: "Healthy".to_string(),
481 node_count: 3,
482 healthy_nodes: 3,
483 leader_id: Some("node-0".to_string()),
484 version: env!("CARGO_PKG_VERSION").to_string(),
485 },
486 performance: PerformanceSummary {
487 queries_per_second: 1250.0,
488 avg_latency_ms: 2.5,
489 active_connections: 45,
490 cpu_usage_percent: 35.0,
491 memory_usage_percent: 62.0,
492 },
493 storage: StorageSummary {
494 total_bytes: 100_000_000_000,
495 used_bytes: 45_000_000_000,
496 usage_percent: 45.0,
497 database_count: 5,
498 table_count: 42,
499 },
500 alerts: AlertSummary {
501 total: 3,
502 critical: 0,
503 warning: 2,
504 unacknowledged: 1,
505 },
506 }
507 }
508
509 pub fn get_nodes(&self) -> Vec<NodeInfo> {
511 let uptime = self.start_time.elapsed().as_secs();
512 vec![
513 NodeInfo {
514 id: "node-0".to_string(),
515 address: "10.0.0.1:9090".to_string(),
516 role: NodeRole::Leader,
517 status: NodeStatus::Online,
518 version: env!("CARGO_PKG_VERSION").to_string(),
519 uptime_seconds: uptime,
520 last_heartbeat: Self::now(),
521 metrics: NodeMetrics {
522 cpu_usage_percent: 35.0,
523 memory_usage_bytes: 2_000_000_000,
524 memory_total_bytes: 8_000_000_000,
525 disk_usage_bytes: 50_000_000_000,
526 disk_total_bytes: 100_000_000_000,
527 connections_active: 15,
528 queries_per_second: 450.0,
529 network_bytes_in: 125_000_000 * uptime,
530 network_bytes_out: 98_000_000 * uptime,
531 network_packets_in: 850_000 * uptime,
532 network_packets_out: 720_000 * uptime,
533 latency_p50_ms: 0.8,
534 latency_p90_ms: 2.1,
535 latency_p95_ms: 3.5,
536 latency_p99_ms: 8.2,
537 latency_max_ms: 45.0,
538 },
539 },
540 NodeInfo {
541 id: "node-1".to_string(),
542 address: "10.0.0.2:9090".to_string(),
543 role: NodeRole::Follower,
544 status: NodeStatus::Online,
545 version: env!("CARGO_PKG_VERSION").to_string(),
546 uptime_seconds: uptime.saturating_sub(100),
547 last_heartbeat: Self::now(),
548 metrics: NodeMetrics {
549 cpu_usage_percent: 28.0,
550 memory_usage_bytes: 1_800_000_000,
551 memory_total_bytes: 8_000_000_000,
552 disk_usage_bytes: 48_000_000_000,
553 disk_total_bytes: 100_000_000_000,
554 connections_active: 12,
555 queries_per_second: 380.0,
556 network_bytes_in: 98_000_000 * uptime.saturating_sub(100),
557 network_bytes_out: 76_000_000 * uptime.saturating_sub(100),
558 network_packets_in: 720_000 * uptime.saturating_sub(100),
559 network_packets_out: 580_000 * uptime.saturating_sub(100),
560 latency_p50_ms: 0.9,
561 latency_p90_ms: 2.3,
562 latency_p95_ms: 3.8,
563 latency_p99_ms: 9.1,
564 latency_max_ms: 52.0,
565 },
566 },
567 NodeInfo {
568 id: "node-2".to_string(),
569 address: "10.0.0.3:9090".to_string(),
570 role: NodeRole::Follower,
571 status: NodeStatus::Online,
572 version: env!("CARGO_PKG_VERSION").to_string(),
573 uptime_seconds: uptime.saturating_sub(50),
574 last_heartbeat: Self::now(),
575 metrics: NodeMetrics {
576 cpu_usage_percent: 32.0,
577 memory_usage_bytes: 1_900_000_000,
578 memory_total_bytes: 8_000_000_000,
579 disk_usage_bytes: 49_000_000_000,
580 disk_total_bytes: 100_000_000_000,
581 connections_active: 18,
582 queries_per_second: 420.0,
583 network_bytes_in: 105_000_000 * uptime.saturating_sub(50),
584 network_bytes_out: 82_000_000 * uptime.saturating_sub(50),
585 network_packets_in: 780_000 * uptime.saturating_sub(50),
586 network_packets_out: 620_000 * uptime.saturating_sub(50),
587 latency_p50_ms: 1.1,
588 latency_p90_ms: 2.8,
589 latency_p95_ms: 4.2,
590 latency_p99_ms: 10.5,
591 latency_max_ms: 58.0,
592 },
593 },
594 ]
595 }
596
597 pub fn get_storage_info(&self) -> StorageInfo {
599 StorageInfo {
600 total_bytes: 100_000_000_000,
601 used_bytes: 45_000_000_000,
602 available_bytes: 55_000_000_000,
603 data_bytes: 35_000_000_000,
604 index_bytes: 8_000_000_000,
605 wal_bytes: 1_500_000_000,
606 temp_bytes: 500_000_000,
607 }
608 }
609
610 pub fn get_query_stats(&self) -> QueryStats {
612 QueryStats {
613 total_queries: 1_250_000,
614 queries_per_second: 1250.0,
615 avg_duration_ms: 2.5,
616 p50_duration_ms: 1.2,
617 p95_duration_ms: 8.5,
618 p99_duration_ms: 25.0,
619 slow_queries: 150,
620 failed_queries: 12,
621 }
622 }
623
624 fn now() -> u64 {
626 std::time::SystemTime::now()
627 .duration_since(std::time::UNIX_EPOCH)
628 .unwrap_or_default()
629 .as_millis() as u64
630 }
631}
632
633impl Default for AdminService {
634 fn default() -> Self {
635 Self::new()
636 }
637}
638
639#[cfg(test)]
644mod tests {
645 use super::*;
646
647 #[test]
648 fn test_cluster_info() {
649 let service = AdminService::new();
650 let info = service.get_cluster_info();
651
652 assert_eq!(info.name, "aegis-cluster");
653 assert_eq!(info.state, ClusterState::Healthy);
654 }
655
656 #[test]
657 fn test_dashboard_summary() {
658 let service = AdminService::new();
659 let summary = service.get_dashboard_summary();
660
661 assert_eq!(summary.cluster.node_count, 3);
662 assert!(summary.performance.queries_per_second > 0.0);
663 }
664
665 #[test]
666 fn test_get_nodes() {
667 let service = AdminService::new();
668 let nodes = service.get_nodes();
669
670 assert_eq!(nodes.len(), 3);
671 assert_eq!(nodes[0].role, NodeRole::Leader);
672 assert_eq!(nodes[1].role, NodeRole::Follower);
673 }
674
675 #[test]
676 fn test_storage_info() {
677 let service = AdminService::new();
678 let storage = service.get_storage_info();
679
680 assert!(storage.total_bytes > storage.used_bytes);
681 assert_eq!(
682 storage.available_bytes,
683 storage.total_bytes - storage.used_bytes
684 );
685 }
686
687 #[test]
688 fn test_query_stats() {
689 let service = AdminService::new();
690 let stats = service.get_query_stats();
691
692 assert!(stats.total_queries > 0);
693 assert!(stats.p50_duration_ms < stats.p95_duration_ms);
694 assert!(stats.p95_duration_ms < stats.p99_duration_ms);
695 }
696
697 #[test]
698 fn test_node_metrics() {
699 let service = AdminService::new();
700 let nodes = service.get_nodes();
701
702 for node in nodes {
703 assert!(node.metrics.cpu_usage_percent <= 100.0);
704 assert!(node.metrics.memory_usage_bytes <= node.metrics.memory_total_bytes);
705 }
706 }
707}