Skip to main content
当客户购买 Vibestrap 后,源码不是做成 zip 让他下载,而是他在 /settings/purchases 填自己的 GitHub 用户名 → 后端把他作为只读(pull)collaborator 邀请进你的私有 Org repo。 他接受邀请后 git clone,从此 git pull 就能拉你后续的所有版本。 这个设计不是 “license key + 签名下载链接”。zip 包发出去那一刻就过期了, 但 GitHub 邀请会跟着你一起成长。

门槛是怎么挡的

三层架构,故意解耦。整个流程只有一条业务规则:用户必须付过 Vibestrap 的钱。 其他都是水管。
买家看到的是 Sonner toast,根据 result reason 显示对应文案。

一次性配置

1. 用 Organization repo(不能是个人 repo)

GitHub 的 permission: 'pull' 字段在个人 repo 上会被悄悄忽略 —— 任何被加进个人 repo 的 collaborator 自动有 push 权限,与 API body 无关。必须用 Organization repo。 如果你的开发工作在个人 repo,把 Org repo 当作 release 镜像:
更新 Vibestrap 配置:

2. 锁住 Org

进 Org 设置 → Member privileges:
  • 取消勾选 “Allow members to delete or transfer repositories” —— 即使 token 有 Administration:write,也调不了 DELETE /repos
  • Default repository permissionNone
  • 可选:禁止 fork 私有 repo(防买家 fork 出去泄露)

3. 生成 fine-grained PAT

https://github.com/settings/personal-access-tokens(用 Org 成员账号登录, 建议是个 bot 账号,不是你个人账号):
  • Resource owner:你的 Org
  • Repository access:只选买家 repo
  • Permissions:
    • Metadata: Read-only(GitHub 强制必填)
    • Administration: Read and write(Add a collaborator 这个 endpoint 要求这个权限 —— 这是 GitHub 提供的最窄选项;防误用靠 Org 级”禁删”配置)
  • Expiration:1 年,日历提醒 60 天后 rotate
复制 token(只出现一次),粘进 prod env:
src/github/invite.ts 在请求时读它,rotate 不需要重启。

4. 本地验证

201 = 成功,204 = 已经是 collaborator,404 = 用户名错,403 = token 权限不对。

买家在线上的流程

  1. 用户注册、付款(Stripe)
  2. Webhook 写一行 paymentstatus='paid'scene=siteConfig.product.id
  3. 用户进 /dashboard —— 页面检测到付款记录,渲染买家视图,里面嵌着邀请表单
  4. 用户输 GitHub 用户名,点 Send invite
  5. server action 查 payment、调 GitHub、返回 reason,前端 toast 显示
  6. 用户在 GitHub 邮箱接受邀请,然后:
如果邮件丢了或要换用户名,再填一次再点 → 服务端是幂等的。

Rotation runbook

每 60 天一次(GitHub 90 天提醒邮件之前):
  1. 生成新 PAT,scope 一致
  2. 更 prod env(GITHUB_INVITE_TOKEN
  3. 重新部署
  4. 拿测试账号试一次邀请
  5. 验证完了去 https://github.com/settings/personal-access-tokens revoke 旧 token

排错

  • 买家说”not_paid”但其实付了 —— 检查 payment.scene 是否跟 siteConfig.product.id 完全一样 (或 'vibestrap-lifetime' 这个旧别名)。SQL 查一下
  • 买家说”invalid_username” —— 多半拼错了。或者带了 @ 前缀。本地验证一下
  • 所有 invite 都返 “forbidden” —— PAT 过期或 revoke 了,重新生成
  • 买家收到邀请但发现没 push 权限 —— 这就是设计目标:permission: 'pull' 在 Org repo 上 只给 clone,push 会被 remote rejected
  • 为啥不做 zip 下载? —— zip 包跟不上你后续 release。买家要的是 git pull,不是 下载完第二天就过时的 tarball

从老的 license-key 流程迁移

之前如果你跑过 license 表和 LICENSE_DOWNLOAD_URL
  • 两个都没了。Schema migration 删掉 license 表;env var 移除;/api/license/download 路由删了
  • sendGitHubInviteAction 里查 payment 表的 gate 替换了原本的查 license 行
  • 老买家(如果有)—— 手动 curl 邀请,或让他们去 /settings/purchases 重新申请

源码链接