From 2707e9ecdb62ef571c0cb4da89a201a68eeeacc5 Mon Sep 17 00:00:00 2001 From: Vixalie Date: Sun, 29 Mar 2026 22:12:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8A=A0=E8=BD=BD=E5=92=8C=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/Cargo.toml | 3 ++ src-tauri/src/commands.rs | 21 ++++++++ src-tauri/src/config.rs | 101 ++++++++++++++++++++++++++++++++++++++ src-tauri/src/lib.rs | 15 +++++- 4 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 src-tauri/src/config.rs diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 8eb2016..1a7290a 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -24,4 +24,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" tauri-plugin-fs = "2" tauri-plugin-dialog = "2" +blake3 = { version = "1.8.3", features = ["serde", "digest", "pure"] } +anyhow = "1.0.102" +thiserror = "2.0.18" diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index ec7405e..c74913f 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -1,3 +1,5 @@ +use crate::config::{AppConfig, AppConfigState}; +use tauri::State; use tauri::{AppHandle, WebviewWindow}; #[tauri::command] @@ -12,3 +14,22 @@ pub fn set_window_title( .unwrap_or(app_name); window.set_title(&new_title).map_err(|e| e.to_string()) } + +#[tauri::command] +pub fn load_app_config(state: State<'_, AppConfigState>) -> Result { + state.get().map_err(|e| format!("读取应用配置失败: {e}")) +} + +#[tauri::command] +pub fn save_app_config( + app_handle: AppHandle, + state: State<'_, AppConfigState>, + config: AppConfig, +) -> Result<(), String> { + config + .save(&app_handle) + .map_err(|e| format!("保存应用配置失败: {e}"))?; + state + .set(config) + .map_err(|e| format!("更新应用配置状态失败: {e}")) +} diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs new file mode 100644 index 0000000..f233a53 --- /dev/null +++ b/src-tauri/src/config.rs @@ -0,0 +1,101 @@ +use serde::{Deserialize, Serialize}; +use std::{fs, path::PathBuf, sync::RwLock}; +use tauri::{path::BaseDirectory, AppHandle, Manager}; + +#[derive(Clone, Deserialize, Serialize)] +pub enum ProxyKind { + Http, + Socks5, +} + +impl Default for ProxyKind { + fn default() -> Self { + Self::Http + } +} + +#[derive(Clone, Deserialize, Serialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct ProxyConfig { + pub enabled: bool, + pub kind: ProxyKind, + pub host: Option, + pub port: Option, + pub username: Option, + pub password: Option, +} + +#[derive(Clone, Deserialize, Serialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct AppConfig { + pub proxy: Option, +} + +pub struct AppConfigState { + inner: RwLock, +} + +impl AppConfigState { + pub fn new(config: AppConfig) -> Self { + Self { + inner: RwLock::new(config), + } + } + + pub fn get(&self) -> anyhow::Result { + let guard = self + .inner + .read() + .map_err(|_| anyhow::anyhow!("读取应用配置状态失败"))?; + Ok(guard.clone()) + } + + pub fn set(&self, config: AppConfig) -> anyhow::Result<()> { + let mut guard = self + .inner + .write() + .map_err(|_| anyhow::anyhow!("写入应用配置状态失败"))?; + *guard = config; + Ok(()) + } +} + +impl AppConfig { + fn config_path(app_handle: &AppHandle) -> anyhow::Result { + Ok(app_handle + .path() + .resolve("config.json", BaseDirectory::AppData)?) + } + + pub fn save(&self, app_handle: &AppHandle) -> anyhow::Result<()> { + let config_path = Self::config_path(app_handle)?; + if let Some(parent_dir) = config_path.parent() { + fs::create_dir_all(parent_dir)?; + } + + let content = serde_json::to_string_pretty(self)?; + fs::write(config_path, content)?; + Ok(()) + } + + pub fn load(app_handle: &AppHandle) -> anyhow::Result { + let config_path = Self::config_path(app_handle)?; + if !config_path.exists() { + return Ok(Self::default()); + } + + let content = fs::read_to_string(config_path)?; + Ok(serde_json::from_str(&content)?) + } + + pub fn load_or_init(app_handle: &AppHandle) -> anyhow::Result { + let config_path = Self::config_path(app_handle)?; + if !config_path.exists() { + let default_config = Self::default(); + default_config.save(app_handle)?; + return Ok(default_config); + } + + Self::load(app_handle) + } +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index c03b2be..b17a3f5 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,12 +1,25 @@ mod commands; +mod config; + +use tauri::Manager; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() + .setup(|app| { + let app_handle = app.handle().clone(); + let config = config::AppConfig::load_or_init(&app_handle)?; + app.manage(config::AppConfigState::new(config)); + Ok(()) + }) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_opener::init()) - .invoke_handler(tauri::generate_handler![commands::set_window_title]) + .invoke_handler(tauri::generate_handler![ + commands::set_window_title, + commands::load_app_config, + commands::save_app_config + ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }