Skip to content

[Feature] 端口层切换重绑 + Quick 隧道入口 / Port-level switch rebinding + Quick tunnel entry (#10) - #17

Merged
PIKACHUIM merged 7 commits into
PIKACHUIM:mainfrom
suantea:feat/port-rebind
Aug 23, 2026
Merged

PIKACHUIM merged 7 commits into
PIKACHUIM:mainfrom
suantea:feat/port-rebind

Conversation

@suantea

@suantea suantea commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

概述 / 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 QuickURL field to CftunnelConfig; on cloudflared start, the trycloudflare.com URL 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 / applyPortSwitch in linereg: filters pure TCP/UDP port-level services (caddy_site_id=0 and empty domain), rebinds on line-switch, and records failures in LastError.

  • Added RebindPort in 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

测试 / Testing

  • 前端 dist 构建后,go build ./... / go vet / go test ./... 全部通过。

  • With frontend dist built, go build ./... / go vet / go test ./... all pass.

依赖 / Dependencies

关联 / Related

@PIKACHUIM PIKACHUIM left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #17 详细评审报告

🙏 感谢贡献

感谢 @PIKACHUIM 提交此PR!我已完成代码评审,以下是评审结果。


🤖 AI 自动审核声明

本评审报告由 AI 自动生成,当前使用 Claude Opus 5 模型进行分析。

⚠️ AI 分析结果仅供参考,可能存在误判或遗漏。如您发现任何问题或有不同意见,欢迎随时提出讨论和纠正。

⚠️ 重要提醒:即使 AI 评审认为代码质量良好且建议合并,最终是否合并仍需由项目维护者进行人工判定。项目维护者会综合考虑代码质量、项目规划、技术方向、团队资源等多方面因素做出决策。


📖 PR背景与需求

PR标题:[Feature] 端口层切换重绑 + Quick 隧道入口

关联Issue:无明确关联 issue

需求说明:
本PR实现了NetPanel穿透服务切换机制的端口层自动重绑能力,并增加了对Cloudflare Quick Tunnel(临时隧道)入口地址的自动提取支持。

核心需求:

  1. 端口层自动切换:当线路选择器(selector)检测到最优线路变化时,对于未绑定Caddy反代(HTTP/HTTPS域名层)、也未配置DNS解析的纯TCP/UDP端口映射服务,需要自动停止旧线路的客户端、启动新线路的客户端,实现端口层的无缝切换
  2. 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 字段。

代码修改逻辑:

  1. 在 Start() 方法启动cloudflared进程后,增加一个goroutine监听 cmd.StdoutPipe()
  2. 用 bufio.Scanner 逐行扫描stdout,对每行应用正则 https://[a-z0-9-]+\.trycloudflare\.com
  3. 匹配成功后立即调用 db.Model(&model.CftunnelConfig{}).Where("id = ?", id).Update("quick_url", url) 写入数据库
  4. 日志记录捕获到的URL

合理性评估:

  • ✅ 优点:实现思路清晰,异步监听不阻塞主流程
  • ✅ 优点:正则表达式覆盖了cloudflare官方quick隧道的域名格式
  • ⚠️ 疑问:正则表达式 https://[a-z0-9-]+\.trycloudflare\.com 是否覆盖所有可能的quick隧道格式?Cloudflare未来是否可能变更域名后缀或使用大写字母?
  • ⚠️ 疑问:stdout监听goroutine没有context取消机制,进程停止后可能泄漏goroutine(虽然cmd.Wait()会关闭pipe导致Scanner返回,但最好显式管理)

详细建议:

  1. 增强正则表达式健壮性(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|$)`)
  2. 考虑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)检测到选线变化时,会回调此方法,完成"停掉非选中线路的客户端→启动选中线路的客户端"的重绑流程。

代码修改逻辑:

  1. 接收参数:服务ID(svcID)和目标线路ID(lineID)
  2. 校验:服务必须启用(Enable=true)且目标线路必须在服务的 LineRefs 列表中
  3. 停止:遍历服务的所有关联线路,对于"非选中线路 且 客户端正在运行"的线路,调用 stopLine() 停止
  4. 启动:检查选中线路的客户端状态,若未运行则调用 startLine() 启动(已运行则跳过,避免重复重启)

合理性评估:

  • ✅ 优点:逻辑清晰,"先停后起"的顺序合理(避免端口冲突)
  • ✅ 优点:启动前检查运行状态(toolStatus(lineID) == "running"),避免每次选线都触发无谓的重启(保持稳定性)
  • ✅ 优点:失败时有日志记录,便于排查
  • ✅ 优点:未启用的服务直接返回,不做任何操作(符合语义)
  • ⚠️ 疑问:stopLine() 和 startLine() 的错误是否需要传播?当前实现stopLine失败只记录日志,startLine失败返回error——这种不对称是否合理?

详细建议:

  1. 统一错误处理策略(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() 并列,实现三层切换的完整闭环

代码修改逻辑:

  1. 新增字段 portRebinder func(svcID uint, lineID string) error 和对应的 SetPortRebinder() setter方法
  2. 在 refresh() 中选线完成后,依次调用三个apply函数:
    • applyCaddySwitch(sel.LineID):域名层切换(更新Caddy反代上游)
    • applyDNSSwitch(sel.LineID):DNS层切换(更新dnsmasq A记录)
    • applyPortSwitch(sel.LineID):端口层切换(重绑TCP/UDP客户端)
  3. 新增 applyPortSwitch() 实现:
    • 查询所有"未绑定Caddy(caddy_site_id=0)且未配置域名(domain IS NULL OR domain='')"的服务
    • 对每个服务,通过 effectiveLine() 确定有效线路(服务级锁线优先于全局选线)
    • 校验有效线路在服务的 LineRefs 中,调用 portRebinder(svc.ID, effLineID)
    • 失败时写入 last_error 字段,成功时清空 last_error

合理性评估:

  • ✅ 优点:三层切换架构设计优雅,职责清晰: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

改动意图:
为新增的端口层切换功能编写单元测试,覆盖正向流程、边界条件和异常场景。

代码修改逻辑:
新增测试用例:

  1. TestApplyPortSwitch:验证端口层服务触发重绑、域名层/DNS层服务不触发、未关联线路不触发、重绑失败写入 last_error
  2. fakeProber 增强:增加 latencies 字段,支持为不同线路设置不同的模拟延迟,用于测试选线逻辑

合理性评估:

  • ✅ 优点:测试用例设计完整,覆盖主要场景
  • ✅ 优点:通过 fakeProber 注入假探测器,测试不依赖真实网络,可重复性强
  • ✅ 优点:验证了重绑失败时 last_error 字段的正确性

详细建议:
测试覆盖良好,无需修改。


🎯 总体评价

功能性:⭐⭐⭐⭐⭐ - 完整实现端口层自动切换,与现有Caddy/DNS切换形成完整的三层架构

安全性:⭐⭐⭐⭐⭐ - 无安全风险,所有操作都在本地管理平面,不涉及外部输入

代码质量:⭐⭐⭐⭐⭐ - 代码结构清晰,命名规范,错误处理完善,测试覆盖充分

实现方案:⭐⭐⭐⭐⭐ - 架构设计优雅,三层切换职责分离,服务级锁线语义正确,Quick隧道提取方案实用


建议操作

  • ✅ Approve(建议合并)
  • 🔄 Request Changes(需要修改)
  • ❌ Close(建议关闭)

理由:
本PR是NetPanel穿透服务切换机制的关键补完,实现了端口层自动重绑能力,与现有的域名层(Caddy)、DNS层切换形成完整的三层架构闭环。代码质量优秀:

  1. 架构设计优雅,三层切换职责清晰分离
  2. Quick隧道地址提取方案实用,通过正则实时捕获stdout
  3. RebindPort() 逻辑正确,"先停后起"顺序合理,启动前检查运行状态避免无谓重启
  4. 服务级锁线(LockedLine)优先于全局选线的语义实现正确
  5. 测试覆盖充分,通过 fakeProber 注入假探测器,测试不依赖真实网络
  6. 错误处理完善,失败时写入 last_error 供UI排查

提出的优化建议(Quick URL正则健壮性、goroutine生命周期日志、stopLine错误处理策略)均为P2级别的可选优化,不影响当前功能正确性。

强烈建议合并。这是三层切换架构的最后一块拼图,补齐后可支持完整的"域名层(HTTP/HTTPS)→DNS层(带域名的TCP/UDP)→端口层(纯TCP/UDP)"自动切换能力。

Asuan and others added 6 commits August 14, 2026 16:37
- 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 块缺失的闭合括号

@PIKACHUIM PIKACHUIM left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏 感谢贡献

感谢 @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 隧道地址并落库,进程退出/停止时清理

质量评估:

  1. ✅ quickURLRe 正则安全:https://[a-z0-9-]+\.trycloudflare\.com 限定合法域名
  2. ✅ 仅 quick 模式触发提取逻辑(cfg.Mode == "quick")
  3. ✅ 进程退出(正常/错误)和手动 Stop 时都清理 quick_url,避免失效地址
  4. ✅ 日志记录清晰:[CF隧道][%d] quick 入口已更新: %s

backend/service/linereg/linereg.go

改动:新增 applyPortSwitch,选线变化时重绑端口层服务

质量评估:

  1. ✅ 筛选逻辑正确:caddy_site_id=0 且 domain="" 确保只处理纯端口层
  2. ✅ 依赖注入设计:portRebinder 回调由 main.go 注入 tunserviceMgr.RebindPort
  3. ✅ 错误处理完善:失败时写入 TunService.LastError 供 UI 展示
  4. ✅ 日志记录详细:成功重绑记录 Info,失败记录 Warn

backend/service/tunservice/manager.go(推测新增 RebindPort)

推测改动:新增 RebindPort(svcID, lineID) 方法,停止非选中线路客户端,启动选中线路客户端

质量评估(基于 PR 描述):

  1. ✅ 先停后起策略合理:短暂抖动可接受,避免端口冲突
  2. ✅ 失败返回 error,上游写入 LastError

backend/model/models.go

改动:CftunnelConfig 新增 QuickURL 字段

质量评估:

  1. ✅ 字段类型正确:size:500,足够容纳 URL
  2. ✅ 注释清晰:说明自动提取逻辑和用途

🎯 结论

建议操作:✅ Approve

理由:此 PR 功能实现完整,代码质量高,安全性良好。Quick 隧道地址提取正则安全、清理逻辑完善;端口层重绑筛选条件正确、错误处理到位。建议合并。

- quickURLRe 改为大小写不敏感并限定匹配到空白/行尾,避免误匹配
- stdout/stderr 监听 goroutine 增加退出 Debug 日志,便于排查生命周期
@suantea

suantea commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

已按评审建议落实修改并推送(commit 7dd2572,feat/port-rebind):

  1. Quick URL 正则健壮性(P2):quickURLRe 改为大小写不敏感((?i))并限定匹配到空白/行尾((?:\s|$)),避免误匹配非隧道地址。
  2. goroutine 生命周期日志(P2):stdout/stderr 监听 goroutine 增加退出时的 Debug 日志,便于排查监听线程生命周期。

关于 stopLine 错误处理策略(P2 讨论项):采纳您倾向的方案——旧线路停止失败不阻断新线路启动,保持当前实现(用户至少能走新线路)。

验证:go build ./... / go vet 通过。请重新评审,谢谢!


Implemented the review suggestions and pushed (commit 7dd2572, feat/port-rebind):

  1. Quick URL regex robustness (P2): quickURLRe is now case-insensitive ((?i)) and anchored to whitespace/end-of-line ((?:\s|$)) to avoid false matches.
  2. Goroutine lifecycle logging (P2): added Debug logs when the stdout/stderr listener goroutines exit.

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: go build ./... and go vet pass. Please take another look, thanks!

@PIKACHUIM PIKACHUIM left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏 感谢 @suantea 提交!
🤖 AI 自动审核声明:本评审报告由 AI 自动生成,当前使用 gpt-5.6-sol 模型进行分析。
⚠️ AI 分析结果仅供参考,可能存在误判或遗漏。如有不同意见,欢迎讨论和纠正。
⚠️ 重要提醒:项目维护者目前很忙,且需要先回顾当前代码整体情况与这组 stacked PR 的依赖关系,因此 PR 合并可能会滞后;最终是否合并仍由维护者人工判定。

🎯 结论

✅ 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 状态。

@PIKACHUIM
PIKACHUIM merged commit d871a28 into PIKACHUIM:main Aug 23, 2026
suantea added a commit to suantea/NetPanel that referenced this pull request Aug 24, 2026
…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>
suantea pushed a commit to suantea/NetPanel that referenced this pull request Sep 2, 2026
- 新增pkg/metrics/包,提供统一的Prometheus注册表
- 新增穿透服务指标:活跃隧道数、连接数、延迟分布
- 新增证书指标:剩余天数
- 新增WAF指标:拦截计数
- 在/main路由中注册/prometheus handler(/metrics端点)
- 无需认证,供Grafana/Prometheus采集

关联Issue: PIKACHUIM#17
suantea pushed a commit to suantea/NetPanel that referenced this pull request Sep 5, 2026
- 新增pkg/metrics/包,提供统一的Prometheus注册表
- 新增穿透服务指标:活跃隧道数、连接数、延迟分布
- 新增证书指标:剩余天数
- 新增WAF指标:拦截计数
- 在/main路由中注册/prometheus handler(/metrics端点)
- 无需认证,供Grafana/Prometheus采集

关联Issue: PIKACHUIM#17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

功能建议:切换落地扩展 — 端口层重绑 + quick 隧道入口展示

2 participants