refactor(backend): migrate settings storage to database
- Add bind_db function to initialize database-backed settings - Implement load_initial_settings with database fallback - Replace direct file save with settings store management - Add SETTINGS_DB static for database binding - Maintain backward compatibility with file-based settings - Keep settings.json for legacy migration support
This commit is contained in:
@@ -2,8 +2,9 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{OnceLock, RwLock};
|
use std::sync::{Arc, OnceLock, RwLock};
|
||||||
|
|
||||||
|
use crate::database::Database;
|
||||||
use crate::error::AppError;
|
use crate::error::AppError;
|
||||||
|
|
||||||
/// 自定义端点配置
|
/// 自定义端点配置
|
||||||
@@ -90,8 +91,7 @@ impl Default for AppSettings {
|
|||||||
|
|
||||||
impl AppSettings {
|
impl AppSettings {
|
||||||
fn settings_path() -> PathBuf {
|
fn settings_path() -> PathBuf {
|
||||||
// settings.json 必须使用固定路径,不能被 app_config_dir 覆盖
|
// settings.json 保留用于旧版本迁移和无数据库场景
|
||||||
// 否则会造成循环依赖:读取 settings 需要知道路径,但路径在 settings 中
|
|
||||||
dirs::home_dir()
|
dirs::home_dir()
|
||||||
.expect("无法获取用户主目录")
|
.expect("无法获取用户主目录")
|
||||||
.join(".cc-switch")
|
.join(".cc-switch")
|
||||||
@@ -128,7 +128,7 @@ impl AppSettings {
|
|||||||
.map(|s| s.to_string());
|
.map(|s| s.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load() -> Self {
|
fn load_from_file() -> Self {
|
||||||
let path = Self::settings_path();
|
let path = Self::settings_path();
|
||||||
if let Ok(content) = fs::read_to_string(&path) {
|
if let Ok(content) = fs::read_to_string(&path) {
|
||||||
match serde_json::from_str::<AppSettings>(&content) {
|
match serde_json::from_str::<AppSettings>(&content) {
|
||||||
@@ -149,26 +149,80 @@ impl AppSettings {
|
|||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn save(&self) -> Result<(), AppError> {
|
fn save_settings_file(settings: &AppSettings) -> Result<(), AppError> {
|
||||||
let mut normalized = self.clone();
|
let mut normalized = settings.clone();
|
||||||
normalized.normalize_paths();
|
normalized.normalize_paths();
|
||||||
let path = Self::settings_path();
|
let path = AppSettings::settings_path();
|
||||||
|
|
||||||
if let Some(parent) = path.parent() {
|
if let Some(parent) = path.parent() {
|
||||||
fs::create_dir_all(parent).map_err(|e| AppError::io(parent, e))?;
|
fs::create_dir_all(parent).map_err(|e| AppError::io(parent, e))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let json = serde_json::to_string_pretty(&normalized)
|
let json = serde_json::to_string_pretty(&normalized)
|
||||||
.map_err(|e| AppError::JsonSerialize { source: e })?;
|
.map_err(|e| AppError::JsonSerialize { source: e })?;
|
||||||
fs::write(&path, json).map_err(|e| AppError::io(&path, e))?;
|
fs::write(&path, json).map_err(|e| AppError::io(&path, e))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
static SETTINGS_STORE: OnceLock<RwLock<AppSettings>> = OnceLock::new();
|
||||||
|
|
||||||
|
fn settings_store() -> &'static RwLock<AppSettings> {
|
||||||
|
SETTINGS_STORE.get_or_init(|| RwLock::new(load_initial_settings()))
|
||||||
|
}
|
||||||
|
|
||||||
|
static SETTINGS_DB: OnceLock<Arc<Database>> = OnceLock::new();
|
||||||
|
const APP_SETTINGS_KEY: &str = "app_settings";
|
||||||
|
|
||||||
|
pub fn bind_db(db: Arc<Database>) {
|
||||||
|
if SETTINGS_DB.set(db).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(store) = SETTINGS_STORE.get() {
|
||||||
|
let mut guard = store.write().expect("写入设置锁失败");
|
||||||
|
*guard = load_initial_settings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn settings_store() -> &'static RwLock<AppSettings> {
|
fn load_initial_settings() -> AppSettings {
|
||||||
static STORE: OnceLock<RwLock<AppSettings>> = OnceLock::new();
|
if let Some(db) = SETTINGS_DB.get() {
|
||||||
STORE.get_or_init(|| RwLock::new(AppSettings::load()))
|
if let Some(from_db) = load_from_db(db.as_ref()) {
|
||||||
|
return from_db;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从文件迁移一次并写入数据库
|
||||||
|
let file_settings = AppSettings::load_from_file();
|
||||||
|
if let Err(e) = save_to_db(db.as_ref(), &file_settings) {
|
||||||
|
log::warn!("迁移设置到数据库失败,将继续使用内存副本: {e}");
|
||||||
|
}
|
||||||
|
return file_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppSettings::load_from_file()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_from_db(db: &Database) -> Option<AppSettings> {
|
||||||
|
let raw = db.get_setting(APP_SETTINGS_KEY).ok()??;
|
||||||
|
match serde_json::from_str::<AppSettings>(&raw) {
|
||||||
|
Ok(mut settings) => {
|
||||||
|
settings.normalize_paths();
|
||||||
|
Some(settings)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("解析数据库中 app_settings 失败: {err}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_to_db(db: &Database, settings: &AppSettings) -> Result<(), AppError> {
|
||||||
|
let mut normalized = settings.clone();
|
||||||
|
normalized.normalize_paths();
|
||||||
|
let json =
|
||||||
|
serde_json::to_string(&normalized).map_err(|e| AppError::JsonSerialize { source: e })?;
|
||||||
|
db.set_setting(APP_SETTINGS_KEY, &json)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_override_path(raw: &str) -> PathBuf {
|
fn resolve_override_path(raw: &str) -> PathBuf {
|
||||||
@@ -195,7 +249,11 @@ pub fn get_settings() -> AppSettings {
|
|||||||
|
|
||||||
pub fn update_settings(mut new_settings: AppSettings) -> Result<(), AppError> {
|
pub fn update_settings(mut new_settings: AppSettings) -> Result<(), AppError> {
|
||||||
new_settings.normalize_paths();
|
new_settings.normalize_paths();
|
||||||
new_settings.save()?;
|
if let Some(db) = SETTINGS_DB.get() {
|
||||||
|
save_to_db(db, &new_settings)?;
|
||||||
|
} else {
|
||||||
|
save_settings_file(&new_settings)?;
|
||||||
|
}
|
||||||
|
|
||||||
let mut guard = settings_store().write().expect("写入设置锁失败");
|
let mut guard = settings_store().write().expect("写入设置锁失败");
|
||||||
*guard = new_settings;
|
*guard = new_settings;
|
||||||
|
|||||||
Reference in New Issue
Block a user