convergio_backup/
types.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, thiserror::Error)]
7pub enum BackupError {
8 #[error("database error: {0}")]
9 Db(#[from] rusqlite::Error),
10
11 #[error("pool error: {0}")]
12 Pool(#[from] r2d2::Error),
13
14 #[error("io error: {0}")]
15 Io(#[from] std::io::Error),
16
17 #[error("json error: {0}")]
18 Json(#[from] serde_json::Error),
19
20 #[error("snapshot not found: {0}")]
21 SnapshotNotFound(String),
22
23 #[error("invalid config: {0}")]
24 InvalidConfig(String),
25
26 #[error("restore failed: {0}")]
27 RestoreFailed(String),
28}
29
30pub type BackupResult<T> = Result<T, BackupError>;
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct RetentionRule {
35 pub table: String,
37 pub timestamp_column: String,
39 pub max_age_days: u32,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct SnapshotRecord {
46 pub id: String,
47 pub path: String,
48 pub size_bytes: i64,
49 pub checksum: String,
50 pub created_at: String,
51 pub node: String,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct OrgExportMeta {
57 pub org_id: String,
58 pub org_name: String,
59 pub exported_at: String,
60 pub node: String,
61 pub tables: Vec<String>,
62 pub row_counts: Vec<(String, i64)>,
63 pub version: String,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct PurgeEvent {
69 pub table: String,
70 pub rows_deleted: i64,
71 pub cutoff_date: String,
72 pub executed_at: String,
73}
74
75pub fn default_retention_rules() -> Vec<RetentionRule> {
77 vec![
78 RetentionRule {
79 table: "audit_log".into(),
80 timestamp_column: "created_at".into(),
81 max_age_days: 365,
82 },
83 RetentionRule {
84 table: "ipc_messages".into(),
85 timestamp_column: "created_at".into(),
86 max_age_days: 30,
87 },
88 ]
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn default_rules_cover_required_tables() {
97 let rules = default_retention_rules();
98 assert_eq!(rules.len(), 2);
99 assert_eq!(rules[0].table, "audit_log");
100 assert_eq!(rules[0].max_age_days, 365);
101 assert_eq!(rules[1].table, "ipc_messages");
102 assert_eq!(rules[1].max_age_days, 30);
103 }
104
105 #[test]
106 fn snapshot_record_serializes() {
107 let rec = SnapshotRecord {
108 id: "snap-001".into(),
109 path: "/tmp/backup.db".into(),
110 size_bytes: 1024,
111 checksum: "abc123".into(),
112 created_at: "2026-04-03T00:00:00Z".into(),
113 node: "m5max".into(),
114 };
115 let json = serde_json::to_string(&rec).unwrap();
116 assert!(json.contains("snap-001"));
117 }
118
119 #[test]
120 fn purge_event_serializes() {
121 let ev = PurgeEvent {
122 table: "audit_log".into(),
123 rows_deleted: 42,
124 cutoff_date: "2025-04-03".into(),
125 executed_at: "2026-04-03T00:00:00Z".into(),
126 };
127 let json = serde_json::to_string(&ev).unwrap();
128 assert!(json.contains("audit_log"));
129 assert!(json.contains("42"));
130 }
131}