mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-29 09:37:37 +08:00
fix(usage): treat usage_script credentials as explicit overrides (#4654)
* fix(usage): treat usage_script credentials as explicit overrides * fix: treat usage script credentials as explicit overrides
This commit is contained in:
@@ -185,6 +185,48 @@ fn get_primary_endpoint(request: &DeepLinkImportRequest) -> String {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn normalize_deeplink_api_key(api_key: &str) -> String {
|
||||
api_key.trim().to_string()
|
||||
}
|
||||
|
||||
fn normalize_deeplink_base_url(base_url: &str) -> String {
|
||||
base_url.trim().trim_end_matches('/').to_string()
|
||||
}
|
||||
|
||||
fn usage_api_key_override(request: &DeepLinkImportRequest) -> Option<String> {
|
||||
let usage_api_key = normalize_deeplink_api_key(request.usage_api_key.as_deref()?);
|
||||
if usage_api_key.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let provider_api_key = request
|
||||
.api_key
|
||||
.as_deref()
|
||||
.map(normalize_deeplink_api_key)
|
||||
.unwrap_or_default();
|
||||
|
||||
if !provider_api_key.is_empty() && usage_api_key == provider_api_key {
|
||||
None
|
||||
} else {
|
||||
Some(usage_api_key)
|
||||
}
|
||||
}
|
||||
|
||||
fn usage_base_url_override(request: &DeepLinkImportRequest) -> Option<String> {
|
||||
let usage_base_url = normalize_deeplink_base_url(request.usage_base_url.as_deref()?);
|
||||
if usage_base_url.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let provider_base_url = normalize_deeplink_base_url(&get_primary_endpoint(request));
|
||||
|
||||
if !provider_base_url.is_empty() && usage_base_url == provider_base_url {
|
||||
None
|
||||
} else {
|
||||
Some(usage_base_url)
|
||||
}
|
||||
}
|
||||
|
||||
/// Build provider meta with usage script configuration
|
||||
fn build_provider_meta(request: &DeepLinkImportRequest) -> Result<Option<ProviderMeta>, AppError> {
|
||||
// Check if any usage script fields are provided
|
||||
@@ -211,25 +253,13 @@ fn build_provider_meta(request: &DeepLinkImportRequest) -> Result<Option<Provide
|
||||
// Determine enabled state: explicit param > has code > false
|
||||
let enabled = request.usage_enabled.unwrap_or(!code.is_empty());
|
||||
|
||||
// Build UsageScript - use provider's API key and endpoint as defaults
|
||||
// Note: use primary endpoint only (first one if comma-separated)
|
||||
let usage_script = UsageScript {
|
||||
enabled,
|
||||
language: "javascript".to_string(),
|
||||
code,
|
||||
timeout: Some(10),
|
||||
api_key: request
|
||||
.usage_api_key
|
||||
.clone()
|
||||
.or_else(|| request.api_key.clone()),
|
||||
base_url: request.usage_base_url.clone().or_else(|| {
|
||||
let primary = get_primary_endpoint(request);
|
||||
if primary.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(primary)
|
||||
}
|
||||
}),
|
||||
api_key: usage_api_key_override(request),
|
||||
base_url: usage_base_url_override(request),
|
||||
access_token: request.usage_access_token.clone(),
|
||||
user_id: request.usage_user_id.clone(),
|
||||
template_type: None, // Deeplink providers don't specify template type (will use backward compatibility logic)
|
||||
|
||||
@@ -260,6 +260,154 @@ fn test_build_gemini_provider_without_model() {
|
||||
assert!(env.get("GEMINI_MODEL").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deeplink_usage_script_does_not_copy_provider_credentials() {
|
||||
use super::provider::build_provider_from_request;
|
||||
|
||||
let request = DeepLinkImportRequest {
|
||||
version: "v1".to_string(),
|
||||
resource: "provider".to_string(),
|
||||
app: Some("claude".to_string()),
|
||||
name: Some("Test Claude".to_string()),
|
||||
homepage: Some("https://example.com".to_string()),
|
||||
endpoint: Some("https://api.example.com/v1/".to_string()),
|
||||
api_key: Some("sk-main".to_string()),
|
||||
icon: None,
|
||||
model: None,
|
||||
notes: None,
|
||||
haiku_model: None,
|
||||
sonnet_model: None,
|
||||
opus_model: None,
|
||||
config: None,
|
||||
config_format: None,
|
||||
config_url: None,
|
||||
apps: None,
|
||||
repo: None,
|
||||
directory: None,
|
||||
branch: None,
|
||||
content: None,
|
||||
description: None,
|
||||
enabled: None,
|
||||
usage_enabled: Some(true),
|
||||
usage_script: None,
|
||||
usage_api_key: None,
|
||||
usage_base_url: None,
|
||||
usage_access_token: None,
|
||||
usage_user_id: None,
|
||||
usage_auto_interval: None,
|
||||
};
|
||||
|
||||
let provider = build_provider_from_request(&AppType::Claude, &request).unwrap();
|
||||
let script = provider
|
||||
.meta
|
||||
.as_ref()
|
||||
.and_then(|meta| meta.usage_script.as_ref())
|
||||
.expect("usage script should be created");
|
||||
|
||||
assert!(script.enabled);
|
||||
assert_eq!(script.api_key, None);
|
||||
assert_eq!(script.base_url, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deeplink_usage_script_omits_explicit_credentials_that_match_provider() {
|
||||
use super::provider::build_provider_from_request;
|
||||
|
||||
let request = DeepLinkImportRequest {
|
||||
version: "v1".to_string(),
|
||||
resource: "provider".to_string(),
|
||||
app: Some("claude".to_string()),
|
||||
name: Some("Test Claude".to_string()),
|
||||
homepage: Some("https://example.com".to_string()),
|
||||
endpoint: Some("https://api.example.com/v1/".to_string()),
|
||||
api_key: Some("sk-main".to_string()),
|
||||
icon: None,
|
||||
model: None,
|
||||
notes: None,
|
||||
haiku_model: None,
|
||||
sonnet_model: None,
|
||||
opus_model: None,
|
||||
config: None,
|
||||
config_format: None,
|
||||
config_url: None,
|
||||
apps: None,
|
||||
repo: None,
|
||||
directory: None,
|
||||
branch: None,
|
||||
content: None,
|
||||
description: None,
|
||||
enabled: None,
|
||||
usage_enabled: Some(true),
|
||||
usage_script: None,
|
||||
usage_api_key: Some(" sk-main ".to_string()),
|
||||
usage_base_url: Some(" https://api.example.com/v1/ ".to_string()),
|
||||
usage_access_token: None,
|
||||
usage_user_id: None,
|
||||
usage_auto_interval: None,
|
||||
};
|
||||
|
||||
let provider = build_provider_from_request(&AppType::Claude, &request).unwrap();
|
||||
let script = provider
|
||||
.meta
|
||||
.as_ref()
|
||||
.and_then(|meta| meta.usage_script.as_ref())
|
||||
.expect("usage script should be created");
|
||||
|
||||
assert_eq!(script.api_key, None);
|
||||
assert_eq!(script.base_url, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deeplink_usage_script_preserves_distinct_usage_credentials() {
|
||||
use super::provider::build_provider_from_request;
|
||||
|
||||
let request = DeepLinkImportRequest {
|
||||
version: "v1".to_string(),
|
||||
resource: "provider".to_string(),
|
||||
app: Some("claude".to_string()),
|
||||
name: Some("Test Claude".to_string()),
|
||||
homepage: Some("https://example.com".to_string()),
|
||||
endpoint: Some("https://api.example.com/v1".to_string()),
|
||||
api_key: Some("sk-main".to_string()),
|
||||
icon: None,
|
||||
model: None,
|
||||
notes: None,
|
||||
haiku_model: None,
|
||||
sonnet_model: None,
|
||||
opus_model: None,
|
||||
config: None,
|
||||
config_format: None,
|
||||
config_url: None,
|
||||
apps: None,
|
||||
repo: None,
|
||||
directory: None,
|
||||
branch: None,
|
||||
content: None,
|
||||
description: None,
|
||||
enabled: None,
|
||||
usage_enabled: Some(true),
|
||||
usage_script: None,
|
||||
usage_api_key: Some(" sk-usage ".to_string()),
|
||||
usage_base_url: Some(" https://usage.example/api/ ".to_string()),
|
||||
usage_access_token: None,
|
||||
usage_user_id: None,
|
||||
usage_auto_interval: None,
|
||||
};
|
||||
|
||||
let provider = build_provider_from_request(&AppType::Claude, &request).unwrap();
|
||||
let script = provider
|
||||
.meta
|
||||
.as_ref()
|
||||
.and_then(|meta| meta.usage_script.as_ref())
|
||||
.expect("usage script should be created");
|
||||
|
||||
assert_eq!(script.api_key.as_deref(), Some("sk-usage"));
|
||||
assert_eq!(
|
||||
script.base_url.as_deref(),
|
||||
Some("https://usage.example/api")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_and_merge_config_claude() {
|
||||
// Prepare Base64 encoded Claude config
|
||||
|
||||
Reference in New Issue
Block a user