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

@ -184,15 +184,45 @@ func (h *Handler) applyCanvas(w http.ResponseWriter, r *http.Request, canvas *ca
// extended so a per-forward stop made on the forwards page (disabled=true)
// is not re-activated by a later canvas save. Local-only forwards are NOT
// submitted — they stay on this node.
localByName := map[string]store.Local{}
localByName := make(map[string]store.Local, len(canvas.Locals))
for _, l := range canvas.Locals {
localByName[l.Name] = l
}
remoteByName := map[string]store.Remote{}
remoteByName := make(map[string]store.Remote, len(canvas.Remotes))
for _, r := range canvas.Remotes {
remoteByName[r.Name] = r
}
// Build a set of (local, remote, remotePort) triples from the incoming
// canvas links so we can revoke any stale topology entries no longer
// present in the canvas (e.g. links that were deleted from the UI).
type triple struct{ local, remote string; port int }
canvasTriples := make(map[triple]bool, len(canvas.Links))
for _, ln := range canvas.Links {
canvasTriples[triple{ln.Local, ln.Remote, ln.RemotePort}] = true
}
if h.Ring != nil {
// Revoke topology entries that the canvas no longer references.
// Only revoke entries whose local name is known to this node (we only
// own tasks for locals we created). The topology is a ring-wide view
// and includes forwards owned by other nodes.
snap := h.Ring.Snapshot()
for _, t := range snap.Topology {
if _, ok := localByName[t.Local.Name]; !ok {
continue // local not in this node's canvas — skip
}
if t.Local.LocalOnly {
continue
}
if canvasTriples[triple{t.Local.Name, t.Remote.Name, t.Link.RemotePort}] {
continue // still in canvas — keep
}
// This forward is in the ring topology but NOT in the new canvas.
// Submit a REVOKE so the owning node cleans it up.
h.Ring.RevokeTask(t.Local, t.Remote, t.Link)
}
for _, ln := range canvas.Links {
loc, ok := localByName[ln.Local]
if !ok || loc.LocalOnly {