1a7abfd059
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
## 交付内容
- 客户端新增 `project_snapshot` 模块:项目扫描与排除口径、增量索引、差异对比、上传编排、状态查询。
- 触发:工作区窗口存活期间的周期定时器 + 工作区窗口关闭(`CloseRequested`);应用退出只在有界预算内等待在途同步收尾,不重复发起。
- 服务端新增两条登录态路由 `POST /api/agc/project-snapshots/files` 与 `/manifest`,由 api-server 用服务端凭据写入私有前缀 `agc/project-snapshots/v1/{user}/{project}/`;客户端不持有 OSS 凭据、不直连 OSS。
- `platform-oss` 新增内部对象精确写入与探测、项目快照对象键构造;修复 `head_internal_object` 读 HEAD 响应长度恒为 0(会导致服务端"已存在即跳过"永不生效)。
- `shared-contracts` 新增 `agc_project_snapshots` DTO 与项目 ID、相对路径、摘要校验。
- 真实 OSS 存储层冒烟示例 + 客户端真实链路冒烟用例(`#[ignore]`,env 驱动)。
- 登记 `check-config.mjs` 的 native-only 命令白名单,恢复 `npm run agc` 可启动。
## 验证证据
- 存储层:`cargo run -p platform-oss --example agc_project_snapshot_live_smoke` 对真实 `agc-dev` 完成写入 → 读回(contentLength 32)→ 清单写入 → 探针清理。
- HTTP 层:登录态下 7 项校验(401 无 token、400 路径穿越/长度不一致/非法摘要/空正文/重复路径清单)全部符合预期且不写对象。
- 客户端链路(真实项目 gameagent-033b6cf3…):第一次 `synced uploaded=6 uploadedBytes=36694 remoteSkipped=6`,紧接着第二次 `no-op uploaded=0`。
- GUI 触发:`trigger=periodic ... uploaded=6`;把周期设为 600 秒排除干扰、改一个文件后关窗得到 `trigger=project-close ... revision=2 uploaded=1`,索引摘要由 `28d837cd84f8ab62` 推进到 `bff34e2e336d901f`(同步在进程退出前完成)。
- 门禁:AGC `project_snapshot` 14 passed(+1 ignored live)、api-server `project_snapshots` 3 passed、platform-oss 39 passed、shared-contracts 87 passed、`cargo fmt --check`(两处)、`check:encoding`、`check:doc-index`、`git diff --check`、`check-config.mjs` 全部通过。
## 已知未决(不阻塞本里程碑)
- 远端对象只增不减:没有删除路径,也没有 bucket 生命周期规则;要收口需要先定保留语义(清单引用 GC 还是 OSS 生命周期)。
- 部署环境需确认 `GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_*` 或回退的 `ALIYUN_OSS_*` 具备目标 bucket 私有前缀的 `PutObject` 权限;未配置时接口返回 503、客户端失败关闭(不推进索引)。
- 缺少按用户/项目的配额与限流(error_reports 有每小时提交上限的先例,本功能没有)。
- 没有界面入口与状态可见性:`read_local_project_snapshot_state` 已注册但未接 UI,失败只写本机日志。
## 不做
- 不做云端下载/恢复、跨设备合并、版本回滚。
- 不改 `/api/external/v1` 与 External OpenAPI,不新增 SpacetimeDB 表。
---------
Co-authored-by: kdletters <61648117+kdletters@users.noreply.github.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/400
platform-oss 平台适配 package 说明
日期:2026-04-20
1. package 职责
platform-oss 是 OSS 平台适配 package,后续负责:
- OSS 直传签名、STS、上传策略适配
- 对象上传、下载、签名 URL 与
cdn_url解析适配 - 对象元数据、标签与内容 hash 适配
- 供
module-assets、module-custom-world等模块复用的对象存储基础设施能力
2. 当前阶段说明
当前提交已落地最小可用 OSS 基础设施:
PostObject浏览器直传签名- 旧
/generated-*公开前缀到 OSSobject_key的兼容映射 - 私有对象短期签名读 URL
- 私有对象
HEAD Object探测 - 服务端
PutObject上传 helper x-oss-meta-*元数据归一化与大小限制校验content-type、content-length-range、success_action_statuspolicy 条件生成PostObject签名、GetObject读签名、HEAD Object和PutObject的结构化日志- generated 私有对象上传默认写入
Cache-Control: public, max-age=31536000, immutable editor-agent/服务端内部消息文档前缀,以及仅供后端使用的内部短期读签名能力
当前仍未落地的内容:
STS真实临时授权下发- multipart 分片上传
- 内容 hash 自动计算与标签写入
补充说明:
- 当前产品口径为服务器上传 AI 生成资源、Web 端只负责读取。
- 因此
STS不作为默认上传主链,api-server只暴露禁用式 contract,避免浏览器拿到 OSS 写权限。 - 服务端生成资源应优先复用
OssClient::put_object,上传成功后再走对象确认链路写入asset_object。 - 读签名和
HEAD Object的入参必须直接传 object_key,不要把 bucket 名拼进路径;例如generated-square-hole-assets/.../image.png才是正确入参,xushi-dev/...这类前缀不属于 object_key。 - OSS V4
x-oss-date必须固定为yyyyMMdd'T'HHmmss'Z',不能依赖time::Time::to_string();后者在小时小于 10 时可能输出非补零时间,导致签名格式错误。 - 结构化日志只记录
provider、operation、bucket、endpoint、object_key/key_prefix、access、content_type、content_length、status、status_class、error_kind和elapsed_ms等排障字段;禁止输出 AccessKey、policy、signature、Authorization header 或完整 signed URL。 - 完整 OSS URL 不能当作 object key 传入签名接口;前端收到
https://*.oss-*.aliyuncs.com/generated-*时应先归一为 legacy public path,再通过/api/assets/read-url换取短期 signed URL。 - generated 资源缓存的主路径是 OSS 对象头、浏览器 / WebView HTTP 缓存和后续 CDN,不允许改成 api-server 本地磁盘静态资源兜底。
PostObject是 OSS 基础设施能力,不代表所有前缀都允许浏览器直传;editor-agent/只用于画布 Agent 会话消息文档,属于服务端内部前缀,不向浏览器开放写入。- 画布 Agent 消息文档固定使用
editor-agent/{conversationId}.json,由api-server通过服务端PutObject整体写入,通过内部读签名读取;浏览器只调用会话 API,不直接持有editor-agent/对象的 OSS 写权限、STS 写权限或直传表单。 editor-agent/不属于 legacy/generated-*公开资源前缀,也不应通过通用/api/assets/read-url暴露为前端可自行读写的对象地址。- 画布 Agent 消息文档的业务上限、会话内串行写入、失败 generation record 保留等规则由
api-server与 ADR 约束;platform-oss只负责对象 key 规范化、签名和服务端上传能力。
3. 边界约束
platform-oss只承接对象存储平台适配,不承接业务实体状态与业务规则。- 资产状态与对象绑定最终由业务模块和
apps/spacetime-module管理,前端接口由apps/api-server暴露。 - 不允许把 OSS SDK、签名逻辑和 URL 策略重新散落到多个业务模块里各自实现。
editor-agent/前缀是服务端内部消息文档命名空间,只允许保存editor-agent/{conversationId}.json形态的会话级 JSON 文档。- 浏览器直传能力不得用于
editor-agent/前缀;若未来需要上传用户附件,应走独立的附件 / 资产前缀和确认链路,不得复用会话消息文档对象。