mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-25 13:45:03 +08:00
3faf22f1c9
Add comprehensive first-launch data import system: Database layer: - Add is_empty_for_first_import() to detect empty database - Add init_default_skill_repos() to initialize 3 default skill repositories Services layer: - Implement McpService::import_from_claude/codex/gemini() to import MCP servers from existing config files - Implement PromptService::import_from_file_on_first_launch() to import prompt files (CLAUDE.md, AGENTS.md, GEMINI.md) Startup flow (lib.rs): - Check if database is empty on startup - Import existing configurations if detected: 1. Initialize default skill repositories 2. Import provider configurations from live settings 3. Import MCP servers from config files 4. Import prompt files - All imports are fault-tolerant and logged This ensures seamless migration from file-based configs to database.
248 lines
8.2 KiB
Rust
248 lines
8.2 KiB
Rust
use indexmap::IndexMap;
|
||
use std::collections::HashMap;
|
||
|
||
use crate::app_config::{AppType, McpServer};
|
||
use crate::error::AppError;
|
||
use crate::mcp;
|
||
use crate::store::AppState;
|
||
|
||
/// MCP 相关业务逻辑(v3.7.0 统一结构)
|
||
pub struct McpService;
|
||
|
||
impl McpService {
|
||
/// 获取所有 MCP 服务器(统一结构)
|
||
pub fn get_all_servers(state: &AppState) -> Result<IndexMap<String, McpServer>, AppError> {
|
||
state.db.get_all_mcp_servers()
|
||
}
|
||
|
||
/// 添加或更新 MCP 服务器
|
||
pub fn upsert_server(state: &AppState, server: McpServer) -> Result<(), AppError> {
|
||
state.db.save_mcp_server(&server)?;
|
||
|
||
// 同步到各个启用的应用
|
||
Self::sync_server_to_apps(state, &server)?;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 删除 MCP 服务器
|
||
pub fn delete_server(state: &AppState, id: &str) -> Result<bool, AppError> {
|
||
let server = state.db.get_all_mcp_servers()?.shift_remove(id);
|
||
|
||
if let Some(server) = server {
|
||
state.db.delete_mcp_server(id)?;
|
||
|
||
// 从所有应用的 live 配置中移除
|
||
Self::remove_server_from_all_apps(state, id, &server)?;
|
||
Ok(true)
|
||
} else {
|
||
Ok(false)
|
||
}
|
||
}
|
||
|
||
/// 切换指定应用的启用状态
|
||
pub fn toggle_app(
|
||
state: &AppState,
|
||
server_id: &str,
|
||
app: AppType,
|
||
enabled: bool,
|
||
) -> Result<(), AppError> {
|
||
let mut servers = state.db.get_all_mcp_servers()?;
|
||
|
||
if let Some(server) = servers.get_mut(server_id) {
|
||
server.apps.set_enabled_for(&app, enabled);
|
||
state.db.save_mcp_server(server)?;
|
||
|
||
// 同步到对应应用
|
||
if enabled {
|
||
Self::sync_server_to_app(state, server, &app)?;
|
||
} else {
|
||
Self::remove_server_from_app(state, server_id, &app)?;
|
||
}
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 将 MCP 服务器同步到所有启用的应用
|
||
fn sync_server_to_apps(_state: &AppState, server: &McpServer) -> Result<(), AppError> {
|
||
for app in server.apps.enabled_apps() {
|
||
Self::sync_server_to_app_no_config(server, &app)?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 将 MCP 服务器同步到指定应用
|
||
fn sync_server_to_app(
|
||
_state: &AppState,
|
||
server: &McpServer,
|
||
app: &AppType,
|
||
) -> Result<(), AppError> {
|
||
Self::sync_server_to_app_no_config(server, app)
|
||
}
|
||
|
||
fn sync_server_to_app_no_config(server: &McpServer, app: &AppType) -> Result<(), AppError> {
|
||
match app {
|
||
AppType::Claude => {
|
||
mcp::sync_single_server_to_claude(&Default::default(), &server.id, &server.server)?;
|
||
}
|
||
AppType::Codex => {
|
||
// Codex uses TOML format, must use the correct function
|
||
mcp::sync_single_server_to_codex(&Default::default(), &server.id, &server.server)?;
|
||
}
|
||
AppType::Gemini => {
|
||
mcp::sync_single_server_to_gemini(&Default::default(), &server.id, &server.server)?;
|
||
}
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
/// 从所有曾启用过该服务器的应用中移除
|
||
fn remove_server_from_all_apps(
|
||
state: &AppState,
|
||
id: &str,
|
||
server: &McpServer,
|
||
) -> Result<(), AppError> {
|
||
// 从所有曾启用的应用中移除
|
||
for app in server.apps.enabled_apps() {
|
||
Self::remove_server_from_app(state, id, &app)?;
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
fn remove_server_from_app(_state: &AppState, id: &str, app: &AppType) -> Result<(), AppError> {
|
||
match app {
|
||
AppType::Claude => mcp::remove_server_from_claude(id)?,
|
||
AppType::Codex => mcp::remove_server_from_codex(id)?,
|
||
AppType::Gemini => mcp::remove_server_from_gemini(id)?,
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
/// 手动同步所有启用的 MCP 服务器到对应的应用
|
||
pub fn sync_all_enabled(state: &AppState) -> Result<(), AppError> {
|
||
let servers = Self::get_all_servers(state)?;
|
||
|
||
for server in servers.values() {
|
||
Self::sync_server_to_apps(state, server)?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
// ========================================================================
|
||
// 兼容层:支持旧的 v3.6.x 命令(已废弃,将在 v4.0 移除)
|
||
// ========================================================================
|
||
|
||
/// [已废弃] 获取指定应用的 MCP 服务器(兼容旧 API)
|
||
#[deprecated(since = "3.7.0", note = "Use get_all_servers instead")]
|
||
pub fn get_servers(
|
||
state: &AppState,
|
||
app: AppType,
|
||
) -> Result<HashMap<String, serde_json::Value>, AppError> {
|
||
let all_servers = Self::get_all_servers(state)?;
|
||
let mut result = HashMap::new();
|
||
|
||
for (id, server) in all_servers {
|
||
if server.apps.is_enabled_for(&app) {
|
||
result.insert(id, server.server);
|
||
}
|
||
}
|
||
|
||
Ok(result)
|
||
}
|
||
|
||
/// [已废弃] 设置 MCP 服务器在指定应用的启用状态(兼容旧 API)
|
||
#[deprecated(since = "3.7.0", note = "Use toggle_app instead")]
|
||
pub fn set_enabled(
|
||
state: &AppState,
|
||
app: AppType,
|
||
id: &str,
|
||
enabled: bool,
|
||
) -> Result<bool, AppError> {
|
||
Self::toggle_app(state, id, app, enabled)?;
|
||
Ok(true)
|
||
}
|
||
|
||
/// [已废弃] 同步启用的 MCP 到指定应用(兼容旧 API)
|
||
#[deprecated(since = "3.7.0", note = "Use sync_all_enabled instead")]
|
||
pub fn sync_enabled(state: &AppState, app: AppType) -> Result<(), AppError> {
|
||
let servers = Self::get_all_servers(state)?;
|
||
|
||
for server in servers.values() {
|
||
if server.apps.is_enabled_for(&app) {
|
||
Self::sync_server_to_app(state, server, &app)?;
|
||
}
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 从 Claude 导入 MCP(v3.7.0 已更新为统一结构)
|
||
pub fn import_from_claude(state: &AppState) -> Result<usize, AppError> {
|
||
// 创建临时 MultiAppConfig 用于导入
|
||
let mut temp_config = crate::app_config::MultiAppConfig::default();
|
||
|
||
// 调用原有的导入逻辑(从 mcp.rs)
|
||
let count = crate::mcp::import_from_claude(&mut temp_config)?;
|
||
|
||
// 如果有导入的服务器,保存到数据库
|
||
if count > 0 {
|
||
if let Some(servers) = &temp_config.mcp.servers {
|
||
for server in servers.values() {
|
||
state.db.save_mcp_server(server)?;
|
||
// 同步到 Claude live 配置
|
||
Self::sync_server_to_apps(state, server)?;
|
||
}
|
||
}
|
||
}
|
||
|
||
Ok(count)
|
||
}
|
||
|
||
/// 从 Codex 导入 MCP(v3.7.0 已更新为统一结构)
|
||
pub fn import_from_codex(state: &AppState) -> Result<usize, AppError> {
|
||
// 创建临时 MultiAppConfig 用于导入
|
||
let mut temp_config = crate::app_config::MultiAppConfig::default();
|
||
|
||
// 调用原有的导入逻辑(从 mcp.rs)
|
||
let count = crate::mcp::import_from_codex(&mut temp_config)?;
|
||
|
||
// 如果有导入的服务器,保存到数据库
|
||
if count > 0 {
|
||
if let Some(servers) = &temp_config.mcp.servers {
|
||
for server in servers.values() {
|
||
state.db.save_mcp_server(server)?;
|
||
// 同步到 Codex live 配置
|
||
Self::sync_server_to_apps(state, server)?;
|
||
}
|
||
}
|
||
}
|
||
|
||
Ok(count)
|
||
}
|
||
|
||
/// 从 Gemini 导入 MCP(v3.7.0 已更新为统一结构)
|
||
pub fn import_from_gemini(state: &AppState) -> Result<usize, AppError> {
|
||
// 创建临时 MultiAppConfig 用于导入
|
||
let mut temp_config = crate::app_config::MultiAppConfig::default();
|
||
|
||
// 调用原有的导入逻辑(从 mcp.rs)
|
||
let count = crate::mcp::import_from_gemini(&mut temp_config)?;
|
||
|
||
// 如果有导入的服务器,保存到数据库
|
||
if count > 0 {
|
||
if let Some(servers) = &temp_config.mcp.servers {
|
||
for server in servers.values() {
|
||
state.db.save_mcp_server(server)?;
|
||
// 同步到 Gemini live 配置
|
||
Self::sync_server_to_apps(state, server)?;
|
||
}
|
||
}
|
||
}
|
||
|
||
Ok(count)
|
||
}
|
||
}
|