Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src-tauri/src/database/dao/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,40 @@ impl Database {
}
}

/// 通用配置片段“已被用户显式清空”标记的键名
fn config_snippet_cleared_key(app_type: &str) -> String {
format!("common_config_{app_type}_cleared")
}

/// 通用配置片段是否被用户显式清空
pub fn is_config_snippet_cleared(&self, app_type: &str) -> Result<bool, AppError> {
Ok(self
.get_setting(&Self::config_snippet_cleared_key(app_type))?
.as_deref()
== Some("true"))
}

/// 设置/清除“通用配置片段已被显式清空”标记
pub fn set_config_snippet_cleared(
&self,
app_type: &str,
cleared: bool,
) -> Result<(), AppError> {
let key = Self::config_snippet_cleared_key(app_type);
if cleared {
self.set_setting(&key, "true")
} else {
self.delete_setting(&key)
}
}

/// 当前是否允许从 live 配置自动播种通用配置片段:
/// 片段为空且未被用户显式清空时返回 true
pub fn should_auto_extract_config_snippet(&self, app_type: &str) -> Result<bool, AppError> {
Ok(self.get_config_snippet(app_type)?.is_none()
&& !self.is_config_snippet_cleared(app_type)?)
}

// --- 全局出站代理 ---

/// 全局代理 URL 的存储键名
Expand Down
40 changes: 40 additions & 0 deletions src-tauri/src/database/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1824,3 +1824,43 @@ fn model_pricing_upsert_rejects_invalid_values() {
assert!(ModelPricingUpdate::new("bad-negative", "Bad Negative", "-1", "1", "0", "0").is_err());
assert!(ModelPricingUpdate::new("", "Blank Model", "1", "1", "0", "0").is_err());
}

#[test]
fn should_auto_extract_config_snippet_respects_snippet_and_cleared_flag() {
let db = Database::memory().expect("create memory db");

// 全新状态:无片段、未清空 → 允许自动播种
assert!(db
.should_auto_extract_config_snippet("claude")
.expect("gate"));
assert!(!db.is_config_snippet_cleared("claude").expect("cleared"));

// 有片段 → 不再自动播种
db.set_config_snippet("claude", Some("{\"a\":1}".to_string()))
.expect("set snippet");
assert!(!db
.should_auto_extract_config_snippet("claude")
.expect("gate after set"));

// 删除片段并标记为已清空 → 仍不自动播种
db.set_config_snippet("claude", None)
.expect("clear snippet");
db.set_config_snippet_cleared("claude", true)
.expect("set cleared");
assert!(db
.is_config_snippet_cleared("claude")
.expect("cleared true"));
assert!(!db
.should_auto_extract_config_snippet("claude")
.expect("gate after cleared"));

// 取消清空标记 → 重新允许自动播种
db.set_config_snippet_cleared("claude", false)
.expect("unset cleared");
assert!(!db
.is_config_snippet_cleared("claude")
.expect("cleared false"));
assert!(db
.should_auto_extract_config_snippet("claude")
.expect("gate after unset"));
}
14 changes: 13 additions & 1 deletion src-tauri/src/services/provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1532,6 +1532,10 @@ impl ProviderService {
});
Self::validate_common_config_snippet(&app_type, normalized_snippet.as_deref())?;

// 清空(None)→ 记录 cleared=true,避免下次启动自动重新播种;
// 设置非空片段 → cleared=false。
let snippet_cleared = normalized_snippet.is_none();

let app_type_clone = app_type.clone();
let (effective_current_provider, db_providers) = if app_type.is_additive_mode() {
(None, None)
Expand Down Expand Up @@ -1648,7 +1652,15 @@ impl ProviderService {
)?;
Ok(((), action))
},
)
)?;

// 与启动期 best-effort 播种不同,这里传播错误:若该标记写入失败,
// 下次启动会误判为"未清空"并把刚清空的片段重新播种回来。
state
.db
.set_config_snippet_cleared(app_type.as_str(), snippet_cleared)?;

Ok(())
}

pub fn clear_common_config_snippet(
Expand Down
Loading