1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use crate::app_config::AppType;
use crate::error::AppError;
use crate::provider::Provider;
use crate::settings;
use super::ProviderService;
/// Gemini 认证类型枚举
///
/// 区分 OAuth 和 API Key 两种认证方式
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum GeminiAuthType {
/// Google 官方(使用 OAuth 认证)
GoogleOfficial,
/// API Key 认证(包括所有第三方供应商:PackyCode、Generic 等)
ApiKey,
}
impl ProviderService {
// 认证类型常量
const API_KEY_SECURITY_SELECTED_TYPE: &'static str = "gemini-api-key";
const GOOGLE_OAUTH_SECURITY_SELECTED_TYPE: &'static str = "oauth-personal";
// Partner Promotion Key 常量
const GOOGLE_OFFICIAL_PARTNER_KEY: &'static str = "google-official";
/// 检测 Gemini 供应商的认证类型
///
/// 只区分两种认证方式:OAuth (Google 官方) 和 API Key (所有其他供应商)
///
/// # 返回值
///
/// - `GeminiAuthType::GoogleOfficial`: Google 官方,使用 OAuth
/// - `GeminiAuthType::ApiKey`: 其他所有供应商,使用 API Key
pub(super) fn detect_gemini_auth_type(provider: &Provider) -> GeminiAuthType {
// 检查 partner_promotion_key 是否为 google-official
if let Some(key) = provider
.meta
.as_ref()
.and_then(|meta| meta.partner_promotion_key.as_deref())
{
if key.eq_ignore_ascii_case(Self::GOOGLE_OFFICIAL_PARTNER_KEY) {
return GeminiAuthType::GoogleOfficial;
}
}
// 检查名称是否为 Google
let name_lower = provider.name.to_ascii_lowercase();
if name_lower == "google" || name_lower.starts_with("google ") {
return GeminiAuthType::GoogleOfficial;
}
// 其他所有情况:API Key 认证
GeminiAuthType::ApiKey
}
/// 确保 Google 官方 Gemini 供应商的安全标志正确设置(OAuth 模式)
///
/// Google 官方 Gemini 使用 OAuth 个人认证,不需要 API Key。
///
/// # 写入两处 settings.json 的原因
///
/// 1. **`~/.cc-switch-tui/settings.json`** (应用级配置):
/// - CC-Switch 应用的全局设置
/// - 确保应用知道当前使用的认证类型
/// - 用于 UI 显示和其他应用逻辑
///
/// 2. **`~/.gemini/settings.json`** (Gemini 客户端配置):
/// - Gemini CLI 客户端读取的配置文件
/// - 直接影响 Gemini 客户端的认证行为
/// - 确保 Gemini 使用正确的认证方式连接 API
///
/// # 设置的值
///
/// ```json
/// {
/// "security": {
/// "auth": {
/// "selectedType": "oauth-personal"
/// }
/// }
/// }
/// ```
///
/// # OAuth 认证流程
///
/// 1. 用户切换到 Google 官方供应商
/// 2. CC-Switch 设置 `selectedType = "oauth-personal"`
/// 3. 用户首次使用 Gemini CLI 时,会自动打开浏览器进行 OAuth 登录
/// 4. 登录成功后,凭证保存在 Gemini 的 credential store 中
/// 5. 后续请求自动使用保存的凭证
pub(crate) fn ensure_google_oauth_security_flag(provider: &Provider) -> Result<(), AppError> {
// 检测是否为 Google 官方
let auth_type = Self::detect_gemini_auth_type(provider);
if auth_type != GeminiAuthType::GoogleOfficial {
return Ok(());
}
// 写入应用级别的 settings.json (~/.cc-switch-tui/settings.json)
settings::ensure_security_auth_selected_type(Self::GOOGLE_OAUTH_SECURITY_SELECTED_TYPE)?;
// 写入 Gemini 目录的 settings.json (~/.gemini/settings.json)
if crate::sync_policy::should_sync_live(&AppType::Gemini) {
use crate::gemini_config::write_google_oauth_settings;
write_google_oauth_settings()?;
}
Ok(())
}
/// 确保 API Key 供应商的安全标志正确设置
///
/// 此函数适用于所有使用 API Key 认证的 Gemini 供应商,包括:
/// - PackyCode(合作伙伴)
/// - 其他第三方 Gemini API 服务
///
/// 所有 API Key 供应商使用相同的认证方式和配置逻辑。
///
/// # 设置的值
///
/// ```json
/// {
/// "security": {
/// "auth": {
/// "selectedType": "gemini-api-key"
/// }
/// }
/// }
/// ```
pub(crate) fn ensure_api_key_security_flag(_provider: &Provider) -> Result<(), AppError> {
// 写入应用级别的 settings.json (~/.cc-switch-tui/settings.json)
settings::ensure_security_auth_selected_type(Self::API_KEY_SECURITY_SELECTED_TYPE)?;
// 写入 Gemini 目录的 settings.json (~/.gemini/settings.json)
if crate::sync_policy::should_sync_live(&AppType::Gemini) {
use crate::gemini_config::write_generic_settings;
write_generic_settings()?;
}
Ok(())
}
}