diff --git a/AGENTS.md b/AGENTS.md index bf0d3b6..57ac662 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,10 +10,11 @@ Core pipeline: GitHub Webhook → Worker (verify + filter + format) → Durable - Runtime: Cloudflare Workers - HTTP framework: Hono -- Discord Gateway: Durable Object (persistent WebSocket + channel cache) -- Storage: Cloudflare KV (tokens, OAuth state, route config) +- Discord Gateway: optional (`DISCORD_GATEWAY_ENABLED=true`); only keeps bot online — messages always sent via REST +- Storage: Cloudflare KV (tokens, OAuth state, route config, admin sessions) - Signature verification: Web Crypto API (HMAC-SHA256, timing-safe) - GitHub OAuth: octokit + jose (JWT) +- Admin WebUI: `/admin` config console, OAuth-session protected via `ADMIN_USER_IDS` whitelist - Local dev: wrangler + Miniflare ## Architecture @@ -22,15 +23,19 @@ Core pipeline: GitHub Webhook → Worker (verify + filter + format) → Durable src/ ├── index.ts # CF Workers entry (fetch + scheduled), exports DiscordGateway DO ├── types.ts # Env, Config, Route, Filter, WebhookEvent, FormattedMessage -├── config.ts # Loads routes from KV (fallback to 7 defaults), builds Config from env -├── server.ts # Hono app: /health, /webhook, mounts /auth + / +├── config.ts # loadRoutes/saveRoutes (KV config:routes, cache w/ 60s TTL), loadConfig from env +├── server.ts # Hono app: /health, /webhook, mounts /auth, /admin + / ├── webhook.ts # HMAC verify (Web Crypto), parseEvent, extractBranch, matchRoute -├── discord.ts # Dispatch via DO RPC, initGateway (scheduled) -├── discord-gateway.ts # Durable Object: Discord Gateway WS, heartbeat, channel cache, send +├── discord.ts # Dispatch via REST (or DO RPC when gateway enabled), initGateway (scheduled) +├── discord-rest.ts # Discord REST sendMessage with retry + rate-limit handling +├── discord-gateway.ts # Optional Durable Object: Discord Gateway WS, heartbeat, channel cache, send ├── formatter.ts # 23 event formatters + generic fallback (~1380 lines) ├── github-oauth.ts # OAuth URL, callback token exchange, getUserOctokit -├── oauth-routes.ts # GET /auth/github, callback, DELETE /token/:userId (KV state) +├── oauth-routes.ts # GET /auth/github, callback (sets admin session if redirect=/admin), DELETE /token/:userId ├── action-routes.ts # POST /api/comment|merge|react (Bearer token auth via KV lookup) +├── admin-routes.ts # /admin UI + GET/PUT /admin/api/routes (session + ADMIN_USER_IDS auth, validation) +├── admin-session.ts # Session CRUD (KV session:{id}), isAdminUser, cookie helpers +├── admin-ui.ts # ADMIN_HTML: single-file config console (vanilla HTML/CSS/JS) ├── token-store.ts # KV-based token CRUD with findUserIdByToken reverse lookup └── log.ts # JSON console logger (info/warn/error/fatal) ``` diff --git a/README.md b/README.md index 16dc41a..ca306c0 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ GitHub webhook → Discord dispatcher. Receives webhook events via Cloudflare Wo - Rich Discord embeds with color coding, author avatars, fields, and timestamps - Route to channels or threads - GitHub App OAuth for user actions (comment, merge, react) +- **Web UI config console** (`/admin`) — manage routes with GitHub OAuth + admin whitelist - Durable Object for persistent Discord Gateway connection + channel cache - Cloudflare KV for token/state/config storage - Graceful degradation (webhook-only mode if Discord unavailable) @@ -40,20 +41,21 @@ npx wrangler dev # Start local dev server ### Secrets (`.dev.vars` for local, Worker Secrets for production) -| Variable | Description | -| ----------------------- | ------------------------------ | -| `GITHUB_WEBHOOK_SECRET` | Webhook secret from GitHub | -| `GITHUB_APP_ID` | GitHub App ID | -| `GITHUB_PRIVATE_KEY` | App private key (PEM) | -| `GITHUB_CLIENT_ID` | OAuth client ID | -| `GITHUB_CLIENT_SECRET` | OAuth client secret | -| `DISCORD_TOKEN` | Bot token | -| `DISCORD_CHANNEL_ID` | Default target channel | -| `BASE_URL` | Public URL for OAuth callbacks | +| Variable | Description | +| ------------------------- | --------------------------------------------------------------------------------------------- | +| `GITHUB_WEBHOOK_SECRET` | Webhook secret from GitHub | +| `GITHUB_APP_ID` | GitHub App ID | +| `GITHUB_PRIVATE_KEY` | App private key (PEM) | +| `GITHUB_CLIENT_ID` | OAuth client ID | +| `GITHUB_CLIENT_SECRET` | OAuth client secret | +| `DISCORD_TOKEN` | Bot token | +| `BASE_URL` | Public URL for OAuth callbacks | +| `ADMIN_USER_IDS` | Comma-separated GitHub user IDs (or logins) allowed to access `/admin` | +| `DISCORD_GATEWAY_ENABLED` | `true` to enable the Discord Gateway (bot online status); messaging works without it via REST | ### Routes -Routes are stored in KV (`config:keys` as JSON). On first boot, 7 default routes are used. To customize, store a JSON array in KV: +Routes are stored in KV (`config:routes` as JSON). There are **no default routes** — every route (including its target `channelId` / `threadId`) must be defined explicitly, either via the Web UI (`/admin`) or by storing a JSON array in KV: ```json [ @@ -67,6 +69,18 @@ Routes are stored in KV (`config:keys` as JSON). On first boot, 7 default routes ] ``` +The `target.channelId` is required and is used as-is; there is no fallback to a default channel. + +### Web UI (`/admin`) + +The built-in config console lets you manage routes in the browser (add / edit / delete / toggle / reorder), no KV access needed: + +1. Set `ADMIN_USER_IDS` to the GitHub user IDs (or logins) allowed to manage the console, e.g. `ADMIN_USER_IDS=12345,RhenCloud`. +2. Visit `/admin` and sign in with GitHub. Only users in the whitelist get access. +3. Changes are written to KV `config:routes` immediately and picked up by the webhook pipeline. + +Sign out at `/admin/logout`. + See `config.example.yaml` for full syntax examples. ### Filter Types @@ -100,6 +114,14 @@ Set `exclude: true` to invert any filter. - `POST /api/merge` — Merge pull request - `POST /api/react` — Add reaction to issue +### Admin (require admin OAuth session) + +- `GET /admin` — Config console UI +- `GET /admin/login` — Start admin sign-in (GitHub OAuth) +- `GET /admin/logout` — Sign out +- `GET /admin/api/routes` — List routes +- `PUT /admin/api/routes` — Replace routes + ## GitHub App Setup ### 1. Create App @@ -129,6 +151,58 @@ Set `exclude: true` to invert any filter. 2. Set **Callback URL**: `https://your-domain/auth/github/callback` 3. Copy Client ID and Client Secret to env +## Discord Bot Setup + +Create a bot at , copy its token to `DISCORD_TOKEN`. + +### OAuth2 Invite + +Add the bot to your server with the `bot` scope and the following permissions: + +| Permission | Value | Why | +| ------------------------ | -------------- | ----------------------------------------------- | +| View Channels | `1024` | See the target channel to post messages | +| Send Messages | `2048` | Send embeds/messages to channels | +| Send Messages in Threads | `274877906944` | Send to threads when a route targets `threadId` | + +Combined permission integer: `274877910016` + +Invite URL (replace `CLIENT_ID` with your bot's client ID): + +``` +https://discord.com/oauth2/authorize?client_id=YOUR_BOT_CLIENT_ID&permissions=274877910016&scope=bot +``` + +### Intents + +The Gateway connection uses the **GUILDS** intent only (`1 << 0`). No privileged intents (e.g. Message Content) are required. + +### Gateway (optional) + +The Discord Gateway connection only keeps the bot showing as **online** — it is **not** required for sending messages. Messages are sent via the Discord REST API, so push works with just `DISCORD_TOKEN`. + +- `DISCORD_GATEWAY_ENABLED=false` (default): messages are sent directly via REST; the Gateway is not connected. +- `DISCORD_GATEWAY_ENABLED=true`: the Durable Object connects to the Gateway to keep the bot online; messages are still sent via REST. + +### Bot Command (`!gh`) + +When the Gateway is enabled, reply (quote) a bot-issued issue / PR notification and type: + +``` +!gh +``` + +The bot posts the text as a GitHub comment on that issue / PR (authenticated as the GitHub App). + +**Extra requirements:** + +| Item | How | +| ----------------- | ----------------------------------------------------------------------------------------- | +| Gateway enabled | `DISCORD_GATEWAY_ENABLED=true` | +| Privileged intent | Enable **Message Content** in Discord Developer Portal → Bot → Privileged Gateway Intents | +| Permissions | **Read Message History** (to read the quoted message) in addition to sending | +| GitHub App | Installed on the repo with **Issues (write)** permission | + ## Deployment ```bash diff --git a/README.zh.md b/README.zh.md index 7af1e9a..a446268 100644 --- a/README.zh.md +++ b/README.zh.md @@ -10,6 +10,7 @@ GitHub webhook → Discord 分发服务。通过 Cloudflare Workers 接收 webho - 富 Discord embed:颜色编码、作者头像、字段、时间戳 - 路由到频道或子区 - GitHub App OAuth 用户授权(评论、合并、反应) +- **Web 配置控制台**(`/admin`)— 通过 GitHub OAuth + 管理员白名单管理路由 - Durable Object 维持 Discord Gateway WebSocket 连接 + 频道缓存 - Cloudflare KV 存储 token/状态/配置 - 优雅降级(Discord 不可用时仅 webhook 模式) @@ -40,20 +41,21 @@ npx wrangler dev # 启动本地开发服务器 ### 密钥(本地用 `.dev.vars`,生产用 Worker Secrets) -| 变量 | 说明 | -| ----------------------- | --------------------------- | -| `GITHUB_WEBHOOK_SECRET` | GitHub webhook 密钥 | -| `GITHUB_APP_ID` | GitHub App ID | -| `GITHUB_PRIVATE_KEY` | App 私钥(PEM) | -| `GITHUB_CLIENT_ID` | OAuth Client ID | -| `GITHUB_CLIENT_SECRET` | OAuth Client Secret | -| `DISCORD_TOKEN` | 机器人 token | -| `DISCORD_CHANNEL_ID` | 默认目标频道 | -| `BASE_URL` | 公网地址(用于 OAuth 回调) | +| 变量 | 说明 | +| ------------------------- | -------------------------------------------------------------------------- | +| `GITHUB_WEBHOOK_SECRET` | GitHub webhook 密钥 | +| `GITHUB_APP_ID` | GitHub App ID | +| `GITHUB_PRIVATE_KEY` | App 私钥(PEM) | +| `GITHUB_CLIENT_ID` | OAuth Client ID | +| `GITHUB_CLIENT_SECRET` | OAuth Client Secret | +| `DISCORD_TOKEN` | 机器人 token | +| `BASE_URL` | 公网地址(用于 OAuth 回调) | +| `ADMIN_USER_IDS` | 允许访问 `/admin` 的 GitHub 用户 ID(或登录名),逗号分隔 | +| `DISCORD_GATEWAY_ENABLED` | 设为 `true` 启用 Discord Gateway(bot 在线状态);不启用也能通过 REST 推送 | ### 路由配置 -路由存储在 KV(`config:routes`,JSON 格式)。首次启动使用 7 个默认路由。自定义时在 KV 中存储 JSON 数组: +路由存储在 KV(`config:routes`,JSON 格式)。**没有默认路由**——每条路由(包括目标 `channelId` / `threadId`)都必须显式定义,可通过 Web 控制台(`/admin`)或直接向 KV 存储 JSON 数组: ```json [ @@ -67,6 +69,18 @@ npx wrangler dev # 启动本地开发服务器 ] ``` +`target.channelId` 必填且按原样使用,不存在默认频道回退。 + +### Web 控制台(`/admin`) + +内置的配置控制台让你在浏览器中管理路由(新增 / 编辑 / 删除 / 开关),无需操作 KV: + +1. 设置 `ADMIN_USER_IDS` 为允许管理控制台的 GitHub 用户 ID(或登录名),例如 `ADMIN_USER_IDS=12345,RhenCloud`。 +2. 访问 `/admin` 并用 GitHub 登录,仅白名单内用户可进入。 +3. 修改会立即写入 KV `config:routes`,webhook 管线随即生效。 + +在 `/admin/logout` 退出登录。 + 完整语法示例见 `config.example.yaml`。 ### 过滤器类型 @@ -100,6 +114,14 @@ npx wrangler dev # 启动本地开发服务器 - `POST /api/merge` — 合并 PR - `POST /api/react` — 添加 issue 反应 +### 管理接口(需要管理员 OAuth 会话) + +- `GET /admin` — 配置控制台页面 +- `GET /admin/login` — 开始管理员登录(GitHub OAuth) +- `GET /admin/logout` — 退出登录 +- `GET /admin/api/routes` — 列出路由 +- `PUT /admin/api/routes` — 替换路由 + ## GitHub App 配置教程 ### 1. 创建 App @@ -128,6 +150,58 @@ npx wrangler dev # 启动本地开发服务器 2. 设置 **Callback URL**:`https://your-domain/auth/github/callback` 3. 复制 Client ID 和 Client Secret 到环境变量 +## Discord 机器人配置 + +在 创建机器人,将 Token 复制到 `DISCORD_TOKEN`。 + +### OAuth2 邀请 + +使用 `bot` scope 将机器人加入服务器,需要以下权限: + +| 权限 | 数值 | 用途 | +| ------------------------------------------- | -------------- | ---------------------------------------- | +| 查看频道 (View Channels) | `1024` | 查看目标频道以发送消息 | +| 发送消息 (Send Messages) | `2048` | 向频道发送 embed/消息 | +| 在线程中发送消息 (Send Messages in Threads) | `274877906944` | 当路由配置了 `threadId` 时向线程发送消息 | + +权限组合整数值:`274877910016` + +邀请链接(将 `CLIENT_ID` 替换为机器人的 Client ID): + +``` +https://discord.com/oauth2/authorize?client_id=你的机器人CLIENT_ID&permissions=274877910016&scope=bot +``` + +### Intents + +Gateway 连接仅使用 **GUILDS** intent(`1 << 0`)。无需特权 intent(如 Message Content)。 + +### Gateway(可选) + +Discord Gateway 连接仅用于让 bot 显示为**在线**——发送消息**不需要**它。消息通过 Discord REST API 发送,因此只要有 `DISCORD_TOKEN` 即可推送。 + +- `DISCORD_GATEWAY_ENABLED=false`(默认):直接通过 REST 发送消息,不建立 Gateway 连接。 +- `DISCORD_GATEWAY_ENABLED=true`:由 Durable Object 连接 Gateway 以维持 bot 在线状态;消息仍走 REST。 + +### Bot 指令(`!gh`) + +启用 Gateway 后,**引用(回复)**一条 bot 推送的 issue / PR 通知,然后输入: + +``` +!gh <评论内容> +``` + +bot 会以 GitHub App 的身份把内容发为对该 issue / PR 的评论。 + +**额外要求:** + +| 项目 | 说明 | +| ------------ | --------------------------------------------------------------------------------------- | +| 启用 Gateway | `DISCORD_GATEWAY_ENABLED=true` | +| 特权 intent | 在 Discord Developer Portal → Bot → Privileged Gateway Intents 开启 **Message Content** | +| 权限 | 除发送外还需 **Read Message History**(读取被引用的消息) | +| GitHub App | 已安装到该仓库且有 **Issues (write)** 权限 | + ## 部署 ```bash diff --git a/docs/api/overview.md b/docs/api/overview.md index 229ac2a..cef6ad2 100644 --- a/docs/api/overview.md +++ b/docs/api/overview.md @@ -20,6 +20,17 @@ https://your-worker.workers.dev | `POST` | `/api/comment` | Bearer token | Create issue comment | | `POST` | `/api/merge` | Bearer token | Merge pull request | | `POST` | `/api/react` | Bearer token | Add reaction to issue | +| `GET` | `/admin` | Admin session | Config console UI | +| `GET` | `/admin/api/routes` | Admin session | List routes | +| `PUT` | `/admin/api/routes` | Admin session | Replace routes | + +## Admin Console + +See [Configuration → Web UI](../guide/configuration.md#web-ui) for setup. Admin endpoints require a session cookie obtained via `GET /admin/login` (GitHub OAuth); the signed-in user must be listed in `ADMIN_USER_IDS`. + +- `GET /admin` — Serves the config console HTML +- `GET /admin/api/routes` — Returns `{ "routes": Route[] }` +- `PUT /admin/api/routes` — Body `{ "routes": Route[] }`; validates each route (id pattern, unique id, name, enabled, ≥1 valid filter, string `target.channelId`) and persists to KV `config:routes`. Returns `200 { ok, count }` or `400 { error }` / `401 { error }`. ## Health Check diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md index 975a55c..314f3ba 100644 --- a/docs/guide/configuration.md +++ b/docs/guide/configuration.md @@ -14,19 +14,42 @@ WebHooker requires several secrets to function. For local development, store the | `GITHUB_CLIENT_ID` | OAuth client ID from App settings | | `GITHUB_CLIENT_SECRET` | OAuth client secret from App settings | | `DISCORD_TOKEN` | Discord bot token | -| `DISCORD_CHANNEL_ID` | Default Discord channel ID for messages | ### Optional Secrets -| Variable | Description | Default | -| ---------- | ------------------------------ | ----------------------- | -| `BASE_URL` | Public URL for OAuth callbacks | `http://localhost:8787` | +| Variable | Description | Default | +| ------------------------- | ----------------------------------------------------------------------------------------------------- | ----------------------- | +| `BASE_URL` | Public URL for OAuth callbacks | `http://localhost:8787` | +| `ADMIN_USER_IDS` | Comma-separated GitHub user IDs (or logins) allowed to access the Web UI | Disabled | +| `DISCORD_GATEWAY_ENABLED` | Set to `true` to connect the Discord Gateway (bot online status); messaging works without it via REST | `false` | + +## Web UI + +WebHooker ships with a built-in config console at `/admin` for managing routes in the browser. It is protected by GitHub OAuth plus an admin whitelist. + +### Setup + +1. Configure `ADMIN_USER_IDS` with the GitHub user IDs allowed to manage routes. Logins are also accepted, e.g. `ADMIN_USER_IDS=12345,RhenCloud`. If unset, the console is disabled. +2. Open `/admin` and sign in with GitHub. +3. Only users in the whitelist receive a session cookie; everyone else gets `403`. + +### Endpoints + +| Endpoint | Description | +| ----------------------- | --------------------------- | +| `GET /admin` | Config console UI | +| `GET /admin/login` | Start GitHub OAuth sign-in | +| `GET /admin/logout` | Destroy session | +| `GET /admin/api/routes` | List routes (admin only) | +| `PUT /admin/api/routes` | Replace routes (admin only) | + +The console lets you add, edit, delete, and toggle routes. Saved routes are written to KV `config:routes` immediately and the config cache is invalidated so the webhook pipeline picks them up on the next run. ## Routes Routes define which events get forwarded to which Discord channels. They are stored in Cloudflare KV under the key `config:routes` as a JSON array. -On first boot, 7 default routes are used if no KV config exists. +There are **no default routes** — each route must define its own target. If no routes are configured, no events are forwarded. ### Route Schema @@ -40,23 +63,13 @@ On first boot, 7 default routes are used if no KV config exists. { "type": "repo", "match": "org/repo", "exclude": false } ], "target": { - "channelId": "DISCORD_CHANNEL_ID", + "channelId": "REQUIRED_CHANNEL_ID", "threadId": "OPTIONAL_THREAD_ID" } } ``` -### Default Routes - -| ID | Event(s) | Description | -| ----------------- | ------------------ | -------------------------------- | -| `all-push` | `push` | All push events | -| `pull-requests` | `pull_request` | All PR activity | -| `issues` | `issues` | Issue open/close/edit | -| `issue-comments` | `issue_comment` | Issue and PR comments | -| `workflow-runs` | `workflow_run` | CI/CD workflow completions | -| `releases` | `release` | Release publish/edit | -| `branch-activity` | `create`, `delete` | Branch/tag creation and deletion | +`target.channelId` is required and used as-is; there is no fallback to a default channel. ### Custom Route Example @@ -108,8 +121,9 @@ Filters accept either a single string or an array of strings: ## KV Storage Layout -| Key Pattern | Value | TTL | -| ---------------- | ---------------------------- | ------------ | -| `config:routes` | JSON array of routes | Permanent | -| `token:{userId}` | `{ accessToken, expiresAt }` | Until expiry | -| `state:{hex}` | `{ userId, createdAt }` | 600 seconds | +| Key Pattern | Value | TTL | +| ---------------- | --------------------------------- | ------------ | +| `config:routes` | JSON array of routes | Permanent | +| `session:{id}` | Admin session `{ userId, login }` | 7 days | +| `token:{userId}` | `{ accessToken, expiresAt }` | Until expiry | +| `state:{hex}` | `{ userId, createdAt }` | 600 seconds | diff --git a/docs/zh/guide/configuration.md b/docs/zh/guide/configuration.md index e1742a2..bd02b74 100644 --- a/docs/zh/guide/configuration.md +++ b/docs/zh/guide/configuration.md @@ -6,27 +6,50 @@ WebHooker 需要多个密钥才能运行。本地开发时存储在 `.dev.vars` ### 必需密钥 -| 变量 | 说明 | -| --- | --- | -| `GITHUB_WEBHOOK_SECRET` | GitHub App 设置中的 Webhook 密钥 | -| `GITHUB_APP_ID` | GitHub App 的数字 ID | -| `GITHUB_PRIVATE_KEY` | App 私钥(PEM 格式,用 `\n` 转义) | -| `GITHUB_CLIENT_ID` | App 设置中的 OAuth 客户端 ID | -| `GITHUB_CLIENT_SECRET` | App 设置中的 OAuth 客户端密钥 | -| `DISCORD_TOKEN` | Discord Bot Token | -| `DISCORD_CHANNEL_ID` | 消息发送的默认 Discord 频道 ID | +| 变量 | 说明 | +| ----------------------- | ---------------------------------- | +| `GITHUB_WEBHOOK_SECRET` | GitHub App 设置中的 Webhook 密钥 | +| `GITHUB_APP_ID` | GitHub App 的数字 ID | +| `GITHUB_PRIVATE_KEY` | App 私钥(PEM 格式,用 `\n` 转义) | +| `GITHUB_CLIENT_ID` | App 设置中的 OAuth 客户端 ID | +| `GITHUB_CLIENT_SECRET` | App 设置中的 OAuth 客户端密钥 | +| `DISCORD_TOKEN` | Discord Bot Token | ### 可选密钥 -| 变量 | 说明 | 默认值 | -| --- | --- | --- | -| `BASE_URL` | OAuth 回调的公开 URL | `http://localhost:8787` | +| 变量 | 说明 | 默认值 | +| ------------------------- | -------------------------------------------------------------------------- | ----------------------- | +| `BASE_URL` | OAuth 回调的公开 URL | `http://localhost:8787` | +| `ADMIN_USER_IDS` | 允许访问 WebUI 的 GitHub 用户 ID(或登录名),逗号分隔 | 未设置时 WebUI 关闭 | +| `DISCORD_GATEWAY_ENABLED` | 设为 `true` 连接 Discord Gateway(bot 在线状态);不启用也能通过 REST 推送 | `false` | + +## Web 控制台 + +WebHooker 内置了位于 `/admin` 的配置控制台,可在浏览器中管理路由。它由 GitHub OAuth 和管理员白名单保护。 + +### 设置 + +1. 配置 `ADMIN_USER_IDS`,填写允许管理路由的 GitHub 用户 ID,也支持登录名,例如 `ADMIN_USER_IDS=12345,RhenCloud`。未设置时控制台禁用。 +2. 打开 `/admin` 并使用 GitHub 登录。 +3. 只有白名单中的用户会获得会话 Cookie;其他人收到 `403`。 + +### 端点 + +| 端点 | 说明 | +| ----------------------- | ---------------------- | +| `GET /admin` | 配置控制台页面 | +| `GET /admin/login` | 开始 GitHub OAuth 登录 | +| `GET /admin/logout` | 销毁会话 | +| `GET /admin/api/routes` | 列出路由(仅管理员) | +| `PUT /admin/api/routes` | 替换路由(仅管理员) | + +控制台支持新增、编辑、删除和开关路由。保存后立即写入 KV `config:routes` 并使配置缓存失效,下一次 webhook 处理即会生效。 ## 路由 路由定义了哪些事件被转发到哪些 Discord 频道。它们以 JSON 数组形式存储在 Cloudflare KV 中,键为 `config:routes`。 -首次启动时,如果 KV 中没有配置,则使用 7 条默认路由。 +**没有默认路由**——每条路由必须自行定义目标频道。若未配置任何路由,则不会转发任何事件。 ### 路由模式 @@ -40,23 +63,13 @@ WebHooker 需要多个密钥才能运行。本地开发时存储在 `.dev.vars` { "type": "repo", "match": "org/repo", "exclude": false } ], "target": { - "channelId": "DISCORD_CHANNEL_ID", - "threadId": "OPTIONAL_THREAD_ID" + "channelId": "必填频道ID", + "threadId": "可选线程ID" } } ``` -### 默认路由 - -| ID | 事件 | 说明 | -| --- | --- | --- | -| `all-push` | `push` | 所有推送事件 | -| `pull-requests` | `pull_request` | 所有 PR 活动 | -| `issues` | `issues` | 议题打开/关闭/编辑 | -| `issue-comments` | `issue_comment` | 议题和 PR 评论 | -| `workflow-runs` | `workflow_run` | CI/CD 工作流完成 | -| `releases` | `release` | 发布创建/编辑 | -| `branch-activity` | `create`, `delete` | 分支/标签创建和删除 | +`target.channelId` 必填且按原样使用,不存在默认频道回退。 ### 自定义路由示例 @@ -81,14 +94,14 @@ WebHooker 需要多个密钥才能运行。本地开发时存储在 `.dev.vars` ## 过滤器类型 -| 类型 | 匹配对象 | 示例 | -| --- | --- | --- | -| `event` | GitHub 事件名称 | `push`, `pull_request`, `issues` | -| `repo` | 仓库全名 | `org/repo` | -| `actor` | 发送者登录名 | `username`, `[bot]` | -| `action` | 事件操作 | `opened`, `closed`, `published` | -| `branch` | 分支名称 | `main`, `feature/*` | -| `keyword` | 载荷正文中的文本 | `deploy`, `/fix\s+\d+/` (正则) | +| 类型 | 匹配对象 | 示例 | +| --------- | ---------------- | -------------------------------- | +| `event` | GitHub 事件名称 | `push`, `pull_request`, `issues` | +| `repo` | 仓库全名 | `org/repo` | +| `actor` | 发送者登录名 | `username`, `[bot]` | +| `action` | 事件操作 | `opened`, `closed`, `published` | +| `branch` | 分支名称 | `main`, `feature/*` | +| `keyword` | 载荷正文中的文本 | `deploy`, `/fix\s+\d+/` (正则) | ### 过滤器行为 @@ -108,8 +121,9 @@ WebHooker 需要多个密钥才能运行。本地开发时存储在 `.dev.vars` ## KV 存储布局 -| 键模式 | 值 | TTL | -| --- | --- | --- | -| `config:routes` | JSON 路由数组 | 永久 | -| `token:{userId}` | `{ accessToken, expiresAt }` | 至过期 | -| `state:{hex}` | `{ userId, createdAt }` | 600 秒 | +| 键模式 | 值 | TTL | +| ---------------- | ------------------------------ | ------ | +| `config:routes` | JSON 路由数组 | 永久 | +| `session:{id}` | 管理员会话 `{ userId, login }` | 7 天 | +| `token:{userId}` | `{ accessToken, expiresAt }` | 至过期 | +| `state:{hex}` | `{ userId, createdAt }` | 600 秒 |