fix(profile): handle missing chain files without breaking rollback

This commit is contained in:
wonfen
2026-07-19 08:44:24 +08:00
parent 6e9288437f
commit 50d828d0d7
4 changed files with 51 additions and 16 deletions
+1
View File
@@ -42,6 +42,7 @@
- 修复内核启动慢导致首页上下跳动的问题
- 修复 VLESS xhttp 链接解析及 IPv6 地址格式问题
- 修复通过普通导入或深链导入订阅后,自动更新任务未及时生效的问题
- 修复链式配置文件首次编辑失败及校验回滚后遗留空文件的问题,并优化文件读取错误提示
<details>
<summary><strong> ✨ 新增功能 </strong></summary>
+12
View File
@@ -404,6 +404,18 @@ pub async fn read_profile_file(index: String) -> CmdResult<String> {
..Default::default()
}
};
if let Some(file) = item.file.as_ref() {
let path = dirs::app_profiles_dir().stringify_err()?.join(file.as_str());
match tokio::fs::try_exists(&path).await {
Ok(true) => {}
Ok(false) => return Ok(String::new()),
Err(err) => {
return Err(format!("failed to check profile file \"{}\": {err}", path.display()).into());
}
}
}
let data = item.read_file().await.stringify_err()?;
Ok(data)
}
+35 -15
View File
@@ -40,19 +40,29 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
(path, is_merge, is_script, affects_runtime)
};
// 读取原始内容(在释放profiles_guard后进行)
let original_content = PrfItem {
file: Some(rel_path.clone()),
..Default::default()
}
.read_file()
.await
.stringify_err()?;
let profiles_dir = dirs::app_profiles_dir().stringify_err()?;
let file_path = profiles_dir.join(rel_path.as_str());
let file_path_str = file_path.to_string_lossy().to_string();
// 读取原始内容(在释放profiles_guard后进行)
let original_existed = fs::try_exists(&file_path).await.map_err(|err| {
String::from(format!(
"failed to check profile file \"{}\": {err}",
file_path.display()
))
})?;
let original_content = if original_existed {
PrfItem {
file: Some(rel_path.clone()),
..Default::default()
}
.read_file()
.await
.stringify_err()?
} else {
String::new()
};
// 保存新的配置文件
fs::write(&file_path, &file_data).await.stringify_err()?;
@@ -68,6 +78,7 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
&file_path_str,
&file_path,
&original_content,
original_existed,
is_merge_file,
is_script_file,
affects_runtime,
@@ -83,8 +94,16 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
Ok(changes_applied)
}
async fn restore_original(file_path: &std::path::Path, original_content: &str) -> Result<(), String> {
fs::write(file_path, original_content).await.stringify_err()
async fn restore_original(
file_path: &std::path::Path,
original_content: &str,
original_existed: bool,
) -> Result<(), String> {
if original_existed {
fs::write(file_path, original_content).await.stringify_err()
} else {
fs::remove_file(file_path).await.stringify_err()
}
}
fn profile_affects_runtime(profiles: &IProfiles, index: &str) -> bool {
@@ -112,6 +131,7 @@ async fn handle_saved_profile_file(
file_path_str: &str,
file_path: &std::path::Path,
original_content: &str,
original_existed: bool,
is_merge_file: bool,
is_script_file: bool,
affects_runtime: bool,
@@ -138,13 +158,13 @@ async fn handle_saved_profile_file(
}
Ok(outcome) => {
logging!(warn, Type::Config, "[cmd配置save] 文件验证失败: {}", outcome);
restore_original(file_path, original_content).await?;
restore_original(file_path, original_content, original_existed).await?;
handle_validation_notice(&outcome, target, file_type);
return Ok(outcome);
}
Err(e) => {
logging!(error, Type::Config, "[cmd配置save] 验证过程发生错误: {}", e);
restore_original(file_path, original_content).await?;
restore_original(file_path, original_content, original_existed).await?;
return Err(e.to_string().into());
}
}
@@ -165,13 +185,13 @@ async fn handle_saved_profile_file(
}
Ok(outcome) => {
logging!(warn, Type::Config, "[cmd配置save] 运行时配置应用失败: {}", outcome);
restore_original(file_path, original_content).await?;
restore_original(file_path, original_content, original_existed).await?;
handle_validation_notice(&outcome, ValidationNoticeTarget::Runtime, "运行时配置");
Ok(outcome)
}
Err(err) => {
logging!(error, Type::Config, "[cmd配置save] 运行时配置应用错误: {}", err);
restore_original(file_path, original_content).await?;
restore_original(file_path, original_content, original_existed).await?;
Err(err.to_string().into())
}
}
+3 -1
View File
@@ -533,7 +533,9 @@ impl PrfItem {
.as_ref()
.ok_or_else(|| anyhow::anyhow!("could not find the file"))?;
let path = dirs::app_profiles_dir()?.join(file.as_str());
let content = fs::read_to_string(path).await.context("failed to read the file")?;
let content = fs::read_to_string(&path)
.await
.with_context(|| format!("failed to read the file \"{}\"", path.display()))?;
Ok(content.into())
}