cc_switch_lib/database/dao/
settings.rs1use crate::database::{lock_conn, Database};
6use crate::error::AppError;
7use rusqlite::params;
8
9impl Database {
10 pub fn get_setting(&self, key: &str) -> Result<Option<String>, AppError> {
12 let conn = lock_conn!(self.conn);
13 let mut stmt = conn
14 .prepare("SELECT value FROM settings WHERE key = ?1")
15 .map_err(|e| AppError::Database(e.to_string()))?;
16
17 let mut rows = stmt
18 .query(params![key])
19 .map_err(|e| AppError::Database(e.to_string()))?;
20
21 if let Some(row) = rows.next().map_err(|e| AppError::Database(e.to_string()))? {
22 Ok(Some(
23 row.get(0).map_err(|e| AppError::Database(e.to_string()))?,
24 ))
25 } else {
26 Ok(None)
27 }
28 }
29
30 pub fn set_setting(&self, key: &str, value: &str) -> Result<(), AppError> {
32 let conn = lock_conn!(self.conn);
33 conn.execute(
34 "INSERT OR REPLACE INTO settings (key, value) VALUES (?1, ?2)",
35 params![key, value],
36 )
37 .map_err(|e| AppError::Database(e.to_string()))?;
38 Ok(())
39 }
40
41 pub fn get_bool_flag(&self, key: &str) -> Result<bool, AppError> {
43 Ok(self
44 .get_setting(key)?
45 .is_some_and(|value| value == "true" || value == "1"))
46 }
47
48 pub fn delete_setting(&self, key: &str) -> Result<(), AppError> {
50 let conn = lock_conn!(self.conn);
51 conn.execute("DELETE FROM settings WHERE key = ?1", params![key])
52 .map_err(|e| AppError::Database(e.to_string()))?;
53 Ok(())
54 }
55
56 pub fn get_config_snippet(&self, app_type: &str) -> Result<Option<String>, AppError> {
60 self.get_setting(&format!("common_config_{app_type}"))
61 }
62
63 pub fn set_config_snippet(
65 &self,
66 app_type: &str,
67 snippet: Option<String>,
68 ) -> Result<(), AppError> {
69 let key = format!("common_config_{app_type}");
70 if let Some(value) = snippet {
71 self.set_setting(&key, &value)
72 } else {
73 self.delete_setting(&key)
74 }
75 }
76
77 const GLOBAL_PROXY_URL_KEY: &'static str = "global_proxy_url";
81
82 pub fn get_global_proxy_url(&self) -> Result<Option<String>, AppError> {
87 self.get_setting(Self::GLOBAL_PROXY_URL_KEY)
88 }
89
90 pub fn set_global_proxy_url(&self, url: Option<&str>) -> Result<(), AppError> {
95 match url {
96 Some(u) if !u.trim().is_empty() => {
97 self.set_setting(Self::GLOBAL_PROXY_URL_KEY, u.trim())
98 }
99 _ => {
100 let conn = lock_conn!(self.conn);
102 conn.execute(
103 "DELETE FROM settings WHERE key = ?1",
104 params![Self::GLOBAL_PROXY_URL_KEY],
105 )
106 .map_err(|e| AppError::Database(e.to_string()))?;
107 Ok(())
108 }
109 }
110 }
111
112 #[deprecated(since = "3.9.0", note = "使用 get_proxy_config_for_app().enabled 替代")]
119 pub fn get_proxy_takeover_enabled(&self, app_type: &str) -> Result<bool, AppError> {
120 let key = format!("proxy_takeover_{app_type}");
121 match self.get_setting(&key)? {
122 Some(value) => Ok(value == "true"),
123 None => Ok(false),
124 }
125 }
126
127 #[deprecated(
131 since = "3.9.0",
132 note = "使用 update_proxy_config_for_app() 修改 enabled 字段"
133 )]
134 pub fn set_proxy_takeover_enabled(
135 &self,
136 app_type: &str,
137 enabled: bool,
138 ) -> Result<(), AppError> {
139 let key = format!("proxy_takeover_{app_type}");
140 let value = if enabled { "true" } else { "false" };
141 self.set_setting(&key, value)
142 }
143
144 #[deprecated(since = "3.9.0", note = "使用 is_live_takeover_active() 替代")]
148 pub fn has_any_proxy_takeover(&self) -> Result<bool, AppError> {
149 let conn = lock_conn!(self.conn);
150 let count: i64 = conn
151 .query_row(
152 "SELECT COUNT(*) FROM settings WHERE key LIKE 'proxy_takeover_%' AND value = 'true'",
153 [],
154 |row| row.get(0),
155 )
156 .map_err(|e| AppError::Database(e.to_string()))?;
157 Ok(count > 0)
158 }
159
160 #[deprecated(
164 since = "3.9.0",
165 note = "使用 update_proxy_config_for_app() 清除各应用的 enabled 字段"
166 )]
167 pub fn clear_all_proxy_takeover(&self) -> Result<(), AppError> {
168 let conn = lock_conn!(self.conn);
169 conn.execute(
170 "UPDATE settings SET value = 'false' WHERE key LIKE 'proxy_takeover_%'",
171 [],
172 )
173 .map_err(|e| AppError::Database(e.to_string()))?;
174 log::info!("已清除所有代理接管状态");
175 Ok(())
176 }
177
178 pub fn get_rectifier_config(&self) -> Result<crate::proxy::types::RectifierConfig, AppError> {
184 match self.get_setting("rectifier_config")? {
185 Some(json) => serde_json::from_str(&json)
186 .map_err(|e| AppError::Database(format!("解析整流器配置失败: {e}"))),
187 None => Ok(crate::proxy::types::RectifierConfig::default()),
188 }
189 }
190
191 pub fn set_rectifier_config(
193 &self,
194 config: &crate::proxy::types::RectifierConfig,
195 ) -> Result<(), AppError> {
196 let json = serde_json::to_string(config)
197 .map_err(|e| AppError::Database(format!("序列化整流器配置失败: {e}")))?;
198 self.set_setting("rectifier_config", &json)
199 }
200
201 pub fn get_optimizer_config(&self) -> Result<crate::proxy::types::OptimizerConfig, AppError> {
204 match self.get_setting("optimizer_config")? {
205 Some(json) => serde_json::from_str(&json)
206 .map_err(|e| AppError::Database(format!("解析优化器配置失败: {e}"))),
207 None => Ok(crate::proxy::types::OptimizerConfig::default()),
208 }
209 }
210
211 pub fn set_optimizer_config(
212 &self,
213 config: &crate::proxy::types::OptimizerConfig,
214 ) -> Result<(), AppError> {
215 let json = serde_json::to_string(config)
216 .map_err(|e| AppError::Database(format!("序列化优化器配置失败: {e}")))?;
217 self.set_setting("optimizer_config", &json)
218 }
219
220 pub fn get_log_config(&self) -> Result<crate::proxy::types::LogConfig, AppError> {
224 match self.get_setting("log_config")? {
225 Some(json) => serde_json::from_str(&json)
226 .map_err(|e| AppError::Database(format!("解析日志配置失败: {e}"))),
227 None => Ok(crate::proxy::types::LogConfig::default()),
228 }
229 }
230
231 pub fn set_log_config(&self, config: &crate::proxy::types::LogConfig) -> Result<(), AppError> {
233 let json = serde_json::to_string(config)
234 .map_err(|e| AppError::Database(format!("序列化日志配置失败: {e}")))?;
235 self.set_setting("log_config", &json)
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242
243 #[test]
244 fn optimizer_config_roundtrip_uses_settings_storage() {
245 let db = Database::memory().expect("create memory db");
246 let config = crate::proxy::types::OptimizerConfig {
247 enabled: true,
248 thinking_optimizer: false,
249 cache_injection: true,
250 cache_ttl: "5m".to_string(),
251 };
252
253 db.set_optimizer_config(&config)
254 .expect("persist optimizer config");
255
256 let loaded = db.get_optimizer_config().expect("load optimizer config");
257
258 assert!(loaded.enabled);
259 assert!(!loaded.thinking_optimizer);
260 assert!(loaded.cache_injection);
261 assert_eq!(loaded.cache_ttl, "5m");
262 }
263}