mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-09-23 10:27:55 +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:
@ -2,6 +2,7 @@ package httpapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@ -10,6 +11,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"webui4frpc/internal/cluster"
|
||||
"webui4frpc/internal/process"
|
||||
"webui4frpc/internal/store"
|
||||
)
|
||||
@ -192,3 +194,58 @@ func TestFetchProxyStatesFromAdminAPI(t *testing.T) {
|
||||
t.Fatalf("connStateAdmin(wait start) = %q, want connecting", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSaveCanvasPublishesRevokeTask: deleting a non-localOnly forward from the
|
||||
// canvas publishes a REVOKE task to the ring (owner cancels it cluster-wide).
|
||||
func TestSaveCanvasPublishesRevokeTask(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
st, err := store.New(filepath.Join(dir, "test.db"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer st.Close()
|
||||
pm := process.NewManager(process.Options{
|
||||
ConfigsDir: filepath.Join(dir, "configs"), LogsDir: filepath.Join(dir, "logs"),
|
||||
BinaryPath: func() string { return "" }, Render: func(string) ([]byte, error) { return []byte(`{}`), nil },
|
||||
AutoRestart: func(string) bool { return false }, RestartInterval: func() int { return 5 },
|
||||
})
|
||||
ring := cluster.NewEngine("n1", "n1:7500", "u", "p", "0.1.0", nil,
|
||||
&cluster.AppHandler{}, func(ctx context.Context, next string, tk *cluster.Token) error { return nil },
|
||||
"n1:7500", true)
|
||||
h := &Handler{Store: st, Process: pm, WorkDir: dir, User: "admin", Password: "pw", Ring: ring}
|
||||
mux, _ := NewServeMux(h)
|
||||
ts := httptest.NewServer(mux)
|
||||
defer ts.Close()
|
||||
|
||||
// establish a cluster forward first via save (non-localOnly)
|
||||
body := `{"locals":[{"name":"web","ip":"127.0.0.1","port":8080,"protocol":"tcp"}],
|
||||
"remotes":[{"name":"frps1","ip":"10.0.0.1","port":7000,"enabled":true}],
|
||||
"links":[{"local":"web","remote":"frps1","remotePort":18081}]}`
|
||||
req, _ := http.NewRequest(http.MethodPut, ts.URL+"/api/manager/canvas", strings.NewReader(body))
|
||||
req.SetBasicAuth("admin", "pw")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
// now save canvas WITHOUT the forward -> should publish revoke
|
||||
body2 := `{"locals":[],"remotes":[],"links":[]}`
|
||||
req2, _ := http.NewRequest(http.MethodPut, ts.URL+"/api/manager/canvas", strings.NewReader(body2))
|
||||
req2.SetBasicAuth("admin", "pw")
|
||||
resp2, err := http.DefaultClient.Do(req2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp2.Body.Close()
|
||||
// Ring should now have a pending REVOKE task for web->frps1
|
||||
var sawRevoke bool
|
||||
for _, tk := range ring.State().PendingList() {
|
||||
if tk.Revoke && tk.Local.Name == "web" {
|
||||
sawRevoke = true
|
||||
}
|
||||
}
|
||||
if !sawRevoke {
|
||||
t.Fatalf("expected revoke task for web, pending=%+v", ring.State().PendingList())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user