WebHooker/docs/zh/guide/groups.md
RhenCloud 25ebae4ae5
feat(storage): migrate config, dedup and delivery state to D1
- Move oversized queue payloads from KV to R2 (PAYLOAD binding, webhooks/YYYY/MM/DD/*.json, KV queue:payload:* fallback)
- Persist routes/groups to D1 (d1_routes/d1_groups) with memory -> KV -> D1 three-tier cache, seeded from legacy KV config keys
- Move webhook dedup (dedup_keys), delivery state (delivery_state) and message tracking (message_tracking) to D1 via canUseD1 probe with automatic KV fallback
- Batch send_logs inserts (recordSendBatch) and add group_id/ts index
- Add storage-prune scheduled task for expired dedup/state/tracking rows
- Add TTL to invite:group:{id} index and audit all ephemeral KV keys
- Add D1 indexes for the new tables
- Sync AGENTS.md, README.md/zh and docs/ (en/zh) with the new storage layout
2026-08-17 15:01:20 +08:00

7.5 KiB
Raw Blame History

分组与访问控制

路由归属于分组。分组用于划分管理权限,并可限制进入其中的事件。它们存储在 D1d1_groups,首次加载时从旧版 KV config:groups 键同步),可通过 Web 控制台Admin API 管理。每个实例最多可保存 100 个分组

分组模式

{
  "id": "backend-team",
  "name": "Backend Team",
  "members": [
    { "login": "rhencloud", "role": "owner" },
    { "login": "octobot", "role": "admin" },
    { "login": "reader", "role": "viewer" }
  ],
  "owners": ["myorg"],
  "providers": ["github", "gitea"],
  "installationId": 12345678,
  "logTarget": { "platform": "discord", "channelId": "123456789", "threadId": "987654321" }
}
字段 类型 必需 说明
id string 小写 ida-z0-9-);被每条路由的 groupId 引用。可编辑:重命名分组会同步其路由、分组 webhook secret 与待处理邀请
name string 人类可读的分组名
members object[] { login, role } 条目;角色为 owneradminviewer
adminIds string[] 已废弃的旧字段;存在时视为角色为 ownermembers
owners string[] 允许进入该分组的事件所属组织/用户登录名;空 = 全部
providers string[] 允许进入该分组的来源平台(githubgitea);空 = 全部
installationId number 绑定到该分组的 GitHub App 安装 id仅接受该安装的事件空 = 全部)
emoji boolean 该分组消息是否包含表情(默认 true
forgeSources object[] 来源主机:{ host, type, name? } 条目(typegithubgiteaname 为可选显示名称),用于标注该分组消息底部;空 = 不标注
lang string 该分组所有路由的消息语言(如 enzh;可通过 KV i18n:<lang> 自定义)——见消息语言——默认 en
logTarget object Webhook 日志频道Discord 目标 { platform, channelId, threadId? } 或 Telegram 目标 { platform, chatId, topicId? },接收该分组路由每次分发 webhook 的摘要

角色

每个分组成员拥有三种角色之一。超级管理员(ADMIN_USER_IDS)始终绕过这些限制。

角色 查看路由/日志 编辑路由 管理成员与邀请 编辑分组设置
owner ✓(除 owners
admin
viewer ✓(只读)

权限模型

  • 超级管理员ADMIN_USER_IDS)可查看和编辑所有分组与全部路由;只有他们能编辑分组的 owners 列表。
  • Owner 管理自己分组的路由、成员、邀请、名称、id、emojiproviders。不能移除最后一个 owner也不能在没有其他 owner 时降级自己。
  • Admin 编辑自己分组内的路由并查看日志;viewer 只有只读控制台。
  • 分组管理端点通过 /admin/api/groups/:id/routes 一次操作一个分组;groupId 强制取自路径参数。
  • owners 列表限制该分组路由究竟会分发哪些事件操作者(发送者登录名)的事件。
  • providers 列表限制该分组路由会分发哪个 forgegithubgitea)的事件。即使组织/用户名冲突,也可借此将 GitHub 与 Gitea 分组分开。

Webhook 日志频道

分组可以设置 logTarget 指向一个 Discord 频道/子区或 Telegram 群组/话题。每当该分组的路由分发dispatch一个 webhook就会向那里发送一条摘要消息事件类型/动作、仓库、投递 ID以及每条「路由 × 目标」一行的 / 结果(失败时附带错误信息;最多列出前 10 行,其余以 +N 汇总)。全部成功时消息为绿色,任一失败则为红色。摘要使用分组的消息语言。日志消息尽力发送,本身不会被记入 D1 发送日志。

来源平台标识

分组通过 forgeSources 定义自己接收事件的 forge即一组 { host, type, name? } 条目(typegithubgitea)。该分组路由发出的每条消息都会使用与事件来源类型匹配、且 host 与仓库 URL 主机名一致的第一条配置作为底部标识GitHub 事件匹配 github.com)。标识文本为条目的可选 name——未填则回退为 host——因此两个自建 Gitea 实例可以显示为「内网 Gitea」/「Git2 仓库」,同时通过各自主机名完成匹配:

{
  "forgeSources": [
    { "host": "github.com", "type": "github", "name": "GitHub 主站" },
    { "host": "git1.example.com", "type": "gitea", "name": "内网 Gitea" },
    { "host": "git2.example.com", "type": "gitea" }
  ]
}
  • Discord — embed 底部在仓库名旁显示标识(内网 Gitea · acme/widget并以站点图标作为底部图标GitHub 使用 fluidicon.pngGitea 实例使用 /assets/img/favicon.png(均为 Discord 可渲染的栅格 PNG——.ico favicon 会被 Discord 静默忽略)。
  • Telegram — 底部行以带超链接的标识开头([内网 Gitea](https://git1.example.com))。
  • 仓库主机没有匹配条目的事件(如自定义 webhook或仓库托管在别处不显示标识。

该标识与 Group.emoji 相互独立,并跟随该分组分发的所有消息(包括工作流/检查消息的就地更新)。

邀请

Owner与超级管理员可以在分组的 成员 面板创建单次使用、有效期 7 天的邀请链接。接受邀请后,用户以被邀请的角色(adminviewer——绝不会是 owner)加入;已有 viewer 会被升级为 admin。邀请存储在 KV 的 invite:{token} 键下。

自助注册

设置 ALLOW_SELF_SIGNUP=1 后,没有分组权限的 GitHub 用户首次登录时会获得个人分组(u-{userId},归其所有),而不是 403。这是完全自助式 SaaS 安装的入口;关闭它可保持控制台仅邀请制。