mirror of
https://gitee.com/IrisVega/frp.git
synced 2024-11-01 22:31:29 +08:00
web/frpc: support more info (#3334)
This commit is contained in:
parent
871511ba52
commit
8c6303c1e5
10
README.md
10
README.md
@ -22,9 +22,9 @@
|
|||||||
|
|
||||||
## What is frp?
|
## What is frp?
|
||||||
|
|
||||||
frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the Internet. As of now, it supports **TCP** and **UDP**, as well as **HTTP** and **HTTPS** protocols, where requests can be forwarded to internal services by domain name.
|
frp is a fast reverse proxy that allows you to expose a local server located behind a NAT or firewall to the Internet. It currently supports **TCP** and **UDP**, as well as **HTTP** and **HTTPS** protocols, enabling requests to be forwarded to internal services via domain name.
|
||||||
|
|
||||||
frp also has a P2P connect mode.
|
frp also offers a P2P connect mode.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
@ -89,11 +89,11 @@ frp also has a P2P connect mode.
|
|||||||
|
|
||||||
## Development Status
|
## Development Status
|
||||||
|
|
||||||
frp is under development. Try the latest release version in the `master` branch, or use the `dev` branch for the version in development.
|
frp is currently under development. You can try the latest release version in the `master` branch, or use the `dev` branch to access the version currently in development.
|
||||||
|
|
||||||
We are working on v2 version and trying to do some code refactor and improvements. It won't be compatible with v1.
|
We are currently working on version 2 and attempting to perform some code refactoring and improvements. However, please note that it will not be compatible with version 1.
|
||||||
|
|
||||||
We will switch v0 to v1 at the right time and only accept bug fixes and improvements instead of big feature requirements.
|
We will transition from version 0 to version 1 at the appropriate time and will only accept bug fixes and improvements, rather than big feature requests.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -4,7 +4,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>frp client admin UI</title>
|
<title>frp client admin UI</title>
|
||||||
<script type="module" crossorigin src="./index-a2ed7ed4.js"></script>
|
<script type="module" crossorigin src="./index-7dd223da.js"></script>
|
||||||
<link rel="stylesheet" href="./index-aa3c7267.css">
|
<link rel="stylesheet" href="./index-aa3c7267.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/fatedier/frp/client/proxy"
|
"github.com/fatedier/frp/client/proxy"
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
"github.com/fatedier/frp/pkg/util/log"
|
"github.com/fatedier/frp/pkg/util/log"
|
||||||
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GeneralResponse struct {
|
type GeneralResponse struct {
|
||||||
@ -70,15 +71,7 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
|
|||||||
log.Info("success reload conf")
|
log.Info("success reload conf")
|
||||||
}
|
}
|
||||||
|
|
||||||
type StatusResp struct {
|
type StatusResp map[string][]ProxyStatusResp
|
||||||
TCP []ProxyStatusResp `json:"tcp"`
|
|
||||||
UDP []ProxyStatusResp `json:"udp"`
|
|
||||||
HTTP []ProxyStatusResp `json:"http"`
|
|
||||||
HTTPS []ProxyStatusResp `json:"https"`
|
|
||||||
STCP []ProxyStatusResp `json:"stcp"`
|
|
||||||
XTCP []ProxyStatusResp `json:"xtcp"`
|
|
||||||
SUDP []ProxyStatusResp `json:"sudp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProxyStatusResp struct {
|
type ProxyStatusResp struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@ -90,12 +83,6 @@ type ProxyStatusResp struct {
|
|||||||
RemoteAddr string `json:"remote_addr"`
|
RemoteAddr string `json:"remote_addr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ByProxyStatusResp []ProxyStatusResp
|
|
||||||
|
|
||||||
func (a ByProxyStatusResp) Len() int { return len(a) }
|
|
||||||
func (a ByProxyStatusResp) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
||||||
func (a ByProxyStatusResp) Less(i, j int) bool { return strings.Compare(a[i].Name, a[j].Name) < 0 }
|
|
||||||
|
|
||||||
func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxyStatusResp {
|
func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxyStatusResp {
|
||||||
psr := ProxyStatusResp{
|
psr := ProxyStatusResp{
|
||||||
Name: status.Name,
|
Name: status.Name,
|
||||||
@ -103,53 +90,17 @@ func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxySta
|
|||||||
Status: status.Phase,
|
Status: status.Phase,
|
||||||
Err: status.Err,
|
Err: status.Err,
|
||||||
}
|
}
|
||||||
switch cfg := status.Cfg.(type) {
|
baseCfg := status.Cfg.GetBaseInfo()
|
||||||
case *config.TCPProxyConf:
|
if baseCfg.LocalPort != 0 {
|
||||||
if cfg.LocalPort != 0 {
|
psr.LocalAddr = net.JoinHostPort(baseCfg.LocalIP, strconv.Itoa(baseCfg.LocalPort))
|
||||||
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
|
|
||||||
}
|
}
|
||||||
psr.Plugin = cfg.Plugin
|
psr.Plugin = baseCfg.Plugin
|
||||||
if status.Err != "" {
|
|
||||||
psr.RemoteAddr = net.JoinHostPort(serverAddr, strconv.Itoa(cfg.RemotePort))
|
if status.Err == "" {
|
||||||
} else {
|
|
||||||
psr.RemoteAddr = serverAddr + status.RemoteAddr
|
|
||||||
}
|
|
||||||
case *config.UDPProxyConf:
|
|
||||||
if cfg.LocalPort != 0 {
|
|
||||||
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
|
|
||||||
}
|
|
||||||
if status.Err != "" {
|
|
||||||
psr.RemoteAddr = net.JoinHostPort(serverAddr, strconv.Itoa(cfg.RemotePort))
|
|
||||||
} else {
|
|
||||||
psr.RemoteAddr = serverAddr + status.RemoteAddr
|
|
||||||
}
|
|
||||||
case *config.HTTPProxyConf:
|
|
||||||
if cfg.LocalPort != 0 {
|
|
||||||
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
|
|
||||||
}
|
|
||||||
psr.Plugin = cfg.Plugin
|
|
||||||
psr.RemoteAddr = status.RemoteAddr
|
psr.RemoteAddr = status.RemoteAddr
|
||||||
case *config.HTTPSProxyConf:
|
if util.InSlice(status.Type, []string{"tcp", "udp"}) {
|
||||||
if cfg.LocalPort != 0 {
|
psr.RemoteAddr = serverAddr + psr.RemoteAddr
|
||||||
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
|
|
||||||
}
|
}
|
||||||
psr.Plugin = cfg.Plugin
|
|
||||||
psr.RemoteAddr = status.RemoteAddr
|
|
||||||
case *config.STCPProxyConf:
|
|
||||||
if cfg.LocalPort != 0 {
|
|
||||||
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
|
|
||||||
}
|
|
||||||
psr.Plugin = cfg.Plugin
|
|
||||||
case *config.XTCPProxyConf:
|
|
||||||
if cfg.LocalPort != 0 {
|
|
||||||
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
|
|
||||||
}
|
|
||||||
psr.Plugin = cfg.Plugin
|
|
||||||
case *config.SUDPProxyConf:
|
|
||||||
if cfg.LocalPort != 0 {
|
|
||||||
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
|
|
||||||
}
|
|
||||||
psr.Plugin = cfg.Plugin
|
|
||||||
}
|
}
|
||||||
return psr
|
return psr
|
||||||
}
|
}
|
||||||
@ -158,15 +109,8 @@ func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxySta
|
|||||||
func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
|
func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
buf []byte
|
buf []byte
|
||||||
res StatusResp
|
res StatusResp = make(map[string][]ProxyStatusResp)
|
||||||
)
|
)
|
||||||
res.TCP = make([]ProxyStatusResp, 0)
|
|
||||||
res.UDP = make([]ProxyStatusResp, 0)
|
|
||||||
res.HTTP = make([]ProxyStatusResp, 0)
|
|
||||||
res.HTTPS = make([]ProxyStatusResp, 0)
|
|
||||||
res.STCP = make([]ProxyStatusResp, 0)
|
|
||||||
res.XTCP = make([]ProxyStatusResp, 0)
|
|
||||||
res.SUDP = make([]ProxyStatusResp, 0)
|
|
||||||
|
|
||||||
log.Info("Http request [/api/status]")
|
log.Info("Http request [/api/status]")
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -177,30 +121,17 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
ps := svr.ctl.pm.GetAllProxyStatus()
|
ps := svr.ctl.pm.GetAllProxyStatus()
|
||||||
for _, status := range ps {
|
for _, status := range ps {
|
||||||
switch status.Type {
|
res[status.Type] = append(res[status.Type], NewProxyStatusResp(status, svr.cfg.ServerAddr))
|
||||||
case "tcp":
|
|
||||||
res.TCP = append(res.TCP, NewProxyStatusResp(status, svr.cfg.ServerAddr))
|
|
||||||
case "udp":
|
|
||||||
res.UDP = append(res.UDP, NewProxyStatusResp(status, svr.cfg.ServerAddr))
|
|
||||||
case "http":
|
|
||||||
res.HTTP = append(res.HTTP, NewProxyStatusResp(status, svr.cfg.ServerAddr))
|
|
||||||
case "https":
|
|
||||||
res.HTTPS = append(res.HTTPS, NewProxyStatusResp(status, svr.cfg.ServerAddr))
|
|
||||||
case "stcp":
|
|
||||||
res.STCP = append(res.STCP, NewProxyStatusResp(status, svr.cfg.ServerAddr))
|
|
||||||
case "xtcp":
|
|
||||||
res.XTCP = append(res.XTCP, NewProxyStatusResp(status, svr.cfg.ServerAddr))
|
|
||||||
case "sudp":
|
|
||||||
res.SUDP = append(res.SUDP, NewProxyStatusResp(status, svr.cfg.ServerAddr))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, arrs := range res {
|
||||||
|
if len(arrs) <= 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sort.Slice(arrs, func(i, j int) bool {
|
||||||
|
return strings.Compare(arrs[i].Name, arrs[j].Name) < 0
|
||||||
|
})
|
||||||
}
|
}
|
||||||
sort.Sort(ByProxyStatusResp(res.TCP))
|
|
||||||
sort.Sort(ByProxyStatusResp(res.UDP))
|
|
||||||
sort.Sort(ByProxyStatusResp(res.HTTP))
|
|
||||||
sort.Sort(ByProxyStatusResp(res.HTTPS))
|
|
||||||
sort.Sort(ByProxyStatusResp(res.STCP))
|
|
||||||
sort.Sort(ByProxyStatusResp(res.XTCP))
|
|
||||||
sort.Sort(ByProxyStatusResp(res.SUDP))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET api/config
|
// GET api/config
|
||||||
|
@ -81,67 +81,27 @@ func status(clientCfg config.ClientCommonConf) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
res := &client.StatusResp{}
|
res := make(client.StatusResp)
|
||||||
err = json.Unmarshal(body, &res)
|
err = json.Unmarshal(body, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Proxy Status...")
|
fmt.Println("Proxy Status...")
|
||||||
if len(res.TCP) > 0 {
|
types := []string{"tcp", "udp", "tcpmux", "http", "https", "stcp", "sudp", "xtcp"}
|
||||||
fmt.Println("TCP")
|
for _, pxyType := range types {
|
||||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
arrs := res[pxyType]
|
||||||
for _, ps := range res.TCP {
|
if len(arrs) == 0 {
|
||||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
continue
|
||||||
}
|
|
||||||
tbl.Print()
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
if len(res.UDP) > 0 {
|
|
||||||
fmt.Println("UDP")
|
|
||||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
|
||||||
for _, ps := range res.UDP {
|
|
||||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
|
||||||
}
|
|
||||||
tbl.Print()
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
if len(res.HTTP) > 0 {
|
|
||||||
fmt.Println("HTTP")
|
|
||||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
|
||||||
for _, ps := range res.HTTP {
|
|
||||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
|
||||||
}
|
|
||||||
tbl.Print()
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
if len(res.HTTPS) > 0 {
|
|
||||||
fmt.Println("HTTPS")
|
|
||||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
|
||||||
for _, ps := range res.HTTPS {
|
|
||||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
|
||||||
}
|
|
||||||
tbl.Print()
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
if len(res.STCP) > 0 {
|
|
||||||
fmt.Println("STCP")
|
|
||||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
|
||||||
for _, ps := range res.STCP {
|
|
||||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
|
||||||
}
|
|
||||||
tbl.Print()
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
if len(res.XTCP) > 0 {
|
|
||||||
fmt.Println("XTCP")
|
|
||||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
|
||||||
for _, ps := range res.XTCP {
|
|
||||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
|
||||||
}
|
|
||||||
tbl.Print()
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println(strings.ToUpper(pxyType))
|
||||||
|
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||||
|
for _, ps := range arrs {
|
||||||
|
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||||
|
}
|
||||||
|
tbl.Print()
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
14
go.mod
14
go.mod
@ -14,19 +14,19 @@ require (
|
|||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/hashicorp/yamux v0.1.1
|
github.com/hashicorp/yamux v0.1.1
|
||||||
github.com/onsi/ginkgo v1.16.4
|
github.com/onsi/ginkgo v1.16.4
|
||||||
github.com/onsi/gomega v1.20.2
|
github.com/onsi/gomega v1.23.0
|
||||||
github.com/pires/go-proxyproto v0.6.2
|
github.com/pires/go-proxyproto v0.6.2
|
||||||
github.com/prometheus/client_golang v1.13.0
|
github.com/prometheus/client_golang v1.13.0
|
||||||
github.com/quic-go/quic-go v0.32.0
|
github.com/quic-go/quic-go v0.32.0
|
||||||
github.com/rodaine/table v1.0.1
|
github.com/rodaine/table v1.0.1
|
||||||
github.com/spf13/cobra v1.1.3
|
github.com/spf13/cobra v1.1.3
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.8.0
|
||||||
golang.org/x/net v0.4.0
|
golang.org/x/net v0.4.0
|
||||||
golang.org/x/oauth2 v0.3.0
|
golang.org/x/oauth2 v0.3.0
|
||||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8
|
||||||
gopkg.in/ini.v1 v1.67.0
|
gopkg.in/ini.v1 v1.67.0
|
||||||
k8s.io/apimachinery v0.25.0
|
k8s.io/apimachinery v0.26.1
|
||||||
k8s.io/client-go v0.25.0
|
k8s.io/client-go v0.26.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@ -41,7 +41,7 @@ require (
|
|||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/golang/snappy v0.0.3 // indirect
|
github.com/golang/snappy v0.0.3 // indirect
|
||||||
github.com/google/go-cmp v0.5.8 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
|
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
|
||||||
@ -49,7 +49,7 @@ require (
|
|||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
github.com/nxadm/tail v1.4.8 // indirect
|
github.com/nxadm/tail v1.4.8 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
github.com/onsi/ginkgo/v2 v2.4.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
@ -73,5 +73,5 @@ require (
|
|||||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
|
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect
|
||||||
)
|
)
|
||||||
|
32
go.sum
32
go.sum
@ -141,7 +141,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
|||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||||
@ -205,8 +205,9 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
|
||||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
@ -338,12 +339,12 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
|||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||||
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=
|
||||||
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY=
|
github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys=
|
||||||
github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
|
github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
|
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
|
||||||
@ -414,7 +415,6 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9
|
|||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
|
github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
|
||||||
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
||||||
@ -425,13 +425,16 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
|||||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
|
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
|
||||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
|
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
|
||||||
@ -967,13 +970,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
|||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU=
|
k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ=
|
||||||
k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0=
|
k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
|
||||||
k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E=
|
k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU=
|
||||||
k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8=
|
k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE=
|
||||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs=
|
||||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4=
|
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
25
pkg/util/util/slice.go
Normal file
25
pkg/util/util/slice.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
func InSlice[T comparable](v T, s []T) bool {
|
||||||
|
for _, vv := range s {
|
||||||
|
if v == vv {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func InSliceAny[T any](v T, s []T, equalFn func(a, b T) bool) bool {
|
||||||
|
for _, vv := range s {
|
||||||
|
if equalFn(v, vv) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func InSliceAnyFunc[T any](equalFn func(a, b T) bool) func(v T, s []T) bool {
|
||||||
|
return func(v T, s []T) bool {
|
||||||
|
return InSliceAny(v, s, equalFn)
|
||||||
|
}
|
||||||
|
}
|
49
pkg/util/util/slice_test.go
Normal file
49
pkg/util/util/slice_test.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInSlice(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
require.True(InSlice(1, []int{1, 2, 3}))
|
||||||
|
require.False(InSlice(0, []int{1, 2, 3}))
|
||||||
|
require.True(InSlice("foo", []string{"foo", "bar"}))
|
||||||
|
require.False(InSlice("not exist", []string{"foo", "bar"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
type testStructA struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInSliceAny(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
a := testStructA{Name: "foo", Age: 20}
|
||||||
|
b := testStructA{Name: "foo", Age: 30}
|
||||||
|
c := testStructA{Name: "bar", Age: 20}
|
||||||
|
|
||||||
|
equalFn := func(o, p testStructA) bool {
|
||||||
|
return o.Name == p.Name
|
||||||
|
}
|
||||||
|
require.True(InSliceAny(a, []testStructA{b, c}, equalFn))
|
||||||
|
require.False(InSliceAny(c, []testStructA{a, b}, equalFn))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInSliceAnyFunc(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
a := testStructA{Name: "foo", Age: 20}
|
||||||
|
b := testStructA{Name: "foo", Age: 30}
|
||||||
|
c := testStructA{Name: "bar", Age: 20}
|
||||||
|
|
||||||
|
equalFn := func(o, p testStructA) bool {
|
||||||
|
return o.Name == p.Name
|
||||||
|
}
|
||||||
|
testStructAInSlice := InSliceAnyFunc(equalFn)
|
||||||
|
require.True(testStructAInSlice(a, []testStructA{b, c}))
|
||||||
|
require.False(testStructAInSlice(c, []testStructA{a, b}))
|
||||||
|
}
|
@ -38,7 +38,7 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
|
|||||||
err = p.Start()
|
err = p.Start()
|
||||||
ExpectNoError(err)
|
ExpectNoError(err)
|
||||||
}
|
}
|
||||||
time.Sleep(time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
currentClientProcesses := make([]*process.Process, 0, len(clientTemplates))
|
currentClientProcesses := make([]*process.Process, 0, len(clientTemplates))
|
||||||
for i := range clientTemplates {
|
for i := range clientTemplates {
|
||||||
|
@ -10,8 +10,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Allocator struct {
|
type Allocator struct {
|
||||||
reserved sets.Int
|
reserved sets.Set[int]
|
||||||
used sets.Int
|
used sets.Set[int]
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,8 +20,8 @@ type Allocator struct {
|
|||||||
// Reserved ports: 13, 17
|
// Reserved ports: 13, 17
|
||||||
func NewAllocator(from int, to int, mod int, index int) *Allocator {
|
func NewAllocator(from int, to int, mod int, index int) *Allocator {
|
||||||
pa := &Allocator{
|
pa := &Allocator{
|
||||||
reserved: sets.NewInt(),
|
reserved: sets.New[int](),
|
||||||
used: sets.NewInt(),
|
used: sets.New[int](),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := from; i <= to; i++ {
|
for i := from; i <= to; i++ {
|
||||||
|
@ -39,44 +39,16 @@ func (c *Client) GetProxyStatus(name string) (*client.ProxyStatusResp, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
allStatus := &client.StatusResp{}
|
allStatus := make(client.StatusResp)
|
||||||
if err = json.Unmarshal([]byte(content), &allStatus); err != nil {
|
if err = json.Unmarshal([]byte(content), &allStatus); err != nil {
|
||||||
return nil, fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(content))
|
return nil, fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(content))
|
||||||
}
|
}
|
||||||
for _, s := range allStatus.TCP {
|
for _, pss := range allStatus {
|
||||||
if s.Name == name {
|
for _, ps := range pss {
|
||||||
return &s, nil
|
if ps.Name == name {
|
||||||
|
return &ps, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, s := range allStatus.UDP {
|
|
||||||
if s.Name == name {
|
|
||||||
return &s, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, s := range allStatus.HTTP {
|
|
||||||
if s.Name == name {
|
|
||||||
return &s, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, s := range allStatus.HTTPS {
|
|
||||||
if s.Name == name {
|
|
||||||
return &s, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, s := range allStatus.STCP {
|
|
||||||
if s.Name == name {
|
|
||||||
return &s, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, s := range allStatus.XTCP {
|
|
||||||
if s.Name == name {
|
|
||||||
return &s, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, s := range allStatus.SUDP {
|
|
||||||
if s.Name == name {
|
|
||||||
return &s, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no proxy status found")
|
return nil, fmt.Errorf("no proxy status found")
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
/* eslint-env node */
|
/* eslint-env node */
|
||||||
require("@rushstack/eslint-patch/modern-module-resolution");
|
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
extends: [
|
extends: [
|
||||||
"plugin:vue/vue3-essential",
|
'plugin:vue/vue3-essential',
|
||||||
"eslint:recommended",
|
'eslint:recommended',
|
||||||
"@vue/eslint-config-typescript",
|
'@vue/eslint-config-typescript',
|
||||||
"@vue/eslint-config-prettier",
|
'@vue/eslint-config-prettier',
|
||||||
],
|
],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: "latest",
|
ecmaVersion: 'latest',
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
"@typescript-eslint/no-unused-vars": [
|
'@typescript-eslint/no-unused-vars': [
|
||||||
"warn",
|
'warn',
|
||||||
{
|
{
|
||||||
argsIgnorePattern: "^_",
|
argsIgnorePattern: '^_',
|
||||||
varsIgnorePattern: "^_",
|
varsIgnorePattern: '^_',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"vue/multi-word-component-names": [
|
'vue/multi-word-component-names': [
|
||||||
"error",
|
'error',
|
||||||
{
|
{
|
||||||
ignores: ["Overview"],
|
ignores: ['Overview'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
@ -1 +1,5 @@
|
|||||||
{}
|
{
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true
|
||||||
|
}
|
||||||
|
@ -48,18 +48,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from 'vue'
|
||||||
import { useDark, useToggle } from "@vueuse/core";
|
import { useDark, useToggle } from '@vueuse/core'
|
||||||
|
|
||||||
const isDark = useDark();
|
const isDark = useDark()
|
||||||
const darkmodeSwitch = ref(isDark);
|
const darkmodeSwitch = ref(isDark)
|
||||||
const toggleDark = useToggle(isDark);
|
const toggleDark = useToggle(isDark)
|
||||||
|
|
||||||
const handleSelect = (key: string) => {
|
const handleSelect = (key: string) => {
|
||||||
if (key == "") {
|
if (key == '') {
|
||||||
window.open("https://github.com/fatedier/frp");
|
window.open('https://github.com/fatedier/frp')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -14,85 +14,85 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from "element-plus";
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
let textarea = ref("");
|
let textarea = ref('')
|
||||||
|
|
||||||
const fetchData = () => {
|
const fetchData = () => {
|
||||||
fetch("/api/config", { credentials: "include" })
|
fetch('/api/config', { credentials: 'include' })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res.text();
|
return res.text()
|
||||||
})
|
})
|
||||||
.then((text) => {
|
.then((text) => {
|
||||||
textarea.value = text;
|
textarea.value = text
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
showClose: true,
|
showClose: true,
|
||||||
message: "Get configure content from frpc failed!",
|
message: 'Get configure content from frpc failed!',
|
||||||
type: "warning",
|
type: 'warning',
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const uploadConfig = () => {
|
const uploadConfig = () => {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(
|
||||||
"This operation will upload your frpc configure file content and hot reload it, do you want to continue?",
|
'This operation will upload your frpc configure file content and hot reload it, do you want to continue?',
|
||||||
"Notice",
|
'Notice',
|
||||||
{
|
{
|
||||||
confirmButtonText: "Yes",
|
confirmButtonText: 'Yes',
|
||||||
cancelButtonText: "No",
|
cancelButtonText: 'No',
|
||||||
type: "warning",
|
type: 'warning',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (textarea.value == "") {
|
if (textarea.value == '') {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: "Configure content can not be empty!",
|
message: 'Configure content can not be empty!',
|
||||||
type: "warning",
|
type: 'warning',
|
||||||
});
|
})
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch("/api/config", {
|
fetch('/api/config', {
|
||||||
credentials: "include",
|
credentials: 'include',
|
||||||
method: "PUT",
|
method: 'PUT',
|
||||||
body: textarea.value,
|
body: textarea.value,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
fetch("/api/reload", { credentials: "include" })
|
fetch('/api/reload', { credentials: 'include' })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
type: "success",
|
type: 'success',
|
||||||
message: "Success",
|
message: 'Success',
|
||||||
});
|
})
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
showClose: true,
|
showClose: true,
|
||||||
message: "Reload frpc configure file error, " + err,
|
message: 'Reload frpc configure file error, ' + err,
|
||||||
type: "warning",
|
type: 'warning',
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
showClose: true,
|
showClose: true,
|
||||||
message: "Put config to frpc and hot reload failed!",
|
message: 'Put config to frpc and hot reload failed!',
|
||||||
type: "warning",
|
type: 'warning',
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: "Canceled",
|
message: 'Canceled',
|
||||||
type: "info",
|
type: 'info',
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
fetchData();
|
fetchData()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -9,30 +9,39 @@
|
|||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
:default-sort="{ prop: 'type', order: 'ascending' }"
|
:default-sort="{ prop: 'type', order: 'ascending' }"
|
||||||
>
|
>
|
||||||
<el-table-column prop="name" label="name"></el-table-column>
|
<el-table-column
|
||||||
|
prop="name"
|
||||||
|
label="name"
|
||||||
|
sortable
|
||||||
|
></el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="type"
|
prop="type"
|
||||||
label="type"
|
label="type"
|
||||||
width="150"
|
width="150"
|
||||||
|
sortable
|
||||||
></el-table-column>
|
></el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="local_addr"
|
prop="local_addr"
|
||||||
label="local address"
|
label="local address"
|
||||||
width="200"
|
width="200"
|
||||||
|
sortable
|
||||||
></el-table-column>
|
></el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="plugin"
|
prop="plugin"
|
||||||
label="plugin"
|
label="plugin"
|
||||||
width="200"
|
width="200"
|
||||||
|
sortable
|
||||||
></el-table-column>
|
></el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="remote_addr"
|
prop="remote_addr"
|
||||||
label="remote address"
|
label="remote address"
|
||||||
|
sortable
|
||||||
></el-table-column>
|
></el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="status"
|
prop="status"
|
||||||
label="status"
|
label="status"
|
||||||
width="150"
|
width="150"
|
||||||
|
sortable
|
||||||
></el-table-column>
|
></el-table-column>
|
||||||
<el-table-column prop="err" label="info"></el-table-column>
|
<el-table-column prop="err" label="info"></el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@ -43,49 +52,34 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from 'vue'
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
let status = ref<any[]>([]);
|
let status = ref<any[]>([])
|
||||||
|
|
||||||
const fetchData = () => {
|
const fetchData = () => {
|
||||||
fetch("/api/status", { credentials: "include" })
|
fetch('/api/status', { credentials: 'include' })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res.json();
|
return res.json()
|
||||||
})
|
})
|
||||||
.then((json) => {
|
.then((json) => {
|
||||||
status.value = new Array();
|
status.value = new Array()
|
||||||
for (let s of json.tcp) {
|
for (let key in json) {
|
||||||
status.value.push(s);
|
for (let ps of json[key]) {
|
||||||
|
console.log(ps)
|
||||||
|
status.value.push(ps)
|
||||||
}
|
}
|
||||||
for (let s of json.udp) {
|
|
||||||
status.value.push(s);
|
|
||||||
}
|
|
||||||
for (let s of json.http) {
|
|
||||||
status.value.push(s);
|
|
||||||
}
|
|
||||||
for (let s of json.https) {
|
|
||||||
status.value.push(s);
|
|
||||||
}
|
|
||||||
for (let s of json.stcp) {
|
|
||||||
status.value.push(s);
|
|
||||||
}
|
|
||||||
for (let s of json.sudp) {
|
|
||||||
status.value.push(s);
|
|
||||||
}
|
|
||||||
for (let s of json.xtcp) {
|
|
||||||
status.value.push(s);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err) => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
showClose: true,
|
showClose: true,
|
||||||
message: "Get status info from frpc failed!",
|
message: 'Get status info from frpc failed!' + err,
|
||||||
type: "warning",
|
type: 'warning',
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
fetchData();
|
fetchData()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style></style>
|
<style></style>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { createApp } from "vue";
|
import { createApp } from 'vue'
|
||||||
import "element-plus/dist/index.css";
|
import 'element-plus/dist/index.css'
|
||||||
import "element-plus/theme-chalk/dark/css-vars.css";
|
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||||
import App from "./App.vue";
|
import App from './App.vue'
|
||||||
import router from "./router";
|
import router from './router'
|
||||||
|
|
||||||
import "./assets/dark.css";
|
import './assets/dark.css'
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App)
|
||||||
|
|
||||||
app.use(router);
|
app.use(router)
|
||||||
|
|
||||||
app.mount("#app");
|
app.mount('#app')
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import { createRouter, createWebHashHistory } from "vue-router";
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
import Overview from "../components/Overview.vue";
|
import Overview from '../components/Overview.vue'
|
||||||
import ClientConfigure from "../components/ClientConfigure.vue";
|
import ClientConfigure from '../components/ClientConfigure.vue'
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHashHistory(),
|
history: createWebHashHistory(),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: '/',
|
||||||
name: "Overview",
|
name: 'Overview',
|
||||||
component: Overview,
|
component: Overview,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/configure",
|
path: '/configure',
|
||||||
name: "ClientConfigure",
|
name: 'ClientConfigure',
|
||||||
component: ClientConfigure,
|
component: ClientConfigure,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
})
|
||||||
|
|
||||||
export default router;
|
export default router
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { fileURLToPath, URL } from "node:url";
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from 'vite'
|
||||||
import vue from "@vitejs/plugin-vue";
|
import vue from '@vitejs/plugin-vue'
|
||||||
import AutoImport from "unplugin-auto-import/vite";
|
import AutoImport from 'unplugin-auto-import/vite'
|
||||||
import Components from "unplugin-vue-components/vite";
|
import Components from 'unplugin-vue-components/vite'
|
||||||
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
|
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
base: "",
|
base: '',
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
AutoImport({
|
AutoImport({
|
||||||
@ -20,10 +20,10 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
assetsDir: "",
|
assetsDir: '',
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user