Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions src-tauri/src/commands/config_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ pub async fn generate_api_key_for_tool(tool: String) -> Result<GenerateApiKeyRes
.await?
.ok_or("请先配置用户ID和系统访问令牌")?;

// 检查已废弃的用户凭证(现由 Provider 系统管理)
let user_id = global_config
.user_id
.as_ref()
.ok_or("请先配置用户ID(已废弃,建议使用供应商管理功能)")?;
let system_token = global_config
.system_token
.as_ref()
.ok_or("请先配置系统访问令牌(已废弃,建议使用供应商管理功能)")?;

// 根据工具名称获取配置
let (name, group) = match tool.as_str() {
"claude-code" => ("Claude Code一键创建", "Claude Code专用"),
Expand All @@ -119,11 +129,8 @@ pub async fn generate_api_key_for_tool(tool: String) -> Result<GenerateApiKeyRes

let create_response = client
.post(create_url)
.header(
"Authorization",
format!("Bearer {}", global_config.system_token),
)
.header("New-Api-User", &global_config.user_id)
.header("Authorization", format!("Bearer {}", system_token))
.header("New-Api-User", user_id)
.header("Content-Type", "application/json")
.json(&create_body)
.send()
Expand Down Expand Up @@ -151,11 +158,8 @@ pub async fn generate_api_key_for_tool(tool: String) -> Result<GenerateApiKeyRes

let search_response = client
.get(&search_url)
.header(
"Authorization",
format!("Bearer {}", global_config.system_token),
)
.header("New-Api-User", &global_config.user_id)
.header("Authorization", format!("Bearer {}", system_token))
.header("New-Api-User", user_id)
.header("Content-Type", "application/json")
.send()
.await
Expand Down
86 changes: 86 additions & 0 deletions src-tauri/src/commands/dashboard_commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Dashboard Commands
//
// 仪表板状态管理 Tauri 命令

use ::duckcoding::services::DashboardManager;
use anyhow::Result;
use tauri::State;

/// Dashboard 管理器 State
pub struct DashboardManagerState {
pub manager: DashboardManager,
}

impl DashboardManagerState {
pub fn new() -> Self {
Self {
manager: DashboardManager::new().expect("Failed to create DashboardManager"),
}
}
}

impl Default for DashboardManagerState {
fn default() -> Self {
Self::new()
}
}

/// 获取工具实例选择
#[tauri::command]
pub async fn get_tool_instance_selection(
tool_id: String,
state: State<'_, DashboardManagerState>,
) -> Result<Option<String>, String> {
if tool_id.is_empty() {
return Err("工具 ID 不能为空".to_string());
}

state
.manager
.get_tool_instance_selection(&tool_id)
.map_err(|e| format!("获取工具实例选择失败: {}", e))
}

/// 设置工具实例选择
#[tauri::command]
pub async fn set_tool_instance_selection(
tool_id: String,
instance_id: String,
state: State<'_, DashboardManagerState>,
) -> Result<(), String> {
// 验证参数
if tool_id.is_empty() {
return Err("工具 ID 不能为空".to_string());
}
if instance_id.is_empty() {
return Err("实例 ID 不能为空".to_string());
}

state
.manager
.set_tool_instance_selection(tool_id, instance_id)
.map_err(|e| format!("设置工具实例选择失败: {}", e))
}

/// 获取最后选中的供应商 ID
#[tauri::command]
pub async fn get_selected_provider_id(
state: State<'_, DashboardManagerState>,
) -> Result<Option<String>, String> {
state
.manager
.get_selected_provider_id()
.map_err(|e| format!("获取选中供应商失败: {}", e))
}

/// 设置最后选中的供应商 ID
#[tauri::command]
pub async fn set_selected_provider_id(
provider_id: Option<String>,
state: State<'_, DashboardManagerState>,
) -> Result<(), String> {
state
.manager
.set_selected_provider_id(provider_id)
.map_err(|e| format!("设置选中供应商失败: {}", e))
}
4 changes: 4 additions & 0 deletions src-tauri/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
pub mod balance_commands;
pub mod config_commands;
pub mod dashboard_commands; // 仪表板状态管理命令
pub mod error; // 错误处理统一模块
pub mod log_commands;
pub mod onboarding;
pub mod profile_commands; // Profile 管理命令(v2.0)
pub mod provider_commands; // 供应商管理命令(v1.5.0)
pub mod proxy_commands;
pub mod session_commands;
pub mod startup_commands; // 开机自启动管理命令
Expand All @@ -18,9 +20,11 @@ pub mod window_commands;
// 重新导出所有命令函数
pub use balance_commands::*;
pub use config_commands::*;
pub use dashboard_commands::*; // 仪表板状态管理命令
pub use log_commands::*;
pub use onboarding::*;
pub use profile_commands::*; // Profile 管理命令(v2.0)
pub use provider_commands::*; // 供应商管理命令(v1.5.0)
pub use proxy_commands::*;
pub use session_commands::*;
pub use startup_commands::*; // 开机自启动管理命令
Expand Down
4 changes: 2 additions & 2 deletions src-tauri/src/commands/onboarding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use tracing::{error, info};
fn create_minimal_config() -> GlobalConfig {
GlobalConfig {
version: Some("0.0.0".to_string()),
user_id: String::new(),
system_token: String::new(),
user_id: Some(String::new()),
system_token: Some(String::new()),
proxy_enabled: false,
proxy_type: None,
proxy_host: None,
Expand Down
192 changes: 192 additions & 0 deletions src-tauri/src/commands/provider_commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// Provider Commands
//
// 供应商管理 Tauri 命令

use ::duckcoding::models::provider::Provider;
use ::duckcoding::services::ProviderManager;
use anyhow::Result;
use tauri::State;

/// Provider 管理器 State
pub struct ProviderManagerState {
pub manager: ProviderManager,
}

impl ProviderManagerState {
pub fn new() -> Self {
Self {
manager: ProviderManager::new().expect("Failed to create ProviderManager"),
}
}
}

impl Default for ProviderManagerState {
fn default() -> Self {
Self::new()
}
}

/// 列出所有供应商
#[tauri::command]
pub async fn list_providers(
state: State<'_, ProviderManagerState>,
) -> Result<Vec<Provider>, String> {
state
.manager
.list_providers()
.map_err(|e| format!("获取供应商列表失败: {}", e))
}

/// 创建新供应商
#[tauri::command]
pub async fn create_provider(
provider: Provider,
state: State<'_, ProviderManagerState>,
) -> Result<Provider, String> {
// 基础验证
if provider.id.is_empty() {
return Err("供应商 ID 不能为空".to_string());
}
if provider.name.is_empty() {
return Err("供应商名称不能为空".to_string());
}
if provider.website_url.is_empty() {
return Err("官网地址不能为空".to_string());
}

state
.manager
.create_provider(provider)
.map_err(|e| format!("创建供应商失败: {}", e))
}

/// 更新供应商
#[tauri::command]
pub async fn update_provider(
id: String,
provider: Provider,
state: State<'_, ProviderManagerState>,
) -> Result<Provider, String> {
// 基础验证
if provider.name.is_empty() {
return Err("供应商名称不能为空".to_string());
}
if provider.website_url.is_empty() {
return Err("官网地址不能为空".to_string());
}

state
.manager
.update_provider(&id, provider)
.map_err(|e| format!("更新供应商失败: {}", e))
}

/// 删除供应商
#[tauri::command]
pub async fn delete_provider(
id: String,
state: State<'_, ProviderManagerState>,
) -> Result<(), String> {
if id.is_empty() {
return Err("供应商 ID 不能为空".to_string());
}

state
.manager
.delete_provider(&id)
.map_err(|e| format!("删除供应商失败: {}", e))
}

/// 验证结果结构
#[derive(serde::Serialize)]
pub struct ValidationResult {
pub success: bool,
pub username: Option<String>,
pub error: Option<String>,
}

/// 验证供应商配置(检查 API 连通性)
#[tauri::command]
pub async fn validate_provider_config(provider: Provider) -> Result<ValidationResult, String> {
use reqwest::Client;
use std::time::Duration;

// 基础验证
if provider.website_url.is_empty() {
return Ok(ValidationResult {
success: false,
username: None,
error: Some("官网地址不能为空".to_string()),
});
}
if provider.user_id.is_empty() {
return Ok(ValidationResult {
success: false,
username: None,
error: Some("用户 ID 不能为空".to_string()),
});
}
if provider.access_token.is_empty() {
return Ok(ValidationResult {
success: false,
username: None,
error: Some("访问令牌不能为空".to_string()),
});
}

// 构建 API 端点
let api_url = format!(
"{}/api/user/self",
provider.website_url.trim_end_matches('/')
);

// 发送验证请求
let client = Client::builder()
.timeout(Duration::from_secs(10))
.build()
.map_err(|e| format!("创建 HTTP 客户端失败: {}", e))?;

let response = client
.get(&api_url)
.header("Authorization", format!("Bearer {}", provider.access_token))
.header("New-Api-User", &provider.user_id)
.send()
.await
.map_err(|e| format!("API 请求失败: {}", e))?;

if response.status().is_success() {
// 尝试解析响应,提取用户名
let json_result = response.json::<serde_json::Value>().await;
match json_result {
Ok(json) => {
// 尝试从响应中提取用户名 (假设在 data.username 或 username 字段)
let username = json
.get("data")
.and_then(|data| data.get("username"))
.or_else(|| json.get("username"))
.and_then(|u| u.as_str())
.map(|s| s.to_string());

Ok(ValidationResult {
success: true,
username,
error: None,
})
}
Err(e) => Ok(ValidationResult {
success: false,
username: None,
error: Some(format!("API 响应格式错误: {}", e)),
}),
}
} else {
Ok(ValidationResult {
success: false,
username: None,
error: Some(format!(
"API 验证失败,状态码: {}",
response.status().as_u16()
)),
})
}
}
Loading