Skip to main content

cc_switch_lib/database/dao/
settings.rs

1//! 通用设置数据访问对象
2//!
3//! 提供键值对形式的通用设置存储。
4
5use crate::database::{lock_conn, Database};
6use crate::error::AppError;
7use rusqlite::params;
8
9impl Database {
10    /// 获取设置值
11    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    /// 设置值
31    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    /// 获取一次性布尔 flag。兼容历史写法 `"1"`。
42    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    /// 删除设置值
49    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    // --- Config Snippets 辅助方法 ---
57
58    /// 获取通用配置片段
59    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    /// 设置通用配置片段
64    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    // --- 全局出站代理 ---
78
79    /// 全局代理 URL 的存储键名
80    const GLOBAL_PROXY_URL_KEY: &'static str = "global_proxy_url";
81
82    /// 获取全局出站代理 URL
83    ///
84    /// 返回 None 表示未配置或已清除代理(直连)
85    /// 返回 Some(url) 表示已配置代理
86    pub fn get_global_proxy_url(&self) -> Result<Option<String>, AppError> {
87        self.get_setting(Self::GLOBAL_PROXY_URL_KEY)
88    }
89
90    /// 设置全局出站代理 URL
91    ///
92    /// - 传入非空字符串:启用代理
93    /// - 传入空字符串或 None:清除代理设置(直连)
94    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                // 清除代理设置
101                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    // --- 代理接管状态管理(已废弃,使用 proxy_config.enabled 替代)---
113
114    /// 获取指定应用的代理接管状态
115    ///
116    /// **已废弃**: 请使用 `proxy_config.enabled` 字段替代
117    /// 此方法仅用于数据库迁移时读取旧数据
118    #[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    /// 设置指定应用的代理接管状态
128    ///
129    /// **已废弃**: 请使用 `proxy_config.enabled` 字段替代
130    #[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    /// 检查是否有任一应用开启了代理接管
145    ///
146    /// **已废弃**: 请使用 `is_live_takeover_active()` 替代
147    #[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    /// 清除所有代理接管状态(将所有 proxy_takeover_* 设置为 false)
161    ///
162    /// **已废弃**: settings 表不再用于存储代理状态
163    #[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    // --- 整流器配置 ---
179
180    /// 获取整流器配置
181    ///
182    /// 返回整流器配置,如果不存在则返回默认值(全部启用)
183    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    /// 更新整流器配置
192    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    // --- 优化器配置 ---
202
203    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    // --- 日志配置 ---
221
222    /// 获取日志配置
223    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    /// 更新日志配置
232    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}