Commit Graph

5148 Commits

Author SHA1 Message Date
k88936 8af02daeea 3D 产物上传带上退避重试,一次瞬时网络失败不再废掉整笔生成
- platform-oss:新增按前缀拼对象键的 `put_object_with_transient_retry`,与内部对象 PUT 共用同一套「可判定才重试」规则与退避(传输 / 超时 / 408 / 429 / 5xx 才重试,确定性 4xx 直接失败),body 只准备一次、每次 attempt 复用引用计数字节;重试次数或退避配置不足时按配置错误失败关闭
- api-server storage:3D 产物的模型 / 预览图 PUT 改用该入口,最多 3 次尝试、退避 250 / 500 ms(与角色动画同一口径);模型上限 512 MiB 但实际产物远小,重试成本可控
- 新增用例:重试次数为 0 与退避不足都失败关闭;对一定解析不出的域名会真的按退避重试多次
- docs/technical:写明产物 PUT 的重试口径与「每个网络动作都要能重试」的理由
2026-09-24 20:43:39 +08:00
k88936 75f757f807 图生 3D 在真正提交前再确认一次剩余预算
- worker:`submit_and_checkpoint` 接收 provider 截止时间,图生 3D 在下载源图、上传给 provider 之后、真正 submit 之前再调一次 `ensure_submit_budget`
- 原因写进注释:解析源图这两步都可能耗时到把预算用光,而 submit 不可撤销且计费,宁可本次尝试判失败,也不要提交上去立刻被截止时间判失败、白花一次外部调用
- `ensure_submit_budget` 的说明补上两处调用点
2026-09-24 20:40:14 +08:00
k88936 4ca1329d24 任务状态查询本身带上退避重试,瞬时故障不再作废一次计费任务
- platform-tripo:`get_task` 内部按 `TripoSettings::retries` 对瞬时故障(连接抖动 / 超时、408、429、5xx)退避重试,确定性失败(任务 id 非法、响应结构不符、404)立即返回;重试字段与退避函数改名,不再只服务产物下载
- api-server worker:查询在重试后仍是瞬时故障时继续按轮询间隔查到预算用尽,只有确定性失败才立刻收口
- 新增用例:一次 500 后成功(重试生效)、404 只打一次请求(不重试),并给测试客户端加了可指定 base_url 的构造
- docs/technical:写明查询带退避重试与「确定性失败立即返回」的边界
2026-09-24 20:39:12 +08:00
k88936 a08e3bf262 产物地址只允许公网主机,回环与内网段一律拒收
- platform-tripo:`TripoUrl::parse` 在协议与 userinfo 检查之后,再加一道主机检查——回环、内网、链路本地、组播与保留段(含云元数据 169.254.169.254)全部拒收;`localhost` 及其子域同样拒收;IPv4-mapped 的 IPv6 地址按内嵌的 IPv4 判定,`::ffff:127.0.0.1` 绕不过去
- 保留段按 IANA 特殊用途表逐段列出(0/8、100.64/10、192.0.0/24、198.18/15、240/4),因为 `Ipv4Addr::is_reserved` 与 `is_global` 在当前工具链上仍是 nightly
- 新增用例覆盖 17 个拒收地址与 5 个放行地址;产物下载用例本来就跑在 127.0.0.1 的本地 mock 上,改用 `parse_unchecked_for_test` 构造
- docs/technical:写明这条主机规则与「域名解析结果在这一层看不见」的已知限制
2026-09-24 20:36:57 +08:00
k88936 a6409d79dc 3D 定价不再在加载时检查加总溢出
- pricing:加载校验删掉「两段加价项相加 + 全表最高底价」的溢出检查与对应的 overflow_error 助手,只保留每段键齐全与合法性的校验
- 理由写进注释:真实查价只用一个端点的底价与它自己的加价项,价格都是手填的小数字,撞不到 u32 上限;按两段相加去挡反而会拒掉能正常计费的配置
- 计费点的 checked_add 保留为唯一一道溢出闸,溢出即失败退款(PriceOverflow 的说明同步改掉「配置阶段已挡过一次」的说法)
2026-09-24 20:34:57 +08:00
k88936 93ef6cebf3 图生 3D 的引用预检改为按归类信号收敛,不再拿状态码猜
- api-server:`resolve_editor_reference_record_by_id_for_owner` 拆出 `..._classified` 版本,失败带「引用不可用 / 其它」归类;原函数保持逐字段一致的对外行为(其它调用方不受影响)
- api-server tripo3d/image_source.rs:注入的解析闭包改为返回归类后的失败,只对 `Unavailable` 收敛成「图片输入必须是当前账号已登记的画布资源或素材」,400 / 404 里的 runtime 与通用过程错误原样上报;模块注释同步
- 用例:`image_source_resolution_only_collapses_unavailable_references` 改成「Unavailable 收敛、400/404/403/409/5xx 一律保留原错」
- docs/project-memory:pitfalls 新增「4xx 不等于用户输入错」一条,记下这条口径与新增模块文案时要同步的清单
2026-09-24 19:55:21 +08:00
k88936 d04f74bd34 Tripo 启动检查连解析后的字段一起校验,不再只看环境变量原文
- api-server tripo3d/provider.rs:`validate_tripo_startup_config` 在原有 env 校验之后,追加对 `AppConfig` 字段值的校验(零超时、重试越界),与请求期 `tripo_settings` 读的是同一对字段
- 用例:新增 `startup_rejects_invalid_parsed_fields_from_any_construction_path`,覆盖「直接构造的配置带着非法值也不能通过启动检查」
2026-09-24 19:54:08 +08:00
k88936 6e081676f0 provider 的 4 字头不再一律按「你的参数不对」回客户端
- api-server tripo3d/errors.rs:401/403 按上游凭据或权限不可用(502,`tripo-api-unauthorized`)、404 按上游找不到(502,`tripo-api-not-found`)、409 按上游冲突(409,`tripo-api-conflict`)、其余 4 字头按上游拒绝(502,`tripo-api-refused`);只有 400 / 422 继续回 400 `tripo-api-rejected`
- 文件头部说明补上这条分档口径
- 用例:`provider_client_side_4xx_stays_bad_request` 覆盖 400 / 422;新增 `provider_non_parameter_4xx_is_not_reported_as_bad_request` 覆盖 401/403/404/409/402 的状态码、reason 与「不回传上游原文」
2026-09-24 19:53:52 +08:00
k88936 14e72379fc 落点预检改为按归类信号收敛,服务端没配好不再被说成「落点选错了」
- api-server:新增定点查询失败归类 `EditorPointLookupKind` / `EditorPointLookupFailure` 与 `classify_editor_point_lookup_failure`,只把「目标不存在 / 不属于当前账号」的固定文案认成用户侧不可用,其余(服务身份 403、定价身份未初始化、版本冲突、runtime 与连接故障)原样带出
- api-server tripo3d/target.rs:`collapse_target_unavailable` 改为只看归类,不再按 `is_client_error()` 分档,落点预检的模块注释同步
- 用例:target 侧覆盖「Unavailable 收敛、403/400/409/5xx 一律保留」;editor_project 侧新增归类用例,钉住运行时服务无权的 403 与「模型定价服务身份尚未初始化」不在不可用清单里
2026-09-24 19:53:38 +08:00
k88936 453e351fc6 3D 资源没有预览图时封面留空,不再拿模型对象顶上去
- api-server:`project_editor_client_media_src` 对 `model3d` 在没有预览(`thumbnail_src` 与存储的 `image_src` 都缺)时直接返回空,不再回落到模型对象;用例改成断言空串,并补「存储的 image_src 是空白」一例
- docs/adr 0003:写明这是唯一允许「有对象但 `imageSrc` 为空」的类别,消费方按空值显示占位;被否决项里补上「拿模型对象冒充 imageSrc 的投影回落」
- docs/technical Tripo 集成方案:资源表说明改成「有预览才填」,验收项 11 改成「预览缺失时留空、不得回落成模型对象」
- 画布侧 `Model3dViewerModel` 的注释同步:历史布局里仍可能留着指向模型本体的地址,图片元素不能加载它
2026-09-24 19:51:47 +08:00
k88936 2dfb60a34e 格式判定的注释与文档改成「信任后端的检测结果」,不再说魔数纠正错误声明
- packages/model3d-viewer:`resolveModel3dViewerFormat` 与字节识别函数的注释改写——声明优先是因为后端的 `content_type` 写入前已按字节核过,魔数只在声明缺失或认不出来时兜住,不负责纠错一个认得出来但不对的声明
- docs/technical Tripo 集成方案:补上同一条口径,与实现里「声明 → 字节魔数 → 地址扩展名」的顺序对齐

行为不变,只改注释与文档。
2026-09-24 19:51:25 +08:00
k88936 a61725003e 补齐定价文档里漏掉的版本比对:技术方案与决策记录同步为整段覆盖
- docs/technical Tripo 集成方案:删掉「后台保存携带定价版本做事务内比对、不一致 409」这句,改成整段覆盖、不做版本比对
- docs/project-memory 决策记录:在 2026-09-23 那条设计决策末尾追加 2026-09-24 的修订说明,指向同日「模型定价保存去掉版本比对」
2026-09-24 19:51:19 +08:00
k88936 ad2b37c6d3 后台模型定价保存去掉版本比对:保存即整段覆盖
- shared-contracts:删掉 `EDITOR_GENERATION_PRICING_VERSION_CONFLICT` 常量
- spacetime-module:`EditorGenerationPricingConfigUpsertInput` 去掉期望版本字段,删掉版本比对方法与它的用例,入库只按请求内容整段覆盖
- spacetime-client:重新生成定价 upsert input 绑定(少一个字段),记录映射同步去掉版本
- api-server:进程内缓存去掉版本字段与 409 冲突错误,`editor_generation_pricing()` / `save_editor_generation_pricing()` 只返回配置本身,`EditorGenerationPricingStore::replace` 注释写明「只是缓存刷新、不做版本校验、不是拦截点」
- api-server admin/app:后台 GET 不再返回 `updatedAtMicros`,POST 不再读 `expectedUpdatedAtMicros`,删掉 409 映射与「旧版本被拒」用例,新增「连续保存两次以后一次为准」
- admin-web:定价类型去掉版本字段,保存请求不再回传版本,删掉 409 分支与「重新读取」提示及其用例
- docs:主规范新增「保存语义」段(整段覆盖、同时只有一个管理员在操作、缓存替换不是拦截点),后端数据契约、ADR 0005、里程碑 / 实施计划、决策记录与 pitfalls 同步为删除版本比对
2026-09-24 19:33:41 +08:00
k88936 3900aa9157 容器尺寸变化时只重算相机取景,不再重新采一遍顶点
- packages/model3d-viewer/framing:取景求解器把每个采样点投影后的「上下向 / 左右向 / 深度」存成样本,resolve 可以带上新的宽高比重算;顶点投影与宽高比无关,比例变化时只在这份样本上重取最大值,距离算法与取值口径不变(含非有限顶点作废整次取景的旧行为)
- packages/model3d-viewer/scene:顶点采样改成加载后只做一次,computeFraming 只调用 resolve(aspect);拖拽面板连续触发 ResizeObserver 时不再重复 updateMatrixWorld 与逐顶点读取(单网格上限 20 万点)
- framing.test:补两条用例——带新比例重算与全新求解器结果一致、非有限顶点回落到与尺度相关的默认距离
2026-09-24 19:19:58 +08:00
k88936 6679dd2ebf 把 Tripo 配置写成明确的破坏性部署要求,并说明刻意不做开关
- docs 开发运维:Tripo 两项配置改成以「破坏性部署要求」表述,写明升级前必须配好、不做开关也没有缺配置先启动的逃生口,不需要 3D 的机器只跑不检查该配置的两个角色
- docs/adr 0006:补上同一条口径与理由(允许缺配置启动就会退回「起得来但悄悄坏掉」的形态),并写明被否决的逃生口
2026-09-24 19:18:27 +08:00
k88936 647822bbc4 写明产物是单文件,多文件模型的兄弟资源拼接只算兜底
- packages/model3d-viewer:把 resolveModel3dViewerResourceUrl 的注释改成实话——产物是单文件(.glb 自带几何贴图,.fbx 媒体内嵌),多文件模型不会出现,最多是「模型 + 一个 bin」;这段补签名只是最后一层兜底,签名按单个文件签发时会被云端拒绝,不指望它能读成功
- docs/adr 0003:同一条前提写进 ADR,并写明将来真要支持多文件模型时应当由宿主为每个兄弟文件分别换取临时地址,而不是继续扩展这段拼接
2026-09-24 19:18:10 +08:00
k88936 5748cf303f 种子字段的对外类型改成普通数字,不再导出 bigint
- shared-contracts:多视图请求与文生 / 图生参数的 seed 注记从 `bigint | null` 改成 `number | null`,并写明只支持 JS 安全整数范围(2^53-1)内的取值;Rust 侧仍是 `Option<i64>`,反序列化行为不变
- packages/shared:重新生成 model3d 绑定,seed 变成 `number | null`,请求体可以直接 JSON 序列化(带 bigint 的请求在 JSON.stringify 阶段必定抛错);顺带补上此前漏生成的多视图请求文档注释(「多视图生 3D」→「多视图生成 3D」)
- docs/technical:在 Tripo Provider 集成方案里写明 seed 为什么按 number 导出、代价是超出 2^53-1 的取值无法从 TS 侧精确构造
2026-09-24 19:17:42 +08:00
k88936 c39798e491 写明 ts-rs 的 feature 一律保持全开
- server-rs/Cargo.toml:把注释里「ts-rs 一律常开」改成明确口径——保持默认 features 原样,不写 default-features = false、也不逐个关掉,成员 crate 需要新能力时优先用默认已覆盖的部分,不改任何依赖声明
2026-09-24 19:07:48 +08:00
k88936 9bcd8f728b 后台各页的整数解析统一到共享助手
- apps/admin-web/src/pages/pageUtils.ts:新增 parsePositiveInteger,用 Number + Number.isSafeInteger,空串 / 非数字 / 小数 / 0 / 负数一律收敛成 0
- 删除 AdminTaskConfigPage、AdminProfileWalletConfigPage、AdminRechargeProductPage、AdminRedeemCodePage 与 adminEditorGenerationPricing 里各自的一份实现,5 个页面统一从 pageUtils 引入,修掉「钱包页拒 1.5、任务页把 1.5 当 1」这类口径不一致
- adminEditorGenerationPricing.test.ts 的引入路径同步改到 pageUtils
2026-09-24 19:07:32 +08:00
k88936 acdfb8ed73 3D 冒烟示例把产物写到 target 目录,不再落到仓库里
- server-rs/crates/platform-tripo/examples/tripo_generation_smoke.rs:产物落盘目录从「当前工作目录」改为 <cargo target>/tripo-smoke-artifacts,开始前建目录并打印路径;该目录本来就在入库排除范围内,根目录不会再攒出临时模型
- server-rs/crates/platform-tripo/examples/tripo_generation_smoke.rs:文件头文档同步改为说明新的产物落点
2026-09-24 19:05:51 +08:00
k88936 02f334b04a 删掉根目录临时 3D 样例模型并移除对应的根级忽略规则
- 删除仓库根目录手工下载的 image_to_model.glb / multiview_to_model.glb / text_to_model.glb 三个临时样例(合计约 125 MB,本来就没入库)
- .gitignore:移除只为它们存在的十条根级规则(/*.glb、/*.gltf、/*.fbx、/*.bin、/*.png、/*.jpg、/*.jpeg、/*.webp、/*.ktx2、/*.tga),根目录再出现同类文件会正常显示为未跟踪,不再被静默忽略
2026-09-24 19:05:43 +08:00
k88936 cc472d0736 图生 3D 的引用类型判别改用枚举
- server-rs/crates/api-server/src/tripo3d/image_source.rs:ResolvedReference.kind 从 &'static str 改成 ResolvedReferenceKind 枚举,From<EditorReferenceRecord> 写值与匹配比较由同一处产出,字符串错字不再只在运行期暴露
- server-rs/crates/api-server/src/tripo3d/image_source.rs:对外文案仍由 as_str() 生成,用例同步改用枚举取值
2026-09-24 18:03:05 +08:00
k88936 06f091f0ff 平台层校验补上 pbr 依赖贴图
- server-rs/crates/platform-tripo/src/common/validation.rs:validate_generation_options 增加 texture=false 与 pbr=true 的组合拒绝(TripoField::Pbr),直连调用方不用再把这种不会成立的组合拖到 provider 侧才报错;放在 generate_parts 分支之后,不改变该分支原有的归因
- server-rs/crates/platform-tripo/src/common/validation.rs:补用例锁住 texture=false + pbr=true 被拒、texture=true 时放行
2026-09-24 18:01:24 +08:00
k88936 b83113619e 产物结构体不再派生 Debug,改成只打元数据
- server-rs/crates/platform-tripo/src/common/types.rs:TripoArtifactBytes 去掉 derive(Debug),改为手写实现,只输出 url / content_type / content_length / 字节数,几十 MB 的 bytes 不会再被一次 {:?} 刷进日志
- server-rs/crates/platform-tripo/src/common/types.rs:补用例锁住 Debug 只带字节数、不带字节内容
2026-09-24 18:00:45 +08:00
k88936 03aec35350 写明 TripoUrl 脱敏只覆盖 query 与 fragment
- server-rs/crates/platform-tripo/src/common/types.rs:redacted() 的文档注明脱敏范围只到 query / fragment,若 provider 改成把 token 写进路径(如 path-style SAS)则需一并处理路径段;无 query 分支补注释说明路径原样保留
2026-09-24 18:00:30 +08:00
k88936 925be4573d 产物扩展名兜底认下 image/jpg
- server-rs/crates/platform-tripo/src/common/types.rs:fallback_artifact_extension 补上 image/jpg 分支,非标准写法但确有 provider 这么回,预览图不再落到默认 .glb 后缀
- server-rs/crates/platform-tripo/src/common/types.rs:补用例覆盖 png / jpg / webp 与读不出形态时的 glb 兜底
2026-09-24 18:00:21 +08:00
k88936 0c716d7d9c 产物文件名主干加上长度上限
- server-rs/crates/platform-tripo/src/common/types.rs:sanitize_artifact_file_stem 在字符过滤后按 MAX_ARTIFACT_FILE_STEM_LEN=128 截断,远端来的超长主干不再拼出超过文件系统名字上限的一长串
- server-rs/crates/platform-tripo/src/common/types.rs:新建 types 的用例模块,锁住超长 / 带路径的主干被中性化并截断、全中性化后为空时退回固定名
2026-09-24 18:00:05 +08:00
k88936 2330a25172 传输判据补上 is_body,并把截断体的归因写清
- server-rs/crates/platform-tripo/src/common/error.rs:transport_error_is_transient 补上 is_body(),与 TripoError::Request.retryable 文档列举的瞬时类型对齐
- server-rs/crates/platform-tripo/src/common/error.rs:注释写明「传输中途截断的响应体在 reqwest 里落在 Decode」这一实测结论,说明为何不把 Decode 判成可重试
- server-rs/crates/platform-tripo/src/common/error.rs:补用例用截断响应体锁住 Decode 不判可重试
2026-09-24 17:59:26 +08:00
k88936 ebce5f9304 平台层转发 base_url 前去掉首尾空白
- server-rs/crates/platform-tripo/src/common/config.rs:client_options 里 base_url 先 trim 再交给 SDK,带空白的地址不再以低层解析错误的形式暴露出来
- server-rs/crates/platform-tripo/src/common/config.rs:把 client_options 的用例扩到 base_url,锁住「校验看到的」与「发出去的」是同一个值
2026-09-24 17:58:03 +08:00
k88936 80ae730dc5 平台层转发 API Key 前去掉首尾空白
- server-rs/crates/platform-tripo/src/common/config.rs:client_options 里 api_key 先 trim 再交给 SDK,校验口径与发送口径统一,带空白的密钥不再变成 provider 侧的 401
- server-rs/crates/platform-tripo/src/common/config.rs:补用例锁住 client_options 发送的 api_key 已 trim
2026-09-24 17:57:44 +08:00
k88936 b54ff06bc7 平台层 Tripo 配置把 base_url 校验成绝对 http(s) 地址
- server-rs/crates/platform-tripo/src/common/config.rs:validate 用 url::Url 解析 base_url,缺 scheme(openapi.tripo3d.com)、非 http(s)(ftp://)与无 host 的值都在构造点按 Configuration 失败,不再拖到请求期变成传输层报错
- server-rs/crates/platform-tripo/src/common/config.rs:补用例覆盖缺 scheme / 非 http(s) / 非法 URL 被拒、前后带空白的合法地址放行
2026-09-24 17:57:31 +08:00
k88936 7a4ccb8bfe 3D 产物哈希挪到 blocking 线程池
- server-rs/crates/api-server/src/tripo3d/storage.rs:上传前对最大 512 MiB 的产物做 sha256 会占住一个 Tokio worker,改成 spawn_blocking 并把 bytes 原样带回(不做整块复制),失败按 500 收口
2026-09-24 17:56:51 +08:00
k88936 f82eba4c94 3D 查价的加价累加改成溢出即报错
- server-rs/crates/api-server/src/tripo3d/pricing.rs:endpoint 查价用 checked_add 替换 saturating_add,溢出走新的 Model3dPricingError::PriceOverflow,与加载校验的 fail-closed 口径一致,不再静默夹到 u32 上限按错价收费
- server-rs/crates/api-server/src/tripo3d/pricing.rs:补用例锁住「底价 + 加价项溢出必须报错」
2026-09-24 17:55:12 +08:00
k88936 98f2c21872 3D 定价整段兜底也要过端点校验
- server-rs/crates/api-server/src/editor_generation_model3d_records.rs:记录整段缺失时回退到本地配置前先跑 endpoint.validate(),缺键分支与兜底分支走同一条 fail-closed 路径,不再依赖调用方事后补 config.validate()
- server-rs/crates/api-server/src/editor_generation_model3d_records.rs:补用例锁住「整段缺失 + 兜底配置不合法」必须被拒绝
2026-09-24 17:53:34 +08:00
k88936 a8dc0d8870 后台定价用例改键前先断言目标键存在
- server-rs/crates/api-server/src/app.rs:改默认定价 fixture 时先用 pointer_mut 断言目标键存在再赋值,不再依赖 Value::index_mut 的隐式 panic,键名或版本号漂移时报错能指出是哪一层对不上
2026-09-24 17:51:08 +08:00
k88936 f2d3e2218d 后台缺 3D 段的负例改成断言中文文案
- server-rs/crates/api-server/src/app.rs:用例文案说「必须以中文文案拒绝」,断言却只查小写英文 model3d,改成断言「定价段必须整段给出」,真正守住中文拒绝这条契约
2026-09-24 17:51:02 +08:00
k88936 40c97bf876 旧 external_generation_job 归一化用例改成真断言
- server-rs/crates/spacetime-module/src/migration.rs:provider_kind / provider_task_id 的断言改成先 as_object() 取键再比 null,原先用 Value 索引在键缺失时返回 Null,归一化被删掉也照样通过
2026-09-24 17:48:41 +08:00
k88936 14294a2c9b 修掉多视图生成 3D 请求文档注释里的漏字
- server-rs/crates/shared-contracts/src/model3d/multiview_to_model/request.rs:公开文档注释「多视图生 3D」补回漏字,改成「多视图生成 3D」
2026-09-24 17:46:54 +08:00
k88936 82e889dc6d 带地址响应体的过滤改成大小写不敏感
- server-rs/crates/platform-tripo/src/common/error.rs:body_snippet 先整串转小写再匹配 http:// 与 https://,`HTTPS://…` 这类大小写变体不再绕过过滤,签名 URL 不会被带进持久化的任务错误信息
2026-09-24 17:44:20 +08:00
k88936 79ae24fa43 3D 产物的 content type 与落库值同源
- server-rs/crates/api-server/src/tripo3d/storage.rs:返回给调用方的 content_type 改为优先取 HEAD 复核回来的值(取不到才回落到本地解析值),与 asset_object.content_type 用同一份取值。此前接口返回的是本地解析值、落库的是 OSS 回显值,OSS 一旦归一化或回显空值,两个来源就会给出互相矛盾的元数据
2026-09-24 17:43:52 +08:00
k88936 8af4287bf3 落点校验的文档注释改成与实现一致
- server-rs/crates/api-server/src/tripo3d/validation.rs:validate_target 的注释此前写「项目资源与素材库二选一 / 不接受两个都给」,与实际实现(两个都接受、只要求至少一个)以及用例 target_accepts_both_locators_but_requires_at_least_one 矛盾,改为按实际口径描述
2026-09-24 17:43:43 +08:00
k88936 791011eacf 落点纵深防御的兜底字段名与归因口径对齐
- server-rs/crates/api-server/src/tripo3d/target.rs:两个落点都没给时的兜底报错由点 projectId 改为点 target,与 target_unavailable_field 对 both-missing 的归因一致,不再把错误指到一个调用方可能根本没传的键上
2026-09-24 17:43:36 +08:00
k88936 d927a8cfa7 后台保存定价的版本推进改为饱和加法
- server-rs/crates/api-server/src/state.rs:expected_updated_at_micros 来自后台请求体、只校验了必须给出,没有范围校验;expected + 1 在 i64::MAX 上会溢出(debug 下 panic、release 下回绕成 i64::MIN 并让版本推进失效),改用 saturating_add(与测试分支同一口径)
2026-09-24 17:43:32 +08:00
k88936 0b3c15ee7c 图生 3D 的源图对象键为空时按缺失处理
- server-rs/crates/api-server/src/tripo3d/image_source.rs:预检对 object_key 加非空判断,空串 / 纯空白与缺字段同义。此前 Some("") 能过预检,等任务已入队计费后才在下载源图时失败
2026-09-24 17:43:24 +08:00
k88936 643ffedfb6 provider 4xx 不再把原文错误体回给客户端
- server-rs/crates/api-server/src/tripo3d/errors.rs:400..=499 分支不再拼接 error.to_string()。TripoError::Api 的 Display 直接转述 provider 的 code/message/suggestion(HTTP 错误体原文,未做 URL 清洗),可能把带签名的地址等内部信息带进用户可见的任务错误;改为固定中文文案
2026-09-24 17:43:17 +08:00
k88936 72cdabbef3 定点引用查询补上调用方必须传本人 user_id 的说明
- server-rs/crates/api-server/src/editor_project.rs:resolve_editor_reference_record_by_id_for_owner 从 pub 收窄到 pub(crate) 后授权边界移到调用方,给函数补上「必须传已认证用户自己的 ID,本函数不做授权」的前置条件说明,避免后续 crate 内调用方传入客户端可控的 owner 造成越权读取
2026-09-24 17:43:13 +08:00
k88936 110354fad9 请求路径也拦住写歪的 Tripo 网关地址
- server-rs/crates/api-server/src/tripo3d/provider.rs:URL 判据(合法 URL + HTTP(S) + 有主机名)抽成 validate_tripo_base_url_syntax,启动期门禁与 tripo_settings 共用;非空但缺 scheme / 非 HTTP(S) / 无主机名的地址在请求前就映射 503,不再拖到 SDK 里变成一条指向连接失败的报错
- server-rs/crates/api-server/src/tripo3d/provider.rs:补请求期拒绝写歪地址的用例
2026-09-24 17:38:28 +08:00
k88936 62600fe881 请求路径也拦住越界的 TRIPO_RETRIES
- server-rs/crates/api-server/src/tripo3d/provider.rs:tripo_settings 显式校验 config.tripo_retries 不超过 TRIPO_MAX_RETRIES 并映射 503。TripoSettings::validate 只查空值与零超时,越界值会原样进 artifact_retries,把产物下载的挂起时间成倍拉长;注释里「第二道防线」的说法此前并不成立
- server-rs/crates/api-server/src/tripo3d/provider.rs:补越界拒绝 / 上限放行的用例
2026-09-24 17:36:55 +08:00
k88936 1600c107e0 修掉没测到 API Key 分支的用例
- server-rs/crates/api-server/src/tripo3d/provider.rs:missing_api_key_is_reported_as_unavailable 先补全 TRIPO_BASE_URL 再断言缺密钥。AppConfig::default() 的网关是空串,原用例一直停在「缺网关」分支上,API Key 校验删掉也能过
2026-09-24 17:35:45 +08:00
k88936 2877ede719 3D 面板的栅格覆盖不再依赖规则先后顺序
- src/index.css:`--model3d` 面板的 grid/width/gap 覆盖把 `--image` 一起写进选择器提高特异性。它与 `--image` 的声明特异性相同,此前只靠 `--model3d` 排在后面取胜,规则顺序一变就会退回两栏布局
2026-09-24 17:33:25 +08:00