Merge branch 'master' of gitee.com:meepo_vip/mixvideo

This commit is contained in:
imeepos
2025-08-08 19:29:20 +08:00
8 changed files with 491 additions and 80 deletions

View File

@@ -159,6 +159,27 @@ impl ServiceManager {
}
}
/// 获取当前配置
pub async fn get_config(&self) -> Result<crate::data::models::comfyui::ComfyUIConfig> {
// 从数据库加载配置
self.repository.get_config().await
}
/// 更新配置
pub async fn update_config(&self, config: crate::data::models::comfyui::ComfyUIConfig) -> Result<()> {
// 保存配置到数据库
self.repository.save_config(&config).await?;
// 重新初始化ComfyUI管理器以使用新配置
let mut comfyui_manager = self.comfyui_manager.write().await;
*comfyui_manager = Some(Arc::new(
ComfyUIManager::new(config.clone())?
));
info!("配置更新成功ComfyUI管理器已重新初始化");
Ok(())
}
/// 获取配置管理器
pub fn get_config_manager(&self) -> Arc<ConfigManager> {
Arc::clone(&self.config_manager)

View File

@@ -294,6 +294,7 @@ mod tests {
use super::*;
use serde_json::json;
use chrono::Utc;
use crate::data::models::workflow_template::WorkflowType;
fn create_test_template() -> WorkflowTemplate {
WorkflowTemplate {

View File

@@ -653,4 +653,73 @@ impl ComfyUIRepository {
node_outputs,
})
}
/// 获取配置
pub async fn get_config(&self) -> Result<crate::data::models::comfyui::ComfyUIConfig> {
self.with_connection(|conn| {
let mut stmt = conn.prepare(
"SELECT base_url, timeout_seconds, retry_attempts, retry_delay_ms,
enable_websocket, enable_cache, max_concurrency, custom_headers
FROM comfyui_configs WHERE id = 'default'"
)?;
let config_iter = stmt.query_map([], |row| {
let custom_headers_json: Option<String> = row.get(7)?;
let custom_headers = if let Some(json) = custom_headers_json {
serde_json::from_str(&json).ok()
} else {
None
};
Ok(crate::data::models::comfyui::ComfyUIConfig {
base_url: row.get(0)?,
timeout_seconds: row.get(1)?,
retry_attempts: row.get(2)?,
retry_delay_ms: row.get(3)?,
enable_websocket: row.get(4)?,
enable_cache: row.get(5)?,
max_concurrency: row.get(6)?,
custom_headers,
})
})?;
for config in config_iter {
return Ok(config?);
}
// 如果没有找到配置,返回默认配置
Ok(crate::data::models::comfyui::ComfyUIConfig::default())
})
}
/// 保存配置
pub async fn save_config(&self, config: &crate::data::models::comfyui::ComfyUIConfig) -> Result<()> {
self.with_connection(|conn| {
let custom_headers_json = if let Some(headers) = &config.custom_headers {
Some(serde_json::to_string(headers)?)
} else {
None
};
conn.execute(
"INSERT OR REPLACE INTO comfyui_configs
(id, base_url, timeout_seconds, retry_attempts, retry_delay_ms,
enable_websocket, enable_cache, max_concurrency, custom_headers, updated_at)
VALUES ('default', ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, CURRENT_TIMESTAMP)",
params![
config.base_url,
config.timeout_seconds,
config.retry_attempts,
config.retry_delay_ms,
config.enable_websocket,
config.enable_cache,
config.max_concurrency,
custom_headers_json
],
)?;
debug!("保存配置成功: {}", config.base_url);
Ok(())
})
}
}

View File

@@ -604,12 +604,12 @@ pub fn run() {
commands::comfyui_v2_commands::comfyui_v2_health_check,
commands::comfyui_v2_commands::comfyui_v2_get_system_info,
commands::comfyui_v2_commands::comfyui_v2_get_queue_basic_status,
commands::comfyui_v2_commands::comfyui_v2_get_config,
commands::comfyui_v2_commands::comfyui_v2_update_config,
commands::comfyui_v2_commands::comfyui_v2_validate_config,
commands::comfyui_v2_commands::comfyui_v2_reset_config,
commands::comfyui_v2_commands::comfyui_v2_get_config_stats,
commands::comfyui_v2_commands::comfyui_v2_get_config_health,
// ComfyUI V2 配置命令
commands::comfyui_v2_config_commands::comfyui_v2_get_config,
commands::comfyui_v2_config_commands::comfyui_v2_update_config,
commands::comfyui_v2_config_commands::comfyui_v2_validate_config,
commands::comfyui_v2_config_commands::comfyui_v2_reset_config,
commands::comfyui_v2_config_commands::comfyui_v2_test_connection,
// ComfyUI V2 工作流命令
commands::comfyui_v2_commands::comfyui_v2_create_workflow,
commands::comfyui_v2_commands::comfyui_v2_list_workflows,
@@ -811,6 +811,24 @@ pub fn run() {
// 不返回错误,让应用继续启动,只是记录错误
}
// 初始化ComfyUI V2管理器异步
let app_handle = app.handle().clone();
tauri::async_runtime::spawn(async move {
let state: tauri::State<AppState> = app_handle.state();
if let Err(e) = state.initialize_config_manager().await {
error!("初始化配置管理器失败: {}", e);
} else {
info!("配置管理器初始化成功");
}
if let Err(e) = state.initialize_comfyui_manager().await {
error!("初始化ComfyUI V2管理器失败: {}", e);
} else {
info!("ComfyUI V2管理器初始化成功");
}
});
Ok(())
})
.run(tauri::generate_context!())
@@ -828,6 +846,7 @@ mod tests {
// 新的测试模块
pub mod test_utils;
pub mod basic_tests;
pub mod comfyui_config_test;
}

View File

@@ -292,80 +292,7 @@ pub async fn comfyui_v2_get_queue_basic_status(
// ==================== 配置管理命令 ====================
/// 获取 ComfyUI 配置
#[tauri::command]
pub async fn comfyui_v2_get_config(
state: State<'_, AppState>,
) -> Result<ComfyUIConfig, String> {
info!("Command: comfyui_v2_get_config");
let config_manager = get_config_manager(&state).await?;
let config = config_manager.get_comfyui_config().await;
Ok(config)
}
/// 更新 ComfyUI 配置
#[tauri::command]
pub async fn comfyui_v2_update_config(
config: ComfyUIV2Config,
state: State<'_, AppState>,
) -> Result<String, String> {
info!("Command: comfyui_v2_update_config");
let config_manager = get_config_manager(&state).await?;
// 转换为 ComfyUIConfig
let converted_config = convert_v2_config_to_config(config);
match config_manager.update_comfyui_config(converted_config).await {
Ok(_) => {
info!("ComfyUI 配置更新成功");
Ok("配置更新成功".to_string())
}
Err(e) => {
error!("配置更新失败: {}", e);
Err(format!("配置更新失败: {}", e))
}
}
}
/// 验证配置
#[tauri::command]
pub async fn comfyui_v2_validate_config(
config: ComfyUIV2Config,
state: State<'_, AppState>,
) -> Result<ValidationResult, String> {
info!("Command: comfyui_v2_validate_config");
// 转换为 ComfyUIConfig 进行验证
let converted_config = convert_v2_config_to_config(config);
let validation = converted_config.validate();
Ok(validation)
}
/// 重置配置为默认值
#[tauri::command]
pub async fn comfyui_v2_reset_config(
state: State<'_, AppState>,
) -> Result<String, String> {
info!("Command: comfyui_v2_reset_config");
let config_manager = get_config_manager(&state).await?;
match config_manager.reset_comfyui_config().await {
Ok(_) => {
info!("ComfyUI 配置重置成功");
Ok("配置重置成功".to_string())
}
Err(e) => {
error!("配置重置失败: {}", e);
Err(format!("配置重置失败: {}", e))
}
}
}
// 配置管理命令已移动到 comfyui_v2_config_commands.rs
// ==================== 统计和监控命令 ====================

View File

@@ -0,0 +1,231 @@
use tauri::State;
use tracing::{info, error, debug};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::app_state::AppState;
use crate::data::models::comfyui::ComfyUIConfig;
/// ComfyUI V2 配置请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComfyUIV2ConfigRequest {
pub base_url: String,
pub timeout: Option<u64>,
pub retry_attempts: Option<u32>,
pub enable_cache: Option<bool>,
pub max_concurrency: Option<u32>,
pub websocket_url: Option<String>,
}
/// ComfyUI V2 配置响应
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComfyUIV2ConfigResponse {
pub base_url: String,
pub timeout: u64,
pub retry_attempts: u32,
pub enable_cache: bool,
pub max_concurrency: u32,
pub websocket_url: String,
pub timeout_seconds: u64,
pub retry_delay_ms: u64,
pub enable_websocket: bool,
pub custom_headers: Option<HashMap<String, String>>,
}
impl From<ComfyUIConfig> for ComfyUIV2ConfigResponse {
fn from(config: ComfyUIConfig) -> Self {
// 从base_url推导websocket_url
let websocket_url = if config.base_url.starts_with("https://") {
config.base_url.replace("https://", "wss://") + "/ws"
} else {
config.base_url.replace("http://", "ws://") + "/ws"
};
Self {
base_url: config.base_url.clone(),
timeout: config.timeout_seconds * 1000, // 转换为毫秒
retry_attempts: config.retry_attempts,
enable_cache: config.enable_cache,
max_concurrency: config.max_concurrency,
websocket_url,
timeout_seconds: config.timeout_seconds,
retry_delay_ms: config.retry_delay_ms,
enable_websocket: config.enable_websocket,
custom_headers: config.custom_headers,
}
}
}
impl From<ComfyUIV2ConfigRequest> for ComfyUIConfig {
fn from(request: ComfyUIV2ConfigRequest) -> Self {
Self {
base_url: request.base_url,
timeout_seconds: request.timeout.unwrap_or(30000) / 1000, // 转换为秒
retry_attempts: request.retry_attempts.unwrap_or(3),
retry_delay_ms: 1000, // 默认1秒
enable_websocket: true, // 默认启用
enable_cache: request.enable_cache.unwrap_or(true),
max_concurrency: request.max_concurrency.unwrap_or(4),
custom_headers: None,
}
}
}
/// 获取ComfyUI配置
#[tauri::command]
pub async fn comfyui_v2_get_config(
state: State<'_, AppState>,
) -> Result<ComfyUIV2ConfigResponse, String> {
info!("Command: comfyui_v2_get_config");
let service_manager = state.get_comfyui_manager().await
.map_err(|e| format!("获取ComfyUI管理器失败: {}", e))?;
match service_manager.get_config().await {
Ok(config) => {
debug!("获取配置成功: {:?}", config);
Ok(ComfyUIV2ConfigResponse::from(config))
},
Err(e) => {
error!("获取配置失败: {}", e);
// 返回默认配置
let default_config = ComfyUIConfig::default();
Ok(ComfyUIV2ConfigResponse::from(default_config))
}
}
}
/// 更新ComfyUI配置
#[tauri::command]
pub async fn comfyui_v2_update_config(
state: State<'_, AppState>,
config_request: ComfyUIV2ConfigRequest,
) -> Result<ComfyUIV2ConfigResponse, String> {
info!("Command: comfyui_v2_update_config - {:?}", config_request);
let service_manager = state.get_comfyui_manager().await
.map_err(|e| format!("获取ComfyUI管理器失败: {}", e))?;
let config = ComfyUIConfig::from(config_request);
match service_manager.update_config(config.clone()).await {
Ok(_) => {
info!("配置更新成功");
Ok(ComfyUIV2ConfigResponse::from(config))
},
Err(e) => {
error!("配置更新失败: {}", e);
Err(format!("配置更新失败: {}", e))
}
}
}
/// 重置ComfyUI配置为默认值
#[tauri::command]
pub async fn comfyui_v2_reset_config(
state: State<'_, AppState>,
) -> Result<ComfyUIV2ConfigResponse, String> {
info!("Command: comfyui_v2_reset_config");
let service_manager = state.get_comfyui_manager().await
.map_err(|e| format!("获取ComfyUI管理器失败: {}", e))?;
let default_config = ComfyUIConfig::default();
match service_manager.update_config(default_config.clone()).await {
Ok(_) => {
info!("配置重置成功");
Ok(ComfyUIV2ConfigResponse::from(default_config))
},
Err(e) => {
error!("配置重置失败: {}", e);
Err(format!("配置重置失败: {}", e))
}
}
}
/// 验证ComfyUI配置
#[tauri::command]
pub async fn comfyui_v2_validate_config(
config_request: ComfyUIV2ConfigRequest,
) -> Result<bool, String> {
info!("Command: comfyui_v2_validate_config - {:?}", config_request);
// 基本验证
if config_request.base_url.is_empty() {
return Err("API地址不能为空".to_string());
}
if !config_request.base_url.starts_with("http://") && !config_request.base_url.starts_with("https://") {
return Err("API地址必须以http://或https://开头".to_string());
}
if let Some(timeout) = config_request.timeout {
if timeout == 0 {
return Err("超时时间必须大于0".to_string());
}
if timeout > 300000 { // 5分钟
return Err("超时时间不能超过5分钟".to_string());
}
}
if let Some(retry_attempts) = config_request.retry_attempts {
if retry_attempts > 10 {
return Err("重试次数不能超过10次".to_string());
}
}
if let Some(max_concurrency) = config_request.max_concurrency {
if max_concurrency == 0 {
return Err("最大并发数必须大于0".to_string());
}
if max_concurrency > 20 {
return Err("最大并发数不能超过20".to_string());
}
}
Ok(true)
}
/// 测试ComfyUI连接
#[tauri::command]
pub async fn comfyui_v2_test_connection(
config_request: ComfyUIV2ConfigRequest,
) -> Result<bool, String> {
info!("Command: comfyui_v2_test_connection - {:?}", config_request);
// 首先验证配置
comfyui_v2_validate_config(config_request.clone()).await?;
// 创建临时的ComfyUI客户端进行连接测试
let config = ComfyUIConfig::from(config_request);
// 使用ComfyUI SDK进行连接测试
let client_config = comfyui_sdk::types::ComfyUIClientConfig {
base_url: config.base_url.clone(),
timeout: Some(std::time::Duration::from_secs(config.timeout_seconds)),
retry_attempts: Some(config.retry_attempts),
retry_delay: Some(std::time::Duration::from_millis(config.retry_delay_ms)),
headers: config.custom_headers.clone(),
};
match comfyui_sdk::client::ComfyUIClient::new(client_config) {
Ok(client) => {
// 尝试获取系统信息来测试连接
match client.get_system_stats().await {
Ok(_) => {
info!("连接测试成功");
Ok(true)
},
Err(e) => {
error!("连接测试失败: {}", e);
Err(format!("连接测试失败: {}", e))
}
}
},
Err(e) => {
error!("创建客户端失败: {}", e);
Err(format!("创建客户端失败: {}", e))
}
}
}

View File

@@ -49,6 +49,7 @@ pub mod comfyui_sdk_commands;
// 新的基于 SDK 的 ComfyUI 命令
pub mod comfyui_v2_commands;
pub mod comfyui_v2_config_commands;
pub mod comfyui_v2_template_commands;
pub mod comfyui_v2_execution_commands;
pub mod comfyui_v2_realtime_commands;

View File

@@ -0,0 +1,142 @@
#[cfg(test)]
mod comfyui_config_tests {
use super::*;
use crate::app_state::AppState;
use crate::data::models::comfyui::ComfyUIConfig;
use crate::infrastructure::database::Database;
use std::sync::Arc;
use tempfile::tempdir;
async fn create_test_app_state() -> AppState {
let temp_dir = tempdir().unwrap();
let db_path = temp_dir.path().join("test.db");
let database = Arc::new(Database::new(&db_path).unwrap());
let state = AppState::new();
// 初始化数据库
state.initialize_database().unwrap();
state
}
#[tokio::test]
async fn test_config_manager_initialization() {
let state = create_test_app_state().await;
// 测试配置管理器初始化
let result = state.initialize_config_manager().await;
assert!(result.is_ok(), "配置管理器初始化应该成功");
// 测试获取配置管理器
let config_manager = state.get_config_manager().await;
assert!(config_manager.is_ok(), "应该能够获取配置管理器");
}
#[tokio::test]
async fn test_comfyui_manager_initialization() {
let state = create_test_app_state().await;
// 先初始化配置管理器
state.initialize_config_manager().await.unwrap();
// 测试ComfyUI管理器初始化
let result = state.initialize_comfyui_manager().await;
assert!(result.is_ok(), "ComfyUI管理器初始化应该成功");
// 测试获取ComfyUI管理器
let comfyui_manager = state.get_comfyui_manager().await;
assert!(comfyui_manager.is_ok(), "应该能够获取ComfyUI管理器");
}
#[tokio::test]
async fn test_config_persistence() {
let state = create_test_app_state().await;
// 初始化管理器
state.initialize_config_manager().await.unwrap();
state.initialize_comfyui_manager().await.unwrap();
let service_manager = state.get_comfyui_manager().await.unwrap();
// 创建测试配置
let test_config = ComfyUIConfig {
base_url: "http://192.168.0.193:8188".to_string(),
timeout_seconds: 300,
retry_attempts: 3,
retry_delay_ms: 1000,
enable_websocket: true,
enable_cache: true,
max_concurrency: 4,
custom_headers: None,
};
// 保存配置
let save_result = service_manager.update_config(test_config.clone()).await;
assert!(save_result.is_ok(), "配置保存应该成功");
// 读取配置
let loaded_config = service_manager.get_config().await;
assert!(loaded_config.is_ok(), "配置读取应该成功");
let loaded_config = loaded_config.unwrap();
assert_eq!(loaded_config.base_url, test_config.base_url, "base_url应该匹配");
assert_eq!(loaded_config.timeout_seconds, test_config.timeout_seconds, "timeout_seconds应该匹配");
assert_eq!(loaded_config.retry_attempts, test_config.retry_attempts, "retry_attempts应该匹配");
}
#[tokio::test]
async fn test_default_config_loading() {
let state = create_test_app_state().await;
// 初始化管理器
state.initialize_config_manager().await.unwrap();
state.initialize_comfyui_manager().await.unwrap();
let service_manager = state.get_comfyui_manager().await.unwrap();
// 读取默认配置(数据库中没有配置时应该返回默认值)
let config = service_manager.get_config().await;
assert!(config.is_ok(), "应该能够获取默认配置");
let config = config.unwrap();
assert_eq!(config.base_url, "http://localhost:8188", "默认base_url应该是localhost");
assert_eq!(config.timeout_seconds, 300, "默认超时应该是300秒");
assert_eq!(config.retry_attempts, 3, "默认重试次数应该是3");
}
#[tokio::test]
async fn test_config_validation() {
// 测试有效配置
let valid_config = ComfyUIConfig {
base_url: "http://localhost:8188".to_string(),
timeout_seconds: 300,
retry_attempts: 3,
retry_delay_ms: 1000,
enable_websocket: true,
enable_cache: true,
max_concurrency: 4,
custom_headers: None,
};
let validation = valid_config.validate();
assert!(validation.valid, "有效配置应该通过验证");
assert!(validation.errors.is_empty(), "有效配置不应该有错误");
// 测试无效配置
let invalid_config = ComfyUIConfig {
base_url: "invalid-url".to_string(), // 无效的URL
timeout_seconds: 0, // 无效的超时时间
retry_attempts: 0,
retry_delay_ms: 0,
enable_websocket: true,
enable_cache: true,
max_concurrency: 0, // 无效的并发数
custom_headers: None,
};
let validation = invalid_config.validate();
assert!(!validation.valid, "无效配置应该验证失败");
assert!(!validation.errors.is_empty(), "无效配置应该有错误信息");
}
}