1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use sqlx::types::Json;
5use sqlx::FromRow;
6
7pub const BACKUP_SECRET_KEYS: &[&str] = &["pass", "password", "secret_key", "secret"];
10
11#[derive(Debug, Clone, FromRow)]
14pub struct BackupDestination {
15 pub id: String,
16 pub name: String,
17 pub kind: String,
19 pub config: Json<Value>,
20 pub enabled: bool,
21 pub created_at: DateTime<Utc>,
22 pub updated_at: DateTime<Utc>,
23}
24
25#[derive(Debug, Clone, Serialize)]
27pub struct BackupDestinationView {
28 pub id: String,
29 pub name: String,
30 pub kind: String,
31 pub config: Value,
33 pub has_credentials: bool,
35 pub enabled: bool,
36 pub created_at: DateTime<Utc>,
37 pub updated_at: DateTime<Utc>,
38}
39
40pub fn mask_backup_config(mut config: Value) -> (Value, bool) {
42 let mut has_credentials = false;
43 if let Some(obj) = config.as_object_mut() {
44 for key in BACKUP_SECRET_KEYS {
45 if let Some(v) = obj.get_mut(*key) {
46 if v.as_str().map(|s| !s.is_empty()).unwrap_or(false) {
47 has_credentials = true;
48 *v = Value::String("***".to_string());
49 }
50 }
51 }
52 }
53 (config, has_credentials)
54}
55
56impl From<BackupDestination> for BackupDestinationView {
57 fn from(d: BackupDestination) -> Self {
58 let (config, has_credentials) = mask_backup_config(d.config.0);
59 BackupDestinationView {
60 id: d.id,
61 name: d.name,
62 kind: d.kind,
63 config,
64 has_credentials,
65 enabled: d.enabled,
66 created_at: d.created_at,
67 updated_at: d.updated_at,
68 }
69 }
70}
71
72#[derive(Debug, Deserialize)]
73pub struct BackupDestinationCreate {
74 pub name: String,
75 pub kind: String,
77 pub config: Option<Value>,
78 pub enabled: Option<bool>,
79}
80
81#[derive(Debug, Deserialize, Default)]
82pub struct BackupDestinationUpdate {
83 pub name: Option<String>,
84 pub kind: Option<String>,
85 pub config: Option<Value>,
86 pub enabled: Option<bool>,
87}
88
89#[derive(Debug, Clone, Serialize)]
91pub struct BackupTestResult {
92 pub ok: bool,
93 pub error: Option<String>,
94 pub latency_ms: i64,
95}
96
97#[derive(Debug, Clone, Serialize, FromRow)]
99pub struct BackupPolicy {
100 pub id: String,
101 pub name: String,
102 pub destination_id: String,
103 pub camera_ids: Json<Value>,
105 pub incident_lock_only: bool,
106 pub schedule_interval_s: i64,
107 pub lookback_hours: i64,
108 pub last_run_at: Option<DateTime<Utc>>,
109 pub last_job_id: Option<String>,
110 pub enabled: bool,
111 pub created_at: DateTime<Utc>,
112 pub updated_at: DateTime<Utc>,
113}
114
115#[derive(Debug, Deserialize)]
116pub struct BackupPolicyCreate {
117 pub name: String,
118 pub destination_id: String,
119 pub camera_ids: Option<Value>,
120 pub incident_lock_only: Option<bool>,
121 pub schedule_interval_s: Option<i64>,
122 pub lookback_hours: Option<i64>,
123 pub enabled: Option<bool>,
124}
125
126#[derive(Debug, Deserialize, Default)]
127pub struct BackupPolicyUpdate {
128 pub name: Option<String>,
129 pub destination_id: Option<String>,
130 pub camera_ids: Option<Value>,
131 pub incident_lock_only: Option<bool>,
132 pub schedule_interval_s: Option<i64>,
133 pub lookback_hours: Option<i64>,
134 pub enabled: Option<bool>,
135}
136
137#[derive(Debug, Clone, Serialize, FromRow)]
139pub struct BackupJob {
140 pub id: String,
141 pub policy_id: Option<String>,
142 pub destination_id: Option<String>,
143 pub kind: String,
145 pub camera_ids: Json<Value>,
146 pub from_time: Option<DateTime<Utc>>,
147 pub to_time: Option<DateTime<Utc>>,
148 pub incident_lock_only: bool,
149 pub status: String,
151 pub files_total: i64,
152 pub files_copied: i64,
153 pub bytes_copied: i64,
154 pub error: Option<String>,
155 pub output_path: Option<String>,
157 pub output_url: Option<String>,
159 pub started_at: Option<DateTime<Utc>>,
160 pub finished_at: Option<DateTime<Utc>>,
161 pub created_at: DateTime<Utc>,
162}
163
164#[derive(Debug, Deserialize)]
166pub struct ArchiveExportRequest {
167 #[serde(default)]
169 pub camera_ids: Vec<String>,
170 pub from: Option<String>,
171 pub to: Option<String>,
172 pub incident_lock_only: Option<bool>,
173 pub trim: Option<bool>,
175}