fix: Sync vergleicht Timestamps - Server-Aenderungen nicht ueberschreiben

Problem: Wenn eine Datei auf dem Server geaendert wurde, hat der
Client sie trotzdem mit der lokalen (alten) Version ueberschrieben.
Der Sync hat nur Checksums verglichen aber nicht geprueft wer neuer ist.

Fix: Bei unterschiedlichen Checksums wird jetzt der Timestamp verglichen:
- Server neuer (updated_at > lokales modified) -> Download vom Server
- Lokal neuer (modified > Server updated_at) -> Upload zum Server
- Log zeigt "Server->Lokal" oder "Lokal->Server" statt nur "Aktualisiert"

Betrifft alle drei Sync-Methoden:
- sync_virtual (Offline-markierte Dateien)
- sync_upload_new (Virtual Mode Upload)
- sync_full_upload (Full Sync Upload)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Hacker 2026-04-12 01:42:46 +02:00
parent 11fd11aa45
commit 4673423e2f
1 changed files with 86 additions and 20 deletions

View File

@ -98,17 +98,35 @@ impl SyncEngine {
} else {
// Check if real file exists (manually downloaded or offline-marked)
if local_path.exists() {
// Real file exists - check if it's been modified
let local_hash = compute_file_hash(&local_path);
if local_hash != entry.checksum.as_deref().unwrap_or("") {
// Local file changed - upload it
if !entry.locked.unwrap_or(false) {
match self.api.upload_file(&local_path, entry.id.into()).await {
Ok(_) => log.push(format!("Hochgeladen: {}", entry.name)),
Err(e) => log.push(format!("Upload-Fehler {}: {}", entry.name, e)),
let server_hash = entry.checksum.as_deref().unwrap_or("");
if local_hash != server_hash {
if entry.locked.unwrap_or(false) {
log.push(format!("Zurueckgehalten (gesperrt): {}", entry.name));
continue;
}
// Who is newer?
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));
let server_is_newer = match (local_modified, server_modified) {
(Some(lt), Some(st)) => st > lt,
_ => false,
};
if server_is_newer {
match self.api.download_file(entry.id, &local_path).await {
Ok(_) => log.push(format!("Server->Lokal: {}", entry.name)),
Err(e) => log.push(format!("Download-Fehler {}: {}", entry.name, e)),
}
} else {
log.push(format!("Zurueckgehalten (gesperrt): {}", entry.name));
match self.api.upload_file(&local_path, None).await {
Ok(_) => log.push(format!("Lokal->Server: {}", entry.name)),
Err(e) => log.push(format!("Upload-Fehler {}: {}", entry.name, e)),
}
}
}
continue;
@ -198,16 +216,41 @@ impl SyncEngine {
} 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("") {
if se.locked.unwrap_or(false) {
log.push(format!("Zurueckgehalten (gesperrt): {}", name));
continue;
}
let local_hash = compute_file_hash(&path);
let server_hash = se.checksum.as_deref().unwrap_or("");
if local_hash != server_hash {
// Hashes differ - who is newer?
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));
let server_is_newer = match (local_modified, server_modified) {
(Some(local_t), Some(server_t)) => server_t > local_t,
_ => false, // if we can't compare, don't overwrite server
};
if server_is_newer {
// Server is newer -> download
match self.api.download_file(se.id, &path).await {
Ok(_) => log.push(format!("Server->Lokal: {}", name)),
Err(e) => log.push(format!("Download-Fehler {}: {}", name, e)),
}
} else {
// Local is newer -> upload
match self.api.upload_file(&path, parent_id).await {
Ok(_) => log.push(format!("Aktualisiert: {}", name)),
Ok(_) => log.push(format!("Lokal->Server: {}", name)),
Err(e) => log.push(format!("Upload-Fehler {}: {}", name, e)),
}
}
} else {
log.push(format!("Zurueckgehalten (gesperrt): {}", name));
}
}
}
@ -289,17 +332,40 @@ impl SyncEngine {
}
}
} else {
let needs_upload = if let Some(se) = server_names.get(&name) {
if let Some(se) = server_names.get(&name) {
if se.locked.unwrap_or(false) {
log.push(format!("Zurueckgehalten (gesperrt): {}", name));
continue;
}
compute_file_hash(&path) != se.checksum.as_deref().unwrap_or("")
} else {
true
};
let local_hash = compute_file_hash(&path);
let server_hash = se.checksum.as_deref().unwrap_or("");
if local_hash != server_hash {
// Who is newer?
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));
if needs_upload {
let server_is_newer = match (local_modified, server_modified) {
(Some(lt), Some(st)) => st > lt,
_ => false,
};
if server_is_newer {
match self.api.download_file(se.id, &path).await {
Ok(_) => log.push(format!("Server->Lokal: {}", name)),
Err(e) => log.push(format!("Download-Fehler {}: {}", name, e)),
}
} else {
match self.api.upload_file(&path, parent_id).await {
Ok(_) => log.push(format!("Lokal->Server: {}", name)),
Err(e) => log.push(format!("Upload-Fehler {}: {}", name, e)),
}
}
}
} else {
// New file, not on server
match self.api.upload_file(&path, parent_id).await {
Ok(_) => log.push(format!("Hochgeladen: {}", name)),
Err(e) => log.push(format!("Upload-Fehler {}: {}", name, e)),