From 9c036d5b617651ef70ff2c0e61cd1e3b0085ad70 Mon Sep 17 00:00:00 2001 From: victorwon2001 <192616110+victorwon2001@users.noreply.github.com> Date: Fri, 11 Sep 2026 05:18:00 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=8C=89=E5=B1=82=E7=BA=A7=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=20WebDAV=20=E5=90=8C=E6=AD=A5=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/core/cloud_sync/operator.rs | 75 ++++++++++++++++++++++- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/core/cloud_sync/operator.rs b/src-tauri/src/core/cloud_sync/operator.rs index 574a864d..1729a071 100644 --- a/src-tauri/src/core/cloud_sync/operator.rs +++ b/src-tauri/src/core/cloud_sync/operator.rs @@ -437,12 +437,28 @@ fn storage_timeout_layer() -> TimeoutLayer { } pub(super) async fn ensure_remote_layout(remote: &CloudRemote, base_root: &str) -> AppResult<()> { - remote - .create_dir(&remote_path(base_root, super::remote::SYNC_SNAPSHOTS_DIR)) - .await?; + for path in remote_layout_paths(remote, base_root) { + remote.create_dir(&path).await?; + } Ok(()) } +fn remote_layout_paths(remote: &CloudRemote, base_root: &str) -> Vec { + let snapshots_dir = remote_path(base_root, super::remote::SYNC_SNAPSHOTS_DIR); + let is_webdav = matches!( + remote, + CloudRemote::OpenDal(operator) if operator.info().scheme() == "webdav" + ); + if !is_webdav { + return vec![snapshots_dir]; + } + + snapshots_dir + .match_indices('/') + .map(|(index, _)| snapshots_dir[..=index].to_string()) + .collect() +} + pub(super) fn map_storage_error(error: opendal::Error) -> AppError { let raw = error.to_string(); if let Some(message) = map_webdav_auth_error(&raw) { @@ -1227,6 +1243,59 @@ fn escape_digest_value(value: &str) -> String { mod tests { use super::*; + fn webdav_remote() -> CloudRemote { + let mut settings = CloudSyncSettings::default(); + settings.provider = "webdav".to_string(); + settings.webdav.endpoint = "https://dav.example.com".to_string(); + build_remote(&settings).expect("build WebDAV remote") + } + + #[test] + fn remote_layout_paths_expand_only_for_webdav() { + let webdav = webdav_remote(); + assert_eq!( + remote_layout_paths(&webdav, "nyaterm"), + vec![ + "nyaterm/".to_string(), + "nyaterm/sync/".to_string(), + "nyaterm/sync/snapshots/".to_string(), + ] + ); + + let memory = CloudRemote::Memory(MemoryRemote::default()); + assert_eq!( + remote_layout_paths(&memory, "nyaterm"), + vec!["nyaterm/sync/snapshots/".to_string()] + ); + } + + #[test] + fn webdav_remote_layout_paths_support_empty_and_nested_roots() { + let webdav = webdav_remote(); + + assert_eq!( + remote_layout_paths(&webdav, ""), + vec!["sync/".to_string(), "sync/snapshots/".to_string()] + ); + assert_eq!( + remote_layout_paths(&webdav, "/nyaterm/"), + vec![ + "nyaterm/".to_string(), + "nyaterm/sync/".to_string(), + "nyaterm/sync/snapshots/".to_string(), + ] + ); + assert_eq!( + remote_layout_paths(&webdav, "team/nyaterm"), + vec![ + "team/".to_string(), + "team/nyaterm/".to_string(), + "team/nyaterm/sync/".to_string(), + "team/nyaterm/sync/snapshots/".to_string(), + ] + ); + } + #[test] fn webdav_401_error_reports_generic_auth_hint() { let message = map_webdav_auth_error(