v1.0.1: 多拓扑管理、Web SSH终端、扫描进度修复、拓扑连线优化

- 修复扫描进度条不动的问题(分4阶段更新进度)
- 新增Web SSH远程终端(xterm.js + WebSocket)
- 新增多拓扑管理(创建/切换拓扑、全局设备池)
- 简化新建拓扑流程(仅需名称,创建后选择设备)
- 修复拓扑Builder设备去重(按IP去重)
- 修复启动时拓扑设备不加载到Builder的问题
- 优化MAC前缀匹配(避免歧义前缀导致错误连线)
- 拓扑连线改为无向(去除箭头)
- 设备详情面板加宽到600px
This commit is contained in:
Your Name
2026-04-26 13:25:19 +08:00
parent 6e1b010c17
commit 44f7fef1f8
17 changed files with 1940 additions and 54 deletions
+29 -14
View File
@@ -16,8 +16,14 @@ func NewBuilder() *Builder {
return &Builder{}
}
// AddDevice 添加设备
// AddDevice 添加设备(按IP去重)
func (b *Builder) AddDevice(device models.Device) {
for i, existing := range b.devices {
if existing.IP == device.IP {
b.devices[i] = device // 覆盖更新
return
}
}
b.devices = append(b.devices, device)
}
@@ -141,18 +147,25 @@ func (b *Builder) Build() models.TopologyGraph {
}
}
// 策略3b: 通过MAC前缀匹配(新增
// 当精确MAC匹配失败时,尝试通过MAC前缀匹配(适用于同一设备的多个接口)
// 策略3b: 通过MAC前缀匹配(改进:排除歧义前缀
// 当精确MAC匹配失败时,尝试通过MAC前缀匹配
// 但如果同一前缀匹配到多台设备,则跳过(避免错误连接)
if targetIP == "" && neighbor.RemoteMAC != "" {
neighborMACPrefix := getMACPrefix(neighbor.RemoteMAC)
fmt.Printf(" Trying MAC prefix match: %s (prefix: %s)\n", neighbor.RemoteMAC, neighborMACPrefix)
// 先统计有多少台设备匹配此MAC前缀
type prefixMatch struct {
ip string
matchingMACs int
}
var matches []prefixMatch
for _, d := range b.devices {
if d.IP == device.IP {
continue // 跳过自己
}
// 检查该设备的MAC地址是否有相同前缀
matchingMACs := 0
for _, mac := range d.MACAddresses {
if getMACPrefix(mac) == neighborMACPrefix {
@@ -160,18 +173,20 @@ func (b *Builder) Build() models.TopologyGraph {
}
}
// 如果该设备有多个MAC地址使用相同前缀,说明是同一台设备
if matchingMACs >= 3 { // 至少3个MAC使用相同前缀
// 进一步验证:检查是否在同一网段
if getSubnet(d.IP) == getSubnet(device.IP) {
targetIP = d.IP
matchMethod = fmt.Sprintf("MAC-prefix(%s)", neighborMACPrefix)
fmt.Printf(" ✓ Matched by MAC prefix: %s (device has %d MACs with prefix %s, same subnet) -> %s\n",
neighbor.RemoteMAC, matchingMACs, neighborMACPrefix, d.IP)
break
}
if matchingMACs >= 3 {
matches = append(matches, prefixMatch{ip: d.IP, matchingMACs: matchingMACs})
}
}
// 只在唯一匹配时使用前缀匹配
if len(matches) == 1 && getSubnet(matches[0].ip) == getSubnet(device.IP) {
targetIP = matches[0].ip
matchMethod = fmt.Sprintf("MAC-prefix(%s)", neighborMACPrefix)
fmt.Printf(" ✓ Matched by MAC prefix: %s (device has %d MACs with prefix %s, same subnet) -> %s\n",
neighbor.RemoteMAC, matches[0].matchingMACs, neighborMACPrefix, targetIP)
} else if len(matches) > 1 {
fmt.Printf(" ✗ Skipping MAC prefix match: %d devices share prefix %s (ambiguous)\n", len(matches), neighborMACPrefix)
}
}
// 策略4: 通过本地接口IP网段匹配(新增)