Open Design 已经能把一句需求变成可以预览的 HTML 页面,也提供了 Vercel 和 Cloudflare Pages 两种在线发布方式。但当运行环境变成一台家用 NAS,发布目标变成自己的域名、静态目录和导航首页时,内置 Provider 就不够用了。
最直接的想法是给 Open Design 塞入 SSH 私钥,再从主进程执行 rsync。这条路能跑,却把生成工具、服务器凭据和发布策略绑在了一起:Open Design 升级会影响发布,发布服务出问题也可能拖住主应用,SSH 权限边界更难控制。
最终采用的方案是给 Open Design 增加一个通用的 custom-webhook Provider,再单独部署一个静态发布服务。前者只负责整理文件并调用协议,后者负责鉴权、校验、落盘、回滚和对外提供静态资源。
本文记录这次二次开发的完整设计。实现基于 Open Design 0.15.1 和 Codex CLI 0.144.6,时间是 2026 年 8 月。项目的接口和目录可能继续变化,但拆分边界、协议设计和原子发布方法并不依赖某个具体版本。
成果预览
从需求边界开始
目标并不是再造一个 Vercel,而是解决一条很具体的自托管链路:
因此第一版明确不处理动态应用构建、数据库迁移、边缘函数和域名购买,只接收已经可以静态运行的 HTML、CSS、JavaScript、图片和字体。
为什么选择 Webhook,而不是 SSH
Webhook 的关键价值不是“多一次 HTTP 请求”,而是把责任切开:
- Open Design 知道哪些文件属于当前项目,但不需要知道 NAS 的目录结构。
- 发布服务知道怎样安全写磁盘,但不需要访问 Open Design 的数据库和数据卷。
- 反向代理只暴露一个受控路径,不需要把 Docker Socket 或 SSH 端口交给生成工具。
- 将来若换成对象存储、Git 仓库或另一台服务器,只需实现相同协议。
在 Open Design 中增加 Provider
一个部署 Provider 不只是 下拉框里的新选项。为了让 Web UI、HTTP API 和 CLI 行为一致,需要沿着共享契约、Daemon、路由、前端和测试逐层扩展。
先定义共享契约
第一步是在前后端共享类型中加入 Provider ID、配置结构和传输协议。Provider 配置保留六个核心字段:
这些字段分别解决不同问题:
配置的灵活性需要有边界。Daemon 只接受 HTTP/HTTPS 地址;自定义 Header 不能覆盖 Host、Content-Length、Authorization 等受控字段;responseUrlPath 只能使用简单点路径。这样既保留通用性,也避免配置表单变成任意请求注入器。
Provider 配置不能泄露 Token
部署配置保存在 Open Design 的运行数据目录,而不是当前 Shell 的 Home 目录。写入时使用 0600 权限,读取给前端时只返回固定掩码:
用户再次保存表单时,如果 Token 仍是这个掩码,Daemon 复用原值;只有输入新 Token 才会替换。这和常见云平台密钥设置页的行为一致,也避免浏览器拿到真实凭据。
让 HTTP API 成为统一入口
Provider 配置仍然复用 Open Design 原有接口:
触发部署同样走项目文件接口:
