web/frpc: support more info (#3334)

This commit is contained in:
fatedier 2023-02-26 02:54:53 +08:00 committed by GitHub
parent 871511ba52
commit 8c6303c1e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 277 additions and 340 deletions

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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
View File

@ -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
View File

@ -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
View 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)
}
}

View 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}))
}

View File

@ -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 {

View File

@ -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++ {

View File

@ -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")
} }

View File

@ -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'],
}, },
], ],
}, },
}; }

View File

@ -1 +1,5 @@
{} {
"tabWidth": 2,
"semi": false,
"singleQuote": true
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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')

View File

@ -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

View File

@ -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: '',
}, },
}); })