mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-09-20 00:47:57 +00:00
fix: status + canvas pages now merge ring topology — all nodes see full cluster picture
Root cause: handleStatus built forwards only from local SQLite links table; handleCanvasGet returned only local store data. Neither merged the ring topology (which IS synced to every node in-memory). Result: non-claiming nodes saw only their own claimed forwards, not the full cluster. Fix: read-time merge in both handlers — iterate ring topology entries and add any (local, remote, link) not already in the local store. No store writes (avoids the per-forward stop/start issue that broke the old topology-derived-canvas model). Verified: all 4 cluster nodes now show 5 forwards / 4 remotes / 5 links; isolated node-e correctly shows empty.
This commit is contained in:
@ -21,6 +21,43 @@ func (h *Handler) handleCanvasGet(w http.ResponseWriter, _ *http.Request) {
|
|||||||
locals, _ := h.Store.ListLocals()
|
locals, _ := h.Store.ListLocals()
|
||||||
remotes, _ := h.Store.ListRemotes()
|
remotes, _ := h.Store.ListRemotes()
|
||||||
links, _ := h.Store.ListLinks()
|
links, _ := h.Store.ListLinks()
|
||||||
|
|
||||||
|
// Merge ring topology entries not in the local store so every node's
|
||||||
|
// canvas shows the FULL cluster picture. This is a read-time merge —
|
||||||
|
// nothing is written back to SQLite (avoids the per-forward stop/start
|
||||||
|
// issue that broke the old topology-derived-canvas model).
|
||||||
|
if h.Ring != nil {
|
||||||
|
snap := h.Ring.Snapshot()
|
||||||
|
localNames := make(map[string]bool, len(locals))
|
||||||
|
for _, l := range locals {
|
||||||
|
localNames[l.Name] = true
|
||||||
|
}
|
||||||
|
remoteNames := make(map[string]bool, len(remotes))
|
||||||
|
for _, r := range remotes {
|
||||||
|
remoteNames[r.Name] = true
|
||||||
|
}
|
||||||
|
type linkKey struct{ local, remote string; port int }
|
||||||
|
linkSeen := make(map[linkKey]bool, len(links))
|
||||||
|
for _, l := range links {
|
||||||
|
linkSeen[linkKey{l.Local, l.Remote, l.RemotePort}] = true
|
||||||
|
}
|
||||||
|
for _, t := range snap.Topology {
|
||||||
|
if !localNames[t.Local.Name] {
|
||||||
|
locals = append(locals, t.Local)
|
||||||
|
localNames[t.Local.Name] = true
|
||||||
|
}
|
||||||
|
if !remoteNames[t.Remote.Name] {
|
||||||
|
remotes = append(remotes, t.Remote)
|
||||||
|
remoteNames[t.Remote.Name] = true
|
||||||
|
}
|
||||||
|
k := linkKey{t.Link.Local, t.Link.Remote, t.Link.RemotePort}
|
||||||
|
if !linkSeen[k] {
|
||||||
|
links = append(links, t.Link)
|
||||||
|
linkSeen[k] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
writeJSON(w, http.StatusOK, canvasData{Locals: locals, Remotes: remotes, Links: links})
|
writeJSON(w, http.StatusOK, canvasData{Locals: locals, Remotes: remotes, Links: links})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -237,10 +237,12 @@ func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
type topoKey struct{ local, remote string; port int }
|
type topoKey struct{ local, remote string; port int }
|
||||||
ownerOf := map[topoKey]string{}
|
ownerOf := map[topoKey]string{}
|
||||||
|
topoEntries := []*cluster.TopoEntry{} // full topology for canvas/status merge
|
||||||
selfID := h.SelfAddr
|
selfID := h.SelfAddr
|
||||||
if h.Ring != nil {
|
if h.Ring != nil {
|
||||||
snap := h.Ring.Snapshot()
|
snap := h.Ring.Snapshot()
|
||||||
selfID = snap.SelfID
|
selfID = snap.SelfID
|
||||||
|
topoEntries = snap.Topology
|
||||||
for _, e := range snap.Topology {
|
for _, e := range snap.Topology {
|
||||||
k := topoKey{e.Local.Name, e.Remote.Name, e.Link.RemotePort}
|
k := topoKey{e.Local.Name, e.Remote.Name, e.Link.RemotePort}
|
||||||
if _, ok := ownerOf[k]; !ok {
|
if _, ok := ownerOf[k]; !ok {
|
||||||
@ -327,7 +329,10 @@ func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) {
|
|||||||
LocalProto string `json:"localProto,omitempty"`
|
LocalProto string `json:"localProto,omitempty"`
|
||||||
}
|
}
|
||||||
links, _ := h.Store.ListLinks()
|
links, _ := h.Store.ListLinks()
|
||||||
forwards := make([]forwardStatus, 0, len(links))
|
forwards := make([]forwardStatus, 0, len(links)+len(topoEntries))
|
||||||
|
// Track which (local,remote,remotePort) triples are already in forwards
|
||||||
|
// so the topology-merge loop below doesn't duplicate them.
|
||||||
|
seenFwd := map[topoKey]bool{}
|
||||||
for _, ln := range links {
|
for _, ln := range links {
|
||||||
loc, ok := localByName[ln.Local]
|
loc, ok := localByName[ln.Local]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -350,8 +355,24 @@ func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) {
|
|||||||
fs.OwnerID = owner
|
fs.OwnerID = owner
|
||||||
fs.Active = !ln.Disabled && inTopo
|
fs.Active = !ln.Disabled && inTopo
|
||||||
}
|
}
|
||||||
|
seenFwd[topoKey{ln.Local, ln.Remote, ln.RemotePort}] = true
|
||||||
forwards = append(forwards, fs)
|
forwards = append(forwards, fs)
|
||||||
}
|
}
|
||||||
|
// Merge cluster forwards from ring topology that aren't in the local store.
|
||||||
|
// This makes every node's status page show the FULL cluster picture, not
|
||||||
|
// just the forwards this node happens to have in its SQLite links table.
|
||||||
|
for _, t := range topoEntries {
|
||||||
|
k := topoKey{t.Local.Name, t.Remote.Name, t.Link.RemotePort}
|
||||||
|
if seenFwd[k] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
forwards = append(forwards, forwardStatus{
|
||||||
|
Local: t.Local.Name, Remote: t.Remote.Name, RemotePort: t.Link.RemotePort,
|
||||||
|
LocalOnly: false, Kind: "remote", OwnerID: t.OwnerID, Active: t.Active,
|
||||||
|
LocalIP: t.Local.IP, LocalPort: t.Local.Port, LocalProto: t.Local.Protocol,
|
||||||
|
})
|
||||||
|
seenFwd[k] = true
|
||||||
|
}
|
||||||
|
|
||||||
resp := map[string]any{
|
resp := map[string]any{
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user