fix(gemini): relax validation when adding providers (#210)
Allow users to create Gemini provider configurations without API key and fill it in later. Split validation into two modes: - validate_gemini_settings: Basic structure check (used when adding) - validate_gemini_settings_strict: Full validation (used when switching) This fixes the error 'Gemini config missing required field: GEMINI_API_KEY' when trying to add Gemini providers from presets like PackyCode or Google. Changes: - Add validate_gemini_settings_strict for switching validation - Update write_gemini_live to use strict validation - Add comprehensive tests for both validation modes
This commit is contained in:
@@ -217,8 +217,33 @@ pub fn json_to_env(settings: &Value) -> Result<HashMap<String, String>, AppError
|
|||||||
Ok(env_map)
|
Ok(env_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 验证 Gemini 配置的必需字段
|
/// 验证 Gemini 配置的基本结构
|
||||||
|
///
|
||||||
|
/// 此函数只验证配置的基本格式,不强制要求 GEMINI_API_KEY。
|
||||||
|
/// 这允许用户先创建供应商配置,稍后再填写 API Key。
|
||||||
|
///
|
||||||
|
/// API Key 的验证会在切换供应商时进行(通过 `validate_gemini_settings_strict`)。
|
||||||
pub fn validate_gemini_settings(settings: &Value) -> Result<(), AppError> {
|
pub fn validate_gemini_settings(settings: &Value) -> Result<(), AppError> {
|
||||||
|
// 只验证基本结构,不强制要求 GEMINI_API_KEY
|
||||||
|
// 如果有 env 字段,验证它是一个对象
|
||||||
|
if let Some(env) = settings.get("env") {
|
||||||
|
if !env.is_object() {
|
||||||
|
return Err(AppError::localized(
|
||||||
|
"gemini.validation.invalid_env",
|
||||||
|
"Gemini 配置格式错误: env 必须是对象",
|
||||||
|
"Gemini config invalid: env must be an object",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 严格验证 Gemini 配置(要求必需字段)
|
||||||
|
///
|
||||||
|
/// 此函数在切换供应商时使用,确保配置包含所有必需的字段。
|
||||||
|
/// 对于需要 API Key 的供应商(如 PackyCode),会验证 GEMINI_API_KEY 字段。
|
||||||
|
pub fn validate_gemini_settings_strict(settings: &Value) -> Result<(), AppError> {
|
||||||
let env_map = json_to_env(settings)?;
|
let env_map = json_to_env(settings)?;
|
||||||
|
|
||||||
// 如果 env 为空,表示使用 OAuth(如 Google 官方),跳过验证
|
// 如果 env 为空,表示使用 OAuth(如 Google 官方),跳过验证
|
||||||
@@ -557,12 +582,14 @@ KEY_WITH-DASH=value";
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_validate_empty_env_for_oauth() {
|
fn test_validate_empty_env_for_oauth() {
|
||||||
// 测试空 env(Google 官方 OAuth)可以通过验证
|
// 测试空 env(Google 官方 OAuth)可以通过基本验证
|
||||||
let settings = serde_json::json!({
|
let settings = serde_json::json!({
|
||||||
"env": {}
|
"env": {}
|
||||||
});
|
});
|
||||||
|
|
||||||
assert!(validate_gemini_settings(&settings).is_ok());
|
assert!(validate_gemini_settings(&settings).is_ok());
|
||||||
|
// 严格验证也应该通过(空 env 表示 OAuth)
|
||||||
|
assert!(validate_gemini_settings_strict(&settings).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -576,17 +603,31 @@ KEY_WITH-DASH=value";
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert!(validate_gemini_settings(&settings).is_ok());
|
assert!(validate_gemini_settings(&settings).is_ok());
|
||||||
|
assert!(validate_gemini_settings_strict(&settings).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_validate_env_without_api_key_fails() {
|
fn test_validate_env_without_api_key_relaxed() {
|
||||||
// 测试缺少 API Key 的非空配置会失败
|
// 测试缺少 API Key 的非空配置在基本验证中可以通过(用户稍后填写)
|
||||||
let settings = serde_json::json!({
|
let settings = serde_json::json!({
|
||||||
"env": {
|
"env": {
|
||||||
"GEMINI_MODEL": "gemini-2.5-pro"
|
"GEMINI_MODEL": "gemini-2.5-pro"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 基本验证应该通过(允许稍后填写 API Key)
|
||||||
|
assert!(validate_gemini_settings(&settings).is_ok());
|
||||||
|
// 严格验证应该失败(切换时要求完整配置)
|
||||||
|
assert!(validate_gemini_settings_strict(&settings).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_validate_invalid_env_type() {
|
||||||
|
// 测试 env 不是对象时会失败
|
||||||
|
let settings = serde_json::json!({
|
||||||
|
"env": "invalid_string"
|
||||||
|
});
|
||||||
|
|
||||||
assert!(validate_gemini_settings(&settings).is_err());
|
assert!(validate_gemini_settings(&settings).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1465,7 +1465,7 @@ impl ProviderService {
|
|||||||
|
|
||||||
fn write_gemini_live(provider: &Provider) -> Result<(), AppError> {
|
fn write_gemini_live(provider: &Provider) -> Result<(), AppError> {
|
||||||
use crate::gemini_config::{
|
use crate::gemini_config::{
|
||||||
json_to_env, validate_gemini_settings, write_gemini_env_atomic,
|
json_to_env, validate_gemini_settings_strict, write_gemini_env_atomic,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 一次性检测认证类型,避免重复检测
|
// 一次性检测认证类型,避免重复检测
|
||||||
@@ -1479,15 +1479,15 @@ impl ProviderService {
|
|||||||
Self::ensure_google_oauth_security_flag(provider)?;
|
Self::ensure_google_oauth_security_flag(provider)?;
|
||||||
}
|
}
|
||||||
GeminiAuthType::Packycode => {
|
GeminiAuthType::Packycode => {
|
||||||
// PackyCode 供应商,使用 API Key
|
// PackyCode 供应商,使用 API Key(切换时严格验证)
|
||||||
validate_gemini_settings(&provider.settings_config)?;
|
validate_gemini_settings_strict(&provider.settings_config)?;
|
||||||
let env_map = json_to_env(&provider.settings_config)?;
|
let env_map = json_to_env(&provider.settings_config)?;
|
||||||
write_gemini_env_atomic(&env_map)?;
|
write_gemini_env_atomic(&env_map)?;
|
||||||
Self::ensure_packycode_security_flag(provider)?;
|
Self::ensure_packycode_security_flag(provider)?;
|
||||||
}
|
}
|
||||||
GeminiAuthType::Generic => {
|
GeminiAuthType::Generic => {
|
||||||
// 通用供应商,使用 API Key
|
// 通用供应商,使用 API Key(切换时严格验证)
|
||||||
validate_gemini_settings(&provider.settings_config)?;
|
validate_gemini_settings_strict(&provider.settings_config)?;
|
||||||
let env_map = json_to_env(&provider.settings_config)?;
|
let env_map = json_to_env(&provider.settings_config)?;
|
||||||
write_gemini_env_atomic(&env_map)?;
|
write_gemini_env_atomic(&env_map)?;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user