1mod migrator;
2mod security;
3mod utils;
4mod validator;
5
6use crate::encryption::types::{EncryptionAlgorithm, IvMode};
7use crate::error::ConfigError;
8use security::SecurityManager;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11use std::error::Error;
12use std::fs;
13use std::fs::create_dir_all;
14use std::path::PathBuf;
15use tracing::info;
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub enum ProviderType {
19 AliYunDrive,
20 OneOneFive,
21 Quark,
22 WebDAV,
23 SMB,
24 Local,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct AccountConfig {
29 pub id: String,
30 pub provider: ProviderType,
31 pub name: String,
32 pub credentials: HashMap<String, String>,
33 pub rate_limit: Option<RateLimitConfig>,
34 pub retry_policy: RetryPolicy,
35}
36
37impl AccountConfig {
38 pub fn validate(&self) -> Result<(), Box<dyn Error>> {
39 Ok(())
40 }
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct RateLimitConfig {
45 pub requests_per_minute: u32,
46 pub max_concurrent: usize,
47 pub chunk_size: usize,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct RetryPolicy {
52 pub max_retries: u32,
53 pub initial_delay_ms: u64,
54 pub max_delay_ms: u64,
55 pub backoff_factor: f64,
56}
57
58impl Default for RetryPolicy {
59 fn default() -> Self {
60 RetryPolicy {
62 max_retries: 0,
63 initial_delay_ms: 0,
64 max_delay_ms: 0,
65 backoff_factor: 0.0,
66 }
67 }
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct SyncTask {
72 pub id: String,
73 pub name: String,
74 pub source_account: String,
75 pub source_path: String,
76 pub target_account: String,
77 pub target_path: String,
78 pub schedule: Option<Schedule>,
79 pub filters: Vec<FilterRule>,
80 pub encryption: Option<EncryptionConfig>,
81 pub diff_mode: DiffMode,
82 pub preserve_metadata: bool,
83 pub verify_integrity: bool,
84 pub sync_policy: Option<SyncPolicy>,
86}
87
88impl SyncTask {
89 pub fn validate(&self) -> Result<(), Box<dyn Error>> {
90 info!("SyncTask::validate()");
92 todo!()
93 }
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
97pub enum Schedule {
98 Cron(String),
99 Interval { seconds: u64 },
100 Manual,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub enum FilterRule {
105 Include(String),
106 Exclude(String),
107 SizeGreaterThan(u64),
108 SizeLessThan(u64),
109 ModifiedAfter(i64),
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct EncryptionConfig {
114 pub algorithm: EncryptionAlgorithm,
115 pub key_id: String,
116 pub iv_mode: IvMode,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub enum DiffMode {
121 Full,
122 Incremental,
123 Smart,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct SyncPolicy {
129 pub delete_orphans: bool,
131 pub overwrite_existing: bool,
133 pub scan_cooldown_secs: u64,
135}
136
137pub struct ConfigManager {
138 config_path: PathBuf,
139 accounts: HashMap<String, AccountConfig>,
140 tasks: HashMap<String, SyncTask>,
141 security_manager: SecurityManager,
142}
143
144impl ConfigManager {
145 pub fn get_task(&self, task_id: &str) -> Option<SyncTask> {
146 self.tasks.get(task_id).cloned()
147 }
148}
149
150impl ConfigManager {
151 pub fn new() -> Result<Self, ConfigError> {
152 let config_dir = dirs::config_dir()
153 .ok_or(ConfigError::NoConfigDir)?
154 .join("disksync");
155
156 create_dir_all(&config_dir).unwrap();
158
159 let config_path = config_dir.join("config.yaml");
160
161 Self::new_with_path(config_path)
162 }
163
164 pub fn new_with_path(config_path: PathBuf) -> Result<Self, ConfigError> {
165 let parent = config_path
166 .parent()
167 .unwrap_or_else(|| std::path::Path::new("."));
168 create_dir_all(parent).unwrap();
169
170 let security_manager = SecurityManager::new(parent);
171
172 let mut manager = Self {
173 config_path,
174 accounts: HashMap::new(),
175 tasks: HashMap::new(),
176 security_manager,
177 };
178
179 if manager.config_path.exists() {
180 manager.load()?;
181 }
182 Ok(manager)
183 }
184
185 pub fn load(&mut self) -> Result<(), ConfigError> {
186 if self.config_path.exists() {
187 let content = fs::read_to_string(&self.config_path).unwrap();
188 let mut config: ConfigFile = serde_yaml::from_str(&content).unwrap();
189
190 let config_dir = self
192 .config_path
193 .parent()
194 .unwrap_or_else(|| std::path::Path::new("."));
195 let mut migration_occurred = false;
196
197 if config.version != "0.1.0" {
199 if let Err(e) = migrator::ConfigMigrator::migrate(&mut config, config_dir) {
200 tracing::warn!("Config migration failed: {}", e);
201 } else {
202 migration_occurred = true;
203 }
204 }
205
206 self.accounts = config
207 .accounts
208 .into_iter()
209 .map(|mut a| {
210 for (_, v) in a.credentials.iter_mut() {
214 *v = self.security_manager.decrypt(v);
215 }
216 (a.id.clone(), a)
217 })
218 .collect();
219 self.tasks = config
220 .tasks
221 .into_iter()
222 .map(|t| (t.id.clone(), t))
223 .collect();
224
225 if migration_occurred {
227 tracing::info!("Config migration occurred, saving updated config...");
228 if let Err(e) = self.save() {
229 tracing::error!("Failed to save migrated config: {}", e);
230 }
231 }
232 }
233 Ok(())
234 }
235
236 pub fn save(&self) -> Result<(), ConfigError> {
237 let accounts: Vec<AccountConfig> = self
238 .accounts
239 .values()
240 .cloned()
241 .map(|mut a| {
242 for (_, v) in a.credentials.iter_mut() {
244 *v = self.security_manager.encrypt(v);
245 }
246 a
247 })
248 .collect();
249
250 let config = ConfigFile {
251 version: "0.1.0".to_string(), global_settings: Default::default(),
253 accounts,
254 tasks: self.tasks.values().cloned().collect(),
255 encryption_keys: vec![],
256 plugins: vec![],
257 schedules: vec![],
258 network_settings: None,
259 security_settings: None,
260 };
261
262 let content = serde_yaml::to_string(&config).unwrap();
264 fs::write(&self.config_path, content).unwrap();
265 info!("Configuration saved to file: {:?}", self.config_path);
266 Ok(())
267 }
268}
269
270impl ConfigManager {
271 pub fn get_tasks(&self) -> &HashMap<String, SyncTask> {
272 &self.tasks
273 }
274
275 pub fn get_accounts(&self) -> &HashMap<String, AccountConfig> {
276 &self.accounts
277 }
278
279 pub fn add_task(&mut self, task: SyncTask) -> Result<(), ConfigError> {
280 self.tasks.insert(task.id.clone(), task);
281 Ok(())
282 }
283
284 pub fn add_account(&mut self, account: AccountConfig) -> Result<(), ConfigError> {
285 self.accounts.insert(account.id.clone(), account);
286 Ok(())
287 }
288
289 pub fn update_account(&mut self, account: AccountConfig) -> Result<(), ConfigError> {
291 self.accounts.insert(account.id.clone(), account);
292 Ok(())
293 }
294
295 pub fn remove_account(&mut self, account_id: &str) -> Result<(), ConfigError> {
297 self.accounts.remove(account_id);
298 Ok(())
299 }
300
301 pub fn get_account(&self, account_id: &str) -> Option<AccountConfig> {
303 self.accounts.get(account_id).cloned()
304 }
305
306 pub fn update_task(&mut self, task: SyncTask) -> Result<(), ConfigError> {
308 self.tasks.insert(task.id.clone(), task);
309 Ok(())
310 }
311
312 pub fn remove_task(&mut self, task_id: &str) -> Result<(), ConfigError> {
314 self.tasks.remove(task_id);
315 Ok(())
316 }
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct ConfigFile {
322 pub version: String,
323 pub global_settings: GlobalSettings,
324 pub accounts: Vec<AccountConfig>,
325 pub tasks: Vec<SyncTask>,
326 pub encryption_keys: Vec<EncryptionKey>,
327 pub plugins: Vec<PluginConfig>,
328 pub schedules: Vec<ScheduleConfig>,
329 pub network_settings: Option<NetworkSettings>,
330 pub security_settings: Option<SecuritySettings>,
331}
332
333#[derive(Debug, Clone, Serialize, Deserialize)]
334pub struct GlobalSettings {
335 pub data_dir: Option<PathBuf>,
336 pub temp_dir: Option<PathBuf>,
337 pub log_level: LogLevel,
338 pub log_retention_days: u32,
339 pub max_concurrent_tasks: usize,
340 pub default_retry_policy: RetryPolicy,
341 pub enable_telemetry: bool,
342 pub auto_update_check: bool,
343 pub ui_language: String,
344}
345
346#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
347pub enum LogLevel {
348 Off,
349 Error,
350 Warn,
351 Info,
352 Debug,
353 Trace,
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct EncryptionKey {
359 pub id: String,
360 pub name: String,
361 pub algorithm: EncryptionAlgorithm,
362 pub key_data: KeyData,
363 pub description: Option<String>,
364 pub tags: Vec<String>,
365}
366
367#[derive(Debug, Clone, Serialize, Deserialize)]
368pub enum KeyData {
369 Local {
371 encrypted_data: Vec<u8>,
372 salt: Vec<u8>,
373 },
374 External { service: String, key_uri: String },
376 HSM {
378 module_id: String,
379 key_handle: String,
380 },
381}
382
383#[derive(Debug, Clone, Serialize, Deserialize)]
384pub struct PluginConfig {
385 pub name: String,
386 pub enabled: bool,
387 pub version: String,
388 pub source: PluginSource,
389 pub config: HashMap<String, serde_json::Value>,
390 pub hooks: Vec<PluginHookConfig>,
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize)]
394pub enum PluginSource {
395 Builtin,
397 Local { path: PathBuf },
399 Git { url: String, branch: Option<String> },
401 Registry { name: String, version: String },
403}
404
405#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct PluginHookConfig {
407 pub hook_name: String,
408 pub priority: i32,
409 pub enabled: bool,
410 pub config: Option<serde_json::Value>,
411}
412
413#[derive(Debug, Clone, Serialize, Deserialize)]
414pub struct ScheduleConfig {
415 pub id: String,
416 pub name: String,
417 pub schedule: Schedule,
418 pub task_ids: Vec<String>,
419 pub enabled: bool,
420 pub max_runtime: Option<u64>, pub overlap_policy: OverlapPolicy,
422 pub notifications: Vec<NotificationConfig>,
423}
424
425#[derive(Debug, Clone, Serialize, Deserialize)]
426pub enum OverlapPolicy {
427 Allow,
429 Skip,
431 Terminate,
433 Queue,
435}
436
437#[derive(Debug, Clone, Serialize, Deserialize)]
438pub struct NotificationConfig {
439 pub type_: NotificationType,
440 pub destination: String,
441 pub events: Vec<NotificationEvent>,
442 pub enabled: bool,
443}
444
445#[derive(Debug, Clone, Serialize, Deserialize)]
446pub enum NotificationType {
447 Email,
448 Webhook,
449 Slack,
450 Discord,
451 Telegram,
452 Pushover,
453 Custom { command: String },
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
457pub enum NotificationEvent {
458 TaskStarted,
459 TaskCompleted,
460 TaskFailed,
461 TaskCancelled,
462 DiskFull,
463 RateLimited,
464 SecurityAlert,
465 Custom(String),
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
469pub struct NetworkSettings {
470 pub proxy: Option<ProxyConfig>,
471 pub dns_servers: Vec<String>,
472 pub timeout_seconds: u64,
473 pub connection_pool_size: usize,
474 pub enable_compression: bool,
475 pub enable_caching: bool,
476 pub user_agent: Option<String>,
477 pub custom_headers: HashMap<String, String>,
478}
479
480#[derive(Debug, Clone, Serialize, Deserialize)]
481pub struct ProxyConfig {
482 pub url: String,
483 pub username: Option<String>,
484 pub password: Option<String>,
485 pub bypass_for_local: bool,
486 pub bypass_list: Vec<String>,
487}
488
489#[derive(Debug, Clone, Serialize, Deserialize)]
490pub struct SecuritySettings {
491 pub enable_audit_log: bool,
492 pub audit_log_retention_days: u32,
493 pub enable_two_factor_auth: bool,
494 pub session_timeout_minutes: u32,
495 pub ip_whitelist: Vec<String>,
496 pub ip_blacklist: Vec<String>,
497 pub allowed_countries: Vec<String>,
498 pub block_tor_connections: bool,
499 pub rate_limiting: RateLimitingSettings,
500 pub encryption: SecurityEncryptionSettings,
501}
502
503#[derive(Debug, Clone, Serialize, Deserialize)]
504pub struct RateLimitingSettings {
505 pub max_requests_per_minute: u32,
506 pub max_connections_per_ip: u32,
507 pub burst_size: u32,
508}
509
510#[derive(Debug, Clone, Serialize, Deserialize)]
511pub struct SecurityEncryptionSettings {
512 pub default_algorithm: EncryptionAlgorithm,
513 pub key_rotation_days: u32,
514 pub enforce_encryption: bool,
515 pub secure_key_storage: bool,
516}
517
518impl ConfigFile {
519 pub fn new() -> Self {
520 Self {
521 version: "0.1.0".to_string(),
522 global_settings: GlobalSettings::default(),
523 accounts: Vec::new(),
524 tasks: Vec::new(),
525 encryption_keys: Vec::new(),
526 plugins: Vec::new(),
527 schedules: Vec::new(),
528 network_settings: Some(NetworkSettings::default()),
529 security_settings: Some(SecuritySettings::default()),
530 }
531 }
532
533 pub fn validate(&self) -> Result<(), Vec<String>> {
534 let mut errors = Vec::new();
535
536 if self.version != "0.1.0" {
538 errors.push(format!("Unsupported config version: {}", self.version));
539 }
540
541 for (i, account) in self.accounts.iter().enumerate() {
543 if let Err(err) = account.validate() {
544 errors.push(format!("Account {} (index {}): {}", account.name, i, err));
545 }
546 }
547
548 for (i, task) in self.tasks.iter().enumerate() {
550 if let Err(err) = task.validate() {
551 errors.push(format!("Task {} (index {}): {}", task.name, i, err));
552 }
553 }
554
555 let mut key_ids = std::collections::HashSet::new();
557 for key in &self.encryption_keys {
558 if key_ids.contains(&key.id) {
559 errors.push(format!("Duplicate encryption key ID: {}", key.id));
560 }
561 key_ids.insert(key.id.clone());
562 }
563
564 if errors.is_empty() {
565 Ok(())
566 } else {
567 Err(errors)
568 }
569 }
570
571 pub fn find_account(&self, account_id: &str) -> Option<&AccountConfig> {
572 self.accounts.iter().find(|a| a.id == account_id)
573 }
574
575 pub fn find_task(&self, task_id: &str) -> Option<&SyncTask> {
576 self.tasks.iter().find(|t| t.id == task_id)
577 }
578
579 pub fn find_encryption_key(&self, key_id: &str) -> Option<&EncryptionKey> {
580 self.encryption_keys.iter().find(|k| k.id == key_id)
581 }
582
583 pub fn find_schedule(&self, schedule_id: &str) -> Option<&ScheduleConfig> {
584 self.schedules.iter().find(|s| s.id == schedule_id)
585 }
586}
587
588impl Default for GlobalSettings {
589 fn default() -> Self {
590 Self {
591 data_dir: dirs::data_dir().map(|p| p.join("disksync")),
592 temp_dir: std::env::temp_dir().to_path_buf().into(),
593 log_level: LogLevel::Info,
594 log_retention_days: 30,
595 max_concurrent_tasks: 5,
596 default_retry_policy: RetryPolicy::default(),
597 enable_telemetry: false,
598 auto_update_check: true,
599 ui_language: "en".to_string(),
600 }
601 }
602}
603
604impl Default for NetworkSettings {
605 fn default() -> Self {
606 Self {
607 proxy: None,
608 dns_servers: vec!["8.8.8.8".to_string(), "8.8.4.4".to_string()],
609 timeout_seconds: 30,
610 connection_pool_size: 10,
611 enable_compression: true,
612 enable_caching: true,
613 user_agent: Some(format!("DiskSync/{}", env!("CARGO_PKG_VERSION"))),
614 custom_headers: HashMap::new(),
615 }
616 }
617}
618
619impl Default for SecuritySettings {
620 fn default() -> Self {
621 Self {
622 enable_audit_log: true,
623 audit_log_retention_days: 90,
624 enable_two_factor_auth: false,
625 session_timeout_minutes: 60,
626 ip_whitelist: Vec::new(),
627 ip_blacklist: Vec::new(),
628 allowed_countries: Vec::new(),
629 block_tor_connections: true,
630 rate_limiting: RateLimitingSettings::default(),
631 encryption: SecurityEncryptionSettings::default(),
632 }
633 }
634}
635
636impl Default for RateLimitingSettings {
637 fn default() -> Self {
638 Self {
639 max_requests_per_minute: 60,
640 max_connections_per_ip: 10,
641 burst_size: 5,
642 }
643 }
644}
645
646impl Default for SecurityEncryptionSettings {
647 fn default() -> Self {
648 Self {
649 default_algorithm: EncryptionAlgorithm::Aes256Gcm,
650 key_rotation_days: 90,
651 enforce_encryption: false,
652 secure_key_storage: true,
653 }
654 }
655}