Files
cc-switch/src-tauri/src/app_store.rs
YoVinchen 8a05e7bd3d feat(gemini): add Gemini provider integration (#202)
* feat(gemini): add Gemini provider integration

- Add gemini_config.rs module for .env file parsing
- Extend AppType enum to support Gemini
- Implement GeminiConfigEditor and GeminiFormFields components
- Add GeminiIcon with standardized 1024x1024 viewBox
- Add Gemini provider presets configuration
- Update i18n translations for Gemini support
- Extend ProviderService and McpService for Gemini

* fix(gemini): resolve TypeScript errors, add i18n support, and fix MCP logic

**Critical Fixes:**
- Fix TS2741 errors in tests/msw/state.ts by adding missing Gemini type definitions
- Fix ProviderCard.extractApiUrl to support GOOGLE_GEMINI_BASE_URL display
- Add missing apps.gemini i18n keys (zh/en) for proper app name display
- Fix MCP service Gemini cross-app duplication logic to prevent self-copy

**Technical Details:**
- tests/msw/state.ts: Add gemini default providers, current ID, and MCP config
- ProviderCard.tsx: Check both ANTHROPIC_BASE_URL and GOOGLE_GEMINI_BASE_URL
- services/mcp.rs: Skip Gemini in sync_other_side logic with unreachable!() guards
- Run pnpm format to auto-fix code style issues

**Verification:**
-  pnpm typecheck passes
-  pnpm format completed

* feat(gemini): enhance authentication and config parsing

- Add strict and lenient .env parsing modes
- Implement PackyCode partner authentication detection
- Support Google OAuth official authentication
- Auto-configure security.auth.selectedType for PackyCode
- Add comprehensive test coverage for all auth types
- Update i18n for OAuth hints and Gemini config

---------

Co-authored-by: Jason <farion1231@gmail.com>
2025-11-12 10:47:34 +08:00

138 lines
4.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use serde_json::Value;
use std::path::PathBuf;
use std::sync::{OnceLock, RwLock};
use tauri_plugin_store::StoreExt;
use crate::error::AppError;
/// Store 中的键名
const STORE_KEY_APP_CONFIG_DIR: &str = "app_config_dir_override";
/// 缓存当前的 app_config_dir 覆盖路径,避免存储 AppHandle
static APP_CONFIG_DIR_OVERRIDE: OnceLock<RwLock<Option<PathBuf>>> = OnceLock::new();
fn override_cache() -> &'static RwLock<Option<PathBuf>> {
APP_CONFIG_DIR_OVERRIDE.get_or_init(|| RwLock::new(None))
}
fn update_cached_override(value: Option<PathBuf>) {
if let Ok(mut guard) = override_cache().write() {
*guard = value;
}
}
/// 获取缓存中的 app_config_dir 覆盖路径
pub fn get_app_config_dir_override() -> Option<PathBuf> {
override_cache().read().ok()?.clone()
}
fn read_override_from_store(app: &tauri::AppHandle) -> Option<PathBuf> {
let store = match app.store_builder("app_paths.json").build() {
Ok(store) => store,
Err(e) => {
log::warn!("无法创建 Store: {e}");
return None;
}
};
match store.get(STORE_KEY_APP_CONFIG_DIR) {
Some(Value::String(path_str)) => {
let path_str = path_str.trim();
if path_str.is_empty() {
return None;
}
let path = resolve_path(path_str);
if !path.exists() {
log::warn!(
"Store 中配置的 app_config_dir 不存在: {path:?}\n\
将使用默认路径。"
);
return None;
}
log::info!("使用 Store 中的 app_config_dir: {path:?}");
Some(path)
}
Some(_) => {
log::warn!(
"Store 中的 {STORE_KEY_APP_CONFIG_DIR} 类型不正确,应为字符串"
);
None
}
None => None,
}
}
/// 从 Store 刷新 app_config_dir 覆盖值并更新缓存
pub fn refresh_app_config_dir_override(app: &tauri::AppHandle) -> Option<PathBuf> {
let value = read_override_from_store(app);
update_cached_override(value.clone());
value
}
/// 写入 app_config_dir 到 Tauri Store
pub fn set_app_config_dir_to_store(
app: &tauri::AppHandle,
path: Option<&str>,
) -> Result<(), AppError> {
let store = app
.store_builder("app_paths.json")
.build()
.map_err(|e| AppError::Message(format!("创建 Store 失败: {e}")))?;
match path {
Some(p) => {
let trimmed = p.trim();
if !trimmed.is_empty() {
store.set(STORE_KEY_APP_CONFIG_DIR, Value::String(trimmed.to_string()));
log::info!("已将 app_config_dir 写入 Store: {trimmed}");
} else {
store.delete(STORE_KEY_APP_CONFIG_DIR);
log::info!("已从 Store 中删除 app_config_dir 配置");
}
}
None => {
store.delete(STORE_KEY_APP_CONFIG_DIR);
log::info!("已从 Store 中删除 app_config_dir 配置");
}
}
store
.save()
.map_err(|e| AppError::Message(format!("保存 Store 失败: {e}")))?;
refresh_app_config_dir_override(app);
Ok(())
}
/// 解析路径,支持 ~ 开头的相对路径
fn resolve_path(raw: &str) -> PathBuf {
if raw == "~" {
if let Some(home) = dirs::home_dir() {
return home;
}
} else if let Some(stripped) = raw.strip_prefix("~/") {
if let Some(home) = dirs::home_dir() {
return home.join(stripped);
}
} else if let Some(stripped) = raw.strip_prefix("~\\") {
if let Some(home) = dirs::home_dir() {
return home.join(stripped);
}
}
PathBuf::from(raw)
}
/// 从旧的 settings.json 迁移 app_config_dir 到 Store
pub fn migrate_app_config_dir_from_settings(app: &tauri::AppHandle) -> Result<(), AppError> {
// app_config_dir 已从 settings.json 移除,此函数保留但不再执行迁移
// 如果用户在旧版本设置过 app_config_dir需要在 Store 中手动配置
log::info!("app_config_dir 迁移功能已移除,请在设置中重新配置");
let _ = refresh_app_config_dir_override(app);
Ok(())
}