feat(backup): add local backup import flow (#5669)

* feat(backup): add local backup import flow

* refactor(backup): robustify history listing and propagate import error details

* docs: Changelog.md
This commit is contained in:
Sline
2025-12-19 17:46:31 +08:00
committed by GitHub
parent b4e25951b4
commit bd8eccdcea
22 changed files with 240 additions and 29 deletions

View File

@@ -27,6 +27,12 @@ pub async fn restore_local_backup(filename: String) -> CmdResult<()> {
feat::restore_local_backup(filename).await.stringify_err()
}
/// Import local backup into the app's backup directory
#[tauri::command]
pub async fn import_local_backup(source: String) -> CmdResult<String> {
feat::import_local_backup(source).await.stringify_err()
}
/// Export local backup to a user selected destination
#[tauri::command]
pub async fn export_local_backup(filename: String, destination: String) -> CmdResult<()> {

View File

@@ -158,6 +158,53 @@ where
Ok(final_name)
}
/// Import an existing backup file into the local backup directory
pub async fn import_local_backup(source: String) -> Result<String> {
let source_path = PathBuf::from(source.as_str());
if !source_path.exists() {
return Err(anyhow!("Backup file not found: {source}"));
}
if !source_path.is_file() {
return Err(anyhow!("Backup path is not a file: {source}"));
}
let ext = source_path
.extension()
.and_then(|ext| ext.to_str())
.map(|ext| ext.to_ascii_lowercase())
.unwrap_or_default();
if ext != "zip" {
return Err(anyhow!("Only .zip backup files are supported"));
}
let file_name = source_path
.file_name()
.and_then(|name| name.to_str())
.ok_or_else(|| anyhow!("Invalid backup file name"))?;
let backup_dir = local_backup_dir()?;
let target_path = backup_dir.join(file_name);
if target_path == source_path {
// Already located in the backup directory
return Ok(file_name.to_string().into());
}
if let Some(parent) = target_path.parent() {
fs::create_dir_all(parent).await?;
}
if target_path.exists() {
return Err(anyhow!("Backup file already exists: {file_name}"));
}
fs::copy(&source_path, &target_path)
.await
.map_err(|err| anyhow!("Failed to import backup file: {err:#?}"))?;
Ok(file_name.to_string().into())
}
async fn move_file(from: PathBuf, to: PathBuf) -> Result<()> {
if let Some(parent) = to.parent() {
fs::create_dir_all(parent).await?;

View File

@@ -210,6 +210,7 @@ mod app_init {
cmd::list_local_backup,
cmd::delete_local_backup,
cmd::restore_local_backup,
cmd::import_local_backup,
cmd::export_local_backup,
cmd::create_webdav_backup,
cmd::save_webdav_config,