mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-04-16 08:12:42 +08:00
fix(proxy): resolve HalfOpen counter underflow and config field inconsistencies
- Fix HalfOpen counter underflow: increment half_open_requests when transitioning from Open to HalfOpen to prevent underflow in record_success/record_failure - Fix Gemini config field names: unify to GEMINI_API_KEY and GOOGLE_GEMINI_BASE_URL (removed GOOGLE_API_KEY and GEMINI_API_BASE) - Fix Codex proxy takeover: write base_url to config.toml instead of OPENAI_BASE_URL in auth.json (Codex CLI reads from config.toml)
This commit is contained in:
@@ -115,6 +115,8 @@ impl CircuitBreaker {
|
|||||||
"Circuit breaker transitioning from Open to HalfOpen (timeout reached)"
|
"Circuit breaker transitioning from Open to HalfOpen (timeout reached)"
|
||||||
);
|
);
|
||||||
self.transition_to_half_open().await;
|
self.transition_to_half_open().await;
|
||||||
|
// 增加计数,确保 record_success/record_failure 减计数时不会下溢
|
||||||
|
self.half_open_requests.fetch_add(1, Ordering::SeqCst);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,11 +120,7 @@ impl GeminiAdapter {
|
|||||||
/// 从 Provider 配置中提取原始 API Key
|
/// 从 Provider 配置中提取原始 API Key
|
||||||
fn extract_key_raw(&self, provider: &Provider) -> Option<String> {
|
fn extract_key_raw(&self, provider: &Provider) -> Option<String> {
|
||||||
if let Some(env) = provider.settings_config.get("env") {
|
if let Some(env) = provider.settings_config.get("env") {
|
||||||
// 优先使用 GOOGLE_GEMINI_API_KEY
|
// 使用 GEMINI_API_KEY
|
||||||
if let Some(key) = env.get("GOOGLE_GEMINI_API_KEY").and_then(|v| v.as_str()) {
|
|
||||||
return Some(key.to_string());
|
|
||||||
}
|
|
||||||
// 备选 GEMINI_API_KEY
|
|
||||||
if let Some(key) = env.get("GEMINI_API_KEY").and_then(|v| v.as_str()) {
|
if let Some(key) = env.get("GEMINI_API_KEY").and_then(|v| v.as_str()) {
|
||||||
return Some(key.to_string());
|
return Some(key.to_string());
|
||||||
}
|
}
|
||||||
@@ -276,7 +272,7 @@ mod tests {
|
|||||||
let adapter = GeminiAdapter::new();
|
let adapter = GeminiAdapter::new();
|
||||||
let provider = create_provider(json!({
|
let provider = create_provider(json!({
|
||||||
"env": {
|
"env": {
|
||||||
"GOOGLE_GEMINI_API_KEY": "AIza-test-key-12345678"
|
"GEMINI_API_KEY": "AIza-test-key-12345678"
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -291,7 +287,7 @@ mod tests {
|
|||||||
let adapter = GeminiAdapter::new();
|
let adapter = GeminiAdapter::new();
|
||||||
let provider = create_provider(json!({
|
let provider = create_provider(json!({
|
||||||
"env": {
|
"env": {
|
||||||
"GOOGLE_GEMINI_API_KEY": "ya29.test-access-token-12345"
|
"GEMINI_API_KEY": "ya29.test-access-token-12345"
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -308,7 +304,7 @@ mod tests {
|
|||||||
let adapter = GeminiAdapter::new();
|
let adapter = GeminiAdapter::new();
|
||||||
let provider = create_provider(json!({
|
let provider = create_provider(json!({
|
||||||
"env": {
|
"env": {
|
||||||
"GOOGLE_GEMINI_API_KEY": "{\"access_token\":\"ya29.test-token\",\"refresh_token\":\"1//refresh\"}"
|
"GEMINI_API_KEY": "{\"access_token\":\"ya29.test-token\",\"refresh_token\":\"1//refresh\"}"
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -324,7 +320,7 @@ mod tests {
|
|||||||
// API Key
|
// API Key
|
||||||
let api_key_provider = create_provider(json!({
|
let api_key_provider = create_provider(json!({
|
||||||
"env": {
|
"env": {
|
||||||
"GOOGLE_GEMINI_API_KEY": "AIza-test-key"
|
"GEMINI_API_KEY": "AIza-test-key"
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -335,7 +331,7 @@ mod tests {
|
|||||||
// OAuth access_token
|
// OAuth access_token
|
||||||
let oauth_provider = create_provider(json!({
|
let oauth_provider = create_provider(json!({
|
||||||
"env": {
|
"env": {
|
||||||
"GOOGLE_GEMINI_API_KEY": "ya29.test-token"
|
"GEMINI_API_KEY": "ya29.test-token"
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -346,7 +342,7 @@ mod tests {
|
|||||||
// OAuth JSON
|
// OAuth JSON
|
||||||
let oauth_json_provider = create_provider(json!({
|
let oauth_json_provider = create_provider(json!({
|
||||||
"env": {
|
"env": {
|
||||||
"GOOGLE_GEMINI_API_KEY": "{\"access_token\":\"ya29.test\"}"
|
"GEMINI_API_KEY": "{\"access_token\":\"ya29.test\"}"
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ mod tests {
|
|||||||
fn test_from_app_type_gemini_api_key() {
|
fn test_from_app_type_gemini_api_key() {
|
||||||
let provider = create_provider(json!({
|
let provider = create_provider(json!({
|
||||||
"env": {
|
"env": {
|
||||||
"GOOGLE_GEMINI_API_KEY": "AIza-test-key"
|
"GEMINI_API_KEY": "AIza-test-key"
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -381,7 +381,7 @@ mod tests {
|
|||||||
fn test_from_app_type_gemini_cli_oauth() {
|
fn test_from_app_type_gemini_cli_oauth() {
|
||||||
let provider = create_provider(json!({
|
let provider = create_provider(json!({
|
||||||
"env": {
|
"env": {
|
||||||
"GOOGLE_GEMINI_API_KEY": "ya29.test-access-token"
|
"GEMINI_API_KEY": "ya29.test-access-token"
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -393,7 +393,7 @@ mod tests {
|
|||||||
fn test_from_app_type_gemini_cli_json() {
|
fn test_from_app_type_gemini_cli_json() {
|
||||||
let provider = create_provider(json!({
|
let provider = create_provider(json!({
|
||||||
"env": {
|
"env": {
|
||||||
"GOOGLE_GEMINI_API_KEY": "{\"access_token\":\"ya29.test\",\"refresh_token\":\"1//test\"}"
|
"GEMINI_API_KEY": "{\"access_token\":\"ya29.test\",\"refresh_token\":\"1//test\"}"
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -210,13 +210,13 @@ impl ProxyService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gemini: 同步 GOOGLE_API_KEY
|
// Gemini: 同步 GEMINI_API_KEY
|
||||||
if let Ok(live_config) = self.read_gemini_live() {
|
if let Ok(live_config) = self.read_gemini_live() {
|
||||||
if let Some(provider_id) = self.db.get_current_provider("gemini").ok().flatten() {
|
if let Some(provider_id) = self.db.get_current_provider("gemini").ok().flatten() {
|
||||||
if let Ok(Some(mut provider)) = self.db.get_provider_by_id(&provider_id, "gemini") {
|
if let Ok(Some(mut provider)) = self.db.get_provider_by_id(&provider_id, "gemini") {
|
||||||
// 从 live 配置提取 token
|
// 从 live 配置提取 token
|
||||||
if let Some(env) = live_config.get("env") {
|
if let Some(env) = live_config.get("env") {
|
||||||
if let Some(token) = env.get("GOOGLE_API_KEY").and_then(|v| v.as_str()) {
|
if let Some(token) = env.get("GEMINI_API_KEY").and_then(|v| v.as_str()) {
|
||||||
if !token.is_empty() {
|
if !token.is_empty() {
|
||||||
// 更新 provider 的 settings_config
|
// 更新 provider 的 settings_config
|
||||||
if let Some(env_obj) = provider
|
if let Some(env_obj) = provider
|
||||||
@@ -224,10 +224,10 @@ impl ProxyService {
|
|||||||
.get_mut("env")
|
.get_mut("env")
|
||||||
.and_then(|v| v.as_object_mut())
|
.and_then(|v| v.as_object_mut())
|
||||||
{
|
{
|
||||||
env_obj.insert("GOOGLE_API_KEY".to_string(), json!(token));
|
env_obj.insert("GEMINI_API_KEY".to_string(), json!(token));
|
||||||
} else {
|
} else {
|
||||||
provider.settings_config["env"] = json!({
|
provider.settings_config["env"] = json!({
|
||||||
"GOOGLE_API_KEY": token
|
"GEMINI_API_KEY": token
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 保存到数据库
|
// 保存到数据库
|
||||||
@@ -368,27 +368,35 @@ impl ProxyService {
|
|||||||
log::info!("Claude Live 配置已接管,代理地址: {}", proxy_url);
|
log::info!("Claude Live 配置已接管,代理地址: {}", proxy_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Codex: 修改 OPENAI_BASE_URL,使用占位符替代真实 Token(代理会注入真实 Token)
|
// Codex: 修改 config.toml 的 base_url,auth.json 的 OPENAI_API_KEY(代理会注入真实 Token)
|
||||||
if let Ok(mut live_config) = self.read_codex_live() {
|
if let Ok(mut live_config) = self.read_codex_live() {
|
||||||
|
// 1. 修改 auth.json 中的 OPENAI_API_KEY(使用占位符)
|
||||||
if let Some(auth) = live_config.get_mut("auth").and_then(|v| v.as_object_mut()) {
|
if let Some(auth) = live_config.get_mut("auth").and_then(|v| v.as_object_mut()) {
|
||||||
auth.insert("OPENAI_BASE_URL".to_string(), json!(&proxy_url));
|
|
||||||
// 使用占位符,避免显示缺少 key 的警告
|
|
||||||
auth.insert("OPENAI_API_KEY".to_string(), json!("PROXY_MANAGED"));
|
auth.insert("OPENAI_API_KEY".to_string(), json!("PROXY_MANAGED"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. 修改 config.toml 中的 base_url
|
||||||
|
let config_str = live_config
|
||||||
|
.get("config")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("");
|
||||||
|
let updated_config = Self::update_toml_base_url(config_str, &proxy_url);
|
||||||
|
live_config["config"] = json!(updated_config);
|
||||||
|
|
||||||
self.write_codex_live(&live_config)?;
|
self.write_codex_live(&live_config)?;
|
||||||
log::info!("Codex Live 配置已接管,代理地址: {}", proxy_url);
|
log::info!("Codex Live 配置已接管,代理地址: {}", proxy_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gemini: 修改 GEMINI_API_BASE,使用占位符替代真实 Token(代理会注入真实 Token)
|
// Gemini: 修改 GOOGLE_GEMINI_BASE_URL,使用占位符替代真实 Token(代理会注入真实 Token)
|
||||||
if let Ok(mut live_config) = self.read_gemini_live() {
|
if let Ok(mut live_config) = self.read_gemini_live() {
|
||||||
if let Some(env) = live_config.get_mut("env").and_then(|v| v.as_object_mut()) {
|
if let Some(env) = live_config.get_mut("env").and_then(|v| v.as_object_mut()) {
|
||||||
env.insert("GEMINI_API_BASE".to_string(), json!(&proxy_url));
|
env.insert("GOOGLE_GEMINI_BASE_URL".to_string(), json!(&proxy_url));
|
||||||
// 使用占位符,避免显示缺少 key 的警告
|
// 使用占位符,避免显示缺少 key 的警告
|
||||||
env.insert("GOOGLE_API_KEY".to_string(), json!("PROXY_MANAGED"));
|
env.insert("GEMINI_API_KEY".to_string(), json!("PROXY_MANAGED"));
|
||||||
} else {
|
} else {
|
||||||
live_config["env"] = json!({
|
live_config["env"] = json!({
|
||||||
"GEMINI_API_BASE": &proxy_url,
|
"GOOGLE_GEMINI_BASE_URL": &proxy_url,
|
||||||
"GOOGLE_API_KEY": "PROXY_MANAGED"
|
"GEMINI_API_KEY": "PROXY_MANAGED"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.write_gemini_live(&live_config)?;
|
self.write_gemini_live(&live_config)?;
|
||||||
@@ -526,6 +534,19 @@ impl ProxyService {
|
|||||||
|
|
||||||
// ==================== Live 配置读写辅助方法 ====================
|
// ==================== Live 配置读写辅助方法 ====================
|
||||||
|
|
||||||
|
/// 更新 TOML 字符串中的 base_url
|
||||||
|
fn update_toml_base_url(toml_str: &str, new_url: &str) -> String {
|
||||||
|
use toml_edit::DocumentMut;
|
||||||
|
|
||||||
|
let mut doc = toml_str
|
||||||
|
.parse::<DocumentMut>()
|
||||||
|
.unwrap_or_else(|_| DocumentMut::new());
|
||||||
|
|
||||||
|
doc["base_url"] = toml_edit::value(new_url);
|
||||||
|
|
||||||
|
doc.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
fn read_claude_live(&self) -> Result<Value, String> {
|
fn read_claude_live(&self) -> Result<Value, String> {
|
||||||
let path = get_claude_settings_path();
|
let path = get_claude_settings_path();
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
|
|||||||
Reference in New Issue
Block a user