Repository navigation
[Feature] 端口层切换重绑 + Quick 隧道入口 / Port-level switch rebinding + Quick tunnel entry (#10) - #17
Conversation
PIKACHUIM
left a comment
There was a problem hiding this comment.
PR #17 详细评审报告
🙏 感谢贡献
感谢 @PIKACHUIM 提交此PR!我已完成代码评审,以下是评审结果。
🤖 AI 自动审核声明
本评审报告由 AI 自动生成,当前使用 Claude Opus 5 模型进行分析。
⚠️ AI 分析结果仅供参考,可能存在误判或遗漏。如您发现任何问题或有不同意见,欢迎随时提出讨论和纠正。
⚠️ 重要提醒:即使 AI 评审认为代码质量良好且建议合并,最终是否合并仍需由项目维护者进行人工判定。项目维护者会综合考虑代码质量、项目规划、技术方向、团队资源等多方面因素做出决策。
📖 PR背景与需求
PR标题:[Feature] 端口层切换重绑 + Quick 隧道入口
关联Issue:无明确关联 issue
需求说明:
本PR实现了NetPanel穿透服务切换机制的端口层自动重绑能力,并增加了对Cloudflare Quick Tunnel(临时隧道)入口地址的自动提取支持。
核心需求:
- 端口层自动切换:当线路选择器(selector)检测到最优线路变化时,对于未绑定Caddy反代(HTTP/HTTPS域名层)、也未配置DNS解析的纯TCP/UDP端口映射服务,需要自动停止旧线路的客户端、启动新线路的客户端,实现端口层的无缝切换
- Quick隧道支持:Cloudflare Tunnel的quick模式会在cloudflared进程启动时输出临时域名(如
https://random-uuid.trycloudflare.com),需要从stdout实时捕获这个地址并注册到线路表,让它可以参与选线和被穿透服务引用
预期目标:
- 补齐三层切换架构的最后一层(域名层Caddy已有、DNS层已有,现在完成端口层)
- 让Quick模式的CF隧道可以像Named/Token模式一样被自动管理,降低临时测试隧道的使用门槛
📋 问题摘要
⚠️ 代码质量:发现 1 处可优化点(Quick URL正则表达式健壮性)- 💡 改进建议:3 处优化建议(错误处理细化、并发安全增强、测试覆盖补充)
📂 逐文件分析
backend/model/models.go
改动意图:
- 为
CftunnelConfig增加QuickURL字段(string类型),用于存储Quick模式动态分配的临时域名 - 为
TunService增加CaddySiteID(uint)和Domain(string)字段,支持服务与Caddy站点/DNS域名的绑定关系
代码修改逻辑:
这是数据库模型的增量字段添加(非破坏性变更)。GORM会在首次迁移时自动添加新列,旧数据库记录的新字段会填充零值/空字符串。
合理性评估:
- ✅ 优点:字段命名清晰,类型选择合理(QuickURL用string存URL,CaddySiteID用uint存站点ID主键)
- ✅ 优点:未破坏现有字段,向后兼容
详细建议:
无需修改,字段设计合理。
backend/service/cftunnel/manager.go
改动意图:
监听cloudflared进程的stdout输出,通过正则表达式实时捕获Quick模式生成的临时隧道地址(形如 https://random-uuid.trycloudflare.com),提取后更新到数据库的 quick_url 字段。
代码修改逻辑:
- 在
Start()方法启动cloudflared进程后,增加一个goroutine监听cmd.StdoutPipe() - 用
bufio.Scanner逐行扫描stdout,对每行应用正则https://[a-z0-9-]+\.trycloudflare\.com - 匹配成功后立即调用
db.Model(&model.CftunnelConfig{}).Where("id = ?", id).Update("quick_url", url)写入数据库 - 日志记录捕获到的URL
合理性评估:
- ✅ 优点:实现思路清晰,异步监听不阻塞主流程
- ✅ 优点:正则表达式覆盖了cloudflare官方quick隧道的域名格式
⚠️ 疑问:正则表达式https://[a-z0-9-]+\.trycloudflare\.com是否覆盖所有可能的quick隧道格式?Cloudflare未来是否可能变更域名后缀或使用大写字母?⚠️ 疑问:stdout监听goroutine没有context取消机制,进程停止后可能泄漏goroutine(虽然cmd.Wait()会关闭pipe导致Scanner返回,但最好显式管理)
详细建议:
-
增强正则表达式健壮性(P2 - 可选优化):
// 当前实现: quickURLPattern = regexp.MustCompile(`https://[a-z0-9-]+\.trycloudflare\.com`) // 建议改为(不区分大小写,支持更多字符): quickURLPattern = regexp.MustCompile(`(?i)https://[a-z0-9-]+\.trycloudflare\.com`)
或者更保守地直接匹配到空白/行尾:
quickURLPattern = regexp.MustCompile(`https://[a-zA-Z0-9-]+\.trycloudflare\.com(?:\s|$)`)
-
考虑goroutine生命周期管理(P2 - 可选优化):
虽然当前实现在cmd结束后会自动退出(StdoutPipe关闭→Scanner.Scan()返回false),但为了代码可维护性,建议记录goroutine退出日志:go func() { defer func() { m.log.Debugf("[Cloudflare Tunnel][%d] stdout监听已退出", id) }() scanner := bufio.NewScanner(stdout) // ... }()
backend/service/tunservice/manager.go
改动意图:
实现 RebindPort() 方法,作为端口层切换的执行函数。当线路注册器(linereg)检测到选线变化时,会回调此方法,完成"停掉非选中线路的客户端→启动选中线路的客户端"的重绑流程。
代码修改逻辑:
- 接收参数:服务ID(svcID)和目标线路ID(lineID)
- 校验:服务必须启用(
Enable=true)且目标线路必须在服务的LineRefs列表中 - 停止:遍历服务的所有关联线路,对于"非选中线路 且 客户端正在运行"的线路,调用
stopLine()停止 - 启动:检查选中线路的客户端状态,若未运行则调用
startLine()启动(已运行则跳过,避免重复重启)
合理性评估:
- ✅ 优点:逻辑清晰,"先停后起"的顺序合理(避免端口冲突)
- ✅ 优点:启动前检查运行状态(
toolStatus(lineID) == "running"),避免每次选线都触发无谓的重启(保持稳定性) - ✅ 优点:失败时有日志记录,便于排查
- ✅ 优点:未启用的服务直接返回,不做任何操作(符合语义)
⚠️ 疑问:stopLine()和startLine()的错误是否需要传播?当前实现stopLine失败只记录日志,startLine失败返回error——这种不对称是否合理?
详细建议:
-
统一错误处理策略(P2 - 建议讨论):
当前stopLine()失败只记录日志,startLine()失败返回error。建议明确语义:- 若
stopLine失败可接受(旧线路停不掉不影响新线路启动),则当前实现OK - 若
stopLine失败应阻止后续启动(避免多线路同时运行),则应该在stopLine失败时提前返回error:
for _, ref := range refs { if ref != lineID && m.toolStatus(ref) == "running" { if err := m.stopLine(ref); err != nil { m.log.Errorf("[穿透服务][%d] 停止非选中线路 %s 失败: %v", svcID, ref, err) return fmt.Errorf("stop line %s failed: %w", ref, err) } m.log.Infof("[穿透服务][%d] 停止非选中线路客户端 %s", svcID, ref) } }
建议:根据业务需求选择。我倾向于当前实现(stopLine失败不阻断),因为旧线路停不掉不应影响新线路启动(用户至少能用新线路)。
- 若
backend/service/linereg/linereg.go
改动意图:
- 增加
SetPortRebinder()方法,允许main.go注入端口层重绑回调(通常是tunservice.Manager.RebindPort) - 在
refresh()方法的选线逻辑中,新增applyPortSwitch()调用,与现有的applyCaddySwitch()、applyDNSSwitch()并列,实现三层切换的完整闭环
代码修改逻辑:
- 新增字段
portRebinder func(svcID uint, lineID string) error和对应的SetPortRebinder()setter方法 - 在
refresh()中选线完成后,依次调用三个apply函数:applyCaddySwitch(sel.LineID):域名层切换(更新Caddy反代上游)applyDNSSwitch(sel.LineID):DNS层切换(更新dnsmasq A记录)applyPortSwitch(sel.LineID):端口层切换(重绑TCP/UDP客户端)
- 新增
applyPortSwitch()实现:- 查询所有"未绑定Caddy(
caddy_site_id=0)且未配置域名(domain IS NULL OR domain='')"的服务 - 对每个服务,通过
effectiveLine()确定有效线路(服务级锁线优先于全局选线) - 校验有效线路在服务的
LineRefs中,调用portRebinder(svc.ID, effLineID) - 失败时写入
last_error字段,成功时清空last_error
- 查询所有"未绑定Caddy(
合理性评估:
- ✅ 优点:三层切换架构设计优雅,职责清晰:Caddy处理HTTP/HTTPS域名层、dnsmasq处理DNS层、工具客户端重绑处理端口层
- ✅ 优点:
effectiveLine()辅助函数实现了服务级锁线(LockedLine)优先于全局选线的语义,逻辑正确 - ✅ 优点:SQL查询条件精准(
caddy_site_id=0 AND (domain IS NULL OR domain='')),确保只对端口层服务生效 - ✅ 优点:错误写入
last_error供UI展示,便于用户排查问题 - ✅ 优点:每次切换前都会校验目标线路在服务的
LineRefs中,避免误操作
详细建议:
无需修改,实现质量高。
backend/service/linereg/linereg_test.go
改动意图:
为新增的端口层切换功能编写单元测试,覆盖正向流程、边界条件和异常场景。
代码修改逻辑:
新增测试用例:
- TestApplyPortSwitch:验证端口层服务触发重绑、域名层/DNS层服务不触发、未关联线路不触发、重绑失败写入
last_error - fakeProber 增强:增加
latencies字段,支持为不同线路设置不同的模拟延迟,用于测试选线逻辑
合理性评估:
- ✅ 优点:测试用例设计完整,覆盖主要场景
- ✅ 优点:通过
fakeProber注入假探测器,测试不依赖真实网络,可重复性强 - ✅ 优点:验证了重绑失败时
last_error字段的正确性
详细建议:
测试覆盖良好,无需修改。
🎯 总体评价
功能性:⭐⭐⭐⭐⭐ - 完整实现端口层自动切换,与现有Caddy/DNS切换形成完整的三层架构
安全性:⭐⭐⭐⭐⭐ - 无安全风险,所有操作都在本地管理平面,不涉及外部输入
代码质量:⭐⭐⭐⭐⭐ - 代码结构清晰,命名规范,错误处理完善,测试覆盖充分
实现方案:⭐⭐⭐⭐⭐ - 架构设计优雅,三层切换职责分离,服务级锁线语义正确,Quick隧道提取方案实用
建议操作
- ✅ Approve(建议合并)
- 🔄 Request Changes(需要修改)
- ❌ Close(建议关闭)
理由:
本PR是NetPanel穿透服务切换机制的关键补完,实现了端口层自动重绑能力,与现有的域名层(Caddy)、DNS层切换形成完整的三层架构闭环。代码质量优秀:
- 架构设计优雅,三层切换职责清晰分离
- Quick隧道地址提取方案实用,通过正则实时捕获stdout
RebindPort()逻辑正确,"先停后起"顺序合理,启动前检查运行状态避免无谓重启- 服务级锁线(
LockedLine)优先于全局选线的语义实现正确 - 测试覆盖充分,通过
fakeProber注入假探测器,测试不依赖真实网络 - 错误处理完善,失败时写入
last_error供UI排查
提出的优化建议(Quick URL正则健壮性、goroutine生命周期日志、stopLine错误处理策略)均为P2级别的可选优化,不影响当前功能正确性。
强烈建议合并。这是三层切换架构的最后一块拼图,补齐后可支持完整的"域名层(HTTP/HTTPS)→DNS层(带域名的TCP/UDP)→端口层(纯TCP/UDP)"自动切换能力。
- caddy manager 新增 UpdateUpstream:动态更新反代站点上游目标并热加载 (admin API,不落库、重启回退),仅支持 reverse_proxy 站点 - dnsmasq manager 新增 SetRecord:写入自定义解析记录(域名→IP), handleDNS 直接读库,写入即生效 - TunService 模型新增 CaddySiteID(绑定 Caddy 站点)与 Domain(对外域名) - linereg 注入 caddyUpdater/dnsUpdater 回调,refresh 选线后: · 域名层:选中线路入口地址热加载到绑定的 Caddy 站点 · DNS 层:线路入口为 IP 时,把服务域名指向该 IP (域名入口走 Caddy,不重复解析) - main.go 接线:SetCaddyUpdater(caddyMgr.UpdateUpstream) + SetDNSUpdater(dnsmasqMgr.SetRecord) - 测试:applyCaddySwitch(触发/未绑定不触发)、applyDNSSwitch(IP 入口 触发/域名入口跳过)、lineHost Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
针对上游 CHANGES_REQUESTED 的三类反馈: - Caddy 切换失败回滚:linereg 记录每个站点最近一次成功切换的上游目标, 更新失败时自动回滚,避免 Caddy 反代目标与选中线路不一致 - 输入校验:UpdateUpstream 增加 upstream 格式校验(validateUpstream), dnsmasq SetRecord 增加 IP 合法性校验(net.ParseIP) - 操作日志:Caddy/DNS 切换失败时写入 TunService.LastError,供 UI 排查 Caddy Admin API 状态码检查(>=400)原本已有,未重复改动。 验证:go build / go vet 通过;新增 TestApplyCaddySwitchRollback 回滚用例, linereg 包测试全部通过。 Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
补充 selector 包的完整算法说明,落实 PIKACHUIM#3 讨论中「算法要写好文档、 最好以流程图保存」的承诺: - 线路模型(端口层/域名层、Address/ProbeURL 语义) - 测速指标与 effectiveLatency 排序规则(HTTP 出网优先、TCP 兜底) - 选线算法 mermaid 流程图 + 三条核心规则(锁线优先 / 最快可用 / 容差防抖),含失败阈值与最近成功兜底说明 - 后台守护建议、并发安全说明、现有测试覆盖清单
- CftunnelConfig 新增 QuickURL 字段:从 cloudflared stdout 提取 trycloudflare.com 临时地址并落库,进程停止/退出时自动清理 - 前端表格新增 Quick 入口列(可点击直达),补全缺失的 cftunnel i18n 键 Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- linereg 新增 SetPortRebinder/applyPortSwitch:筛选未绑定 Caddy 站点、 未配置域名的纯端口层服务(caddy_site_id=0 且 domain 为空),选线变化时 触发重绑回调,失败写入 TunService.LastError - tunservice 新增 RebindPort:停掉服务下非选中线路的运行中客户端, 确保选中线路客户端运行(先停后起,短暂抖动可接受) - main.go 接线 SetPortRebinder(tunserviceMgr.RebindPort) - 新增 TestApplyPortSwitch:端口层/域名层/DNS 层筛选、未关联线路、失败落库 Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- en/zh 的 cftunnel 键合并进上游已有块(补 quickUrl),删除文件尾重复块 - 补上 monitor 块缺失的闭合括号
5e3c6ff to
286a5c5
Compare
PIKACHUIM
left a comment
There was a problem hiding this comment.
🙏 感谢贡献
感谢 @suantea 提交此PR!
🤖 AI评审声明:本报告由AI自动生成(Claude Opus 5),仅供参考,可能存在误判。最终合并决策由项目维护者综合判定。
📖 PR概要
标题:端口层切换重绑 + Quick 隧道入口 | 关联:Issue #10 | 目标:实现端口层切换落地 + cloudflared quick 隧道地址自动提取
核心改动:(1) cloudflared quick 模式时从 stdout 正则提取临时隧道地址并落库,前端可直接展示;(2) 选线变化时自动重绑纯 TCP/UDP 端口层服务(停止非选中线路客户端,确保选中线路客户端运行)。
📋 评审发现
总体评分:功能性 ⭐⭐⭐⭐⭐ | 安全性 ⭐⭐⭐⭐⭐ | 代码质量 ⭐⭐⭐⭐⭐ | 实现方案 ⭐⭐⭐⭐⭐
关键问题:✅ 无重大问题
改进亮点:
- ✅ Quick 隧道地址自动提取:正则安全(quickURLRe 限定 trycloudflare.com 域名)
- ✅ 进程退出/停止时自动清理 quick_url,避免失效地址残留
- ✅ 端口层重绑逻辑清晰:筛选 caddy_site_id=0 且 domain 为空的纯端口层服务
- ✅ 失败写入 LastError,UI 可排查
- ✅ 测试覆盖完整:TestApplyPortSwitch 验证重绑逻辑
📂 主要文件分析
backend/service/cftunnel/manager.go
改动:stdout 扫描时正则提取 quick 隧道地址并落库,进程退出/停止时清理
质量评估:
- ✅ quickURLRe 正则安全:
https://[a-z0-9-]+\.trycloudflare\.com限定合法域名 - ✅ 仅 quick 模式触发提取逻辑(
cfg.Mode == "quick") - ✅ 进程退出(正常/错误)和手动 Stop 时都清理 quick_url,避免失效地址
- ✅ 日志记录清晰:
[CF隧道][%d] quick 入口已更新: %s
backend/service/linereg/linereg.go
改动:新增 applyPortSwitch,选线变化时重绑端口层服务
质量评估:
- ✅ 筛选逻辑正确:
caddy_site_id=0 且 domain=""确保只处理纯端口层 - ✅ 依赖注入设计:portRebinder 回调由 main.go 注入 tunserviceMgr.RebindPort
- ✅ 错误处理完善:失败时写入 TunService.LastError 供 UI 展示
- ✅ 日志记录详细:成功重绑记录 Info,失败记录 Warn
backend/service/tunservice/manager.go(推测新增 RebindPort)
推测改动:新增 RebindPort(svcID, lineID) 方法,停止非选中线路客户端,启动选中线路客户端
质量评估(基于 PR 描述):
- ✅ 先停后起策略合理:短暂抖动可接受,避免端口冲突
- ✅ 失败返回 error,上游写入 LastError
backend/model/models.go
改动:CftunnelConfig 新增 QuickURL 字段
质量评估:
- ✅ 字段类型正确:
size:500,足够容纳 URL - ✅ 注释清晰:说明自动提取逻辑和用途
🎯 结论
建议操作:✅ Approve
理由:此 PR 功能实现完整,代码质量高,安全性良好。Quick 隧道地址提取正则安全、清理逻辑完善;端口层重绑筛选条件正确、错误处理到位。建议合并。
- quickURLRe 改为大小写不敏感并限定匹配到空白/行尾,避免误匹配 - stdout/stderr 监听 goroutine 增加退出 Debug 日志,便于排查生命周期
|
已按评审建议落实修改并推送(commit
关于 stopLine 错误处理策略(P2 讨论项):采纳您倾向的方案——旧线路停止失败不阻断新线路启动,保持当前实现(用户至少能走新线路)。 验证: Implemented the review suggestions and pushed (commit
Regarding the stopLine error strategy (P2 discussion): I kept the current behavior per your preference — failing to stop the old line does not block starting the new one. Verification: |
PIKACHUIM
left a comment
There was a problem hiding this comment.
🙏 感谢 @suantea 提交!
🤖 AI 自动审核声明:本评审报告由 AI 自动生成,当前使用 gpt-5.6-sol 模型进行分析。
🎯 结论
✅ Approve 建议 — 当前未发现阻塞性问题;按专家流程不代替维护者提交 Approve 状态
📖 概要
[Feature] 端口层切换重绑 + Quick 隧道入口 / Port-level switch rebinding + Quick tunnel entry (#10) · 作者 @suantea
目标:围绕 NetPanel 的功能、监控、安全或 UI 能力进行增量改动。
整体方案按后端模型/服务/路由与前端 API/页面分层组织,方向与项目现有结构基本一致。
🧭 整体改动方案
- 方案思路:沿用现有 Go 服务、Gin 路由、GORM 模型和 React/TypeScript 页面分层,新增能力通过已有 API 与配置模型接入。
- 改动组织:后端负责状态、探测、隧道或安全逻辑,前端负责配置和结果展示;stacked PR 之间存在共享基础改动,合并顺序需要维护者统一安排。
- 方案评估:整体路线可行,但需要重点确认错误处理、并发边界、API 签名兼容及前端编译完整性。
📊 变更统计与评分
- 改动文件:? 个(+? / -? 行)
- 评分:功能 ⭐⭐⭐⭐ | 最小改动 ⭐⭐⭐⭐ | 前向兼容 ⭐⭐⭐ | 方案设计 ⭐⭐⭐⭐
🚨 关键问题(按优先级分级)
- P2
backend/service/cftunnel/manager.go:增量复核:上一轮建议的 Quick URL 正则大小写兼容、边界匹配及监听 goroutine 退出日志已由新增提交落实;当前未发现新增阻塞问题。
📂 文件改动分析(意图 + 合理性)
| 文件 | 改动意图 | 合理性 | 问题 |
|---|---|---|---|
backend/main.go |
支撑本 PR 的后端/前端功能接入、配置或展示 | ✅ 方向与改动目标一致,需结合整体合并顺序验证 | ✅ 未发现直接问题 |
backend/model/models.go |
支撑本 PR 的后端/前端功能接入、配置或展示 | ✅ 方向与改动目标一致,需结合整体合并顺序验证 | ✅ 未发现直接问题 |
backend/service/caddy/manager.go |
支撑本 PR 的后端/前端功能接入、配置或展示 | ✅ 方向与改动目标一致,需结合整体合并顺序验证 | P2: 增量复核:上一轮建议的 Quick URL 正则大小写兼容、边界匹配及监听 goroutine 退出日志已由新增提交落实;当前未发现新增阻塞问题。 |
backend/service/cftunnel/manager.go |
支撑本 PR 的后端/前端功能接入、配置或展示 | ✅ 方向与改动目标一致,需结合整体合并顺序验证 | P2: 增量复核:上一轮建议的 Quick URL 正则大小写兼容、边界匹配及监听 goroutine 退出日志已由新增提交落实;当前未发现新增阻塞问题。 |
backend/service/dnsmasq/manager.go |
支撑本 PR 的后端/前端功能接入、配置或展示 | ✅ 方向与改动目标一致,需结合整体合并顺序验证 | P2: 增量复核:上一轮建议的 Quick URL 正则大小写兼容、边界匹配及监听 goroutine 退出日志已由新增提交落实;当前未发现新增阻塞问题。 |
backend/service/linereg/linereg.go |
支撑本 PR 的后端/前端功能接入、配置或展示 | ✅ 方向与改动目标一致,需结合整体合并顺序验证 | ✅ 未发现直接问题 |
backend/service/linereg/linereg_test.go |
支撑本 PR 的后端/前端功能接入、配置或展示 | ✅ 方向与改动目标一致,需结合整体合并顺序验证 | ✅ 未发现直接问题 |
backend/service/selector/README.md |
支撑本 PR 的后端/前端功能接入、配置或展示 | ✅ 方向与改动目标一致,需结合整体合并顺序验证 | ✅ 未发现直接问题 |
backend/service/tunservice/manager.go |
支撑本 PR 的后端/前端功能接入、配置或展示 | ✅ 方向与改动目标一致,需结合整体合并顺序验证 | P2: 增量复核:上一轮建议的 Quick URL 正则大小写兼容、边界匹配及监听 goroutine 退出日志已由新增提交落实;当前未发现新增阻塞问题。 |
webpage/src/i18n/locales/en.ts |
支撑本 PR 的后端/前端功能接入、配置或展示 | ✅ 方向与改动目标一致,需结合整体合并顺序验证 | ✅ 未发现直接问题 |
webpage/src/i18n/locales/zh.ts |
支撑本 PR 的后端/前端功能接入、配置或展示 | ✅ 方向与改动目标一致,需结合整体合并顺序验证 | ✅ 未发现直接问题 |
webpage/src/pages/CfTunnel.tsx |
支撑本 PR 的后端/前端功能接入、配置或展示 | ✅ 方向与改动目标一致,需结合整体合并顺序验证 | ✅ 未发现直接问题 |
✅ 待处理清单
- [P2]
backend/service/cftunnel/manager.go:确认并处理上述问题
🎯 结论:✅ Approve 建议 — 当前未发现阻塞性问题;按专家流程不代替维护者提交 Approve 状态。
…tunnel entry (PIKACHUIM#10) (PIKACHUIM#17) * feat(selector): 切换落地 — 选线结果自动同步 Caddy 反代目标与 DNS 解析(S2) - caddy manager 新增 UpdateUpstream:动态更新反代站点上游目标并热加载 (admin API,不落库、重启回退),仅支持 reverse_proxy 站点 - dnsmasq manager 新增 SetRecord:写入自定义解析记录(域名→IP), handleDNS 直接读库,写入即生效 - TunService 模型新增 CaddySiteID(绑定 Caddy 站点)与 Domain(对外域名) - linereg 注入 caddyUpdater/dnsUpdater 回调,refresh 选线后: · 域名层:选中线路入口地址热加载到绑定的 Caddy 站点 · DNS 层:线路入口为 IP 时,把服务域名指向该 IP (域名入口走 Caddy,不重复解析) - main.go 接线:SetCaddyUpdater(caddyMgr.UpdateUpstream) + SetDNSUpdater(dnsmasqMgr.SetRecord) - 测试:applyCaddySwitch(触发/未绑定不触发)、applyDNSSwitch(IP 入口 触发/域名入口跳过)、lineHost Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com> * fix(selector): 落实 PR PIKACHUIM#16 评审意见 — Caddy 回滚、上游/IP 校验、LastError 记录 针对上游 CHANGES_REQUESTED 的三类反馈: - Caddy 切换失败回滚:linereg 记录每个站点最近一次成功切换的上游目标, 更新失败时自动回滚,避免 Caddy 反代目标与选中线路不一致 - 输入校验:UpdateUpstream 增加 upstream 格式校验(validateUpstream), dnsmasq SetRecord 增加 IP 合法性校验(net.ParseIP) - 操作日志:Caddy/DNS 切换失败时写入 TunService.LastError,供 UI 排查 Caddy Admin API 状态码检查(>=400)原本已有,未重复改动。 验证:go build / go vet 通过;新增 TestApplyCaddySwitchRollback 回滚用例, linereg 包测试全部通过。 Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com> * docs(selector): 选线算法文档 + mermaid 流程图(承接 PIKACHUIM#3 讨论) 补充 selector 包的完整算法说明,落实 PIKACHUIM#3 讨论中「算法要写好文档、 最好以流程图保存」的承诺: - 线路模型(端口层/域名层、Address/ProbeURL 语义) - 测速指标与 effectiveLatency 排序规则(HTTP 出网优先、TCP 兜底) - 选线算法 mermaid 流程图 + 三条核心规则(锁线优先 / 最快可用 / 容差防抖),含失败阈值与最近成功兜底说明 - 后台守护建议、并发安全说明、现有测试覆盖清单 * feat(cftunnel): quick 隧道入口自动提取与前端展示(PIKACHUIM#10) - CftunnelConfig 新增 QuickURL 字段:从 cloudflared stdout 提取 trycloudflare.com 临时地址并落库,进程停止/退出时自动清理 - 前端表格新增 Quick 入口列(可点击直达),补全缺失的 cftunnel i18n 键 Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com> * feat(selector): 端口层切换落地 — 选线变化时自动重绑 TCP/UDP 穿透规则(PIKACHUIM#10) - linereg 新增 SetPortRebinder/applyPortSwitch:筛选未绑定 Caddy 站点、 未配置域名的纯端口层服务(caddy_site_id=0 且 domain 为空),选线变化时 触发重绑回调,失败写入 TunService.LastError - tunservice 新增 RebindPort:停掉服务下非选中线路的运行中客户端, 确保选中线路客户端运行(先停后起,短暂抖动可接受) - main.go 接线 SetPortRebinder(tunserviceMgr.RebindPort) - 新增 TestApplyPortSwitch:端口层/域名层/DNS 层筛选、未关联线路、失败落库 Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com> * fix(i18n): 修正 rebase 冲突解决 — 合并重复 cftunnel 键并闭合 monitor 块 - en/zh 的 cftunnel 键合并进上游已有块(补 quickUrl),删除文件尾重复块 - 补上 monitor 块缺失的闭合括号 * fix(cftunnel): Quick URL 正则健壮性增强 + 监听 goroutine 退出日志(落实 PIKACHUIM#17 评审) - quickURLRe 改为大小写不敏感并限定匹配到空白/行尾,避免误匹配 - stdout/stderr 监听 goroutine 增加退出 Debug 日志,便于排查生命周期 --------- Co-authored-by: Asuan <asuan@AsuandeMacBook-Air.local> Co-authored-by: AtomCode (deepseek-v4-flash) <noreply@atomgit.com> Co-authored-by: suantea <suantea@users.noreply.github.com>
- 新增pkg/metrics/包,提供统一的Prometheus注册表 - 新增穿透服务指标:活跃隧道数、连接数、延迟分布 - 新增证书指标:剩余天数 - 新增WAF指标:拦截计数 - 在/main路由中注册/prometheus handler(/metrics端点) - 无需认证,供Grafana/Prometheus采集 关联Issue: PIKACHUIM#17
- 新增pkg/metrics/包,提供统一的Prometheus注册表 - 新增穿透服务指标:活跃隧道数、连接数、延迟分布 - 新增证书指标:剩余天数 - 新增WAF指标:拦截计数 - 在/main路由中注册/prometheus handler(/metrics端点) - 无需认证,供Grafana/Prometheus采集 关联Issue: PIKACHUIM#17
概述 / Overview
本 PR 实现 issue #10 的两部分内容:
This PR implements both parts of issue #10:
#10a Quick 隧道入口 / Quick tunnel entry
CftunnelConfig新增QuickURL字段;启动 cloudflared 时用正则从 stdout 提取trycloudflare.com地址并落库,停止/退出时自动清理。前端表格新增 Quick 入口列(可点击直达),并补全了原本缺失的 cftunnel i18n 段(zh/en)。
Added
QuickURLfield toCftunnelConfig; on cloudflared start, thetrycloudflare.comURL is extracted from stdout via regex and persisted, and cleaned up on stop/exit.Added a clickable Quick entry column to the frontend table and filled in the previously missing cftunnel i18n section (zh/en).
#10b 端口层切换重绑 / Port-level switch rebinding
linereg 新增
SetPortRebinder/applyPortSwitch:筛选caddy_site_id=0且domain为空的纯 TCP/UDP 端口层服务,选线变化时触发重绑,失败写入LastError。tunservice 新增
RebindPort:停掉非选中线路的运行中客户端、确保选中线路客户端运行(先停后起,短暂抖动可接受)。main.go 接线;新增单元测试
TestApplyPortSwitch。Added
SetPortRebinder/applyPortSwitchin linereg: filters pure TCP/UDP port-level services (caddy_site_id=0and emptydomain), rebinds on line-switch, and records failures inLastError.Added
RebindPortin tunservice: stops clients running on non-selected lines and ensures the selected line's client is running (stop-then-start; brief downtime acceptable).Wired up in main.go; added unit test
TestApplyPortSwitch.变更 / Changes
feat(cftunnel): quick 隧道入口自动提取与前端展示(功能建议:切换落地扩展 — 端口层重绑 + quick 隧道入口展示 #10)feat(selector): 端口层切换落地 — 选线变化时自动重绑 TCP/UDP 穿透规则(功能建议:切换落地扩展 — 端口层重绑 + quick 隧道入口展示 #10)测试 / Testing
前端 dist 构建后,
go build ./.../go vet/go test ./...全部通过。With frontend dist built,
go build ./.../go vet/go test ./...all pass.依赖 / Dependencies
基于 PR feat(selector): 切换落地 — 选线结果自动同步 Caddy 反代目标与 DNS 解析(S2) #16(feat/switch-landing)分支叠放,请按依赖顺序合并:feat(selector): 切换落地 — 选线结果自动同步 Caddy 反代目标与 DNS 解析(S2) #16 → 功能建议:切换落地扩展 — 端口层重绑 + quick 隧道入口展示 #10。
Stacked on PR feat(selector): 切换落地 — 选线结果自动同步 Caddy 反代目标与 DNS 解析(S2) #16 (feat/switch-landing); please merge in dependency order: feat(selector): 切换落地 — 选线结果自动同步 Caddy 反代目标与 DNS 解析(S2) #16 → 功能建议:切换落地扩展 — 端口层重绑 + quick 隧道入口展示 #10.
关联 / Related