feat(cloud-files): Mount-Pfad persistieren + Force-Cleanup fuer tote Sync-Roots
- cloud_files_mount in AppConfig -> bleibt ueber Neustarts erhalten - Beim Auto-Login wird Cloud-Files automatisch wieder aktiviert - Neue Commands cloud_files_get_mount und cloud_files_force_cleanup - UI zeigt "Aufraeumen"-Button wenn Mount gesetzt aber nicht aktiv, damit User einen Ordner der nach hartem Beenden des Clients als toter Sync-Root haengt wieder freigeben/loeschen kann Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6274567219
commit
be121190b3
|
|
@ -949,6 +949,12 @@ async fn cloud_files_enable(
|
||||||
|
|
||||||
*state.cloud_files_loop.lock().unwrap() = Some(handle);
|
*state.cloud_files_loop.lock().unwrap() = Some(handle);
|
||||||
*state.cloud_files_watcher.lock().unwrap() = Some(watcher);
|
*state.cloud_files_watcher.lock().unwrap() = Some(watcher);
|
||||||
|
|
||||||
|
// Mount-Pfad persistieren, damit er beim Neustart wiederkommt.
|
||||||
|
let mut cfg = AppConfig::load();
|
||||||
|
cfg.cloud_files_mount = mount_point.clone();
|
||||||
|
let _ = cfg.save();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -963,7 +969,33 @@ async fn cloud_files_disable(
|
||||||
let _ = handle.tx.send(cloud_files::sync_loop::LoopMessage::Shutdown);
|
let _ = handle.tx.send(cloud_files::sync_loop::LoopMessage::Shutdown);
|
||||||
}
|
}
|
||||||
state.cloud_files_watcher.lock().unwrap().take();
|
state.cloud_files_watcher.lock().unwrap().take();
|
||||||
cloud_files::unregister_sync_root(&PathBuf::from(mount_point))
|
let result = cloud_files::unregister_sync_root(&PathBuf::from(&mount_point));
|
||||||
|
|
||||||
|
// Auch bei Fehler Mount aus Config loeschen, damit der Client nicht
|
||||||
|
// endlos versucht, einen toten Pfad wiederherzustellen.
|
||||||
|
let mut cfg = AppConfig::load();
|
||||||
|
cfg.cloud_files_mount.clear();
|
||||||
|
let _ = cfg.save();
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn cloud_files_get_mount() -> String {
|
||||||
|
AppConfig::load().cloud_files_mount
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notfall-Aufraeumen: Ordner als Sync-Root deregistrieren, auch wenn
|
||||||
|
/// kein Callback-Handle existiert. Nuetzlich wenn der Client hart beendet
|
||||||
|
/// wurde und ein "toter" Ordner in Windows haengt.
|
||||||
|
#[tauri::command]
|
||||||
|
async fn cloud_files_force_cleanup(mount_point: String) -> Result<(), String> {
|
||||||
|
let mp = PathBuf::from(&mount_point);
|
||||||
|
let _ = cloud_files::unregister_sync_root(&mp);
|
||||||
|
let mut cfg = AppConfig::load();
|
||||||
|
cfg.cloud_files_mount.clear();
|
||||||
|
let _ = cfg.save();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|
@ -1179,6 +1211,8 @@ pub fn run() {
|
||||||
cloud_files_supported,
|
cloud_files_supported,
|
||||||
cloud_files_enable,
|
cloud_files_enable,
|
||||||
cloud_files_disable,
|
cloud_files_disable,
|
||||||
|
cloud_files_get_mount,
|
||||||
|
cloud_files_force_cleanup,
|
||||||
cloud_files_pin,
|
cloud_files_pin,
|
||||||
cloud_files_unpin,
|
cloud_files_unpin,
|
||||||
])
|
])
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,10 @@ pub struct AppConfig {
|
||||||
pub auto_start: bool,
|
pub auto_start: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub start_minimized: bool,
|
pub start_minimized: bool,
|
||||||
|
/// Persistierter Mount-Punkt der Cloud-Files-Integration.
|
||||||
|
/// Leer = nicht aktiv. Wird beim App-Start wieder aktiviert.
|
||||||
|
#[serde(default)]
|
||||||
|
pub cloud_files_mount: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppConfig {
|
impl AppConfig {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,27 @@ const cloudFilesError = ref("");
|
||||||
async function checkCloudFilesSupport() {
|
async function checkCloudFilesSupport() {
|
||||||
try { cloudFilesSupported.value = await invoke("cloud_files_supported"); }
|
try { cloudFilesSupported.value = await invoke("cloud_files_supported"); }
|
||||||
catch { cloudFilesSupported.value = false; }
|
catch { cloudFilesSupported.value = false; }
|
||||||
|
try {
|
||||||
|
const saved = await invoke("cloud_files_get_mount");
|
||||||
|
if (saved) cloudFilesMountPoint.value = saved;
|
||||||
|
} catch { /* no saved mount */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function forceCleanupCloudFiles() {
|
||||||
|
if (!cloudFilesMountPoint.value) return;
|
||||||
|
if (!confirm(`Sync-Root unter ${cloudFilesMountPoint.value} zwangsweise aufraeumen?\n\nDanach kann der Ordner ggf. geloescht werden.`)) return;
|
||||||
|
cloudFilesError.value = "";
|
||||||
|
cloudFilesBusy.value = true;
|
||||||
|
try {
|
||||||
|
await invoke("cloud_files_force_cleanup", { mountPoint: cloudFilesMountPoint.value });
|
||||||
|
cloudFilesActive.value = false;
|
||||||
|
cloudFilesMountPoint.value = "";
|
||||||
|
syncLog.value = [`[${ts()}] Cloud-Files Zwangsbereinigung durchgefuehrt`, ...syncLog.value].slice(0, 200);
|
||||||
|
} catch (err) {
|
||||||
|
cloudFilesError.value = String(err);
|
||||||
|
} finally {
|
||||||
|
cloudFilesBusy.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function browseCfMount() {
|
async function browseCfMount() {
|
||||||
|
|
@ -337,7 +358,7 @@ function formatSize(b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
checkCloudFilesSupport();
|
await checkCloudFilesSupport();
|
||||||
// Try auto-login with saved credentials
|
// Try auto-login with saved credentials
|
||||||
try {
|
try {
|
||||||
const saved = await invoke("load_saved_config");
|
const saved = await invoke("load_saved_config");
|
||||||
|
|
@ -357,6 +378,15 @@ onMounted(async () => {
|
||||||
if (syncPaths.value.length > 0) {
|
if (syncPaths.value.length > 0) {
|
||||||
await startSync();
|
await startSync();
|
||||||
}
|
}
|
||||||
|
// Cloud-Files automatisch reaktivieren, wenn Mount gespeichert.
|
||||||
|
if (cloudFilesSupported.value && cloudFilesMountPoint.value) {
|
||||||
|
try {
|
||||||
|
await invoke("cloud_files_enable", { mountPoint: cloudFilesMountPoint.value });
|
||||||
|
cloudFilesActive.value = true;
|
||||||
|
} catch (e) {
|
||||||
|
cloudFilesError.value = `Auto-Reaktivierung fehlgeschlagen: ${e}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
syncStatus.value = "Auto-Login fehlgeschlagen";
|
syncStatus.value = "Auto-Login fehlgeschlagen";
|
||||||
// Show login screen with pre-filled fields
|
// Show login screen with pre-filled fields
|
||||||
|
|
@ -463,6 +493,12 @@ onUnmounted(() => { unlistenStatus?.(); unlistenLog?.(); unlistenError?.(); unli
|
||||||
</button>
|
</button>
|
||||||
<button v-else class="btn-secondary" :disabled="cloudFilesBusy"
|
<button v-else class="btn-secondary" :disabled="cloudFilesBusy"
|
||||||
@click="disableCloudFiles">Deaktivieren</button>
|
@click="disableCloudFiles">Deaktivieren</button>
|
||||||
|
<button v-if="cloudFilesMountPoint && !cloudFilesActive"
|
||||||
|
class="btn-secondary" :disabled="cloudFilesBusy"
|
||||||
|
@click="forceCleanupCloudFiles"
|
||||||
|
title="Toten Sync-Root nach hartem Beenden des Clients aufraeumen">
|
||||||
|
Aufraeumen
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="cloudFilesError" class="error" style="margin-top:0.5rem">{{ cloudFilesError }}</div>
|
<div v-if="cloudFilesError" class="error" style="margin-top:0.5rem">{{ cloudFilesError }}</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue