#![allow(non_snake_case)] use tauri::AppHandle; fn merge_settings_for_save( mut incoming: crate::settings::AppSettings, existing: &crate::settings::AppSettings, ) -> crate::settings::AppSettings { if incoming.webdav_sync.is_none() { incoming.webdav_sync = existing.webdav_sync.clone(); } incoming } /// 获取设置 #[tauri::command] pub async fn get_settings() -> Result { Ok(crate::settings::get_settings_for_frontend()) } /// 保存设置 #[tauri::command] pub async fn save_settings(settings: crate::settings::AppSettings) -> Result { let existing = crate::settings::get_settings(); let merged = merge_settings_for_save(settings, &existing); crate::settings::update_settings(merged).map_err(|e| e.to_string())?; Ok(true) } /// 重启应用程序(当 app_config_dir 变更后使用) #[tauri::command] pub async fn restart_app(app: AppHandle) -> Result { // 在后台延迟重启,让函数有时间返回响应 tauri::async_runtime::spawn(async move { tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; app.restart(); }); Ok(true) } /// 获取 app_config_dir 覆盖配置 (从 Store) #[tauri::command] pub async fn get_app_config_dir_override(app: AppHandle) -> Result, String> { Ok(crate::app_store::refresh_app_config_dir_override(&app) .map(|p| p.to_string_lossy().to_string())) } /// 设置 app_config_dir 覆盖配置 (到 Store) #[tauri::command] pub async fn set_app_config_dir_override( app: AppHandle, path: Option, ) -> Result { crate::app_store::set_app_config_dir_to_store(&app, path.as_deref())?; Ok(true) } /// 设置开机自启 #[tauri::command] pub async fn set_auto_launch(enabled: bool) -> Result { if enabled { crate::auto_launch::enable_auto_launch().map_err(|e| format!("启用开机自启失败: {e}"))?; } else { crate::auto_launch::disable_auto_launch().map_err(|e| format!("禁用开机自启失败: {e}"))?; } Ok(true) } #[cfg(test)] mod tests { use super::merge_settings_for_save; use crate::settings::{AppSettings, WebDavSyncSettings}; #[test] fn save_settings_should_preserve_existing_webdav_when_payload_omits_it() { let mut existing = AppSettings::default(); existing.webdav_sync = Some(WebDavSyncSettings { base_url: "https://dav.example.com".to_string(), username: "alice".to_string(), password: "secret".to_string(), ..WebDavSyncSettings::default() }); let incoming = AppSettings::default(); let merged = merge_settings_for_save(incoming, &existing); assert!(merged.webdav_sync.is_some()); assert_eq!( merged.webdav_sync.as_ref().map(|v| v.base_url.as_str()), Some("https://dav.example.com") ); } #[test] fn save_settings_should_keep_incoming_webdav_when_present() { let mut existing = AppSettings::default(); existing.webdav_sync = Some(WebDavSyncSettings { base_url: "https://dav.old.example.com".to_string(), username: "old".to_string(), password: "old-pass".to_string(), ..WebDavSyncSettings::default() }); let mut incoming = AppSettings::default(); incoming.webdav_sync = Some(WebDavSyncSettings { base_url: "https://dav.new.example.com".to_string(), username: "new".to_string(), password: "new-pass".to_string(), ..WebDavSyncSettings::default() }); let merged = merge_settings_for_save(incoming, &existing); assert_eq!( merged.webdav_sync.as_ref().map(|v| v.base_url.as_str()), Some("https://dav.new.example.com") ); } } /// 获取开机自启状态 #[tauri::command] pub async fn get_auto_launch_status() -> Result { crate::auto_launch::is_auto_launch_enabled().map_err(|e| format!("获取开机自启状态失败: {e}")) } /// 获取整流器配置 #[tauri::command] pub async fn get_rectifier_config( state: tauri::State<'_, crate::AppState>, ) -> Result { state.db.get_rectifier_config().map_err(|e| e.to_string()) } /// 设置整流器配置 #[tauri::command] pub async fn set_rectifier_config( state: tauri::State<'_, crate::AppState>, config: crate::proxy::types::RectifierConfig, ) -> Result { state .db .set_rectifier_config(&config) .map_err(|e| e.to_string())?; Ok(true) } /// 获取优化器配置 #[tauri::command] pub async fn get_optimizer_config( state: tauri::State<'_, crate::AppState>, ) -> Result { state.db.get_optimizer_config().map_err(|e| e.to_string()) } /// 设置优化器配置 #[tauri::command] pub async fn set_optimizer_config( state: tauri::State<'_, crate::AppState>, config: crate::proxy::types::OptimizerConfig, ) -> Result { // Validate cache_ttl: only allow known values match config.cache_ttl.as_str() { "5m" | "1h" => {} other => { return Err(format!( "Invalid cache_ttl value: '{other}'. Allowed values: '5m', '1h'" )) } } state .db .set_optimizer_config(&config) .map_err(|e| e.to_string())?; Ok(true) } /// 获取日志配置 #[tauri::command] pub async fn get_log_config( state: tauri::State<'_, crate::AppState>, ) -> Result { state.db.get_log_config().map_err(|e| e.to_string()) } /// 设置日志配置 #[tauri::command] pub async fn set_log_config( state: tauri::State<'_, crate::AppState>, config: crate::proxy::types::LogConfig, ) -> Result { state .db .set_log_config(&config) .map_err(|e| e.to_string())?; log::set_max_level(config.to_level_filter()); log::info!( "日志配置已更新: enabled={}, level={}", config.enabled, config.level ); Ok(true) }