From b3da50e6ce3072e44a3df4cf1e1d7c7a31fc65ff Mon Sep 17 00:00:00 2001 From: Stefan Hacker Date: Sun, 12 Apr 2026 02:05:10 +0200 Subject: [PATCH] fix: Server->Client Sync + File Locking repariert Server->Client Sync: - Server sendet Timestamps ohne Timezone (2026-04-11T12:49:24.735436) - parse_from_rfc3339 braucht Timezone -> schlug still fehl - Client dachte IMMER er sei neuer -> Upload statt Download - Fix: parse_server_time() akzeptiert beides (mit/ohne Timezone) - Probiert RFC3339, dann NaiveDateTime mit Microseconds, dann ohne File Locking: - open_cloud_file nutzte API-Clone vom SyncEngine (evtl. alter Token) - Jetzt direkt state.api (immer aktueller Token nach Refresh) - Lock wird zuverlaessig gesetzt beim Oeffnen von .cloud Dateien Co-Authored-By: Claude Opus 4.6 (1M context) --- clients/desktop/src-tauri/src/lib.rs | 6 ++--- clients/desktop/src-tauri/src/sync/engine.rs | 24 ++++++++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/clients/desktop/src-tauri/src/lib.rs b/clients/desktop/src-tauri/src/lib.rs index 8237f90..96a2b09 100644 --- a/clients/desktop/src-tauri/src/lib.rs +++ b/clients/desktop/src-tauri/src/lib.rs @@ -230,10 +230,8 @@ async fn run_sync_now(state: State<'_, AppState>) -> Result, String> #[tauri::command] async fn open_cloud_file(state: State<'_, AppState>, cloud_path: String) -> Result { - let engine = { - let guard = state.sync_engine.lock().unwrap(); - guard.as_ref().ok_or("Sync nicht gestartet")?.api.clone() - }; + let engine = state.api.lock().unwrap().clone() + .ok_or("Nicht eingeloggt")?; let path = PathBuf::from(&cloud_path); let content = std::fs::read_to_string(&path).map_err(|e| e.to_string())?; diff --git a/clients/desktop/src-tauri/src/sync/engine.rs b/clients/desktop/src-tauri/src/sync/engine.rs index 736eb29..1325960 100644 --- a/clients/desktop/src-tauri/src/sync/engine.rs +++ b/clients/desktop/src-tauri/src/sync/engine.rs @@ -109,8 +109,7 @@ impl SyncEngine { let local_modified = std::fs::metadata(&local_path) .and_then(|m| m.modified()).ok(); let server_modified = entry.updated_at.as_deref() - .and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok()) - .map(|dt| std::time::SystemTime::from(dt)); + .and_then(parse_server_time); let server_is_newer = match (local_modified, server_modified) { (Some(lt), Some(st)) => st > lt, @@ -344,8 +343,7 @@ impl SyncEngine { let local_modified = std::fs::metadata(&path) .and_then(|m| m.modified()).ok(); let server_modified = se.updated_at.as_deref() - .and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok()) - .map(|dt| std::time::SystemTime::from(dt)); + .and_then(parse_server_time); let server_is_newer = match (local_modified, server_modified) { (Some(lt), Some(st)) => st > lt, @@ -448,6 +446,24 @@ fn find_subtree(tree: &[FileEntry], folder_id: i64) -> Option> { None } +/// Parse a server timestamp (may or may not have timezone) +fn parse_server_time(s: &str) -> Option { + // Try with timezone first (RFC3339) + if let Ok(dt) = chrono::DateTime::parse_from_rfc3339(s) { + return Some(std::time::SystemTime::from(dt)); + } + // Try without timezone (naive, assume UTC) + if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f") { + let utc = dt.and_utc(); + return Some(std::time::SystemTime::from(utc)); + } + if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S") { + let utc = dt.and_utc(); + return Some(std::time::SystemTime::from(utc)); + } + None +} + pub fn compute_file_hash(path: &Path) -> String { let data = match std::fs::read(path) { Ok(d) => d,