diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index e30122da8..081126c95 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -6669,6 +6669,7 @@ dependencies = [ "parking_lot", "portable-pty", "proptest", + "proxycast-config", "proxycast-core", "proxycast-infra", "proxycast-providers", @@ -6720,6 +6721,22 @@ dependencies = [ "zip", ] +[[package]] +name = "proxycast-config" +version = "0.60.0" +dependencies = [ + "async-trait", + "parking_lot", + "proptest", + "proxycast-core", + "proxycast-infra", + "serde", + "serde_json", + "tempfile", + "tokio", + "tracing", +] + [[package]] name = "proxycast-core" version = "0.60.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 71d348242..9c582cc50 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -12,6 +12,7 @@ homepage = "https://github.com/aiclientproxy/proxycast" [workspace.dependencies] # 项目内 crate 依赖 proxycast-core = { path = "crates/core" } +proxycast-config = { path = "crates/config" } proxycast-infra = { path = "crates/infra" } proxycast-providers = { path = "crates/providers" } proxycast-services = { path = "crates/services" } @@ -188,6 +189,7 @@ tauri-build.workspace = true [dependencies] # 项目内 crate proxycast-core.workspace = true +proxycast-config.workspace = true proxycast-infra.workspace = true proxycast-providers.workspace = true proxycast-services.workspace = true diff --git a/src-tauri/crates/config/Cargo.toml b/src-tauri/crates/config/Cargo.toml new file mode 100644 index 000000000..67195a128 --- /dev/null +++ b/src-tauri/crates/config/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "proxycast-config" +version.workspace = true +edition.workspace = true +authors.workspace = true + +[dependencies] +proxycast-core.workspace = true +proxycast-infra.workspace = true + +serde.workspace = true +serde_json.workspace = true +async-trait.workspace = true +tokio.workspace = true +tracing.workspace = true +parking_lot.workspace = true + +[dev-dependencies] +proptest.workspace = true +tempfile.workspace = true diff --git a/src-tauri/crates/config/src/lib.rs b/src-tauri/crates/config/src/lib.rs new file mode 100644 index 000000000..c3fff80cd --- /dev/null +++ b/src-tauri/crates/config/src/lib.rs @@ -0,0 +1,38 @@ +//! ProxyCast 配置观察者模块 +//! +//! 提供基于观察者模式的配置管理系统。 +//! Tauri 相关的具体实现(TauriObserver、AppHandle 集成) +//! 保留在主 crate 中。 + +pub mod observer; + +// 重新导出观察者模块的核心类型 +pub use observer::emitter::ConfigEventEmit; +pub use observer::events::{ + AmpConfigChangeEvent, ConfigChangeEvent, ConfigChangeSource, CredentialPoolChangeEvent, + EndpointProvidersChangeEvent, FullReloadEvent, InjectionChangeEvent, LoggingChangeEvent, + NativeAgentChangeEvent, RetryChangeEvent, RoutingChangeEvent, ServerChangeEvent, +}; +pub use observer::manager::GlobalConfigManager; +pub use observer::observers::{ + DefaultProviderRefObserver, EndpointObserver, InjectorObserver, LoggingObserver, RouterObserver, +}; +pub use observer::subject::{ConfigSubject, CONFIG_CHANGED_EVENT, CONFIG_RELOAD_EVENT}; +pub use observer::traits::{ConfigObserver, FnObserver, SyncConfigObserver, SyncObserverWrapper}; + +/// 全局配置管理器状态(用于 Tauri 状态管理) +pub struct GlobalConfigManagerState(pub std::sync::Arc); + +impl GlobalConfigManagerState { + pub fn new(manager: GlobalConfigManager) -> Self { + Self(std::sync::Arc::new(manager)) + } +} + +impl std::ops::Deref for GlobalConfigManagerState { + type Target = std::sync::Arc; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/src-tauri/crates/config/src/observer/emitter.rs b/src-tauri/crates/config/src/observer/emitter.rs new file mode 100644 index 000000000..4322d7d06 --- /dev/null +++ b/src-tauri/crates/config/src/observer/emitter.rs @@ -0,0 +1,39 @@ +//! 配置事件发射器 Trait +//! +//! 抽象 Tauri AppHandle 的 emit 功能, +//! 使 observer 模块不直接依赖 Tauri。 + +use super::events::ConfigChangeEvent; + +/// 配置事件发射器 Trait +/// +/// 用于向前端发送配置变更事件。 +/// 主 crate 中通过 Tauri AppHandle 实现此 trait。 +pub trait ConfigEventEmit: Send + Sync { + /// 发送配置变更事件 + fn emit_config_event( + &self, + event_name: &str, + payload: &ConfigChangeEvent, + ) -> Result<(), String>; + + /// 发送无负载事件 + fn emit_empty_event(&self, event_name: &str) -> Result<(), String>; +} + +/// 空操作发射器(用于测试) +pub struct NoOpEmitter; + +impl ConfigEventEmit for NoOpEmitter { + fn emit_config_event( + &self, + _event_name: &str, + _payload: &ConfigChangeEvent, + ) -> Result<(), String> { + Ok(()) + } + + fn emit_empty_event(&self, _event_name: &str) -> Result<(), String> { + Ok(()) + } +} diff --git a/src-tauri/src/config/observer/events.rs b/src-tauri/crates/config/src/observer/events.rs similarity index 99% rename from src-tauri/src/config/observer/events.rs rename to src-tauri/crates/config/src/observer/events.rs index bf9c7765e..4363d1d64 100644 --- a/src-tauri/src/config/observer/events.rs +++ b/src-tauri/crates/config/src/observer/events.rs @@ -11,31 +11,22 @@ use std::collections::HashMap; pub enum ConfigChangeEvent { /// 完整配置重载 FullReload(FullReloadEvent), - /// 路由配置变更 RoutingChanged(RoutingChangeEvent), - /// 注入配置变更 InjectionChanged(InjectionChangeEvent), - /// 端点 Provider 配置变更 EndpointProvidersChanged(EndpointProvidersChangeEvent), - /// 服务器配置变更 ServerChanged(ServerChangeEvent), - /// 日志配置变更 LoggingChanged(LoggingChangeEvent), - /// 重试配置变更 RetryChanged(RetryChangeEvent), - /// Amp CLI 配置变更 AmpConfigChanged(AmpConfigChangeEvent), - /// 凭证池配置变更 CredentialPoolChanged(CredentialPoolChangeEvent), - /// Native Agent 配置变更 NativeAgentChanged(NativeAgentChangeEvent), } diff --git a/src-tauri/src/config/observer/manager.rs b/src-tauri/crates/config/src/observer/manager.rs similarity index 74% rename from src-tauri/src/config/observer/manager.rs rename to src-tauri/crates/config/src/observer/manager.rs index 3f2ecabb2..ebd38da7a 100644 --- a/src-tauri/src/config/observer/manager.rs +++ b/src-tauri/crates/config/src/observer/manager.rs @@ -1,24 +1,24 @@ //! 全局配置管理器 //! -//! 整合配置主题、热重载和观察者管理 +//! 整合配置主题、热重载和观察者管理。 +//! register_processor_observers 和 register_tauri_observer +//! 保留在主 crate(依赖 Tauri / RequestProcessor)。 +use super::emitter::ConfigEventEmit; use super::events::ConfigChangeSource; use super::observers::{ - DefaultProviderRefObserver, EndpointObserver, InjectorObserver, LoggingObserver, - RouterObserver, TauriObserver, + DefaultProviderRefObserver, EndpointObserver, InjectorObserver, LoggingObserver, RouterObserver, }; use super::subject::ConfigSubject; use super::traits::ConfigObserver; -use crate::config::{Config, EndpointProvidersConfig, HotReloadManager, ReloadResult}; -use crate::processor::RequestProcessor; +use proxycast_core::config::{Config, EndpointProvidersConfig, HotReloadManager, ReloadResult}; +use proxycast_core::router::{ModelMapper, Router}; +use proxycast_infra::Injector; use std::path::PathBuf; use std::sync::Arc; -use tauri::AppHandle; use tokio::sync::RwLock; /// 全局配置管理器 -/// -/// 整合配置主题、热重载和观察者管理 pub struct GlobalConfigManager { /// 配置主题 subject: Arc, @@ -54,9 +54,9 @@ impl GlobalConfigManager { self.subject.config() } - /// 设置 Tauri AppHandle - pub fn set_app_handle(&self, handle: AppHandle) { - self.subject.set_app_handle(handle); + /// 设置事件发射器(替代原来的 set_app_handle) + pub fn set_emitter(&self, emitter: Arc) { + self.subject.set_emitter(emitter); } /// 注册观察者 @@ -69,24 +69,23 @@ impl GlobalConfigManager { self.subject.unregister(name); } - /// 注册 RequestProcessor 相关的观察者 - pub fn register_processor_observers(&self, processor: &RequestProcessor) { - // 路由器观察者 - let router_observer = Arc::new(RouterObserver::new( - processor.router.clone(), - processor.mapper.clone(), - )); + /// 注册路由器相关观察者 + pub fn register_router_observers( + &self, + router: Arc>, + mapper: Arc>, + injector: Arc>, + ) { + let router_observer = Arc::new(RouterObserver::new(router, mapper)); self.subject.register(router_observer); - // 注入器观察者 - let injector_observer = Arc::new(InjectorObserver::new(processor.injector.clone())); + let injector_observer = Arc::new(InjectorObserver::new(injector)); self.subject.register(injector_observer); - // 日志观察者 let logging_observer = Arc::new(LoggingObserver); self.subject.register(logging_observer); - tracing::info!("[GlobalConfigManager] 已注册 RequestProcessor 观察者"); + tracing::info!("[GlobalConfigManager] 已注册路由器相关观察者"); } /// 注册端点 Provider 观察者 @@ -107,21 +106,12 @@ impl GlobalConfigManager { self.subject.register(observer); } - /// 注册 Tauri 前端观察者 - pub fn register_tauri_observer(&self, app_handle: AppHandle) { - let observer = Arc::new(TauriObserver::new(app_handle)); - self.subject.register(observer); - } - /// 更新配置并通知观察者 pub async fn update_config(&self, new_config: Config, source: ConfigChangeSource) { - // 更新热重载管理器 { let hot_reload = self.hot_reload.read(); hot_reload.update_config(new_config.clone()); } - - // 通知观察者 self.subject.update_config(new_config, source).await; } @@ -142,7 +132,6 @@ impl GlobalConfigManager { self.subject .update_config(new_config, ConfigChangeSource::HotReload) .await; - tracing::info!("[GlobalConfigManager] 热重载成功"); } ReloadResult::RolledBack { error, .. } => { @@ -158,12 +147,9 @@ impl GlobalConfigManager { /// 保存配置到文件并通知观察者 pub async fn save_config(&self, config: &Config) -> Result<(), String> { - crate::config::save_config(config).map_err(|e| e.to_string())?; - - // 更新内部状态并通知观察者 + proxycast_core::config::save_config(config).map_err(|e| e.to_string())?; self.update_config(config.clone(), ConfigChangeSource::ApiCall) .await; - Ok(()) } @@ -188,23 +174,6 @@ impl GlobalConfigManager { } } -/// 全局配置管理器状态(用于 Tauri 状态管理) -pub struct GlobalConfigManagerState(pub Arc); - -impl GlobalConfigManagerState { - pub fn new(manager: GlobalConfigManager) -> Self { - Self(Arc::new(manager)) - } -} - -impl std::ops::Deref for GlobalConfigManagerState { - type Target = Arc; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src-tauri/crates/config/src/observer/mod.rs b/src-tauri/crates/config/src/observer/mod.rs new file mode 100644 index 000000000..6595e98e6 --- /dev/null +++ b/src-tauri/crates/config/src/observer/mod.rs @@ -0,0 +1,10 @@ +//! 配置观察者模块 +//! +//! 提供基于观察者模式的全局配置管理系统 + +pub mod emitter; +pub mod events; +pub mod manager; +pub mod observers; +pub mod subject; +pub mod traits; diff --git a/src-tauri/src/config/observer/observers.rs b/src-tauri/crates/config/src/observer/observers.rs similarity index 80% rename from src-tauri/src/config/observer/observers.rs rename to src-tauri/crates/config/src/observer/observers.rs index 71eb881b9..087f19089 100644 --- a/src-tauri/src/config/observer/observers.rs +++ b/src-tauri/crates/config/src/observer/observers.rs @@ -1,15 +1,15 @@ //! 内置配置观察者实现 //! -//! 提供常用组件的配置观察者 +//! 提供常用组件的配置观察者。 +//! TauriObserver 保留在主 crate(依赖 Tauri)。 use super::events::ConfigChangeEvent; use super::traits::ConfigObserver; -use crate::config::{Config, EndpointProvidersConfig}; -use crate::injection::Injector; -use crate::router::{ModelMapper, Router}; use async_trait::async_trait; +use proxycast_core::config::{Config, EndpointProvidersConfig}; +use proxycast_core::router::{ModelMapper, Router}; +use proxycast_infra::Injector; use std::sync::Arc; -use tauri::{AppHandle, Emitter}; use tokio::sync::RwLock; /// 路由器观察者 @@ -33,7 +33,7 @@ impl ConfigObserver for RouterObserver { } fn priority(&self) -> i32 { - 10 // 高优先级,路由器需要先更新 + 10 } fn is_interested_in(&self, event: &ConfigChangeEvent) -> bool { @@ -52,7 +52,7 @@ impl ConfigObserver for RouterObserver { if let Ok(provider_type) = config .routing .default_provider - .parse::() + .parse::() { let mut router = self.router.write().await; router.set_default_provider(provider_type); @@ -80,8 +80,6 @@ impl ConfigObserver for RouterObserver { } /// 注入器观察者 -/// -/// 监听注入配置变更,更新 Injector pub struct InjectorObserver { injector: Arc>, } @@ -131,8 +129,6 @@ impl ConfigObserver for InjectorObserver { } /// 端点 Provider 观察者 -/// -/// 监听端点 Provider 配置变更 pub struct EndpointObserver { endpoint_providers: Arc>, } @@ -167,16 +163,12 @@ impl ConfigObserver for EndpointObserver { ) -> Result<(), String> { let mut ep = self.endpoint_providers.write().await; *ep = config.endpoint_providers.clone(); - tracing::info!("[EndpointObserver] 更新端点 Provider 配置"); - Ok(()) } } /// 日志观察者 -/// -/// 监听日志配置变更 pub struct LoggingObserver; #[async_trait] @@ -206,58 +198,11 @@ impl ConfigObserver for LoggingObserver { config.logging.enabled, config.logging.level ); - - Ok(()) - } -} - -/// Tauri 前端通知观察者 -/// -/// 将配置变更事件转发到前端 -pub struct TauriObserver { - app_handle: AppHandle, -} - -impl TauriObserver { - pub fn new(app_handle: AppHandle) -> Self { - Self { app_handle } - } -} - -#[async_trait] -impl ConfigObserver for TauriObserver { - fn name(&self) -> &str { - "TauriObserver" - } - - fn priority(&self) -> i32 { - 1000 // 最低优先级,确保其他观察者先处理 - } - - async fn on_config_changed( - &self, - event: &ConfigChangeEvent, - _config: &Config, - ) -> Result<(), String> { - // 发送详细事件 - self.app_handle - .emit("config-changed-detail", event) - .map_err(|e| e.to_string())?; - - // 发送简化的刷新通知 - self.app_handle - .emit("config-refresh-needed", ()) - .map_err(|e| e.to_string())?; - - tracing::debug!("[TauriObserver] 已通知前端配置变更: {}", event.event_type()); - Ok(()) } } /// 默认 Provider 引用观察者 -/// -/// 更新 default_provider_ref(用于向后兼容) pub struct DefaultProviderRefObserver { default_provider_ref: Arc>, } @@ -277,7 +222,7 @@ impl ConfigObserver for DefaultProviderRefObserver { } fn priority(&self) -> i32 { - 5 // 最高优先级,确保引用先更新 + 5 } fn is_interested_in(&self, event: &ConfigChangeEvent) -> bool { @@ -294,12 +239,10 @@ impl ConfigObserver for DefaultProviderRefObserver { ) -> Result<(), String> { let mut dp = self.default_provider_ref.write().await; *dp = config.routing.default_provider.clone(); - tracing::debug!( "[DefaultProviderRefObserver] 更新 default_provider_ref: {}", config.routing.default_provider ); - Ok(()) } } @@ -307,7 +250,7 @@ impl ConfigObserver for DefaultProviderRefObserver { #[cfg(test)] mod tests { use super::*; - use crate::config::observer::events::{ConfigChangeSource, FullReloadEvent}; + use crate::observer::events::{ConfigChangeSource, FullReloadEvent}; #[tokio::test] async fn test_router_observer_priority() { diff --git a/src-tauri/src/config/observer/subject.rs b/src-tauri/crates/config/src/observer/subject.rs similarity index 82% rename from src-tauri/src/config/observer/subject.rs rename to src-tauri/crates/config/src/observer/subject.rs index 5162b7399..836b596e3 100644 --- a/src-tauri/src/config/observer/subject.rs +++ b/src-tauri/crates/config/src/observer/subject.rs @@ -2,14 +2,14 @@ //! //! 管理配置观察者的注册、注销和通知 +use super::emitter::ConfigEventEmit; use super::events::{ConfigChangeEvent, ConfigChangeSource, FullReloadEvent}; use super::traits::ConfigObserver; -use crate::config::Config; use parking_lot::RwLock; +use proxycast_core::config::Config; use std::collections::BTreeMap; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; -use tauri::{AppHandle, Emitter}; use tokio::sync::broadcast; /// Tauri 事件名称常量 @@ -33,10 +33,10 @@ pub struct ConfigSubject { current_config: RwLock, /// 事件广播通道 event_tx: broadcast::Sender, - /// Tauri AppHandle(用于向前端发送事件) - app_handle: RwLock>, - /// 是否启用 Tauri 事件 - tauri_events_enabled: RwLock, + /// 事件发射器(抽象 Tauri AppHandle) + emitter: RwLock>>, + /// 是否启用事件发射 + events_enabled: RwLock, } impl ConfigSubject { @@ -48,21 +48,21 @@ impl ConfigSubject { observers: RwLock::new(BTreeMap::new()), current_config: RwLock::new(initial_config), event_tx, - app_handle: RwLock::new(None), - tauri_events_enabled: RwLock::new(true), + emitter: RwLock::new(None), + events_enabled: RwLock::new(true), } } - /// 设置 Tauri AppHandle - pub fn set_app_handle(&self, handle: AppHandle) { - let mut app_handle = self.app_handle.write(); - *app_handle = Some(handle); - tracing::debug!("[ConfigSubject] AppHandle 已设置"); + /// 设置事件发射器 + pub fn set_emitter(&self, emitter: Arc) { + let mut e = self.emitter.write(); + *e = Some(emitter); + tracing::debug!("[ConfigSubject] 事件发射器已设置"); } - /// 启用/禁用 Tauri 事件 - pub fn set_tauri_events_enabled(&self, enabled: bool) { - let mut flag = self.tauri_events_enabled.write(); + /// 启用/禁用事件发射 + pub fn set_events_enabled(&self, enabled: bool) { + let mut flag = self.events_enabled.write(); *flag = enabled; } @@ -88,9 +88,7 @@ impl ConfigSubject { for entries in observers.values_mut() { entries.retain(|e| e.observer.name() != name); } - // 清理空的优先级组 observers.retain(|_, v| !v.is_empty()); - tracing::info!("[ConfigSubject] 注销观察者: {}", name); } @@ -112,25 +110,18 @@ impl ConfigSubject { /// 更新配置并通知观察者 pub async fn update_config(&self, new_config: Config, source: ConfigChangeSource) { - // 创建完整重载事件 let event = ConfigChangeEvent::FullReload(FullReloadEvent { timestamp_ms: Self::current_timestamp_ms(), source, }); - // 更新配置 { let mut config = self.current_config.write(); *config = new_config.clone(); } - // 通知观察者 self.notify_observers(&event, &new_config).await; - - // 发送 Tauri 事件 - self.emit_tauri_event(&event); - - // 广播事件 + self.emit_event(&event); let _ = self.event_tx.send(event); } @@ -138,7 +129,7 @@ impl ConfigSubject { pub async fn notify_event(&self, event: ConfigChangeEvent) { let config = self.config(); self.notify_observers(&event, &config).await; - self.emit_tauri_event(&event); + self.emit_event(&event); let _ = self.event_tx.send(event); } @@ -149,7 +140,6 @@ impl ConfigSubject { /// 通知所有观察者 async fn notify_observers(&self, event: &ConfigChangeEvent, config: &Config) { - // 收集所有感兴趣的观察者 let observers: Vec> = { let observers = self.observers.read(); observers @@ -166,7 +156,6 @@ impl ConfigSubject { event.event_type() ); - // 按优先级顺序通知(BTreeMap 已排序) for observer in observers { let name = observer.name().to_string(); match observer.on_config_changed(event, config).await { @@ -180,19 +169,19 @@ impl ConfigSubject { } } - /// 发送 Tauri 事件到前端 - fn emit_tauri_event(&self, event: &ConfigChangeEvent) { - let enabled = *self.tauri_events_enabled.read(); + /// 发送事件(通过抽象发射器) + fn emit_event(&self, event: &ConfigChangeEvent) { + let enabled = *self.events_enabled.read(); if !enabled { return; } - let app_handle = self.app_handle.read(); - if let Some(handle) = app_handle.as_ref() { - if let Err(e) = handle.emit(CONFIG_CHANGED_EVENT, event) { - tracing::error!("[ConfigSubject] 发送 Tauri 事件失败: {}", e); + let emitter = self.emitter.read(); + if let Some(emitter) = emitter.as_ref() { + if let Err(e) = emitter.emit_config_event(CONFIG_CHANGED_EVENT, event) { + tracing::error!("[ConfigSubject] 发送事件失败: {}", e); } else { - tracing::debug!("[ConfigSubject] 已发送 Tauri 事件: {}", event.event_type()); + tracing::debug!("[ConfigSubject] 已发送事件: {}", event.event_type()); } } } @@ -231,7 +220,7 @@ impl Default for ConfigSubject { #[cfg(test)] mod tests { use super::*; - use crate::config::observer::events::ConfigChangeSource; + use crate::observer::events::ConfigChangeSource; use async_trait::async_trait; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -306,7 +295,6 @@ mod tests { async fn test_priority_order() { let subject = ConfigSubject::new(Config::default()); - // 注册不同优先级的观察者 for (name, priority) in [("low", 100), ("high", 10), ("medium", 50)] { let observer = Arc::new(CountingObserver { name: name.to_string(), @@ -316,7 +304,6 @@ mod tests { subject.register(observer); } - // 验证观察者名称列表 let names = subject.observer_names(); assert_eq!(names.len(), 3); } diff --git a/src-tauri/src/config/observer/traits.rs b/src-tauri/crates/config/src/observer/traits.rs similarity index 87% rename from src-tauri/src/config/observer/traits.rs rename to src-tauri/crates/config/src/observer/traits.rs index 40ee3d486..63c225129 100644 --- a/src-tauri/src/config/observer/traits.rs +++ b/src-tauri/crates/config/src/observer/traits.rs @@ -3,8 +3,8 @@ //! 定义观察者接口,支持异步和同步两种模式 use super::events::ConfigChangeEvent; -use crate::config::Config; use async_trait::async_trait; +use proxycast_core::config::Config; use std::sync::Arc; /// 配置观察者 Trait @@ -16,30 +16,18 @@ pub trait ConfigObserver: Send + Sync { fn name(&self) -> &str; /// 处理配置变更事件 - /// - /// # Arguments - /// * `event` - 配置变更事件 - /// * `config` - 变更后的完整配置 - /// - /// # Returns - /// * `Ok(())` - 处理成功 - /// * `Err(String)` - 处理失败,包含错误信息 async fn on_config_changed( &self, event: &ConfigChangeEvent, config: &Config, ) -> Result<(), String>; - /// 是否对特定事件类型感兴趣 - /// - /// 默认实现对所有事件感兴趣 + /// 是否对特定事件类型感兴趣(默认对所有事件感兴趣) fn is_interested_in(&self, _event: &ConfigChangeEvent) -> bool { true } - /// 观察者优先级(数字越小优先级越高) - /// - /// 默认优先级为 100 + /// 观察者优先级(数字越小优先级越高,默认 100) fn priority(&self) -> i32 { 100 } @@ -97,7 +85,6 @@ impl ConfigObserver for SyncObserverWrapper } /// 函数式观察者(用于简单的回调场景) -/// 目前主要用于测试,将来可用于动态注册观察者 #[allow(dead_code)] pub struct FnObserver where @@ -153,7 +140,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::config::observer::events::{ConfigChangeSource, FullReloadEvent}; + use crate::observer::events::{ConfigChangeSource, FullReloadEvent}; struct TestObserver { name: String, diff --git a/src-tauri/src/app/runner.rs b/src-tauri/src/app/runner.rs index 48fe1cf44..13f0c3ab2 100644 --- a/src-tauri/src/app/runner.rs +++ b/src-tauri/src/app/runner.rs @@ -199,12 +199,15 @@ pub fn run() { } } - // 设置 GlobalConfigManager 的 AppHandle(用于向前端发送事件) + // 设置 GlobalConfigManager 的事件发射器(用于向前端发送事件) if let Some(config_manager) = app.try_state::() { - config_manager.0.set_app_handle(app.handle().clone()); - tracing::info!("[启动] GlobalConfigManager AppHandle 已设置"); + let emitter = std::sync::Arc::new( + crate::config::observer::TauriConfigEmitter::new(app.handle().clone()), + ); + config_manager.0.set_emitter(emitter); + tracing::info!("[启动] GlobalConfigManager 事件发射器已设置"); } // 设置 MCP Manager 的 AppHandle(用于发送 mcp:* 事件) diff --git a/src-tauri/src/config/observer/mod.rs b/src-tauri/src/config/observer/mod.rs index 8c7730d26..63ba04412 100644 --- a/src-tauri/src/config/observer/mod.rs +++ b/src-tauri/src/config/observer/mod.rs @@ -1,22 +1,30 @@ //! 配置观察者模块 //! -//! 提供基于观察者模式的全局配置管理系统 +//! 核心逻辑已迁移到 proxycast-config crate。 +//! 本模块保留 Tauri 相关实现和重新导出。 -mod events; -mod manager; -mod observers; -mod subject; -mod traits; +mod tauri_emitter; +mod tauri_observer; -pub use events::{ +// 从 proxycast-config crate 重新导出所有类型 +pub use proxycast_config::observer::emitter::{ConfigEventEmit, NoOpEmitter}; +pub use proxycast_config::observer::events::{ AmpConfigChangeEvent, ConfigChangeEvent, ConfigChangeSource, CredentialPoolChangeEvent, EndpointProvidersChangeEvent, FullReloadEvent, InjectionChangeEvent, LoggingChangeEvent, NativeAgentChangeEvent, RetryChangeEvent, RoutingChangeEvent, ServerChangeEvent, }; -pub use manager::{GlobalConfigManager, GlobalConfigManagerState}; -pub use observers::{ - DefaultProviderRefObserver, EndpointObserver, InjectorObserver, LoggingObserver, - RouterObserver, TauriObserver, +pub use proxycast_config::observer::manager::GlobalConfigManager; +pub use proxycast_config::observer::observers::{ + DefaultProviderRefObserver, EndpointObserver, InjectorObserver, LoggingObserver, RouterObserver, }; -pub use subject::{ConfigSubject, CONFIG_CHANGED_EVENT, CONFIG_RELOAD_EVENT}; -pub use traits::{ConfigObserver, FnObserver, SyncConfigObserver, SyncObserverWrapper}; +pub use proxycast_config::observer::subject::{ + ConfigSubject, CONFIG_CHANGED_EVENT, CONFIG_RELOAD_EVENT, +}; +pub use proxycast_config::observer::traits::{ + ConfigObserver, FnObserver, SyncConfigObserver, SyncObserverWrapper, +}; +pub use proxycast_config::GlobalConfigManagerState; + +// Tauri 相关实现 +pub use tauri_emitter::TauriConfigEmitter; +pub use tauri_observer::TauriObserver; diff --git a/src-tauri/src/config/observer/tauri_emitter.rs b/src-tauri/src/config/observer/tauri_emitter.rs new file mode 100644 index 000000000..a4ace4741 --- /dev/null +++ b/src-tauri/src/config/observer/tauri_emitter.rs @@ -0,0 +1,36 @@ +//! Tauri 配置事件发射器 +//! +//! 实现 ConfigEventEmit trait,通过 Tauri AppHandle 发送事件 + +use proxycast_config::ConfigChangeEvent; +use proxycast_config::ConfigEventEmit; +use tauri::{AppHandle, Emitter}; + +/// Tauri 配置事件发射器 +pub struct TauriConfigEmitter { + app_handle: AppHandle, +} + +impl TauriConfigEmitter { + pub fn new(app_handle: AppHandle) -> Self { + Self { app_handle } + } +} + +impl ConfigEventEmit for TauriConfigEmitter { + fn emit_config_event( + &self, + event_name: &str, + payload: &ConfigChangeEvent, + ) -> Result<(), String> { + self.app_handle + .emit(event_name, payload) + .map_err(|e| e.to_string()) + } + + fn emit_empty_event(&self, event_name: &str) -> Result<(), String> { + self.app_handle + .emit(event_name, ()) + .map_err(|e| e.to_string()) + } +} diff --git a/src-tauri/src/config/observer/tauri_observer.rs b/src-tauri/src/config/observer/tauri_observer.rs new file mode 100644 index 000000000..0ced00e61 --- /dev/null +++ b/src-tauri/src/config/observer/tauri_observer.rs @@ -0,0 +1,49 @@ +//! Tauri 前端通知观察者 +//! +//! 将配置变更事件转发到前端 + +use async_trait::async_trait; +use proxycast_config::observer::events::ConfigChangeEvent; +use proxycast_config::observer::traits::ConfigObserver; +use proxycast_core::config::Config; +use tauri::{AppHandle, Emitter}; + +/// Tauri 前端通知观察者 +pub struct TauriObserver { + app_handle: AppHandle, +} + +impl TauriObserver { + pub fn new(app_handle: AppHandle) -> Self { + Self { app_handle } + } +} + +#[async_trait] +impl ConfigObserver for TauriObserver { + fn name(&self) -> &str { + "TauriObserver" + } + + fn priority(&self) -> i32 { + 1000 + } + + async fn on_config_changed( + &self, + event: &ConfigChangeEvent, + _config: &Config, + ) -> Result<(), String> { + self.app_handle + .emit("config-changed-detail", event) + .map_err(|e| e.to_string())?; + + self.app_handle + .emit("config-refresh-needed", ()) + .map_err(|e| e.to_string())?; + + tracing::debug!("[TauriObserver] 已通知前端配置变更: {}", event.event_type()); + + Ok(()) + } +}