From d04f74bd34443b9b4ce93c491d06435d7dcebbd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 24 Sep 2026 19:54:08 +0800 Subject: [PATCH] =?UTF-8?q?Tripo=20=E5=90=AF=E5=8A=A8=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E8=BF=9E=E8=A7=A3=E6=9E=90=E5=90=8E=E7=9A=84=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E4=B8=80=E8=B5=B7=E6=A0=A1=E9=AA=8C=EF=BC=8C=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E5=8F=AA=E7=9C=8B=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F=E5=8E=9F?= =?UTF-8?q?=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - api-server tripo3d/provider.rs:`validate_tripo_startup_config` 在原有 env 校验之后,追加对 `AppConfig` 字段值的校验(零超时、重试越界),与请求期 `tripo_settings` 读的是同一对字段 - 用例:新增 `startup_rejects_invalid_parsed_fields_from_any_construction_path`,覆盖「直接构造的配置带着非法值也不能通过启动检查」 --- .../crates/api-server/src/tripo3d/provider.rs | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/server-rs/crates/api-server/src/tripo3d/provider.rs b/server-rs/crates/api-server/src/tripo3d/provider.rs index 0bc42c64c..00fa5d309 100644 --- a/server-rs/crates/api-server/src/tripo3d/provider.rs +++ b/server-rs/crates/api-server/src/tripo3d/provider.rs @@ -37,13 +37,35 @@ pub(crate) struct TripoStartupValues<'a> { } /// 读取原始 env 后校验;返回值直接作为启动失败原因展示给运维。 +/// +/// 只看环境变量是不够的:真正发请求时用的是 [`AppConfig`] 上的字段值,而配置文件、测试与 +/// 代码里直接构造的配置都不经过 `from_env`,非法值(零超时、越界重试)能带着它一路通过 +/// 启动检查,直到请求期才以 503 暴露。因此这里把解析后的字段也一并校验,两处用的是同一份值。 pub(crate) fn validate_tripo_startup_config(config: &AppConfig) -> Result<(), String> { validate_tripo_startup_values(TripoStartupValues { base_url: config.tripo_base_url.as_str(), api_key: config.tripo_api_key.as_deref(), request_timeout_ms: env::var(TRIPO_REQUEST_TIMEOUT_MS_ENV).ok().as_deref(), retries: env::var(TRIPO_RETRIES_ENV).ok().as_deref(), - }) + })?; + validate_tripo_startup_fields(config) +} + +/// 解析后的两个运行旋钮:与 [`tripo_settings`] 读的是同一对字段。 +fn validate_tripo_startup_fields(config: &AppConfig) -> Result<(), String> { + if config.tripo_request_timeout_ms == 0 { + return Err(format!( + "{TRIPO_REQUEST_TIMEOUT_MS_ENV} 必须大于 0:零超时在 timeout 与 reqwest \ + 两处含义相反,放它进来只会得到指向错误方向的报错。" + )); + } + if config.tripo_retries > TRIPO_MAX_RETRIES { + return Err(format!( + "{TRIPO_RETRIES_ENV} 不能超过 {TRIPO_MAX_RETRIES},当前为 {}。", + config.tripo_retries + )); + } + Ok(()) } /// 纯函数形态的校验,便于用例直接覆盖各种坏配置。 @@ -268,6 +290,30 @@ mod tests { ); } + /// 启动检查必须看配置对象里的值,不只是环境变量原文:否则直接构造的非法配置 + /// (零超时、越界重试)能通过启动检查,直到请求期才以 503 暴露。 + #[test] + fn startup_rejects_invalid_parsed_fields_from_any_construction_path() { + let mut config = AppConfig::default(); + config.tripo_base_url = "https://openapi.tripo3d.com/v3".to_string(); + config.tripo_api_key = Some("test-key".to_string()); + assert!(validate_tripo_startup_config(&config).is_ok()); + + config.tripo_request_timeout_ms = 0; + let zero_timeout = validate_tripo_startup_config(&config) + .expect_err("零超时必须拒绝启动,即使它不是从环境变量读来的"); + assert!( + zero_timeout.contains("TRIPO_REQUEST_TIMEOUT_MS"), + "{zero_timeout}" + ); + + config.tripo_request_timeout_ms = crate::config::DEFAULT_TRIPO_REQUEST_TIMEOUT_MS; + config.tripo_retries = TRIPO_MAX_RETRIES + 1; + let too_many = validate_tripo_startup_config(&config) + .expect_err("越界重试必须拒绝启动,即使它不是从环境变量读来的"); + assert!(too_many.contains("TRIPO_RETRIES"), "{too_many}"); + } + #[test] fn startup_accepts_declared_knobs_at_their_limits() { let mut values = startup_values("https://openapi.tripo3d.com/v3", Some("key"));