From 81574c8991d329cf1c61e968d245a3a937bd1f6e Mon Sep 17 00:00:00 2001 From: Stefan Hacker Date: Sun, 12 Apr 2026 01:04:03 +0200 Subject: [PATCH] fix: Virtual Mode laedt neue lokale Dateien jetzt hoch Problem: Im Virtual Mode wurden nur .cloud Platzhalter fuer Server-Dateien erstellt, aber neue lokale Dateien wurden nie hochgeladen. Der Watcher hat die Aenderung erkannt aber der Sync hat sie ignoriert. Fix: sync_upload_new() wird jetzt auch im Virtual Mode aufgerufen. Scannt den lokalen Ordner nach Dateien die auf dem Server nicht existieren und laedt sie hoch. Auch geaenderte lokale Dateien (Checksum-Vergleich) werden aktualisiert. Gesperrte Dateien werden zurueckgehalten. Co-Authored-By: Claude Opus 4.6 (1M context) --- clients/desktop/src-tauri/src/sync/engine.rs | 68 ++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/clients/desktop/src-tauri/src/sync/engine.rs b/clients/desktop/src-tauri/src/sync/engine.rs index 5bef6ae..aa089cb 100644 --- a/clients/desktop/src-tauri/src/sync/engine.rs +++ b/clients/desktop/src-tauri/src/sync/engine.rs @@ -67,6 +67,8 @@ impl SyncEngine { match sp.mode { SyncMode::Virtual => { self.sync_virtual(&entries, &local_dir, &sp.server_path, &mut log).await; + // Also upload new local files (not on server yet) + self.sync_upload_new(&entries, &local_dir, sp.server_folder_id, &mut log).await; } SyncMode::Full => { self.sync_full_download(&entries, &local_dir, &mut log).await; @@ -147,6 +149,72 @@ impl SyncEngine { } } + /// Upload new local files that don't exist on server yet (for both Virtual + Full mode) + async fn sync_upload_new(&self, server_entries: &[FileEntry], local_dir: &Path, + parent_id: Option, log: &mut Vec) { + let server_names: std::collections::HashSet = server_entries.iter() + .map(|e| e.name.clone()).collect(); + + let entries = match std::fs::read_dir(local_dir) { + Ok(e) => e, + Err(_) => return, + }; + + for entry in entries.flatten() { + let name = entry.file_name().to_string_lossy().to_string(); + let path = entry.path(); + + // Skip hidden, temp, .cloud files + if name.starts_with('.') || name.starts_with('~') + || name.ends_with(".tmp") || name.ends_with(".cloud") { + continue; + } + + if path.is_dir() { + // New folder: create on server + recurse + if !server_names.contains(&name) { + match self.api.create_folder(&name, parent_id).await { + Ok(folder) => { + log.push(format!("Ordner erstellt: {}", name)); + Box::pin(self.sync_upload_new(&[], &path, Some(folder.id), log)).await; + } + Err(e) => log.push(format!("Ordner-Fehler {}: {}", name, e)), + } + } else { + // Existing folder: recurse into it + let sub = server_entries.iter().find(|e| e.name == name); + let children = sub.and_then(|e| e.children.as_ref()) + .map(|c| c.as_slice()).unwrap_or(&[]); + let sub_id = sub.map(|e| e.id); + Box::pin(self.sync_upload_new(children, &path, sub_id, log)).await; + } + } else { + // New file: upload + if !server_names.contains(&name) { + match self.api.upload_file(&path, parent_id).await { + Ok(_) => log.push(format!("Hochgeladen: {}", name)), + Err(e) => log.push(format!("Upload-Fehler {}: {}", name, e)), + } + } else { + // Existing file: check if changed (checksum compare) + if let Some(se) = server_entries.iter().find(|e| e.name == name) { + if !se.locked.unwrap_or(false) { + let local_hash = compute_file_hash(&path); + if local_hash != se.checksum.as_deref().unwrap_or("") { + match self.api.upload_file(&path, parent_id).await { + Ok(_) => log.push(format!("Aktualisiert: {}", name)), + Err(e) => log.push(format!("Upload-Fehler {}: {}", name, e)), + } + } + } else { + log.push(format!("Zurueckgehalten (gesperrt): {}", name)); + } + } + } + } + } + } + /// Full sync: download all files from server async fn sync_full_download(&self, entries: &[FileEntry], local_dir: &Path, log: &mut Vec) {