mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-30 18:33:04 +08:00
feat(proxy): add ProviderType enum for fine-grained provider detection
Introduce ProviderType enum to distinguish between different provider implementations (Claude, ClaudeAuth, Codex, Gemini, GeminiCli, OpenRouter). This enables proper authentication handling and request transformation based on the actual provider type rather than just AppType. - Add ProviderType enum with detection logic from config - Enhance Claude adapter with OpenRouter detection - Enhance Gemini adapter with CLI mode detection - Add helper methods for provider type inference
This commit is contained in:
@@ -11,12 +11,27 @@ pub struct AuthInfo {
|
||||
pub api_key: String,
|
||||
/// 认证策略
|
||||
pub strategy: AuthStrategy,
|
||||
/// OAuth access_token(用于 GoogleOAuth 策略)
|
||||
pub access_token: Option<String>,
|
||||
}
|
||||
|
||||
impl AuthInfo {
|
||||
/// 创建新的认证信息
|
||||
pub fn new(api_key: String, strategy: AuthStrategy) -> Self {
|
||||
Self { api_key, strategy }
|
||||
Self {
|
||||
api_key,
|
||||
strategy,
|
||||
access_token: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建带有 access_token 的认证信息(用于 OAuth)
|
||||
pub fn with_access_token(api_key: String, access_token: String) -> Self {
|
||||
Self {
|
||||
api_key,
|
||||
strategy: AuthStrategy::GoogleOAuth,
|
||||
access_token: Some(access_token),
|
||||
}
|
||||
}
|
||||
|
||||
/// 返回遮蔽后的 API Key(用于日志输出)
|
||||
@@ -34,6 +49,18 @@ impl AuthInfo {
|
||||
"***".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// 返回遮蔽后的 access_token(用于日志输出)
|
||||
#[allow(dead_code)]
|
||||
pub fn masked_access_token(&self) -> Option<String> {
|
||||
self.access_token.as_ref().map(|token| {
|
||||
if token.len() > 8 {
|
||||
format!("{}...{}", &token[..4], &token[token.len() - 4..])
|
||||
} else {
|
||||
"***".to_string()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// 认证策略
|
||||
@@ -46,13 +73,29 @@ pub enum AuthStrategy {
|
||||
/// - Header: `anthropic-version: 2023-06-01`
|
||||
Anthropic,
|
||||
|
||||
/// Claude 中转服务认证方式(仅 Bearer,无 x-api-key)
|
||||
///
|
||||
/// - Header: `Authorization: Bearer <api_key>`
|
||||
///
|
||||
/// 用于不支持 x-api-key 的中转服务
|
||||
ClaudeAuth,
|
||||
|
||||
/// Bearer Token 认证方式(OpenAI 等)
|
||||
///
|
||||
/// - Header: `Authorization: Bearer <api_key>`
|
||||
Bearer,
|
||||
|
||||
/// Google 认证方式
|
||||
/// Google API Key 认证方式
|
||||
///
|
||||
/// - Header: `x-goog-api-key: <api_key>`
|
||||
Google,
|
||||
|
||||
/// Google OAuth 认证方式
|
||||
///
|
||||
/// - Header: `Authorization: Bearer <access_token>`
|
||||
///
|
||||
/// 用于 Gemini CLI 等需要 OAuth 的场景
|
||||
GoogleOAuth,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -89,4 +132,79 @@ mod tests {
|
||||
assert_ne!(AuthStrategy::Anthropic, AuthStrategy::Bearer);
|
||||
assert_ne!(AuthStrategy::Bearer, AuthStrategy::Google);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_info_new_has_no_access_token() {
|
||||
let auth = AuthInfo::new("api-key".to_string(), AuthStrategy::Bearer);
|
||||
assert!(auth.access_token.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_info_with_access_token() {
|
||||
let auth = AuthInfo::with_access_token(
|
||||
"refresh-token".to_string(),
|
||||
"ya29.access-token-12345".to_string(),
|
||||
);
|
||||
assert_eq!(auth.api_key, "refresh-token");
|
||||
assert_eq!(auth.strategy, AuthStrategy::GoogleOAuth);
|
||||
assert_eq!(
|
||||
auth.access_token,
|
||||
Some("ya29.access-token-12345".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_masked_access_token_long() {
|
||||
let auth =
|
||||
AuthInfo::with_access_token("refresh".to_string(), "ya29.1234567890abcdef".to_string());
|
||||
assert_eq!(auth.masked_access_token(), Some("ya29...cdef".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_masked_access_token_short() {
|
||||
let auth = AuthInfo::with_access_token("refresh".to_string(), "short".to_string());
|
||||
assert_eq!(auth.masked_access_token(), Some("***".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_masked_access_token_none() {
|
||||
let auth = AuthInfo::new("api-key".to_string(), AuthStrategy::Bearer);
|
||||
assert!(auth.masked_access_token().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_claude_auth_strategy() {
|
||||
let auth = AuthInfo::new("sk-test".to_string(), AuthStrategy::ClaudeAuth);
|
||||
assert_eq!(auth.strategy, AuthStrategy::ClaudeAuth);
|
||||
assert_ne!(auth.strategy, AuthStrategy::Anthropic);
|
||||
assert_ne!(auth.strategy, AuthStrategy::Bearer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_google_oauth_strategy() {
|
||||
let auth = AuthInfo::new("refresh-token".to_string(), AuthStrategy::GoogleOAuth);
|
||||
assert_eq!(auth.strategy, AuthStrategy::GoogleOAuth);
|
||||
assert_ne!(auth.strategy, AuthStrategy::Google);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_strategies_are_distinct() {
|
||||
let strategies = [
|
||||
AuthStrategy::Anthropic,
|
||||
AuthStrategy::ClaudeAuth,
|
||||
AuthStrategy::Bearer,
|
||||
AuthStrategy::Google,
|
||||
AuthStrategy::GoogleOAuth,
|
||||
];
|
||||
|
||||
for (i, s1) in strategies.iter().enumerate() {
|
||||
for (j, s2) in strategies.iter().enumerate() {
|
||||
if i == j {
|
||||
assert_eq!(s1, s2);
|
||||
} else {
|
||||
assert_ne!(s1, s2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user