From e209fc689acd7f1c4fe5c8adb7342bce729fb191 Mon Sep 17 00:00:00 2001 From: Antigravity Date: Wed, 18 Feb 2026 02:03:19 +0000 Subject: [PATCH] fix(tunnel): respect IPv6 preference in tunnel creation and diagnostics --- .sisyphus/ralph-loop.local.md | 9 ---- .../internal/http/handler/control_plane.go | 29 +++++++----- go-backend/internal/http/handler/mutations.go | 46 +++++++++++-------- 3 files changed, 45 insertions(+), 39 deletions(-) delete mode 100644 .sisyphus/ralph-loop.local.md diff --git a/.sisyphus/ralph-loop.local.md b/.sisyphus/ralph-loop.local.md deleted file mode 100644 index af00a2a..0000000 --- a/.sisyphus/ralph-loop.local.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -active: true -iteration: 1 -max_iterations: 100 -completion_promise: "DONE" -started_at: "2026-02-15T16:38:58.212Z" -session_id: "ses_39dd49703ffeveg711aA1D1YAk" ---- -现状后端数据库兼容sqlite和postgresql,每一次新增功能需要维护两套数据库sql,需求是使用一个数据库驱动能同时兼容两个数据库,请仔细分析,列出计划,全量迁移,并且写好所有的测试,确保重构后所有的功能都能正常运行,由于工程量大,请写一个计划列表的markdown记录,每次完成一个就记录一下进度 diff --git a/go-backend/internal/http/handler/control_plane.go b/go-backend/internal/http/handler/control_plane.go index 0c0e3f6..4a4beae 100644 --- a/go-backend/internal/http/handler/control_plane.go +++ b/go-backend/internal/http/handler/control_plane.go @@ -383,7 +383,7 @@ func (h *Handler) diagnoseForwardRuntime(forward *forwardRecord) (map[string]int "fromChainType": 1, "toChainType": 2, "toInx": firstNode.Inx, - }) + }, "") } } else { for _, outNode := range outNodes { @@ -391,7 +391,7 @@ func (h *Handler) diagnoseForwardRuntime(forward *forwardRecord) (map[string]int h.appendChainHopDiagnosis(&results, nodeCache, inNode.NodeID, outNode, description, map[string]interface{}{ "fromChainType": 1, "toChainType": 3, - }) + }, "") } } } @@ -406,7 +406,7 @@ func (h *Handler) diagnoseForwardRuntime(forward *forwardRecord) (map[string]int "fromInx": currentNode.Inx, "toChainType": 2, "toInx": nextNode.Inx, - }) + }, "") } } else { for _, outNode := range outNodes { @@ -415,7 +415,7 @@ func (h *Handler) diagnoseForwardRuntime(forward *forwardRecord) (map[string]int "fromChainType": 2, "fromInx": currentNode.Inx, "toChainType": 3, - }) + }, "") } } } @@ -470,6 +470,7 @@ func (h *Handler) diagnoseTunnelRuntime(tunnelID int64) (map[string]interface{}, return nil, errors.New("隧道配置不完整") } + ipPreference := h.repo.GetTunnelIPPreference(tunnelID) inNodes, chainHops, outNodes := splitChainNodeGroups(chainRows) results := make([]map[string]interface{}, 0, len(chainRows)*2) nodeCache := map[int64]*nodeRecord{} @@ -491,7 +492,7 @@ func (h *Handler) diagnoseTunnelRuntime(tunnelID int64) (map[string]interface{}, "fromChainType": 1, "toChainType": 2, "toInx": firstNode.Inx, - }) + }, ipPreference) } } else { for _, outNode := range outNodes { @@ -499,7 +500,7 @@ func (h *Handler) diagnoseTunnelRuntime(tunnelID int64) (map[string]interface{}, h.appendChainHopDiagnosis(&results, nodeCache, inNode.NodeID, outNode, description, map[string]interface{}{ "fromChainType": 1, "toChainType": 3, - }) + }, ipPreference) } } } @@ -514,7 +515,7 @@ func (h *Handler) diagnoseTunnelRuntime(tunnelID int64) (map[string]interface{}, "fromInx": currentNode.Inx, "toChainType": 2, "toInx": nextNode.Inx, - }) + }, ipPreference) } } else { for _, outNode := range outNodes { @@ -523,7 +524,7 @@ func (h *Handler) diagnoseTunnelRuntime(tunnelID int64) (map[string]interface{}, "fromChainType": 2, "fromInx": currentNode.Inx, "toChainType": 3, - }) + }, ipPreference) } } } @@ -693,13 +694,14 @@ func (h *Handler) appendPathDiagnosis(results *[]map[string]interface{}, nodeCac *results = append(*results, item) } -func (h *Handler) appendChainHopDiagnosis(results *[]map[string]interface{}, nodeCache map[int64]*nodeRecord, fromNodeID int64, toNode chainNodeRecord, description string, metadata map[string]interface{}) { +func (h *Handler) appendChainHopDiagnosis(results *[]map[string]interface{}, nodeCache map[int64]*nodeRecord, fromNodeID int64, toNode chainNodeRecord, description string, metadata map[string]interface{}, ipPreference string) { + fromNode, _ := h.cachedNode(nodeCache, fromNodeID) targetNode, err := h.cachedNode(nodeCache, toNode.NodeID) if err != nil { h.appendFailedDiagnosis(results, nodeCache, fromNodeID, "", 0, description, metadata, err.Error()) return } - targetIP, targetPort, err := resolveChainProbeTarget(targetNode, toNode.Port) + targetIP, targetPort, err := resolveChainProbeTarget(fromNode, targetNode, toNode.Port, ipPreference) if err != nil { h.appendFailedDiagnosis(results, nodeCache, fromNodeID, strings.Trim(strings.TrimSpace(targetNode.ServerIP), "[]"), toNode.Port, description, metadata, err.Error()) return @@ -707,11 +709,14 @@ func (h *Handler) appendChainHopDiagnosis(results *[]map[string]interface{}, nod h.appendPathDiagnosis(results, nodeCache, fromNodeID, targetIP, targetPort, description, metadata) } -func resolveChainProbeTarget(targetNode *nodeRecord, preferredPort int) (string, int, error) { +func resolveChainProbeTarget(fromNode, targetNode *nodeRecord, preferredPort int, ipPreference string) (string, int, error) { if targetNode == nil { return "", 0, errors.New("目标节点不存在") } - host := strings.Trim(strings.TrimSpace(targetNode.ServerIP), "[]") + host, err := selectTunnelDialHost(fromNode, targetNode, ipPreference) + if err != nil { + host = strings.Trim(strings.TrimSpace(targetNode.ServerIP), "[]") + } if host == "" { return "", 0, errors.New("目标节点地址为空") } diff --git a/go-backend/internal/http/handler/mutations.go b/go-backend/internal/http/handler/mutations.go index cd25093..2bd332f 100644 --- a/go-backend/internal/http/handler/mutations.go +++ b/go-backend/internal/http/handler/mutations.go @@ -437,7 +437,7 @@ func (h *Handler) tunnelCreate(w http.ResponseWriter, r *http.Request) { } runtimeState.IPPreference = ipPreference if strings.TrimSpace(inIP) == "" { - inIP = buildTunnelInIP(runtimeState.InNodes, runtimeState.Nodes) + inIP = buildTunnelInIP(runtimeState.InNodes, runtimeState.Nodes, ipPreference) } if len(runtimeState.InNodes) > 0 { @@ -634,7 +634,7 @@ func (h *Handler) tunnelUpdate(w http.ResponseWriter, r *http.Request) { runtimeState.TunnelID = id runtimeState.IPPreference = ipPreference - inIp := buildTunnelInIP(runtimeState.InNodes, runtimeState.Nodes) + inIp := buildTunnelInIP(runtimeState.InNodes, runtimeState.Nodes, ipPreference) var federationBindings []repo.FederationTunnelBinding var federationReleaseRefs []federationRuntimeReleaseRef @@ -1995,32 +1995,42 @@ func (h *Handler) prepareTunnelCreateState(tx *gorm.DB, req map[string]interface return state, nil } -func buildTunnelInIP(inNodes []tunnelRuntimeNode, nodes map[int64]*nodeRecord) string { +func buildTunnelInIP(inNodes []tunnelRuntimeNode, nodes map[int64]*nodeRecord, ipPreference string) string { set := make(map[string]struct{}) ordered := make([]string, 0) + preferV6 := strings.TrimSpace(ipPreference) == "v6" for _, inNode := range inNodes { node := nodes[inNode.NodeID] if node == nil { continue } - if v := strings.TrimSpace(node.ServerIPv4); v != "" { - if _, ok := set[v]; !ok { - set[v] = struct{}{} - ordered = append(ordered, v) + v4 := strings.TrimSpace(node.ServerIPv4) + v6 := strings.TrimSpace(node.ServerIPv6) + var addrs []string + if preferV6 { + if v6 != "" { + addrs = append(addrs, v6) + } + if v4 != "" { + addrs = append(addrs, v4) + } + } else { + if v4 != "" { + addrs = append(addrs, v4) + } + if v6 != "" { + addrs = append(addrs, v6) } } - if v := strings.TrimSpace(node.ServerIPv6); v != "" { - if _, ok := set[v]; !ok { - set[v] = struct{}{} - ordered = append(ordered, v) - } - } - if strings.TrimSpace(node.ServerIPv4) == "" && strings.TrimSpace(node.ServerIPv6) == "" { + if len(addrs) == 0 { if v := strings.TrimSpace(node.ServerIP); v != "" { - if _, ok := set[v]; !ok { - set[v] = struct{}{} - ordered = append(ordered, v) - } + addrs = append(addrs, v) + } + } + for _, a := range addrs { + if _, ok := set[a]; !ok { + set[a] = struct{}{} + ordered = append(ordered, a) } } }