fix: 完善同事未完成的修改 — LAN 地址检测/画布删除撤销/连线删除按钮/Del 键/注释修正

- cmd/webui4frpc/main.go: 新增 primaryLANIPv4() 检测内网 LAN 地址,
  解决三台内网主机间集群发现失败 (reachableAddr 只回退到 hostname)
- internal/httpapi/handlers.go: applyCanvas 新增拓扑差异检测,
  画布删除连线时自动 RevokeTask 避免集群残留
- web/src/api.ts: 新增 addLink / deleteLink API 封装
- web/src/components/PortEdge.vue: × 删除按钮移到端口标签右上角 (绝对定位)
- web/src/views/CanvasView.vue: 启用 Del/Backspace 键删除选中连线
  (确认对话框 + 走 onDeleteEdge 逻辑), 修正注释
- embed dist 同步
This commit is contained in:
JianFeeeee
2026-08-24 01:03:14 +08:00
parent b39bd427fa
commit 2292ee7f3a
10 changed files with 258 additions and 84 deletions

View File

@ -568,8 +568,9 @@ func retryRejoinCached(ctx context.Context, ring *cluster.Engine, peersJSON stri
}
// reachableAddr returns an address peers can dial back: an explicit
// W4F_HOST (compose DNS name) wins; otherwise a non-wildcard listen addr; a
// wildcard falls back to the hostname (routable inside docker networks).
// W4F_HOST (compose DNS name) wins; otherwise a non-wildcard listen addr;
// a wildcard falls back to the primary non-loopback IPv4 detected from the
// interfaces (routable on plain LAN hosts); hostname is the last resort.
func reachableAddr(addr string) string {
if h := os.Getenv("W4F_HOST"); h != "" {
if _, port, err := net.SplitHostPort(addr); err == nil {
@ -582,6 +583,9 @@ func reachableAddr(addr string) string {
return addr
}
if host == "" || host == "0.0.0.0" || host == "::" || host == "[::]" {
if ip := primaryLANIPv4(); ip != "" {
return net.JoinHostPort(ip, port)
}
if h, herr := os.Hostname(); herr == nil && h != "" {
return net.JoinHostPort(h, port)
}
@ -589,6 +593,31 @@ func reachableAddr(addr string) string {
return addr
}
// primaryLANIPv4 returns the first global-scope non-loopback IPv4 address of
// any up interface, preferring RFC1918 ranges. Empty string when none found.
func primaryLANIPv4() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return ""
}
var fallback string
for _, a := range addrs {
ipnet, ok := a.(*net.IPNet)
if !ok || ipnet.IP.To4() == nil || ipnet.IP.IsLoopback() || !ipnet.IP.IsGlobalUnicast() {
continue
}
ip := ipnet.IP.To4()
if fallback == "" {
fallback = ip.String()
}
// prefer private ranges over link-local / unusual globals
if ip[0] == 10 || (ip[0] == 172 && ip[1]&0xf0 == 16) || (ip[0] == 192 && ip[1] == 168) {
return ip.String()
}
}
return fallback
}
// splitCSV splits a comma-separated list into a slice (empty -> nil).
func splitCSV(s string) []string {
if strings.TrimSpace(s) == "" {