feat: Desktop Sync Client (Tauri) - Grundgeruest
Tauri 2 Desktop-Client mit: Rust-Backend: - MiniCloudApi: Login, Token-Refresh, Upload, Download, Sync-Tree, Sync-Changes, File-Locking (Lock/Unlock/Heartbeat) - SyncEngine: Full-Sync (Server-Tree vs. lokales Dateisystem), Delta-Sync (nur Aenderungen seit letztem Sync), bidirektionaler Abgleich mit SHA-256 Checksummen, Ordner-Erstellung, Lock-Status-Pruefung vor Upload, Konflikt-Erkennung - FileWatcher: Filesystem-Watcher (notify crate) fuer Echtzeit- Erkennung lokaler Aenderungen, filtert temp/hidden files Vue-Frontend: - Login-Screen: Server-URL, Benutzername, Passwort - Main-Screen: Sync-Ordner setzen, Sync starten, Dateiliste mit Lock-Status, Sync-Protokoll - Dark-Mode Support Tauri-Kommandos: login, set_sync_dir, start_sync, delta_sync, get_status, get_file_tree Zum Bauen (Linux): sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev cd clients/desktop && npm install && npm run tauri build Windows/Mac: Tauri Voraussetzungen installieren, dann gleicher Befehl Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
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>,
|
||||
}
|
||||
|
||||
#[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 })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user