feat(sync): auto-build Nextcloud/ownCloud DAV path from the server URL

For nextcloud/owncloud, WebDavTarget now derives the canonical
…/remote.php/dav/files/<user>/ path from the entered server URL (origin only, so
a pasted full path normalizes too); other providers use the URL verbatim.
test_sync_target also reuses the stored password when editing an existing
webdav target (unless a new one is typed), so Test works after a URL fix without
re-entering the password. Fixes the confusing 'bare host connects, full URL
fails' behavior (the former was a 404 false-positive, the latter a blank-secret
401).
This commit is contained in:
iamdoubz
2026-07-06 11:10:44 -05:00
parent fb790091fd
commit 8f6e8041d6
2 changed files with 104 additions and 7 deletions
+43 -4
View File
@@ -2349,15 +2349,46 @@ pub async fn test_sync_target(
let hints = provider_setup_hints(config.provider_hint.as_deref());
let (target, temp_ref): (Box<dyn SyncTarget>, Option<String>) =
if let Some(id) = config.id.clone() {
// Existing target (any kind) — dispatch to its concrete impl.
let row = state
.store
.get_sync_target(&id)
.await
.map_err(|e| WaError::new("sync", e.to_string()))?;
let target = crate::sync::build_sync_target(&row)
.map_err(|e| WaError::new("sync", e.to_string()))?;
(target, None)
if row.kind == "webdav" {
// Editing an existing webdav target: test the values on screen but
// reuse the STORED password (via its credential_ref) unless the user
// typed a new one — so "Test" works after a URL fix without having to
// re-enter the password.
let (credential_ref, temp_ref) =
if let Some(secret) = config.secret.as_deref().filter(|s| !s.is_empty()) {
let temp_ref = format!("wa-sync-test-{}", uuid::Uuid::new_v4());
crate::sync::credentials::set(&temp_ref, secret)
.map_err(|e| WaError::new("sync", e.to_string()))?;
(temp_ref.clone(), Some(temp_ref))
} else {
(row.credential_ref.clone(), None)
};
let target = crate::sync::WebDavTarget {
base_url: config.base_url.clone().or(row.base_url.clone()).unwrap_or_default(),
provider_hint: config.provider_hint.clone().or(row.provider_hint.clone()),
remote_base_path: config
.remote_base_path
.clone()
.unwrap_or(row.remote_base_path.clone()),
username: config.username.clone().or(row.username.clone()).unwrap_or_default(),
credential_ref,
third_party: false,
allow_plaintext_lan: config
.allow_plaintext_lan
.unwrap_or(row.allow_plaintext_lan),
};
(Box::new(target), temp_ref)
} else {
// Non-webdav (OAuth) target — dispatch to its concrete impl as-is.
let target = crate::sync::build_sync_target(&row)
.map_err(|e| WaError::new("sync", e.to_string()))?;
(target, None)
}
} else {
// Unsaved WebDAV target: stash the secret under a temp credential_ref so
// the same resolve-at-use path works, then clean it up after the test.
@@ -2372,6 +2403,7 @@ pub async fn test_sync_target(
}
let target = crate::sync::WebDavTarget {
base_url,
provider_hint: config.provider_hint.clone(),
remote_base_path: config
.remote_base_path
.clone()
@@ -3028,6 +3060,13 @@ mod tests {
enabled,
third_party,
host: Some(host.to_string()),
upload_transcript: true,
upload_notes: true,
upload_summary: true,
upload_recording: false,
trigger_on_finalize: true,
allow_plaintext_lan: false,
encrypt_before_upload: false,
}
}