AI 工具应用制品接口
实现基于现有 Gin、PostgreSQL、Redis、S3 分片上传和 software_releases 状态机。上传不会隐式发布,机器凭据也不是网页登录凭据。OpenAPI 与 Python CLI(仓库文件:../scripts/release-artifact.py) 可直接使用。
管理与身份
软件版本页的「AI 工具凭据」支持选择应用、授予操作范围、设置有效期、创建、吊销、轮换。需 release-tokens:write;迁移默认只授予 admin。该权限是全局管理权限,与现有 releases:write 的平台管理员模型一致,本仓库没有应用所有者/租户模型,不假定普通用户拥有某个应用。若后续引入租户,需要同时收紧两个管理权限。
凭据使用256位随机值 acr_…,数据库只存 SHA-256,创建/轮换仅返回一次原文;不存浏览器 localStorage,不写日志。轮换创建新记录并在同一事务吊销旧记录,保留范围和到期时间,最长90天。每次请求检查吊销/过期、应用启用、签发者账号有效且仍有 Token 管理权限。吊销不终止已进入执行的请求;已签发 S3 分片 URL 最长15分钟仍可使用,但不能绕过完成校验或发布授权。
机器请求使用 Authorization: Bearer $AC_RELEASE_TOKEN,仅 /api/v1/automation/applications/… 免 Cookie/CSRF,管理端仍需原 Cookie + Origin + CSRF。应用参数使用 UUID,不是公开 client_id。
| Scope | 能力 |
|---|---|
| releases:read | 查询该应用的机器版本、构建和上传状态 |
| releases:create | 创建该应用的草稿版本 |
| uploads:write | 创建/恢复上传、签发分片、上传小文件、校验完成、取消上传 |
| publish:development | alpha 通道发布/撤销 |
| publish:staging | beta 通道发布/撤销 |
稳定通道 stable 映射生产环境,不提供生产机器发布 scope。管理员在已有软件版本页查看 Git commit、构建元数据、安装包及摘要后,使用既有发布/定时发布确认流程完成生产发布。机器不可定时发布、删除版本或撤销生产发布。环境是现有分发通道映射,不代表私有测试环境;alpha/beta 发布后同样可通过公开更新接口下载。
契约与流程
返回沿用 {code:0,message:"success",data:…};错误为 {code:HTTP状态×100,message:…}。400 输入/文件/校验失败,401 凭据无效,403 应用或 scope/生产保护拒绝,404 记录不存在,409 幂等冲突/版本 revision 冲突,429 频率或配额超限,503 存储、数据库、Redis或并发容量不可用。不回传内部异常或凭据。
前缀 P=/api/v1/automation/applications/{application}/releases:
| 请求 | 内容 / 返回 |
|---|---|
| POST P | 必须携带 Idempotency-Key;version、version_code、platform、arch、channel、git_commit、metadata(字符串字典)、notes;返回草稿 Release |
| GET P/{id} | release、assets、build、production_approval_required |
| POST P/{id}/uploads | filename、size、sha256(小写 hex)、revision;返回 asset、part_size、uploaded_parts |
| POST P/{id}/uploads/{asset}/file | multipart/form-data 唯一 file 字段,≤32MiB;只上传一个分片,仍须 complete |
| POST P/{id}/uploads/{asset}/part | part_number、sha256(32字节 Base64);返回预签名 url、必须发送的 headers |
| POST P/{id}/uploads/{asset}/complete | 无正文;合并及同步校验,成功才将 asset 标记 ready,并递增 release.revision |
| DELETE P/{id}/uploads/{asset} | 取消未完成上传,后台重试清理 |
| POST P/{id}/state | action=publish/cancel、revision;受环境 scope 限制 |
管理员 API:GET/POST /api/v1/admin/release-tokens;DELETE /:token;POST /:token/rotate;GET /audit?application_id=…&page=1。列表也必须传 application_id。管理页面仍通过 /admin/releases/:id/state 发布/撤销,保留现有 revision 并发控制。Token 审计 API 只返回记录,不返回任何凭据。
创建幂等键为1–128位字母、数字、.、_、-;唯一范围是应用,跨 Token 和轮换有效。相同规范化 JSON 输入返回同一版本的当前状态,不同输入返回409;删除版本也保留键,不能复用它创建新版本。manifest 幂等不包含文件:上传恢复另按 filename/size/sha256 精确匹配;相同版本有不同文件的会话需先取消。完整上传重复 complete 不递增 revision。发布后先查询状态再重试,状态改变时旧 revision 返回409,避免误撤销或重复发布。
Git commit 必须是完整40或64位小写 hex;metadata 最多16KiB,仅字符串值,不得携带密钥。version_code 为1–9007199254740991,同应用/平台/架构/通道保持既有版本唯一约束。
文件策略、存储与资源界限
采用现有私有 S3 配置、AES-GCM 密钥加密、随机对象 key、分片恢复和清理 worker。机器 API 不接收任意存储地址或对象 key,不执行、部署或解压包。文件名只接受 ASCII 字母数字及 ._-,白名单 .zip .apk .exe .msi .dmg .deb .rpm .appimage,并检查对应文件头/尾;不是病毒扫描或代码签名验证。
机器制品单个≤128MiB(同步校验界限)、小文件表单≤32MiB、每片32MiB、上传有效24小时。每个应用最多100个未发布/取消版本,机器上传按该应用所有非 deleted 制品预留大小计算2GiB配额,包括等待清理对象;应用行锁防止并发超额。既有纯管理员版本上传保持原20GiB规则,故这是机器入口配额,不是管理员全局硬上限。机器版本即使通过管理员上传也执行机器文件策略与完整摘要校验。
机器请求每 IP 每分钟240次、每 Token 每分钟120次(Redis),单进程最多8个并发请求。Redis 故障拒绝请求。同步校验流式读取 S3 对象写入操作系统临时目录,最多128MiB/请求,结束删除临时文件;部署应为临时目录提供容量和访问隔离。分片合并后由服务端重算完整 SHA-256,不能把 S3 metadata 或 multipart 组合校验和当作完整文件摘要。ZIP/APK 不解压,检查目录数量≤10000、声明展开量≤256MiB、异常压缩比例、绝对/父级/Windows路径、特殊文件/符号链接及嵌套常见压缩包。
校验失败不会 ready,也不会发布;已合并的错误对象需取消会话再重新上传,后台清理。网络中断时先 GET 状态:若已 ready 可继续,若 S3 已合并但 DB 未提交,重试 complete 通过 HEAD + GET 恢复。未校验失败对象仍计入配额,防止重试滥用。现有 worker 负责过期/取消对象清理,S3 必须设置终止过期 multipart 生命周期;对象存储版本控制/保留规则可能导致存储费用残留。
管理员配置 S3 的 IAM、Endpoint、Bucket、Region、CORS 与 DATA_KEY_FILE 沿用 安装包存储说明。新增开关 MACHINE_RELEASES_ENABLED=false 可关闭机器路由,管理吊销接口保留。当前资源阈值是源码中的固定安全上限;扩大文件限额需要异步校验队列、超时和磁盘预算设计,本次不提供无限制调大开关。生产网关必须使用 HTTPS,禁止记录 Authorization、表单内容和预签名 URL。
审计与回滚
迁移 012_machine_releases.sql 新增 release_api_tokens、release_builds、release_machine_audit,以及管理权限。迁移沿用自动排序、事务和 advisory lock,可重复执行。未修改原版本/制品状态约束。审计保留 Token ID、应用、路由模板、版本 ID、HTTP 结果;执行前先落 intent,outcome=0 表示中断/未知,需查最终状态。错误应用和 scope 尝试也被记录;无法认证的随机 Token 不持久化其原文/摘要,受 IP 限速。原 release 审计保留签发者身份,新审计补充实际 Token。审计表无公开写入入口;数据库管理员仍属于信任边界,生产应另行归档。
回滚可由相应环境管理员撤销当前版本,使尚未升级的客户端重新看到旧的最高已发布版本。已经升级的客户端不会自动降级:应将已知良好构建作为更高 version_code 的新版本上传、校验和发布。不要先执行「清除历史版本」再计划回滚;被清理制品无法恢复。生产回滚同样须人工确认。
AI 工具使用
Python≥3.11,无第三方依赖。从密钥管理器注入 AC_RELEASE_TOKEN,不要把 Token 放命令行、提交文件或 shell tracing 输出中。
python scripts/release-artifact.py --base-url https://auth.example.com \
--application APPLICATION_UUID --file ./app.zip \
--version 1.2.0 --version-code 102 --git-commit FULL_GIT_COMMIT \
--platform windows --arch x64 --channel beta \
--idempotency-key pipeline-102 --metadata '{"builder":"ai-tool","run":"102"}'
默认只创建并上传草稿,附加 --publish 发布 alpha/beta;stable 明确拒绝该参数。CLI 不跟随重定向,不把 API Token 发给 S3;可用同一键重新执行,已 ready 且摘要/文件名/大小匹配时跳过上传,否则恢复同一会话重新写入分片。不要在同一构建过程中改动文件。结果只打印版本 ID、状态、摘要及生产审批提示。
curl 小文件示例(已有 RELEASE_ID、ASSET_ID 来自上述 API,环境中已有 Token):
curl --fail-with-body "$BASE/api/v1/automation/applications/$APP/releases/$RELEASE/uploads/$ASSET/file" \
-H "Authorization: Bearer $AC_RELEASE_TOKEN" -F 'file=@app.zip'
curl --fail-with-body -X POST "$BASE/api/v1/automation/applications/$APP/releases/$RELEASE/uploads/$ASSET/complete" \
-H "Authorization: Bearer $AC_RELEASE_TOKEN"
共享主机的 curl 命令参数可能被本机进程查看,推荐使用上面的 Python CLI;不得启用 curl -v 或命令跟踪输出密钥。
本地启动与验证
应用直接在本机运行。先检查 docker ps 与 docker port Postgres18 5432/tcp、docker port RedisPwd 6379/tcp;不得运行 compose 或创建/重建数据库容器。连接密码仅保存在被 Git 忽略的 .local/database-connections.md。从该文件在当前进程注入 DATABASE_URL(postgres 用户)和 REDIS_URL,使用检查到的 loopback 端口。
# 设置当前进程的 DATABASE_URL、REDIS_URL,以及本机密钥路径后:
$env:APP_ENV='development'
$env:APP_ORIGIN='http://localhost:13140'
$env:LISTEN_ADDR='127.0.0.1:13141'
$env:JWT_PRIVATE_KEY_FILE='<本机JWT私钥路径>'
$env:DATA_KEY_FILE='<本机32字节数据密钥路径>'
cd server
go run ./cmd/server keygen # 仅新环境,已有密钥不会覆盖;两个父目录须已存在
go run ./cmd/server migrate
go run ./cmd/server
# 另一个本机终端:cd web; npm ci; npm run dev
迁移生产/已有业务库前按惯例备份;本次仅在测试专用随机 schema 应用迁移,没有修改用户业务表。
# 脚本读取忽略目录的凭据表,检查既有容器状态和端口,不打印连接密码。
./scripts/test-machine-releases.ps1 -Go '<go.exe路径>'
./scripts/test-machine-releases.ps1 -Go '<go.exe路径>' -Run '.'
cd server; go vet ./...
cd ../web; npm run typecheck
python ../scripts/release-artifact.py --help
实际结果(2026-09-10):Postgres18 / RedisPwd 均健康,loopback 5432 / 6379;MySQL 未使用。Go 全套执行通过(包含新增机器流程和现有数据库集成测试),只读页面测试因未配置 TEST_READONLY_DATABASE_URL 跳过。新流程覆盖成功 multipart/预签名上传、生产人工发布、状态/撤销、摘要错误、非法文件、应用/操作越权、重复与并发幂等、Token 失效及轮换、速率与草稿配额。Python CLI 通过实际本机 HTTP API 端到端上传、发布并重复执行,确认恢复同一版本。go vet ./...、Go 可执行文件构建、npm run typecheck、npm run build -- --webpack、Python 编译和帮助命令、git diff --check 均通过;OpenAPI YAML 成功解析,12个路径、61个本地引用均可解析。
S3 为本机 httptest 协议服务,不是实际云 Bucket;真实服务的 IAM、SigV4、CORS、跨网超时、大文件吞吐和对象生命周期尚未实测。浏览器人工/自动 UI 交互验收未执行,前端验证范围为类型检查和生产构建。可在配置现有 S3 后通过 CLI 复现完整流程,不需要新增数据库容器。npm 对当前本机 Node 预发布版本给出兼容性提示,但类型检查和 webpack 生产构建均成功;部署建议使用项目支持的稳定 Node 版本。
修改文件索引
| 文件 | 修改 |
|---|---|
| server/internal/app/machine_releases.go | Token、scope、幂等创建、归属、状态、审计与路由 |
| server/internal/app/machine_artifacts.go | 表单上传、文件策略、完整摘要和 ZIP 检查 |
| server/internal/app/app.go | 机器路由注册、CSRF边界和请求大小 |
| server/internal/app/release_uploads.go | 复用上传流程,加入机器配额和完成校验 |
| server/internal/app/releases.go | 管理列表返回构建来源 |
| server/cmd/server/main.go、.env.example | 机器 API 开关 |
| server/internal/database/migrations/012_machine_releases.sql | 三张表、索引和管理权限 |
| server/internal/app/machine_artifacts_test.go | 文件和 scope 边界单测 |
| server/internal/app/machine_releases_integration_test.go | PostgreSQL/Redis/S3 协议及 CLI 联调 |
| web/components/workspace/release-tokens.tsx | 管理 Token 界面 |
| web/components/workspace/releases.tsx | 管理入口及构建详情 |
| scripts/release-artifact.py | AI 工具 CLI |
| scripts/test-machine-releases.ps1 | 既有本地服务检测与测试启动 |
| docs/machine-releases.md、docs/machine-releases.openapi.yaml、docs/releases.md | 设计、契约、操作、安全和验证文档 |