mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-09-21 17:37:56 +00:00
feat(M6): localOnly forward option (loopback→LAN rewrite for cluster, local direct), canvas diff-based create/revoke commands, revoke task semantics, topology derived canvas on sync nodes, LAN addr helper
This commit is contained in:
@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"webui4frpc/internal/cluster"
|
||||
"webui4frpc/internal/store"
|
||||
)
|
||||
|
||||
@ -42,6 +43,16 @@ func (h *Handler) saveCanvas(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
s := h.Store
|
||||
|
||||
// Rewrite loopback backend addresses for cluster-distributed forwards.
|
||||
// Non-localOnly locals targeting 127.0.0.1/0.0.0.0/localhost must be
|
||||
// reachable from whichever node claims them, so swap in this node LAN addr.
|
||||
// Local-only forwards keep the loopback as-is (they never leave this node).
|
||||
for i := range canvas.Locals {
|
||||
if !canvas.Locals[i].LocalOnly && cluster.IsLoopbackIP(canvas.Locals[i].IP) {
|
||||
canvas.Locals[i].IP = cluster.RewriteForCluster(canvas.Locals[i].IP)
|
||||
}
|
||||
}
|
||||
|
||||
// Upsert locals.
|
||||
for _, l := range canvas.Locals {
|
||||
if err := s.UpsertLocal(l); err != nil {
|
||||
@ -49,7 +60,9 @@ func (h *Handler) saveCanvas(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Delete locals not present.
|
||||
// Delete locals not present. A removed localOnly forward is cancelled
|
||||
// locally (stop worker). A removed cluster forward publishes a REVOKE
|
||||
// task through the token so its owning node cancels it everywhere.
|
||||
if existing, err := s.ListLocals(); err == nil {
|
||||
keep := map[string]bool{}
|
||||
for _, l := range canvas.Locals {
|
||||
@ -57,6 +70,19 @@ func (h *Handler) saveCanvas(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
for _, old := range existing {
|
||||
if !keep[old.Name] {
|
||||
if old.LocalOnly {
|
||||
if h.Process != nil {
|
||||
if fwd, _ := s.LinksForLocal(old.Name); len(fwd) > 0 {
|
||||
_ = h.Process.Stop(fwd[0].Remote)
|
||||
}
|
||||
}
|
||||
} else if h.Ring != nil {
|
||||
if fwd, _ := s.LinksForLocal(old.Name); len(fwd) > 0 {
|
||||
if rem, ok := s.GetRemote(fwd[0].Remote); ok {
|
||||
h.Ring.RevokeTask(old, rem, store.Link{Local: old.Name, Remote: rem.Name, RemotePort: fwd[0].RemotePort})
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = s.DeleteLocal(old.Name)
|
||||
}
|
||||
}
|
||||
@ -87,7 +113,34 @@ func (h *Handler) saveCanvas(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Restart affected running workers so changes take effect immediately.
|
||||
// Cluster distribution: non-localOnly forwards are submitted as token-ring
|
||||
// tasks (claimed by the lowest-load member). Local-only forwards are NOT
|
||||
// submitted — they stay on this node and are only visible here.
|
||||
if h.Ring != nil {
|
||||
for _, l := range canvas.Locals {
|
||||
if l.LocalOnly {
|
||||
continue
|
||||
}
|
||||
for _, ln := range canvas.Links {
|
||||
if ln.Local != l.Name {
|
||||
continue
|
||||
}
|
||||
var rem store.Remote
|
||||
for _, rr := range canvas.Remotes {
|
||||
if rr.Name == ln.Remote {
|
||||
rem = rr
|
||||
break
|
||||
}
|
||||
}
|
||||
if rem.Name != "" {
|
||||
h.Ring.SubmitTask(l, rem, ln)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restart affected running workers so changes take effect immediately
|
||||
// (local-only forwards get their worker started right here).
|
||||
if h.SyncWorkers != nil {
|
||||
h.SyncWorkers()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user