diff --git a/src-tauri/src/commands/provider.rs b/src-tauri/src/commands/provider.rs index 2fa6893..7f51389 100644 --- a/src-tauri/src/commands/provider.rs +++ b/src-tauri/src/commands/provider.rs @@ -123,6 +123,7 @@ pub async fn queryProviderUsage( /// 测试用量脚本(使用当前编辑器中的脚本,不保存) #[allow(non_snake_case)] +#[allow(clippy::too_many_arguments)] #[tauri::command] pub async fn testUsageScript( state: State<'_, AppState>, @@ -130,6 +131,8 @@ pub async fn testUsageScript( app: String, #[allow(non_snake_case)] scriptCode: String, timeout: Option, + #[allow(non_snake_case)] apiKey: Option, + #[allow(non_snake_case)] baseUrl: Option, #[allow(non_snake_case)] accessToken: Option, #[allow(non_snake_case)] userId: Option, ) -> Result { @@ -140,6 +143,8 @@ pub async fn testUsageScript( &providerId, &scriptCode, timeout.unwrap_or(10), + apiKey.as_deref(), + baseUrl.as_deref(), accessToken.as_deref(), userId.as_deref(), ) diff --git a/src-tauri/src/provider.rs b/src-tauri/src/provider.rs index 5621e1c..6682dee 100644 --- a/src-tauri/src/provider.rs +++ b/src-tauri/src/provider.rs @@ -63,11 +63,19 @@ pub struct UsageScript { pub code: String, #[serde(skip_serializing_if = "Option::is_none")] pub timeout: Option, - /// 访问令牌(用于需要登录的接口) + /// 用量查询专用的 API Key(通用模板使用) + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(rename = "apiKey")] + pub api_key: Option, + /// 用量查询专用的 Base URL(通用和 NewAPI 模板使用) + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(rename = "baseUrl")] + pub base_url: Option, + /// 访问令牌(用于需要登录的接口,NewAPI 模板使用) #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "accessToken")] pub access_token: Option, - /// 用户ID(用于需要用户标识的接口) + /// 用户ID(用于需要用户标识的接口,NewAPI 模板使用) #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "userId")] pub user_id: Option, diff --git a/src-tauri/src/services/provider.rs b/src-tauri/src/services/provider.rs index 1ceaf28..d8afcfd 100644 --- a/src-tauri/src/services/provider.rs +++ b/src-tauri/src/services/provider.rs @@ -802,7 +802,7 @@ impl ProviderService { app_type: AppType, provider_id: &str, ) -> Result { - let (provider, script_code, timeout, access_token, user_id) = { + let (script_code, timeout, api_key, base_url, access_token, user_id) = { let config = state.config.read().map_err(AppError::from)?; let manager = config .get_manager(&app_type) @@ -814,38 +814,37 @@ impl ProviderService { format!("Provider not found: {}", provider_id), ) })?; - let (script_code, timeout, access_token, user_id) = { - let usage_script = provider - .meta - .as_ref() - .and_then(|m| m.usage_script.as_ref()) - .ok_or_else(|| { - AppError::localized( - "provider.usage.script.missing", - "未配置用量查询脚本", - "Usage script is not configured", - ) - })?; - if !usage_script.enabled { - return Err(AppError::localized( - "provider.usage.disabled", - "用量查询未启用", - "Usage query is disabled", - )); - } - ( - usage_script.code.clone(), - usage_script.timeout.unwrap_or(10), - usage_script.access_token.clone(), - usage_script.user_id.clone(), - ) - }; - (provider, script_code, timeout, access_token, user_id) + let usage_script = provider + .meta + .as_ref() + .and_then(|m| m.usage_script.as_ref()) + .ok_or_else(|| { + AppError::localized( + "provider.usage.script.missing", + "未配置用量查询脚本", + "Usage script is not configured", + ) + })?; + if !usage_script.enabled { + return Err(AppError::localized( + "provider.usage.disabled", + "用量查询未启用", + "Usage query is disabled", + )); + } + + // 直接从 UsageScript 中获取凭证,不再从供应商配置提取 + ( + usage_script.code.clone(), + usage_script.timeout.unwrap_or(10), + usage_script.api_key.clone().unwrap_or_default(), + usage_script.base_url.clone().unwrap_or_default(), + usage_script.access_token.clone(), + usage_script.user_id.clone(), + ) }; - let (api_key, base_url) = Self::extract_credentials(&provider, &app_type)?; - Self::execute_and_format_usage_result( &script_code, &api_key, @@ -858,36 +857,23 @@ impl ProviderService { } /// 测试用量脚本(使用临时脚本内容,不保存) + #[allow(clippy::too_many_arguments)] pub async fn test_usage_script( - state: &AppState, - app_type: AppType, - provider_id: &str, + _state: &AppState, + _app_type: AppType, + _provider_id: &str, script_code: &str, timeout: u64, + api_key: Option<&str>, + base_url: Option<&str>, access_token: Option<&str>, user_id: Option<&str>, ) -> Result { - // 获取 provider 的 API 凭证 - let provider = { - let config = state.config.read().map_err(AppError::from)?; - let manager = config - .get_manager(&app_type) - .ok_or_else(|| Self::app_not_found(&app_type))?; - manager.providers.get(provider_id).cloned().ok_or_else(|| { - AppError::localized( - "provider.not_found", - format!("供应商不存在: {}", provider_id), - format!("Provider not found: {}", provider_id), - ) - })? - }; - - let (api_key, base_url) = Self::extract_credentials(&provider, &app_type)?; - + // 直接使用传入的凭证参数进行测试 Self::execute_and_format_usage_result( script_code, - &api_key, - &base_url, + api_key.unwrap_or(""), + base_url.unwrap_or(""), timeout, access_token, user_id, @@ -1137,6 +1123,7 @@ impl ProviderService { Ok(()) } + #[allow(dead_code)] fn extract_credentials( provider: &Provider, app_type: &AppType, diff --git a/src/components/UsageScriptModal.tsx b/src/components/UsageScriptModal.tsx index a1db595..ce4ed5b 100644 --- a/src/components/UsageScriptModal.tsx +++ b/src/components/UsageScriptModal.tsx @@ -166,6 +166,8 @@ const UsageScriptModal: React.FC = ({ appId, script.code, script.timeout, + script.apiKey, + script.baseUrl, script.accessToken, script.userId, ); @@ -225,23 +227,40 @@ const UsageScriptModal: React.FC = ({ const handleUsePreset = (presetName: string) => { const preset = PRESET_TEMPLATES[presetName]; if (preset) { - // 如果选择的不是 NewAPI 模板,清空高级配置字段 - if (presetName !== TEMPLATE_KEYS.NEW_API) { + // 根据模板类型清空不同的字段 + if (presetName === TEMPLATE_KEYS.CUSTOM) { + // 自定义:清空所有凭证字段 + setScript({ + ...script, + code: preset, + apiKey: undefined, + baseUrl: undefined, + accessToken: undefined, + userId: undefined, + }); + } else if (presetName === TEMPLATE_KEYS.GENERAL) { + // 通用:保留 apiKey 和 baseUrl,清空 NewAPI 字段 setScript({ ...script, code: preset, accessToken: undefined, userId: undefined, }); - } else { - setScript({ ...script, code: preset }); + } else if (presetName === TEMPLATE_KEYS.NEW_API) { + // NewAPI:清空 apiKey(NewAPI 不使用通用的 apiKey) + setScript({ + ...script, + code: preset, + apiKey: undefined, + }); } setSelectedTemplate(presetName); // 记录选择的模板 } }; - // 判断是否应该显示高级配置(仅 NewAPI 模板需要) - const shouldShowAdvancedConfig = selectedTemplate === TEMPLATE_KEYS.NEW_API; + // 判断是否应该显示凭证配置区域 + const shouldShowCredentialsConfig = + selectedTemplate === TEMPLATE_KEYS.GENERAL || selectedTemplate === TEMPLATE_KEYS.NEW_API; return ( !open && onClose()}> @@ -296,38 +315,97 @@ const UsageScriptModal: React.FC = ({ - {/* 高级配置:Access Token 和 User ID(仅 NewAPI 模板显示) */} - {shouldShowAdvancedConfig && ( + {/* 凭证配置区域:通用和 NewAPI 模板显示 */} + {shouldShowCredentialsConfig && (
- +

+ {t("usageScript.credentialsConfig")} +

- + {/* 通用模板:显示 apiKey + baseUrl */} + {selectedTemplate === TEMPLATE_KEYS.GENERAL && ( + <> + + + + + )} + + {/* NewAPI 模板:显示 baseUrl + accessToken + userId */} + {selectedTemplate === TEMPLATE_KEYS.NEW_API && ( + <> + + + + + + + )}
)} diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 2c57f1d..cec1f04 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -356,6 +356,7 @@ "templateCustom": "Custom", "templateGeneral": "General", "templateNewAPI": "NewAPI", + "credentialsConfig": "Credentials", "accessToken": "Access Token", "accessTokenPlaceholder": "Generate in 'Security Settings'", "userId": "User ID", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 7861699..bd0db54 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -356,6 +356,7 @@ "templateCustom": "自定义", "templateGeneral": "通用模板", "templateNewAPI": "NewAPI", + "credentialsConfig": "凭证配置", "accessToken": "访问令牌", "accessTokenPlaceholder": "在'安全设置'里生成", "userId": "用户 ID", diff --git a/src/lib/api/usage.ts b/src/lib/api/usage.ts index 5a4d725..c03f11d 100644 --- a/src/lib/api/usage.ts +++ b/src/lib/api/usage.ts @@ -32,6 +32,8 @@ export const usageApi = { appId: AppId, scriptCode: string, timeout?: number, + apiKey?: string, + baseUrl?: string, accessToken?: string, userId?: string, ): Promise { @@ -41,6 +43,8 @@ export const usageApi = { app: appId, scriptCode: scriptCode, timeout: timeout, + apiKey: apiKey, + baseUrl: baseUrl, accessToken: accessToken, userId: userId, }); diff --git a/src/types.ts b/src/types.ts index ae44b29..761c145 100644 --- a/src/types.ts +++ b/src/types.ts @@ -45,8 +45,10 @@ export interface UsageScript { language: "javascript"; // 脚本语言 code: string; // 脚本代码(JSON 格式配置) timeout?: number; // 超时时间(秒,默认 10) - accessToken?: string; // 访问令牌(用于需要登录的接口) - userId?: string; // 用户ID(用于需要用户标识的接口) + apiKey?: string; // 用量查询专用的 API Key(通用模板使用) + baseUrl?: string; // 用量查询专用的 Base URL(通用和 NewAPI 模板使用) + accessToken?: string; // 访问令牌(NewAPI 模板使用) + userId?: string; // 用户ID(NewAPI 模板使用) autoQueryInterval?: number; // 自动查询间隔(单位:分钟,0 表示禁用) }