50385faa02
Desktop-Client komplett ueberarbeitet nach Nextcloud-Vorbild: - Persistentes SQLite-Journal (journal.rs) speichert letzten bekannten Stand pro Datei - ueberlebt Client-Neustarts (Hauptbug behoben). - Engine.rs neu: 3-Wege-Vergleich Local <-> Journal <-> Server mit sauberer Konflikt-Kopie (inkl. Username + Zeitstempel). - Loesch-Propagation: Lokal geloeschte Dateien landen im Server- Papierkorb des Owners (auch bei Freigaben). Auf dem Server geloeschte Dateien werden lokal entfernt. - Lock-Flow repariert: frischer Token bei jedem Call, Fehler-Feedback. Echtzeit-Sync: - Backend: SSE-Endpoint /api/sync/events mit In-Memory-Broadcaster. Events bei Create/Update/Delete/Lock/Unlock, Zustellung an Owner plus alle User mit Share-Permission. - Client: persistente SSE-Verbindung mit Auto-Reconnect. Events triggern sofortigen Sync (<100ms). 30s-Polling bleibt als Fallback fuer Netzwerk-Aussetzer. Weitere Fixes: - /api/sync/tree filtert is_trashed=False (Papierkorb wird nicht mehr an Clients gesynct). - Web-GUI: Lock/Unlock-Buttons pro Datei, Admin darf fremde Locks zwangsweise loesen. Rename/Delete disabled bei fremdem Lock. - Lock-Check im Backend bei PUT/DELETE (423 Locked Response). - Background-Sync nur noch einmal pro Prozess gestartet, liest sync_paths pro Iteration neu - add/remove wirkt sofort, kein Client-Neustart mehr noetig. - Watcher werden pro Sync-Pfad individuell verwaltet. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
60 lines
2.0 KiB
Rust
60 lines
2.0 KiB
Rust
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
|
use std::path::PathBuf;
|
|
use std::sync::mpsc;
|
|
|
|
pub struct FileWatcher {
|
|
_watcher: RecommendedWatcher,
|
|
pub receiver: mpsc::Receiver<FileChange>,
|
|
pub path: PathBuf,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct FileChange {
|
|
pub path: PathBuf,
|
|
pub kind: ChangeKind,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum ChangeKind {
|
|
Created,
|
|
Modified,
|
|
Deleted,
|
|
}
|
|
|
|
impl FileWatcher {
|
|
pub fn new(watch_dir: &PathBuf) -> Result<Self, String> {
|
|
let (tx, rx) = mpsc::channel();
|
|
|
|
let mut watcher = RecommendedWatcher::new(
|
|
move |result: Result<Event, notify::Error>| {
|
|
if let Ok(event) = result {
|
|
let kind = match event.kind {
|
|
EventKind::Create(_) => Some(ChangeKind::Created),
|
|
EventKind::Modify(_) => Some(ChangeKind::Modified),
|
|
EventKind::Remove(_) => Some(ChangeKind::Deleted),
|
|
_ => None,
|
|
};
|
|
if let Some(kind) = kind {
|
|
for path in event.paths {
|
|
// Skip hidden files and temp files
|
|
let name = path.file_name()
|
|
.and_then(|n| n.to_str())
|
|
.unwrap_or("");
|
|
if name.starts_with('.') || name.starts_with('~') || name.ends_with(".tmp") {
|
|
continue;
|
|
}
|
|
let _ = tx.send(FileChange { path, kind: kind.clone() });
|
|
}
|
|
}
|
|
}
|
|
},
|
|
Config::default(),
|
|
).map_err(|e| format!("Watcher-Fehler: {}", e))?;
|
|
|
|
watcher.watch(watch_dir.as_ref(), RecursiveMode::Recursive)
|
|
.map_err(|e| format!("Watch-Fehler: {}", e))?;
|
|
|
|
Ok(Self { _watcher: watcher, receiver: rx, path: watch_dir.clone() })
|
|
}
|
|
}
|