feat: 通道组——一次开放多台主机 / 多个端口,共用邀请与授权

- 批量创建:主机支持 IP、主机名、末段范围(10.0.1.11-13)和 主机:端口,
  与端口列表展开为 主机 × 端口,自动分配连续的访问端口,创建前可预览
- 授权以组为单位:一条邀请链接、一次审批即可访问组内全部地址;
  计时从首次连上任一地址开始,撤销一次全组断开;后加入组的通道自动获得组授权
- 网关授权状态按作用范围(通道 / 通道组)存储,通道的动态白名单统一由状态库重新计算,
  通道授权与组授权互不干扰;组内最后一个通道关闭或组被解散时,组的邀请与授权失效
- 闲置联动:组内任一地址在用时整组都不算闲置;支持整组开关
- 已有通道可并入组或移出组;组可改名、追加目标、解散(保留或删除成员)
- App 与网页面板:组卡片、新建 / 编辑通道组、组邀请与“连接与授权”,授权页列出全部地址
- 测试:网关 9 个、Agent 5 个通道组用例
This commit is contained in:
DeepFal committed 2026-10-13 20:16:43 +08:00
1 parent 86e159b3f0
commit 3797bef88a
22 files changed
+3894 -471

No files matched your search

+10 -5
View File
@@ -326,6 +326,14 @@ func TestStaleSessionTeardownKeepsNewSessionTunnels(t *testing.T) {
}
}
// grantTestLease stores a running lease in the access state and applies it to the scope's tunnels
func grantTestLease(m *Manager, scope, ip string, exp time.Time) {
m.state.mu.Lock()
m.state.scopeLocked(scope).Leases[ip] = leaseInfo{ExpiresAt: exp}
m.state.mu.Unlock()
m.syncScope(scope)
}
func TestUpdateKeepsConnectionsAndLeases(t *testing.T) {
m := newTestManager()
addr := startServer(t, m)
@@ -339,7 +347,7 @@ func TestUpdateKeepsConnectionsAndLeases(t *testing.T) {
m.mu.RLock()
tun := m.tunnels["a"]
m.mu.RUnlock()
tun.AddDynamicWhitelist("10.1.2.3", time.Now().Add(time.Hour))
grantTestLease(m, "a", "10.1.2.3", time.Now().Add(time.Hour))
cfg.Name = "renamed"
cfg.Whitelist = []string{"127.0.0.1"}
@@ -623,10 +631,7 @@ func TestExpiredLeaseDisconnectsActiveConnection(t *testing.T) {
cfg.Whitelist = nil
a.register(t, protocol.TypeRegisterTunnel, cfg)
m.mu.RLock()
tun := m.tunnels["a"]
m.mu.RUnlock()
tun.AddDynamicWhitelist("127.0.0.1", time.Now().Add(1500*time.Millisecond))
grantTestLease(m, "a", "127.0.0.1", time.Now().Add(1500*time.Millisecond))
c, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", cfg.ListenPort))
if err != nil {