pipeline-product-and-build-ingest-api

流水线到商店的产品与构建 ingest 接口

商店是一个 WooCommerce 站点,但它的产品页和构建下载并不靠手工维护——由一条 CI 打包流水线推送进来。商店上两个 REST 端点,都是 youteacher/v1 命名空间下的 POST,构成这个 ingest 面:一个用来记录新构建出来的二进制文件放在哪里,一个用来从游戏自己的清单(manifest)出发,创建或刷新整份产品列表。两者合在一起,让一次构建跑完时商店就已经更新好了,中间不需要人工介入。

slug 就是整个映射。 流水线只知道两件事:它在构建哪个游戏(游戏的仓库名),以及它刚产出的是哪个平台。它不知道、也不携带任何 WooCommerce 产品 id。把两个系统绑在一起的约定是:产品 slug 等于游戏 slug——这个映射由商店持有,两个端点都只是按路径在 product 文章类型下查一次来定位产品。这让流水线对商店保持无状态:它报出游戏名,商店去找对应的那一行。

/set-build 记录某个平台的构建位置。 它的请求体带着游戏、一个平台,以及一个指向私有对象存储中构建产物的对象键。接受三个平台:两个供下载的桌面构建,以及一个供浏览器里直接玩的 web 构建。端点会把平台校验到那个固定集合里,按 slug 找到产品(若该游戏还没有产品则返回 404),并把这个键写进该产品上一个按平台区分的元字段。之后下载路由和游玩路由读的正是这些按平台区分的字段,所以这一次写入,就是让一个构建对买家变得可达的那一步。

/upsert-product 从游戏清单构建列表。 它的请求体是游戏 game.json 里的 store 块——标语、价格、分类、适合谁、怎么玩、功能卡片、包含什么、设置步骤——外加一个可选的图片列表。处理器按 slug 幂等:若已存在该 slug 的产品就更新,否则用该 slug 新建一个虚拟简单产品,且从不删除任何东西。它设置名称、价格、短描述和分类,并把结构化的清单字段渲染成一段带样式的描述 HTML。每个字段都是可选且带守卫的,整个处理器还被包了起来,好让一个畸形的请求体返回 JSON 错误而不是致命错误——一份坏清单永远打不垮这个端点。

图片画廊是可选的、容错的、并按内容去重的。 图片以 base64 编码随同一份请求体到达。在做任何事之前,处理器会对到来的图片算一个内容指纹;若它和上次成功旁加载(sideload)时存下的指纹相同,画廊这一步就整个跳过——用没变的美术素材重跑流水线不会产生重复媒体。当图片确实变了,每一张都被独立解码并旁加载进媒体库:单张坏图会被跳过并记进一个 warnings 列表,而不是让整批中止。第一张成功的图成为主图,其余的成为画廊。若每一张都失败,现有画廊会被原样保留而不是被清空——一次失败的上传永远不会把一份本来看着没问题的列表弄坏。

两个端点都由一个共享密钥鉴权。 每个都带一道权限检查,把请求头里提供的密钥与一个仅存在于环境中的值做恒定时间比较,且除非确实配置了密钥否则一律拒绝。这是一个机器对机器的面——流水线持有密钥,没有任何浏览器或买家会碰到这些路由——并被刻意保持狭窄:只做创建或更新,绝不破坏性删除。

相关

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →

pipeline-product-and-build-ingest-api