One-click backups for AT Protocol
at main 4.7 kB view raw
1use chrono::{DateTime, Utc}; 2use serde::{Deserialize, Serialize}; 3use serde_json::json; 4use std::sync::Arc; 5use std::time::{Duration, Instant}; 6use tauri::{App, AppHandle, Emitter, Manager}; 7use tauri_plugin_store::StoreExt; 8use tokio::sync::Mutex; 9use tokio::time::sleep; 10 11#[derive(Debug, Clone, Serialize, Deserialize)] 12pub struct BackupSettings { 13 pub backupFrequency: String, // "daily" or "weekly" 14 pub lastBackupDate: Option<String>, 15} 16 17pub struct BackgroundScheduler { 18 app: AppHandle, 19 is_running: Arc<Mutex<bool>>, 20} 21 22impl BackgroundScheduler { 23 pub fn new(app: AppHandle) -> Self { 24 Self { 25 app, 26 is_running: Arc::new(Mutex::new(false)), 27 } 28 } 29 30 pub async fn start(&self) { 31 let mut is_running = self.is_running.lock().await; 32 if *is_running { 33 return; 34 } 35 *is_running = true; 36 drop(is_running); 37 38 let is_running = self.is_running.clone(); 39 let app = self.app.clone(); 40 41 tokio::spawn(async move { 42 loop { 43 // Your shared flag 44 if !*is_running.lock().await { 45 break; 46 } 47 48 // Use cloned app 49 if let Err(e) = Self::check_and_perform_backup(&app).await { 50 eprintln!("Background backup check failed: {}", e); 51 } 52 53 sleep(Duration::from_secs(30 * 60)).await; 54 } 55 }); 56 } 57 58 pub async fn stop(&self) { 59 let mut is_running = self.is_running.lock().await; 60 *is_running = false; 61 } 62 63 async fn check_and_perform_backup(app: &AppHandle) -> Result<(), Box<dyn std::error::Error>> { 64 println!("Background: Checking if backup is needed..."); 65 // Get settings from store 66 let store = app.store("settings.json")?; 67 let raw_settings: Option<serde_json::Value> = store.get("settings"); 68 69 let value = raw_settings.unwrap_or(json!({ 70 "backupFrequency": "daily", 71 "last_backup_date": null 72 })); 73 74 let settings: BackupSettings = serde_json::from_value(value)?; 75 76 // Check if backup is needed 77 if Self::should_perform_backup(&settings).await? { 78 println!("Background: Backup due, starting backup..."); 79 80 // Emit event to frontend to perform backup 81 match app.emit("perform-backup", serde_json::json!({})) { 82 Ok(_) => println!("Event emitted successfully"), 83 Err(e) => eprintln!("Failed to emit event: {}", e), 84 } 85 86 println!("Background: Backup completed"); 87 } 88 89 Ok(()) 90 } 91 92 async fn should_perform_backup( 93 settings: &BackupSettings, 94 ) -> Result<bool, Box<dyn std::error::Error>> { 95 println!("[DEBUG] Checking if backup should be performed..."); 96 97 if settings.lastBackupDate.is_none() { 98 println!("[DEBUG] No last_backup_date found; should perform backup."); 99 return Ok(true); 100 } 101 102 let last_backup_str = settings.lastBackupDate.as_ref().unwrap(); 103 println!("[DEBUG] Last backup date string: {}", last_backup_str); 104 105 let last_backup = DateTime::parse_from_rfc3339(last_backup_str)?; 106 let now = Utc::now(); 107 let time_diff = now.signed_duration_since(last_backup); 108 109 println!("[DEBUG] Current time: {}", now); 110 println!("[DEBUG] Last backup time: {}", last_backup); 111 println!( 112 "[DEBUG] Time since last backup: {} seconds", 113 time_diff.num_seconds() 114 ); 115 116 let required_interval = match settings.backupFrequency.as_str() { 117 "daily" => chrono::Duration::days(1), 118 "weekly" => chrono::Duration::weeks(1), 119 other => { 120 println!("[DEBUG] Unknown frequency '{}', defaulting to daily", other); 121 chrono::Duration::days(1) 122 } 123 }; 124 125 println!( 126 "[DEBUG] Required interval (seconds): {}", 127 required_interval.num_seconds() 128 ); 129 println!( 130 "[DEBUG] Should perform backup? {}", 131 time_diff >= required_interval 132 ); 133 134 Ok(time_diff >= required_interval) 135 } 136} 137 138#[tauri::command] 139pub async fn start_background_scheduler(app: AppHandle) -> Result<(), String> { 140 println!("Starting background scheduler..."); 141 let scheduler = BackgroundScheduler::new(app); 142 scheduler.start().await; 143 Ok(()) 144} 145 146#[tauri::command] 147pub async fn stop_background_scheduler() -> Result<(), String> { 148 // This would need to be implemented with a global scheduler reference 149 // For now, we'll handle this differently 150 Ok(()) 151}