现状 / 根因
webhook 接收端(#9 / PR #49)把隧道命令写死成 Cloudflare Quick Tunnel:
src-tauri/src/pr/webhook.rs 的 spawn_quick_tunnel 硬编码 cloudflared tunnel --no-autoupdate --url http://127.0.0.1:<port>,唯一可配的只有 cloudflaredBin(二进制路径)。
start() 在 cloudflared 未装时直接短路、隧道起不来就 abort 掉本地监听——接收端与隧道强耦合,无法只监听。
后果:
- URL 不固定 — Quick Tunnel 每次重启换随机
*.trycloudflare.com,GitHub webhook 的 Payload URL 要反复改。
- 职责错位 — "用哪个隧道 / 什么域名 / 什么凭证 / URL 固不固定" 本是隧道工具的事,却因为 App 自己 spawn 隧道而被拽进了接收端。接收端本应只需要
port + secret。
目标
把接收端与隧道解耦:接收端只负责 bind 127.0.0.1:port → HMAC 验签 → 解析 → 派发;隧道如何暴露到公网变成可配置、可外置。落地为三种模式供用户选:
- Quick Tunnel(默认,零配置) — 维持现状行为,random URL。图省事的人不用动任何配置。
- 自定义隧道命令 — App spawn 用户配置的命令(如
cloudflared tunnel run my-tunnel / tailscale funnel / ngrok http),指向 127.0.0.1:port。配合官方工具一次性带外登录,拿到固定 URL。
- Listener-only — App 只 bind 本地端口、不 spawn 任何隧道;用户在 App 外完全自管隧道。面板提示"请将隧道指向 127.0.0.1:port"。
非目标(治理红线)
- 不在 App 内做登录 / OAuth / 持有隧道凭证,不自动建 DNS / 注册隧道。理由:与本仓既有姿态一致——webhook 当初特意不自动注册 GitHub webhook(守 gh 只读红线),隧道侧同理不持凭证、不替用户 mutate 外部基础设施。登录是官方工具一次性带外完成的事,App 只编排"已认证好的命令"。
- 不绑死 Cloudflare:自定义命令模式对 Cloudflare 命名隧道 / Tailscale Funnel / ngrok 一视同仁。
设计要点(细化形态在 ship 计划阶段确认)
- 新增 config 字段走既有 serde camelCase golden 契约(
config/model.rs ↔ src/config/types.ts + golden 测试 + fields.ts + SettingsView/OnboardingWizard);#[serde(default)] 前向兼容旧持久化。
- 自定义命令需注入 port(
{port} 占位替换);命令直接 exec、不过 shell(沿用现有防注入约定)。
- 自定义 / listener-only 模式下公网 URL 是用户的固定域名、App 难以从输出可靠抓取 → Payload URL 的来源(用户显式配固定 URL vs 不显示)在计划阶段定。
- 拆
start() 对隧道的强依赖:listener-only 时只起 axum 监听,不要求 cloudflared 存在。
Files
src-tauri/src/pr/webhook.rs(spawn 逻辑解耦 + start 拆隧道强依赖)
src-tauri/src/config/model.rs(新 config 字段 + golden 测试)
src-tauri/src/pr/commands.rs(start_webhook 传参)
src/config/types.ts / src/config/fields.ts / src/config/SettingsView.vue / src/config/OnboardingWizard.vue(前端镜像 + 表单)
src/pr/WebhookPanel.vue / src/pr/types.ts(面板模式展示 + 提示文案)
Source
Discovered via 设计讨论(接收端 ≠ 隧道的职责边界)。与 #9 / PR #49 同一 webhook 切片;PR #49 将一并 Closes 本 issue(基线确认:直接在 Feature/9-webhook-cloudflare 分支上落地)。
现状 / 根因
webhook 接收端(#9 / PR #49)把隧道命令写死成 Cloudflare Quick Tunnel:
src-tauri/src/pr/webhook.rs的spawn_quick_tunnel硬编码cloudflared tunnel --no-autoupdate --url http://127.0.0.1:<port>,唯一可配的只有cloudflaredBin(二进制路径)。start()在 cloudflared 未装时直接短路、隧道起不来就 abort 掉本地监听——接收端与隧道强耦合,无法只监听。后果:
*.trycloudflare.com,GitHub webhook 的 Payload URL 要反复改。port+secret。目标
把接收端与隧道解耦:接收端只负责 bind
127.0.0.1:port→ HMAC 验签 → 解析 → 派发;隧道如何暴露到公网变成可配置、可外置。落地为三种模式供用户选:cloudflared tunnel run my-tunnel/tailscale funnel/ngrok http),指向127.0.0.1:port。配合官方工具一次性带外登录,拿到固定 URL。非目标(治理红线)
设计要点(细化形态在 ship 计划阶段确认)
config/model.rs↔src/config/types.ts+ golden 测试 +fields.ts+ SettingsView/OnboardingWizard);#[serde(default)]前向兼容旧持久化。{port}占位替换);命令直接 exec、不过 shell(沿用现有防注入约定)。start()对隧道的强依赖:listener-only 时只起 axum 监听,不要求 cloudflared 存在。Files
src-tauri/src/pr/webhook.rs(spawn 逻辑解耦 + start 拆隧道强依赖)src-tauri/src/config/model.rs(新 config 字段 + golden 测试)src-tauri/src/pr/commands.rs(start_webhook 传参)src/config/types.ts/src/config/fields.ts/src/config/SettingsView.vue/src/config/OnboardingWizard.vue(前端镜像 + 表单)src/pr/WebhookPanel.vue/src/pr/types.ts(面板模式展示 + 提示文案)Source
Discovered via 设计讨论(接收端 ≠ 隧道的职责边界)。与 #9 / PR #49 同一 webhook 切片;PR #49 将一并 Closes 本 issue(基线确认:直接在
Feature/9-webhook-cloudflare分支上落地)。