@@ -0,0 +1,25 @@
|
|||||||
|
version: 2
|
||||||
|
jobs:
|
||||||
|
test1:
|
||||||
|
docker:
|
||||||
|
- image: circleci/golang:1.15-node
|
||||||
|
working_directory: /go/src/github.com/fatedier/frp
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run: make
|
||||||
|
- run: make alltest
|
||||||
|
test2:
|
||||||
|
docker:
|
||||||
|
- image: circleci/golang:1.14-node
|
||||||
|
working_directory: /go/src/github.com/fatedier/frp
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run: make
|
||||||
|
- run: make alltest
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
version: 2
|
||||||
|
build_and_test:
|
||||||
|
jobs:
|
||||||
|
- test1
|
||||||
|
- test2
|
||||||
@@ -27,4 +27,4 @@ jobs:
|
|||||||
version: latest
|
version: latest
|
||||||
args: release --rm-dist --release-notes=./Release.md
|
args: release --rm-dist --release-notes=./Release.md
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GPR_TOKEN }}
|
||||||
|
|||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
sudo: false
|
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.14.x
|
|
||||||
- 1.15.x
|
|
||||||
|
|
||||||
install:
|
|
||||||
- make
|
|
||||||
|
|
||||||
script:
|
|
||||||
- make alltest
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# frp
|
# frp
|
||||||
|
|
||||||
[](https://travis-ci.org/fatedier/frp)
|
[](https://circleci.com/gh/fatedier/frp)
|
||||||
[](https://github.com/fatedier/frp/releases)
|
[](https://github.com/fatedier/frp/releases)
|
||||||
|
|
||||||
[README](README.md) | [中文文档](README_zh.md)
|
[README](README.md) | [中文文档](README_zh.md)
|
||||||
|
|||||||
+6
-1
@@ -1,3 +1,8 @@
|
|||||||
### New
|
### New
|
||||||
|
|
||||||
* Command line parameters support `enable_prometheus`.
|
* Server Plugin supports HTTPS.
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
* Fix IPv6 address parse problem.
|
||||||
|
* HTTP type proxy can't handle websocket protocol due to error `Connection` header value.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
.el-form-item span{margin-left:15px}.demo-table-expand{font-size:0}.demo-table-expand label{width:90px;color:#99a9bf}.demo-table-expand .el-form-item{margin-right:0;margin-bottom:0;width:50%}body{background-color:#fafafa;margin:0;font-family:-apple-system,BlinkMacSystemFont,Helvetica Neue,sans-serif}header{width:100%;height:60px}.header-color{background:#58b7ff}#content{margin-top:20px;padding-right:40px}.brand{color:#fff;background-color:transparent;margin-left:20px;float:left;line-height:25px;font-size:25px;padding:15px 15px;height:30px;text-decoration:none}.source{border:1px solid #eaeefb;border-radius:4px;transition:.2s;padding:24px}.server_info{margin-left:40px;font-size:0}.server_info label{width:150px;color:#99a9bf}.server_info .el-form-item{margin-right:0;margin-bottom:0;width:100%}
|
||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
|||||||
<!DOCTYPE html> <html lang=en> <head> <meta charset=utf-8> <title>frps dashboard</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?14bea8276eef86cc7c61"></script><script type="text/javascript" src="vendor.js?51925ec1a77936b64d61"></script></body> </html>
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>frps-dashboard</title><link href="css/app.808290ae.css" rel="preload" as="style"><link href="css/chunk-vendors.84bb20f7.css" rel="preload" as="style"><link href="js/app.bb942a48.js" rel="preload" as="script"><link href="js/chunk-vendors.4421b07d.js" rel="preload" as="script"><link href="css/chunk-vendors.84bb20f7.css" rel="stylesheet"><link href="css/app.808290ae.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but frps-dashboard doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="js/chunk-vendors.4421b07d.js"></script><script src="js/app.bb942a48.js"></script></body></html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,u){for(var i,a,f,l=0,s=[];l<t.length;l++)a=t[l],o[a]&&s.push(o[a][0]),o[a]=0;for(i in c)Object.prototype.hasOwnProperty.call(c,i)&&(e[i]=c[i]);for(r&&r(t,c,u);s.length;)s.shift()();if(u)for(l=0;l<u.length;l++)f=n(n.s=u[l]);return f};var t={},o={1:0};n.e=function(e){function r(){i.onerror=i.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var c=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=c;var u=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,n.nc&&i.setAttribute("nonce",n.nc),i.src=n.p+""+e+".js?"+{0:"51925ec1a77936b64d61"}[e];var a=setTimeout(r,12e4);return i.onerror=i.onload=r,u.appendChild(i),c},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n.oe=function(e){throw console.error(e),e}}([]);
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+7
-5
@@ -17,10 +17,10 @@ package client
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -222,8 +222,10 @@ func (ctl *Control) connectServer() (conn net.Conn, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn, err = frpNet.ConnectServerByProxyWithTLS(ctl.clientCfg.HTTPProxy, ctl.clientCfg.Protocol,
|
|
||||||
fmt.Sprintf("%s:%d", ctl.clientCfg.ServerAddr, ctl.clientCfg.ServerPort), tlsConfig)
|
address := net.JoinHostPort(ctl.clientCfg.ServerAddr, strconv.Itoa(ctl.clientCfg.ServerPort))
|
||||||
|
conn, err = frpNet.ConnectServerByProxyWithTLS(ctl.clientCfg.HTTPProxy, ctl.clientCfg.Protocol, address, tlsConfig)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Warn("start new connection to server error: %v", err)
|
xl.Warn("start new connection to server error: %v", err)
|
||||||
return
|
return
|
||||||
@@ -295,7 +297,7 @@ func (ctl *Control) msgHandler() {
|
|||||||
}()
|
}()
|
||||||
defer ctl.msgHandlerShutdown.Done()
|
defer ctl.msgHandlerShutdown.Done()
|
||||||
|
|
||||||
hbSend := time.NewTicker(time.Duration(ctl.clientCfg.HeartBeatInterval) * time.Second)
|
hbSend := time.NewTicker(time.Duration(ctl.clientCfg.HeartbeatInterval) * time.Second)
|
||||||
defer hbSend.Stop()
|
defer hbSend.Stop()
|
||||||
hbCheck := time.NewTicker(time.Second)
|
hbCheck := time.NewTicker(time.Second)
|
||||||
defer hbCheck.Stop()
|
defer hbCheck.Stop()
|
||||||
@@ -314,7 +316,7 @@ func (ctl *Control) msgHandler() {
|
|||||||
}
|
}
|
||||||
ctl.sendCh <- pingMsg
|
ctl.sendCh <- pingMsg
|
||||||
case <-hbCheck.C:
|
case <-hbCheck.C:
|
||||||
if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.HeartBeatTimeout)*time.Second {
|
if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.HeartbeatTimeout)*time.Second {
|
||||||
xl.Warn("heartbeat timeout")
|
xl.Warn("heartbeat timeout")
|
||||||
// let reader() stop
|
// let reader() stop
|
||||||
ctl.conn.Close()
|
ctl.conn.Close()
|
||||||
|
|||||||
+4
-2
@@ -21,6 +21,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -215,8 +216,9 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn, err = frpNet.ConnectServerByProxyWithTLS(svr.cfg.HTTPProxy, svr.cfg.Protocol,
|
|
||||||
fmt.Sprintf("%s:%d", svr.cfg.ServerAddr, svr.cfg.ServerPort), tlsConfig)
|
address := net.JoinHostPort(svr.cfg.ServerAddr, strconv.Itoa(svr.cfg.ServerPort))
|
||||||
|
conn, err = frpNet.ConnectServerByProxyWithTLS(svr.cfg.HTTPProxy, svr.cfg.Protocol, address, tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,17 +157,16 @@ func parseClientCommonCfgFromIni(content string) (config.ClientCommonConf, error
|
|||||||
func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
|
func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
|
||||||
cfg = config.GetDefaultClientConf()
|
cfg = config.GetDefaultClientConf()
|
||||||
|
|
||||||
strs := strings.Split(serverAddr, ":")
|
ipStr, portStr, err := net.SplitHostPort(serverAddr)
|
||||||
if len(strs) < 2 {
|
if err != nil {
|
||||||
err = fmt.Errorf("invalid server_addr")
|
err = fmt.Errorf("invalid server_addr: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if strs[0] != "" {
|
|
||||||
cfg.ServerAddr = strs[0]
|
cfg.ServerAddr = ipStr
|
||||||
}
|
cfg.ServerPort, err = strconv.Atoi(portStr)
|
||||||
cfg.ServerPort, err = strconv.Atoi(strs[1])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("invalid server_addr")
|
err = fmt.Errorf("invalid server_addr: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -105,6 +105,7 @@ var rootCmd = &cobra.Command{
|
|||||||
var cfg config.ServerCommonConf
|
var cfg config.ServerCommonConf
|
||||||
var err error
|
var err error
|
||||||
if cfgFile != "" {
|
if cfgFile != "" {
|
||||||
|
log.Info("frps uses config file: %s", cfgFile)
|
||||||
var content string
|
var content string
|
||||||
content, err = config.GetRenderedConfFromFile(cfgFile)
|
content, err = config.GetRenderedConfFromFile(cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -112,6 +113,7 @@ var rootCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
cfg, err = parseServerCommonCfg(CfgFileTypeIni, content)
|
cfg, err = parseServerCommonCfg(CfgFileTypeIni, content)
|
||||||
} else {
|
} else {
|
||||||
|
log.Info("frps uses command line arguments for config")
|
||||||
cfg, err = parseServerCommonCfg(CfgFileTypeCmd, "")
|
cfg, err = parseServerCommonCfg(CfgFileTypeCmd, "")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -212,7 +214,7 @@ func runServer(cfg config.ServerCommonConf) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Info("start frps success")
|
log.Info("frps started successfully")
|
||||||
svr.Run()
|
svr.Run()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-2
@@ -23,15 +23,30 @@ log_max_days = 3
|
|||||||
disable_log_color = false
|
disable_log_color = false
|
||||||
|
|
||||||
# for authentication, should be same as your frps.ini
|
# for authentication, should be same as your frps.ini
|
||||||
# AuthenticateHeartBeats specifies whether to include authentication token in heartbeats sent to frps. By default, this value is false.
|
# authenticate_heartbeats specifies whether to include authentication token in heartbeats sent to frps. By default, this value is false.
|
||||||
authenticate_heartbeats = false
|
authenticate_heartbeats = false
|
||||||
|
|
||||||
# AuthenticateNewWorkConns specifies whether to include authentication token in new work connections sent to frps. By default, this value is false.
|
# authenticate_new_work_conns specifies whether to include authentication token in new work connections sent to frps. By default, this value is false.
|
||||||
authenticate_new_work_conns = false
|
authenticate_new_work_conns = false
|
||||||
|
|
||||||
# auth token
|
# auth token
|
||||||
token = 12345678
|
token = 12345678
|
||||||
|
|
||||||
|
# oidc_client_id specifies the client ID to use to get a token in OIDC authentication if AuthenticationMethod == "oidc".
|
||||||
|
# By default, this value is "".
|
||||||
|
oidc_client_id =
|
||||||
|
|
||||||
|
# oidc_client_secret specifies the client secret to use to get a token in OIDC authentication if AuthenticationMethod == "oidc".
|
||||||
|
# By default, this value is "".
|
||||||
|
oidc_client_secret =
|
||||||
|
|
||||||
|
# oidc_audience specifies the audience of the token in OIDC authentication if AuthenticationMethod == "oidc". By default, this value is "".
|
||||||
|
oidc_audience =
|
||||||
|
|
||||||
|
# oidc_token_endpoint_url specifies the URL which implements OIDC Token Endpoint.
|
||||||
|
# It will be used to get an OIDC token if AuthenticationMethod == "oidc". By default, this value is "".
|
||||||
|
oidc_token_endpoint_url =
|
||||||
|
|
||||||
# set admin address for control frpc's action by http api such as reload
|
# set admin address for control frpc's action by http api such as reload
|
||||||
admin_addr = 127.0.0.1
|
admin_addr = 127.0.0.1
|
||||||
admin_port = 7400
|
admin_port = 7400
|
||||||
|
|||||||
+20
-13
@@ -23,7 +23,7 @@ vhost_https_port = 443
|
|||||||
# response header timeout(seconds) for vhost http server, default is 60s
|
# response header timeout(seconds) for vhost http server, default is 60s
|
||||||
# vhost_http_timeout = 60
|
# vhost_http_timeout = 60
|
||||||
|
|
||||||
# TcpMuxHttpConnectPort specifies the port that the server listens for TCP
|
# tcpmux_httpconnect_port specifies the port that the server listens for TCP
|
||||||
# HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP
|
# HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP
|
||||||
# requests on one single port. If it's not - it will listen on this value for
|
# requests on one single port. If it's not - it will listen on this value for
|
||||||
# HTTP CONNECT requests. By default, this value is 0.
|
# HTTP CONNECT requests. By default, this value is 0.
|
||||||
@@ -44,6 +44,7 @@ enable_prometheus = true
|
|||||||
|
|
||||||
# dashboard assets directory(only for debug mode)
|
# dashboard assets directory(only for debug mode)
|
||||||
# assets_dir = ./static
|
# assets_dir = ./static
|
||||||
|
|
||||||
# console or real logFile path like ./frps.log
|
# console or real logFile path like ./frps.log
|
||||||
log_file = ./frps.log
|
log_file = ./frps.log
|
||||||
|
|
||||||
@@ -58,12 +59,12 @@ disable_log_color = false
|
|||||||
# DetailedErrorsToClient defines whether to send the specific error (with debug info) to frpc. By default, this value is true.
|
# DetailedErrorsToClient defines whether to send the specific error (with debug info) to frpc. By default, this value is true.
|
||||||
detailed_errors_to_client = true
|
detailed_errors_to_client = true
|
||||||
|
|
||||||
# AuthenticationMethod specifies what authentication method to use authenticate frpc with frps.
|
# authentication_method specifies what authentication method to use authenticate frpc with frps.
|
||||||
# If "token" is specified - token will be read into login message.
|
# If "token" is specified - token will be read into login message.
|
||||||
# If "oidc" is specified - OIDC (Open ID Connect) token will be issued using OIDC settings. By default, this value is "token".
|
# If "oidc" is specified - OIDC (Open ID Connect) token will be issued using OIDC settings. By default, this value is "token".
|
||||||
authentication_method = token
|
authentication_method = token
|
||||||
|
|
||||||
# AuthenticateHeartBeats specifies whether to include authentication token in heartbeats sent to frps. By default, this value is false.
|
# authenticate_heartbeats specifies whether to include authentication token in heartbeats sent to frps. By default, this value is false.
|
||||||
authenticate_heartbeats = false
|
authenticate_heartbeats = false
|
||||||
|
|
||||||
# AuthenticateNewWorkConns specifies whether to include authentication token in new work connections sent to frps. By default, this value is false.
|
# AuthenticateNewWorkConns specifies whether to include authentication token in new work connections sent to frps. By default, this value is false.
|
||||||
@@ -72,25 +73,31 @@ authenticate_new_work_conns = false
|
|||||||
# auth token
|
# auth token
|
||||||
token = 12345678
|
token = 12345678
|
||||||
|
|
||||||
# OidcClientId specifies the client ID to use to get a token in OIDC authentication if AuthenticationMethod == "oidc".
|
# oidc_issuer specifies the issuer to verify OIDC tokens with.
|
||||||
# By default, this value is "".
|
# By default, this value is "".
|
||||||
oidc_client_id =
|
oidc_issuer =
|
||||||
|
|
||||||
# OidcClientSecret specifies the client secret to use to get a token in OIDC authentication if AuthenticationMethod == "oidc".
|
# oidc_audience specifies the audience OIDC tokens should contain when validated.
|
||||||
# By default, this value is "".
|
# By default, this value is "".
|
||||||
oidc_client_secret =
|
|
||||||
|
|
||||||
# OidcAudience specifies the audience of the token in OIDC authentication if AuthenticationMethod == "oidc". By default, this value is "".
|
|
||||||
oidc_audience =
|
oidc_audience =
|
||||||
|
|
||||||
# OidcTokenEndpointUrl specifies the URL which implements OIDC Token Endpoint.
|
# oidc_skip_expiry_check specifies whether to skip checking if the OIDC token is expired.
|
||||||
# It will be used to get an OIDC token if AuthenticationMethod == "oidc". By default, this value is "".
|
# By default, this value is false.
|
||||||
oidc_token_endpoint_url =
|
oidc_skip_expiry_check = false
|
||||||
|
|
||||||
|
|
||||||
|
# oidc_skip_issuer_check specifies whether to skip checking if the OIDC token's issuer claim matches the issuer specified in OidcIssuer.
|
||||||
|
# By default, this value is false.
|
||||||
|
oidc_skip_issuer_check = false
|
||||||
|
|
||||||
# heartbeat configure, it's not recommended to modify the default value
|
# heartbeat configure, it's not recommended to modify the default value
|
||||||
# the default value of heartbeat_timeout is 90
|
# the default value of heartbeat_timeout is 90
|
||||||
# heartbeat_timeout = 90
|
# heartbeat_timeout = 90
|
||||||
|
|
||||||
|
# user_conn_timeout configure, it's not recommended to modify the default value
|
||||||
|
# the default value of user_conn_timeout is 10
|
||||||
|
# user_conn_timeout = 10
|
||||||
|
|
||||||
# only allow frpc to bind ports you list, if you set nothing, there won't be any limit
|
# only allow frpc to bind ports you list, if you set nothing, there won't be any limit
|
||||||
allow_ports = 2000-3000,3001,3003,4000-50000
|
allow_ports = 2000-3000,3001,3003,4000-50000
|
||||||
|
|
||||||
@@ -100,7 +107,7 @@ max_pool_count = 5
|
|||||||
# max ports can be used for each client, default value is 0 means no limit
|
# max ports can be used for each client, default value is 0 means no limit
|
||||||
max_ports_per_client = 0
|
max_ports_per_client = 0
|
||||||
|
|
||||||
# TlsOnly specifies whether to only accept TLS-encrypted connections. By default, the value is false.
|
# tls_only specifies whether to only accept TLS-encrypted connections. By default, the value is false.
|
||||||
tls_only = false
|
tls_only = false
|
||||||
|
|
||||||
# tls_cert_file = server.crt
|
# tls_cert_file = server.crt
|
||||||
|
|||||||
@@ -209,9 +209,10 @@ path = /handler
|
|||||||
ops = NewProxy
|
ops = NewProxy
|
||||||
```
|
```
|
||||||
|
|
||||||
addr: the address where the external RPC service listens on.
|
- addr: the address where the external RPC service listens. Defaults to http. For https, specify the schema: `addr = https://127.0.0.1:9001`.
|
||||||
path: http request url path for the POST request.
|
- path: http request url path for the POST request.
|
||||||
ops: operations plugin needs to handle (e.g. "Login", "NewProxy", ...).
|
- ops: operations plugin needs to handle (e.g. "Login", "NewProxy", ...).
|
||||||
|
- tls_verify: When the schema is https, we verify by default. Set this value to false if you want to skip verification.
|
||||||
|
|
||||||
### Metadata
|
### Metadata
|
||||||
|
|
||||||
|
|||||||
@@ -121,11 +121,11 @@ type ClientCommonConf struct {
|
|||||||
// HeartBeatInterval specifies at what interval heartbeats are sent to the
|
// HeartBeatInterval specifies at what interval heartbeats are sent to the
|
||||||
// server, in seconds. It is not recommended to change this value. By
|
// server, in seconds. It is not recommended to change this value. By
|
||||||
// default, this value is 30.
|
// default, this value is 30.
|
||||||
HeartBeatInterval int64 `json:"heartbeat_interval"`
|
HeartbeatInterval int64 `json:"heartbeat_interval"`
|
||||||
// HeartBeatTimeout specifies the maximum allowed heartbeat response delay
|
// HeartBeatTimeout specifies the maximum allowed heartbeat response delay
|
||||||
// before the connection is terminated, in seconds. It is not recommended
|
// before the connection is terminated, in seconds. It is not recommended
|
||||||
// to change this value. By default, this value is 90.
|
// to change this value. By default, this value is 90.
|
||||||
HeartBeatTimeout int64 `json:"heartbeat_timeout"`
|
HeartbeatTimeout int64 `json:"heartbeat_timeout"`
|
||||||
// Client meta info
|
// Client meta info
|
||||||
Metas map[string]string `json:"metas"`
|
Metas map[string]string `json:"metas"`
|
||||||
// UDPPacketSize specifies the udp packet size
|
// UDPPacketSize specifies the udp packet size
|
||||||
@@ -160,8 +160,8 @@ func GetDefaultClientConf() ClientCommonConf {
|
|||||||
TLSCertFile: "",
|
TLSCertFile: "",
|
||||||
TLSKeyFile: "",
|
TLSKeyFile: "",
|
||||||
TLSTrustedCaFile: "",
|
TLSTrustedCaFile: "",
|
||||||
HeartBeatInterval: 30,
|
HeartbeatInterval: 30,
|
||||||
HeartBeatTimeout: 90,
|
HeartbeatTimeout: 90,
|
||||||
Metas: make(map[string]string),
|
Metas: make(map[string]string),
|
||||||
UDPPacketSize: 1500,
|
UDPPacketSize: 1500,
|
||||||
}
|
}
|
||||||
@@ -312,7 +312,7 @@ func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error
|
|||||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout")
|
err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cfg.HeartBeatTimeout = v
|
cfg.HeartbeatTimeout = v
|
||||||
}
|
}
|
||||||
|
|
||||||
if tmpStr, ok = conf.Get("common", "heartbeat_interval"); ok {
|
if tmpStr, ok = conf.Get("common", "heartbeat_interval"); ok {
|
||||||
@@ -320,7 +320,7 @@ func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error
|
|||||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cfg.HeartBeatInterval = v
|
cfg.HeartbeatInterval = v
|
||||||
}
|
}
|
||||||
for k, v := range conf.Section("common") {
|
for k, v := range conf.Section("common") {
|
||||||
if strings.HasPrefix(k, "meta_") {
|
if strings.HasPrefix(k, "meta_") {
|
||||||
@@ -338,12 +338,12 @@ func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *ClientCommonConf) Check() (err error) {
|
func (cfg *ClientCommonConf) Check() (err error) {
|
||||||
if cfg.HeartBeatInterval <= 0 {
|
if cfg.HeartbeatInterval <= 0 {
|
||||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.HeartBeatTimeout < cfg.HeartBeatInterval {
|
if cfg.HeartbeatTimeout < cfg.HeartbeatInterval {
|
||||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval")
|
err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ type ServerCommonConf struct {
|
|||||||
// AssetsDir specifies the local directory that the dashboard will load
|
// AssetsDir specifies the local directory that the dashboard will load
|
||||||
// resources from. If this value is "", assets will be loaded from the
|
// resources from. If this value is "", assets will be loaded from the
|
||||||
// bundled executable using statik. By default, this value is "".
|
// bundled executable using statik. By default, this value is "".
|
||||||
AssetsDir string `json:"asserts_dir"`
|
AssetsDir string `json:"assets_dir"`
|
||||||
// LogFile specifies a file where logs will be written to. This value will
|
// LogFile specifies a file where logs will be written to. This value will
|
||||||
// only be used if LogWay is set appropriately. By default, this value is
|
// only be used if LogWay is set appropriately. By default, this value is
|
||||||
// "console".
|
// "console".
|
||||||
@@ -154,7 +154,7 @@ type ServerCommonConf struct {
|
|||||||
// HeartBeatTimeout specifies the maximum time to wait for a heartbeat
|
// HeartBeatTimeout specifies the maximum time to wait for a heartbeat
|
||||||
// before terminating the connection. It is not recommended to change this
|
// before terminating the connection. It is not recommended to change this
|
||||||
// value. By default, this value is 90.
|
// value. By default, this value is 90.
|
||||||
HeartBeatTimeout int64 `json:"heart_beat_timeout"`
|
HeartbeatTimeout int64 `json:"heartbeat_timeout"`
|
||||||
// UserConnTimeout specifies the maximum time to wait for a work
|
// UserConnTimeout specifies the maximum time to wait for a work
|
||||||
// connection. By default, this value is 10.
|
// connection. By default, this value is 10.
|
||||||
UserConnTimeout int64 `json:"user_conn_timeout"`
|
UserConnTimeout int64 `json:"user_conn_timeout"`
|
||||||
@@ -199,7 +199,7 @@ func GetDefaultServerConf() ServerCommonConf {
|
|||||||
TLSCertFile: "",
|
TLSCertFile: "",
|
||||||
TLSKeyFile: "",
|
TLSKeyFile: "",
|
||||||
TLSTrustedCaFile: "",
|
TLSTrustedCaFile: "",
|
||||||
HeartBeatTimeout: 90,
|
HeartbeatTimeout: 90,
|
||||||
UserConnTimeout: 10,
|
UserConnTimeout: 10,
|
||||||
Custom404Page: "",
|
Custom404Page: "",
|
||||||
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions),
|
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions),
|
||||||
@@ -421,7 +421,7 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error
|
|||||||
err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
|
err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cfg.HeartBeatTimeout = v
|
cfg.HeartbeatTimeout = v
|
||||||
}
|
}
|
||||||
|
|
||||||
if tmpStr, ok = conf.Get("common", "tls_only"); ok && tmpStr == "true" {
|
if tmpStr, ok = conf.Get("common", "tls_only"); ok && tmpStr == "true" {
|
||||||
@@ -458,11 +458,16 @@ func UnmarshalPluginsFromIni(sections ini.File, cfg *ServerCommonConf) {
|
|||||||
for name, section := range sections {
|
for name, section := range sections {
|
||||||
if strings.HasPrefix(name, "plugin.") {
|
if strings.HasPrefix(name, "plugin.") {
|
||||||
name = strings.TrimSpace(strings.TrimPrefix(name, "plugin."))
|
name = strings.TrimSpace(strings.TrimPrefix(name, "plugin."))
|
||||||
|
var tls_verify, err = strconv.ParseBool(section["tls_verify"])
|
||||||
|
if err != nil {
|
||||||
|
tls_verify = true
|
||||||
|
}
|
||||||
options := plugin.HTTPPluginOptions{
|
options := plugin.HTTPPluginOptions{
|
||||||
Name: name,
|
Name: name,
|
||||||
Addr: section["addr"],
|
Addr: section["addr"],
|
||||||
Path: section["path"],
|
Path: section["path"],
|
||||||
Ops: strings.Split(section["ops"], ","),
|
Ops: strings.Split(section["ops"], ","),
|
||||||
|
TLSVerify: tls_verify,
|
||||||
}
|
}
|
||||||
for i := range options.Ops {
|
for i := range options.Ops {
|
||||||
options.Ops[i] = strings.TrimSpace(options.Ops[i])
|
options.Ops[i] = strings.TrimSpace(options.Ops[i])
|
||||||
|
|||||||
@@ -17,12 +17,14 @@ package plugin
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HTTPPluginOptions struct {
|
type HTTPPluginOptions struct {
|
||||||
@@ -30,6 +32,7 @@ type HTTPPluginOptions struct {
|
|||||||
Addr string
|
Addr string
|
||||||
Path string
|
Path string
|
||||||
Ops []string
|
Ops []string
|
||||||
|
TLSVerify bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type httpPlugin struct {
|
type httpPlugin struct {
|
||||||
@@ -40,10 +43,25 @@ type httpPlugin struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPPluginOptions(options HTTPPluginOptions) Plugin {
|
func NewHTTPPluginOptions(options HTTPPluginOptions) Plugin {
|
||||||
|
var url = fmt.Sprintf("%s%s", options.Addr, options.Path)
|
||||||
|
|
||||||
|
var client *http.Client
|
||||||
|
if strings.HasPrefix(url, "https://") {
|
||||||
|
tr := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: options.TLSVerify == false},
|
||||||
|
}
|
||||||
|
client = &http.Client{Transport: tr}
|
||||||
|
} else {
|
||||||
|
client = &http.Client{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(url, "https://") && !strings.HasPrefix(url, "http://") {
|
||||||
|
url = "http://" + url
|
||||||
|
}
|
||||||
return &httpPlugin{
|
return &httpPlugin{
|
||||||
options: options,
|
options: options,
|
||||||
url: fmt.Sprintf("http://%s%s", options.Addr, options.Path),
|
url: url,
|
||||||
client: &http.Client{},
|
client: client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -33,6 +34,7 @@ func OkResponse() *http.Response {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: use "CanonicalHost" func to replace all "GetHostFromAddr" func.
|
||||||
func GetHostFromAddr(addr string) (host string) {
|
func GetHostFromAddr(addr string) (host string) {
|
||||||
strs := strings.Split(addr, ":")
|
strs := strings.Split(addr, ":")
|
||||||
if len(strs) > 1 {
|
if len(strs) > 1 {
|
||||||
@@ -42,3 +44,34 @@ func GetHostFromAddr(addr string) (host string) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// canonicalHost strips port from host if present and returns the canonicalized
|
||||||
|
// host name.
|
||||||
|
func CanonicalHost(host string) (string, error) {
|
||||||
|
var err error
|
||||||
|
host = strings.ToLower(host)
|
||||||
|
if hasPort(host) {
|
||||||
|
host, _, err = net.SplitHostPort(host)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(host, ".") {
|
||||||
|
// Strip trailing dot from fully qualified domain names.
|
||||||
|
host = host[:len(host)-1]
|
||||||
|
}
|
||||||
|
return host, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasPort reports whether host contains a port number. host may be a host
|
||||||
|
// name, an IPv4 or an IPv6 address.
|
||||||
|
func hasPort(host string) bool {
|
||||||
|
colons := strings.Count(host, ":")
|
||||||
|
if colons == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if colons == 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return host[0] == '[' && strings.Contains(host, "]:")
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string = "0.34.3"
|
var version string = "0.35.0"
|
||||||
|
|
||||||
func Full() string {
|
func Full() string {
|
||||||
return version
|
return version
|
||||||
|
|||||||
+21
-7
@@ -17,6 +17,7 @@ package vhost
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@@ -59,20 +60,25 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
|
|||||||
req.URL.Scheme = "http"
|
req.URL.Scheme = "http"
|
||||||
url := req.Context().Value(RouteInfoURL).(string)
|
url := req.Context().Value(RouteInfoURL).(string)
|
||||||
oldHost := util.GetHostFromAddr(req.Context().Value(RouteInfoHost).(string))
|
oldHost := util.GetHostFromAddr(req.Context().Value(RouteInfoHost).(string))
|
||||||
host := rp.GetRealHost(oldHost, url)
|
rc := rp.GetRouteConfig(oldHost, url)
|
||||||
if host != "" {
|
if rc != nil {
|
||||||
req.Host = host
|
if rc.RewriteHost != "" {
|
||||||
|
req.Host = rc.RewriteHost
|
||||||
}
|
}
|
||||||
req.URL.Host = req.Host
|
// Set {domain}.{location} as URL host here to let http transport reuse connections.
|
||||||
|
req.URL.Host = rc.Domain + "." + base64.StdEncoding.EncodeToString([]byte(rc.Location))
|
||||||
|
|
||||||
headers := rp.GetHeaders(oldHost, url)
|
for k, v := range rc.Headers {
|
||||||
for k, v := range headers {
|
|
||||||
req.Header.Set(k, v)
|
req.Header.Set(k, v)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
req.URL.Host = req.Host
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
ResponseHeaderTimeout: rp.responseHeaderTimeout,
|
ResponseHeaderTimeout: rp.responseHeaderTimeout,
|
||||||
DisableKeepAlives: true,
|
IdleConnTimeout: 60 * time.Second,
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
url := ctx.Value(RouteInfoURL).(string)
|
url := ctx.Value(RouteInfoURL).(string)
|
||||||
host := util.GetHostFromAddr(ctx.Value(RouteInfoHost).(string))
|
host := util.GetHostFromAddr(ctx.Value(RouteInfoHost).(string))
|
||||||
@@ -107,6 +113,14 @@ func (rp *HTTPReverseProxy) UnRegister(domain string, location string) {
|
|||||||
rp.vhostRouter.Del(domain, location)
|
rp.vhostRouter.Del(domain, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rp *HTTPReverseProxy) GetRouteConfig(domain string, location string) *RouteConfig {
|
||||||
|
vr, ok := rp.getVhost(domain, location)
|
||||||
|
if ok {
|
||||||
|
return vr.payload.(*RouteConfig)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (rp *HTTPReverseProxy) GetRealHost(domain string, location string) (host string) {
|
func (rp *HTTPReverseProxy) GetRealHost(domain string, location string) (host string) {
|
||||||
vr, ok := rp.getVhost(domain, location)
|
vr, ok := rp.getVhost(domain, location)
|
||||||
if ok {
|
if ok {
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ func (v *Muxer) handle(c net.Conn) {
|
|||||||
|
|
||||||
sConn, reqInfoMap, err := v.vhostFunc(c)
|
sConn, reqInfoMap, err := v.vhostFunc(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("get hostname from http/https request error: %v", err)
|
log.Debug("get hostname from http/https request error: %v", err)
|
||||||
c.Close()
|
c.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -408,7 +408,7 @@ func (ctl *Control) manager() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-heartbeat.C:
|
case <-heartbeat.C:
|
||||||
if time.Since(ctl.lastPing) > time.Duration(ctl.serverCfg.HeartBeatTimeout)*time.Second {
|
if time.Since(ctl.lastPing) > time.Duration(ctl.serverCfg.HeartbeatTimeout)*time.Second {
|
||||||
xl.Warn("heartbeat timeout")
|
xl.Warn("heartbeat timeout")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ func (svr *Service) APIServerInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
SubdomainHost: svr.cfg.SubDomainHost,
|
SubdomainHost: svr.cfg.SubDomainHost,
|
||||||
MaxPoolCount: svr.cfg.MaxPoolCount,
|
MaxPoolCount: svr.cfg.MaxPoolCount,
|
||||||
MaxPortsPerClient: svr.cfg.MaxPortsPerClient,
|
MaxPortsPerClient: svr.cfg.MaxPortsPerClient,
|
||||||
HeartBeatTimeout: svr.cfg.HeartBeatTimeout,
|
HeartBeatTimeout: svr.cfg.HeartbeatTimeout,
|
||||||
|
|
||||||
TotalTrafficIn: serverStats.TotalTrafficIn,
|
TotalTrafficIn: serverStats.TotalTrafficIn,
|
||||||
TotalTrafficOut: serverStats.TotalTrafficOut,
|
TotalTrafficOut: serverStats.TotalTrafficOut,
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn,
|
|||||||
xl.Warn("failed to get work connection: %v", err)
|
xl.Warn("failed to get work connection: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
xl.Info("get a new work connection: [%s]", workConn.RemoteAddr().String())
|
xl.Debug("get a new work connection: [%s]", workConn.RemoteAddr().String())
|
||||||
xl.Spawn().AppendPrefix(pxy.GetName())
|
xl.Spawn().AppendPrefix(pxy.GetName())
|
||||||
workConn = frpNet.NewContextConn(pxy.ctx, workConn)
|
workConn = frpNet.NewContextConn(pxy.ctx, workConn)
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, net.Conn,
|
|||||||
xl.Info("listener is closed")
|
xl.Info("listener is closed")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
xl.Debug("get a user connection [%s]", c.RemoteAddr().String())
|
xl.Info("get a user connection [%s]", c.RemoteAddr().String())
|
||||||
go handler(p, c, pxy.serverCfg)
|
go handler(p, c, pxy.serverCfg)
|
||||||
}
|
}
|
||||||
}(listener)
|
}(listener)
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ func TestHealthCheck(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
httpSvc3 := mock.NewHTTPServer(15005, func(w http.ResponseWriter, r *http.Request) {
|
httpSvc3 := mock.NewHTTPServer(15005, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
time.Sleep(time.Second)
|
||||||
w.Write([]byte("http3"))
|
w.Write([]byte("http3"))
|
||||||
})
|
})
|
||||||
err = httpSvc3.Start()
|
err = httpSvc3.Start()
|
||||||
@@ -147,6 +148,7 @@ func TestHealthCheck(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
httpSvc4 := mock.NewHTTPServer(15006, func(w http.ResponseWriter, r *http.Request) {
|
httpSvc4 := mock.NewHTTPServer(15006, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
time.Sleep(time.Second)
|
||||||
w.Write([]byte("http4"))
|
w.Write([]byte("http4"))
|
||||||
})
|
})
|
||||||
err = httpSvc4.Start()
|
err = httpSvc4.Start()
|
||||||
@@ -277,16 +279,30 @@ func TestHealthCheck(t *testing.T) {
|
|||||||
|
|
||||||
// ****** load balancing type http ******
|
// ****** load balancing type http ******
|
||||||
result = make([]string, 0)
|
result = make([]string, 0)
|
||||||
|
var wait sync.WaitGroup
|
||||||
|
var mu sync.Mutex
|
||||||
|
wait.Add(2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wait.Done()
|
||||||
|
code, body, _, err := util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test.balancing.com", nil, "")
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(200, code)
|
||||||
|
mu.Lock()
|
||||||
|
result = append(result, body)
|
||||||
|
mu.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wait.Done()
|
||||||
code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test.balancing.com", nil, "")
|
code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test.balancing.com", nil, "")
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.Equal(200, code)
|
assert.Equal(200, code)
|
||||||
|
mu.Lock()
|
||||||
result = append(result, body)
|
result = append(result, body)
|
||||||
|
mu.Unlock()
|
||||||
code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test.balancing.com", nil, "")
|
}()
|
||||||
assert.NoError(err)
|
wait.Wait()
|
||||||
assert.Equal(200, code)
|
|
||||||
result = append(result, body)
|
|
||||||
|
|
||||||
assert.Contains(result, "http3")
|
assert.Contains(result, "http3")
|
||||||
assert.Contains(result, "http4")
|
assert.Contains(result, "http4")
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": [
|
|
||||||
["es2015", { "modules": false }]
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
[
|
|
||||||
"component",
|
|
||||||
{
|
|
||||||
"libraryName": "element-ui",
|
|
||||||
"styleLibraryName": "theme-chalk"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[*.{js,jsx,ts,tsx,vue}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# just a flag
|
||||||
|
ENV = 'development'
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# just a flag
|
||||||
|
ENV = 'production'
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
parserOptions: {
|
||||||
|
parser: 'babel-eslint',
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
node: true,
|
||||||
|
es6: true
|
||||||
|
},
|
||||||
|
extends: ['plugin:vue/recommended', 'eslint:recommended'],
|
||||||
|
|
||||||
|
// add your custom rules here
|
||||||
|
// it is base on https://github.com/vuejs/eslint-config-vue
|
||||||
|
rules: {
|
||||||
|
'vue/max-attributes-per-line': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
singleline: 10,
|
||||||
|
multiline: {
|
||||||
|
max: 1,
|
||||||
|
allowFirstLine: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'vue/singleline-html-element-content-newline': 'off',
|
||||||
|
'vue/multiline-html-element-content-newline': 'off',
|
||||||
|
'vue/name-property-casing': ['error', 'PascalCase'],
|
||||||
|
'vue/no-v-html': 'off',
|
||||||
|
'accessor-pairs': 2,
|
||||||
|
'arrow-spacing': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
before: true,
|
||||||
|
after: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'block-spacing': [2, 'always'],
|
||||||
|
'brace-style': [
|
||||||
|
2,
|
||||||
|
'1tbs',
|
||||||
|
{
|
||||||
|
allowSingleLine: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
camelcase: [
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
properties: 'always'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'comma-dangle': [2, 'never'],
|
||||||
|
'comma-spacing': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
before: false,
|
||||||
|
after: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'comma-style': [2, 'last'],
|
||||||
|
'constructor-super': 2,
|
||||||
|
curly: [2, 'multi-line'],
|
||||||
|
'dot-location': [2, 'property'],
|
||||||
|
'eol-last': 2,
|
||||||
|
eqeqeq: ['error', 'always', { null: 'ignore' }],
|
||||||
|
'generator-star-spacing': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
before: true,
|
||||||
|
after: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'handle-callback-err': [2, '^(err|error)$'],
|
||||||
|
indent: [
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
SwitchCase: 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'jsx-quotes': [2, 'prefer-single'],
|
||||||
|
'key-spacing': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
beforeColon: false,
|
||||||
|
afterColon: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'keyword-spacing': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
before: true,
|
||||||
|
after: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'new-cap': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
newIsCap: true,
|
||||||
|
capIsNew: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'new-parens': 2,
|
||||||
|
'no-array-constructor': 2,
|
||||||
|
'no-caller': 2,
|
||||||
|
'no-console': 'off',
|
||||||
|
'no-class-assign': 2,
|
||||||
|
'no-cond-assign': 2,
|
||||||
|
'no-const-assign': 2,
|
||||||
|
'no-control-regex': 0,
|
||||||
|
'no-delete-var': 2,
|
||||||
|
'no-dupe-args': 2,
|
||||||
|
'no-dupe-class-members': 2,
|
||||||
|
'no-dupe-keys': 2,
|
||||||
|
'no-duplicate-case': 2,
|
||||||
|
'no-empty-character-class': 2,
|
||||||
|
'no-empty-pattern': 2,
|
||||||
|
'no-eval': 2,
|
||||||
|
'no-ex-assign': 2,
|
||||||
|
'no-extend-native': 2,
|
||||||
|
'no-extra-bind': 2,
|
||||||
|
'no-extra-boolean-cast': 2,
|
||||||
|
'no-extra-parens': [2, 'functions'],
|
||||||
|
'no-fallthrough': 2,
|
||||||
|
'no-floating-decimal': 2,
|
||||||
|
'no-func-assign': 2,
|
||||||
|
'no-implied-eval': 2,
|
||||||
|
'no-inner-declarations': [2, 'functions'],
|
||||||
|
'no-invalid-regexp': 2,
|
||||||
|
'no-irregular-whitespace': 2,
|
||||||
|
'no-iterator': 2,
|
||||||
|
'no-label-var': 2,
|
||||||
|
'no-labels': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
allowLoop: false,
|
||||||
|
allowSwitch: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-lone-blocks': 2,
|
||||||
|
'no-mixed-spaces-and-tabs': 2,
|
||||||
|
'no-multi-spaces': 2,
|
||||||
|
'no-multi-str': 2,
|
||||||
|
'no-multiple-empty-lines': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
max: 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-native-reassign': 2,
|
||||||
|
'no-negated-in-lhs': 2,
|
||||||
|
'no-new-object': 2,
|
||||||
|
'no-new-require': 2,
|
||||||
|
'no-new-symbol': 2,
|
||||||
|
'no-new-wrappers': 2,
|
||||||
|
'no-obj-calls': 2,
|
||||||
|
'no-octal': 2,
|
||||||
|
'no-octal-escape': 2,
|
||||||
|
'no-path-concat': 2,
|
||||||
|
'no-proto': 2,
|
||||||
|
'no-redeclare': 2,
|
||||||
|
'no-regex-spaces': 2,
|
||||||
|
'no-return-assign': [2, 'except-parens'],
|
||||||
|
'no-self-assign': 2,
|
||||||
|
'no-self-compare': 2,
|
||||||
|
'no-sequences': 2,
|
||||||
|
'no-shadow-restricted-names': 2,
|
||||||
|
'no-spaced-func': 2,
|
||||||
|
'no-sparse-arrays': 2,
|
||||||
|
'no-this-before-super': 2,
|
||||||
|
'no-throw-literal': 2,
|
||||||
|
'no-trailing-spaces': 2,
|
||||||
|
'no-undef': 2,
|
||||||
|
'no-undef-init': 2,
|
||||||
|
'no-unexpected-multiline': 2,
|
||||||
|
'no-unmodified-loop-condition': 2,
|
||||||
|
'no-unneeded-ternary': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
defaultAssignment: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-unreachable': 2,
|
||||||
|
'no-unsafe-finally': 2,
|
||||||
|
'no-unused-vars': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
vars: 'all',
|
||||||
|
args: 'none'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-useless-call': 2,
|
||||||
|
'no-useless-computed-key': 2,
|
||||||
|
'no-useless-constructor': 2,
|
||||||
|
'no-useless-escape': 0,
|
||||||
|
'no-whitespace-before-property': 2,
|
||||||
|
'no-with': 2,
|
||||||
|
'one-var': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
initialized: 'never'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'operator-linebreak': [
|
||||||
|
2,
|
||||||
|
'after',
|
||||||
|
{
|
||||||
|
overrides: {
|
||||||
|
'?': 'before',
|
||||||
|
':': 'before'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'padded-blocks': [2, 'never'],
|
||||||
|
quotes: [
|
||||||
|
2,
|
||||||
|
'single',
|
||||||
|
{
|
||||||
|
avoidEscape: true,
|
||||||
|
allowTemplateLiterals: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
semi: [2, 'never'],
|
||||||
|
'semi-spacing': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
before: false,
|
||||||
|
after: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'space-before-blocks': [2, 'always'],
|
||||||
|
// 'space-before-function-paren': [2, 'never'],
|
||||||
|
'space-in-parens': [2, 'never'],
|
||||||
|
'space-infix-ops': 2,
|
||||||
|
'space-unary-ops': [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
words: true,
|
||||||
|
nonwords: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'spaced-comment': [
|
||||||
|
2,
|
||||||
|
'always',
|
||||||
|
{
|
||||||
|
markers: ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'template-curly-spacing': [2, 'never'],
|
||||||
|
'use-isnan': 2,
|
||||||
|
'valid-typeof': 2,
|
||||||
|
'wrap-iife': [2, 'any'],
|
||||||
|
'yield-star-spacing': [2, 'both'],
|
||||||
|
yoda: [2, 'never'],
|
||||||
|
'prefer-const': 2,
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||||
|
'object-curly-spacing': [
|
||||||
|
2,
|
||||||
|
'always',
|
||||||
|
{
|
||||||
|
objectsInObjects: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'array-bracket-spacing': [2, 'never']
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
-4
@@ -1,6 +1,25 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules/
|
node_modules
|
||||||
dist/
|
/dist
|
||||||
npm-debug.log
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
.idea
|
.idea
|
||||||
.vscode/settings.json
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
package-lock.json
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"printWidth": 160,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
||||||
+5
-2
@@ -1,7 +1,10 @@
|
|||||||
.PHONY: dist build
|
.PHONY: dist build
|
||||||
|
|
||||||
build:
|
build: install
|
||||||
@npm run build
|
@npm run build
|
||||||
|
|
||||||
dev: install
|
dev: install
|
||||||
@npm run dev
|
@npm run serve
|
||||||
|
|
||||||
|
install:
|
||||||
|
@npm install
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# frps
|
||||||
|
|
||||||
|
## Project setup
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and hot-reloads for development
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run serve
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and minifies for production
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lints and fixes files
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: ['@vue/cli-plugin-babel/preset']
|
||||||
|
}
|
||||||
Generated
-9334
File diff suppressed because it is too large
Load Diff
+34
-36
@@ -4,45 +4,43 @@
|
|||||||
"author": "fatedier",
|
"author": "fatedier",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "webpack-dev-server -d --inline --hot --env.dev",
|
"serve": "vue-cli-service serve",
|
||||||
"build": "rimraf dist && webpack -p --progress --hide-modules"
|
"build": "vue-cli-service build",
|
||||||
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "^3.3.7",
|
"core-js": "^3.7.0",
|
||||||
"echarts": "^3.5.0",
|
"echarts": "^4.9.0",
|
||||||
"element-ui": "^2.3.8",
|
"element-ui": "^2.14.1",
|
||||||
"humanize-plus": "^1.8.2",
|
"humanize-plus": "^1.8.2",
|
||||||
"vue": "^2.5.16",
|
"vue": "^2.6.12",
|
||||||
"vue-resource": "^1.2.1",
|
"vue-router": "^3.4.9",
|
||||||
"vue-router": "^2.3.0",
|
"vuex": "^3.5.1",
|
||||||
"whatwg-fetch": "^2.0.3"
|
"whatwg-fetch": "^3.5.0"
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^6.6.0",
|
"@vue/cli-plugin-babel": "~4.5.9",
|
||||||
"babel-core": "^6.21.0",
|
"@vue/cli-plugin-eslint": "~4.5.9",
|
||||||
"babel-eslint": "^7.1.1",
|
"@vue/cli-plugin-router": "~4.5.9",
|
||||||
"babel-loader": "^6.4.0",
|
"@vue/cli-plugin-vuex": "~4.5.9",
|
||||||
"babel-plugin-component": "^1.1.1",
|
"@vue/cli-service": "~4.5.9",
|
||||||
"babel-preset-es2015": "^6.13.2",
|
"@vue/eslint-config-standard": "^5.1.2",
|
||||||
"css-loader": "^0.27.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"eslint": "^3.12.2",
|
"eslint": "^7.14.0",
|
||||||
"eslint-config-enough": "^0.2.2",
|
"eslint-plugin-import": "^2.22.1",
|
||||||
"eslint-loader": "^1.6.3",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"file-loader": "^0.10.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"html-loader": "^0.4.5",
|
"eslint-plugin-standard": "^4.1.0",
|
||||||
"html-webpack-plugin": "^2.24.1",
|
"eslint-plugin-vue": "^7.1.0",
|
||||||
"less": "^3.0.4",
|
"less": "^3.12.2",
|
||||||
"less-loader": "^4.1.0",
|
"less-loader": "^7.1.0",
|
||||||
"postcss-loader": "^1.3.3",
|
"node-sass": "^5.0.0",
|
||||||
"rimraf": "^2.5.4",
|
"sass-loader": "^10.1.0",
|
||||||
"style-loader": "^0.13.2",
|
"vue-template-compiler": "^2.6.12"
|
||||||
"url-loader": "^1.0.1",
|
},
|
||||||
"vue-loader": "^15.0.10",
|
"browserslist": [
|
||||||
"vue-template-compiler": "^2.1.8",
|
"> 1%",
|
||||||
"webpack": "^2.2.0-rc.4",
|
"last 2 versions",
|
||||||
"webpack-dev-server": "^3.1.4"
|
"not dead"
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: [
|
|
||||||
require('autoprefixer')()
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+14
-7
@@ -6,9 +6,9 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
</header>
|
</header>
|
||||||
<section>
|
<section>
|
||||||
<el-row :gutter="20">
|
<el-row>
|
||||||
<el-col id="side-nav" :xs="24" :md="4">
|
<el-col id="side-nav" :xs="24" :md="4">
|
||||||
<el-menu default-active="1" mode="vertical" theme="light" router="false" @select="handleSelect">
|
<el-menu default-active="1" mode="vertical" theme="light" router @select="handleSelect">
|
||||||
<el-menu-item index="/">Overview</el-menu-item>
|
<el-menu-item index="/">Overview</el-menu-item>
|
||||||
<el-submenu index="/proxies">
|
<el-submenu index="/proxies">
|
||||||
<template slot="title">Proxies</template>
|
<template slot="title">Proxies</template>
|
||||||
@@ -24,21 +24,28 @@
|
|||||||
|
|
||||||
<el-col :xs="24" :md="20">
|
<el-col :xs="24" :md="20">
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<router-view></router-view>
|
<router-view v-if="serverInfo" />
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</section>
|
</section>
|
||||||
<footer></footer>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
|
computed: {
|
||||||
|
serverInfo() {
|
||||||
|
return this.$store.state.serverInfo
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
this.$store.dispatch('fetchServerInfo')
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleSelect(key, path) {
|
handleSelect(key, path) {
|
||||||
if (key == '') {
|
if (key === '') {
|
||||||
window.open("https://github.com/fatedier/frp")
|
window.open('https://github.com/fatedier/frp')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +65,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header-color {
|
.header-color {
|
||||||
background: #58B7FF;
|
background: #58b7ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content {
|
#content {
|
||||||
|
|||||||
@@ -44,8 +44,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :md="12">
|
<el-col :md="12">
|
||||||
<div id="traffic" style="width: 400px;height:250px;margin-bottom: 30px;"></div>
|
<div id="traffic" style="width: 400px; height: 250px; margin-bottom: 30px" />
|
||||||
<div id="proxies" style="width: 400px;height:250px;"></div>
|
<div id="proxies" style="width: 400px; height: 250px" />
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,71 +70,65 @@
|
|||||||
proxy_counts: ''
|
proxy_counts: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
computed: {
|
||||||
this.fetchData()
|
serverInfo() {
|
||||||
|
return this.$store.state.serverInfo
|
||||||
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
mounted() {
|
||||||
'$route': 'fetchData'
|
this.initData()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchData() {
|
initData() {
|
||||||
fetch('/api/serverinfo', {credentials: 'include'})
|
console.log(!!this.serverInfo, this.serverInfo)
|
||||||
.then(res => {
|
if (!this.serverInfo) return
|
||||||
return res.json()
|
|
||||||
}).then(json => {
|
this.version = this.serverInfo.version
|
||||||
this.version = json.version
|
this.bind_port = this.serverInfo.bind_port
|
||||||
this.bind_port = json.bind_port
|
this.bind_udp_port = this.serverInfo.bind_udp_port
|
||||||
this.bind_udp_port = json.bind_udp_port
|
if (this.bind_udp_port === 0) {
|
||||||
if (this.bind_udp_port == 0) {
|
this.bind_udp_port = 'disable'
|
||||||
this.bind_udp_port = "disable"
|
|
||||||
}
|
}
|
||||||
this.vhost_http_port = json.vhost_http_port
|
this.vhost_http_port = this.serverInfo.vhost_http_port
|
||||||
if (this.vhost_http_port == 0) {
|
if (this.vhost_http_port === 0) {
|
||||||
this.vhost_http_port = "disable"
|
this.vhost_http_port = 'disable'
|
||||||
}
|
}
|
||||||
this.vhost_https_port = json.vhost_https_port
|
this.vhost_https_port = this.serverInfo.vhost_https_port
|
||||||
if (this.vhost_https_port == 0) {
|
if (this.vhost_https_port === 0) {
|
||||||
this.vhost_https_port = "disable"
|
this.vhost_https_port = 'disable'
|
||||||
}
|
}
|
||||||
this.subdomain_host = json.subdomain_host
|
this.subdomain_host = this.serverInfo.subdomain_host
|
||||||
this.max_pool_count = json.max_pool_count
|
this.max_pool_count = this.serverInfo.max_pool_count
|
||||||
this.max_ports_per_client = json.max_ports_per_client
|
this.max_ports_per_client = this.serverInfo.max_ports_per_client
|
||||||
if (this.max_ports_per_client == 0) {
|
if (this.max_ports_per_client === 0) {
|
||||||
this.max_ports_per_client = "no limit"
|
this.max_ports_per_client = 'no limit'
|
||||||
}
|
}
|
||||||
this.heart_beat_timeout = json.heart_beat_timeout
|
this.heart_beat_timeout = this.serverInfo.heart_beat_timeout
|
||||||
this.client_counts = json.client_counts
|
this.client_counts = this.serverInfo.client_counts
|
||||||
this.cur_conns = json.cur_conns
|
this.cur_conns = this.serverInfo.cur_conns
|
||||||
this.proxy_counts = 0
|
this.proxy_counts = 0
|
||||||
if (json.proxy_type_count != null) {
|
if (this.serverInfo.proxy_type_count != null) {
|
||||||
if (json.proxy_type_count.tcp != null) {
|
if (this.serverInfo.proxy_type_count.tcp != null) {
|
||||||
this.proxy_counts += json.proxy_type_count.tcp
|
this.proxy_counts += this.serverInfo.proxy_type_count.tcp
|
||||||
}
|
}
|
||||||
if (json.proxy_type_count.udp != null) {
|
if (this.serverInfo.proxy_type_count.udp != null) {
|
||||||
this.proxy_counts += json.proxy_type_count.udp
|
this.proxy_counts += this.serverInfo.proxy_type_count.udp
|
||||||
}
|
}
|
||||||
if (json.proxy_type_count.http != null) {
|
if (this.serverInfo.proxy_type_count.http != null) {
|
||||||
this.proxy_counts += json.proxy_type_count.http
|
this.proxy_counts += this.serverInfo.proxy_type_count.http
|
||||||
}
|
}
|
||||||
if (json.proxy_type_count.https != null) {
|
if (this.serverInfo.proxy_type_count.https != null) {
|
||||||
this.proxy_counts += json.proxy_type_count.https
|
this.proxy_counts += this.serverInfo.proxy_type_count.https
|
||||||
}
|
}
|
||||||
if (json.proxy_type_count.stcp != null) {
|
if (this.serverInfo.proxy_type_count.stcp != null) {
|
||||||
this.proxy_counts += json.proxy_type_count.stcp
|
this.proxy_counts += this.serverInfo.proxy_type_count.stcp
|
||||||
}
|
}
|
||||||
if (json.proxy_type_count.xtcp != null) {
|
if (this.serverInfo.proxy_type_count.xtcp != null) {
|
||||||
this.proxy_counts += json.proxy_type_count.xtcp
|
this.proxy_counts += this.serverInfo.proxy_type_count.xtcp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DrawTrafficChart('traffic', json.total_traffic_in, json.total_traffic_out)
|
DrawTrafficChart('traffic', this.serverInfo.total_traffic_in, this.serverInfo.total_traffic_out)
|
||||||
DrawProxyChart('proxies', json)
|
DrawProxyChart('proxies', this.serverInfo)
|
||||||
}).catch( err => {
|
|
||||||
this.$message({
|
|
||||||
showClose: true,
|
|
||||||
message: 'Get server info from frps failed!',
|
|
||||||
type: 'warning'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,7 +138,7 @@
|
|||||||
.source {
|
.source {
|
||||||
border: 1px solid #eaeefb;
|
border: 1px solid #eaeefb;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: .2s;
|
transition: 0.2s;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,8 @@
|
|||||||
<el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
|
<el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
|
||||||
<el-table-column type="expand">
|
<el-table-column type="expand">
|
||||||
<template slot-scope="props">
|
<template slot-scope="props">
|
||||||
<el-popover
|
<el-popover ref="popover4" placement="right" width="600" style="margin-left: 0px" trigger="click">
|
||||||
ref="popover4"
|
<my-traffic-chart :proxy-name="props.row.name" />
|
||||||
placement="right"
|
|
||||||
width="600"
|
|
||||||
style="margin-left:0px"
|
|
||||||
trigger="click">
|
|
||||||
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
|
|
||||||
</el-popover>
|
</el-popover>
|
||||||
|
|
||||||
<el-button v-popover:popover4 type="primary" size="small" icon="view" style="margin-bottom: 10px">Traffic Statistics</el-button>
|
<el-button v-popover:popover4 type="primary" size="small" icon="view" style="margin-bottom: 10px">Traffic Statistics</el-button>
|
||||||
@@ -48,40 +43,15 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column label="Name" prop="name" sortable />
|
||||||
label="Name"
|
<el-table-column label="Port" prop="port" sortable />
|
||||||
prop="name"
|
<el-table-column label="Connections" prop="conns" sortable />
|
||||||
sortable>
|
<el-table-column label="Traffic In" prop="traffic_in" :formatter="formatTrafficIn" sortable />
|
||||||
</el-table-column>
|
<el-table-column label="Traffic Out" prop="traffic_out" :formatter="formatTrafficOut" sortable />
|
||||||
<el-table-column
|
<el-table-column label="status" prop="status" sortable>
|
||||||
label="Port"
|
|
||||||
prop="port"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Connections"
|
|
||||||
prop="conns"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Traffic In"
|
|
||||||
prop="traffic_in"
|
|
||||||
:formatter="formatTrafficIn"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Traffic Out"
|
|
||||||
prop="traffic_out"
|
|
||||||
:formatter="formatTrafficOut"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="status"
|
|
||||||
prop="status"
|
|
||||||
sortable>
|
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
|
<el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag>
|
||||||
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
|
<el-tag v-else type="danger">{{ scope.row.status }}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -89,24 +59,27 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Humanize from 'humanize-plus';
|
import Humanize from 'humanize-plus'
|
||||||
import Traffic from './Traffic.vue'
|
import Traffic from './Traffic.vue'
|
||||||
import {
|
import { HttpProxy } from '../utils/proxy.js'
|
||||||
HttpProxy
|
|
||||||
} from '../utils/proxy.js'
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
'my-traffic-chart': Traffic
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
proxies: null,
|
proxies: [],
|
||||||
vhost_http_port: "",
|
vhost_http_port: '',
|
||||||
subdomain_host: ""
|
subdomain_host: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
computed: {
|
||||||
this.fetchData()
|
serverInfo() {
|
||||||
|
return this.$store.state.serverInfo
|
||||||
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
mounted() {
|
||||||
'$route': 'fetchData'
|
this.initData()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatTrafficIn(row, column) {
|
formatTrafficIn(row, column) {
|
||||||
@@ -115,34 +88,20 @@
|
|||||||
formatTrafficOut(row, column) {
|
formatTrafficOut(row, column) {
|
||||||
return Humanize.fileSize(row.traffic_out)
|
return Humanize.fileSize(row.traffic_out)
|
||||||
},
|
},
|
||||||
fetchData() {
|
async initData() {
|
||||||
fetch('/api/serverinfo', {credentials: 'include'})
|
if (!this.serverInfo) return
|
||||||
.then(res => {
|
this.vhost_http_port = this.serverInfo.vhost_http_port
|
||||||
return res.json()
|
this.subdomain_host = this.serverInfo.subdomain_host
|
||||||
}).then(json => {
|
if (this.vhost_http_port == null || this.vhost_http_port === 0) return
|
||||||
this.vhost_http_port = json.vhost_http_port
|
|
||||||
this.subdomain_host = json.subdomain_host
|
const json = await this.$fetch('proxy/http')
|
||||||
if (this.vhost_http_port == null || this.vhost_http_port == 0) {
|
if (!json) return
|
||||||
return
|
|
||||||
} else {
|
this.proxies = []
|
||||||
fetch('/api/proxy/http', {credentials: 'include'})
|
for (const proxyStats of json.proxies) {
|
||||||
.then(res => {
|
|
||||||
return res.json()
|
|
||||||
}).then(json => {
|
|
||||||
this.proxies = new Array()
|
|
||||||
for (let proxyStats of json.proxies) {
|
|
||||||
this.proxies.push(new HttpProxy(proxyStats, this.vhost_http_port, this.subdomain_host))
|
this.proxies.push(new HttpProxy(proxyStats, this.vhost_http_port, this.subdomain_host))
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
'my-traffic-chart': Traffic
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -3,13 +3,8 @@
|
|||||||
<el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
|
<el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
|
||||||
<el-table-column type="expand">
|
<el-table-column type="expand">
|
||||||
<template slot-scope="props">
|
<template slot-scope="props">
|
||||||
<el-popover
|
<el-popover ref="popover4" placement="right" width="600" style="margin-left: 0px" trigger="click">
|
||||||
ref="popover4"
|
<my-traffic-chart :proxy-name="props.row.name" />
|
||||||
placement="right"
|
|
||||||
width="600"
|
|
||||||
style="margin-left:0px"
|
|
||||||
trigger="click">
|
|
||||||
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
|
|
||||||
</el-popover>
|
</el-popover>
|
||||||
|
|
||||||
<el-button v-popover:popover4 type="primary" size="small" icon="view" style="margin-bottom: 10px">Traffic Statistics</el-button>
|
<el-button v-popover:popover4 type="primary" size="small" icon="view" style="margin-bottom: 10px">Traffic Statistics</el-button>
|
||||||
@@ -42,66 +37,43 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column label="Name" prop="name" sortable />
|
||||||
label="Name"
|
<el-table-column label="Port" prop="port" sortable />
|
||||||
prop="name"
|
<el-table-column label="Connections" prop="conns" sortable />
|
||||||
sortable>
|
<el-table-column label="Traffic In" prop="traffic_in" :formatter="formatTrafficIn" sortable />
|
||||||
</el-table-column>
|
<el-table-column label="Traffic Out" prop="traffic_out" :formatter="formatTrafficOut" sortable />
|
||||||
<el-table-column
|
<el-table-column label="status" prop="status" sortable>
|
||||||
label="Port"
|
|
||||||
prop="port"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Connections"
|
|
||||||
prop="conns"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Traffic In"
|
|
||||||
prop="traffic_in"
|
|
||||||
:formatter="formatTrafficIn"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Traffic Out"
|
|
||||||
prop="traffic_out"
|
|
||||||
:formatter="formatTrafficOut"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="status"
|
|
||||||
prop="status"
|
|
||||||
sortable>
|
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
|
<el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag>
|
||||||
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
|
<el-tag v-else type="danger">{{ scope.row.status }}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Humanize from 'humanize-plus';
|
import Humanize from 'humanize-plus'
|
||||||
import Traffic from './Traffic.vue'
|
import Traffic from './Traffic.vue'
|
||||||
import {
|
import { HttpsProxy } from '../utils/proxy.js'
|
||||||
HttpsProxy
|
|
||||||
} from '../utils/proxy.js'
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
'my-traffic-chart': Traffic
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
proxies: null,
|
proxies: [],
|
||||||
vhost_https_port: '',
|
vhost_https_port: '',
|
||||||
subdomain_host: ''
|
subdomain_host: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
computed: {
|
||||||
this.fetchData()
|
serverInfo() {
|
||||||
|
return this.$store.state.serverInfo
|
||||||
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
mounted() {
|
||||||
'$route': 'fetchData'
|
this.initData()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatTrafficIn(row, column) {
|
formatTrafficIn(row, column) {
|
||||||
@@ -110,34 +82,21 @@
|
|||||||
formatTrafficOut(row, column) {
|
formatTrafficOut(row, column) {
|
||||||
return Humanize.fileSize(row.traffic_out)
|
return Humanize.fileSize(row.traffic_out)
|
||||||
},
|
},
|
||||||
fetchData() {
|
async initData() {
|
||||||
fetch('/api/serverinfo', {credentials: 'include'})
|
if (!this.serverInfo) return
|
||||||
.then(res => {
|
|
||||||
return res.json()
|
this.vhost_https_port = this.serverInfo.vhost_https_port
|
||||||
}).then(json => {
|
this.subdomain_host = this.serverInfo.subdomain_host
|
||||||
this.vhost_https_port = json.vhost_https_port
|
if (this.vhost_https_port == null || this.vhost_https_port === 0) return
|
||||||
this.subdomain_host = json.subdomain_host
|
|
||||||
if (this.vhost_https_port == null || this.vhost_https_port == 0) {
|
const json = await this.$fetch('proxy/https')
|
||||||
return
|
if (!json) return
|
||||||
} else {
|
|
||||||
fetch('/api/proxy/https', {credentials: 'include'})
|
this.proxies = []
|
||||||
.then(res => {
|
for (const proxyStats of json.proxies) {
|
||||||
return res.json()
|
|
||||||
}).then(json => {
|
|
||||||
this.proxies = new Array()
|
|
||||||
for (let proxyStats of json.proxies) {
|
|
||||||
this.proxies.push(new HttpsProxy(proxyStats, this.vhost_https_port, this.subdomain_host))
|
this.proxies.push(new HttpsProxy(proxyStats, this.vhost_https_port, this.subdomain_host))
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
'my-traffic-chart': Traffic
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -3,16 +3,13 @@
|
|||||||
<el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
|
<el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
|
||||||
<el-table-column type="expand">
|
<el-table-column type="expand">
|
||||||
<template slot-scope="props">
|
<template slot-scope="props">
|
||||||
<el-popover
|
<el-popover ref="popover4" placement="right" width="600" style="margin-left: 0px" trigger="click">
|
||||||
ref="popover4"
|
<my-traffic-chart :proxy-name="props.row.name" />
|
||||||
placement="right"
|
|
||||||
width="600"
|
|
||||||
style="margin-left:0px"
|
|
||||||
trigger="click">
|
|
||||||
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
|
|
||||||
</el-popover>
|
</el-popover>
|
||||||
|
|
||||||
<el-button v-popover:popover4 type="primary" size="small" icon="view" :name="props.row.name" style="margin-bottom:10px" @click="fetchData2">Traffic Statistics</el-button>
|
<el-button v-popover:popover4 type="primary" size="small" icon="view" :name="props.row.name" style="margin-bottom: 10px" @click="fetchData2">
|
||||||
|
Traffic Statistics
|
||||||
|
</el-button>
|
||||||
|
|
||||||
<el-form label-position="left" inline class="demo-table-expand">
|
<el-form label-position="left" inline class="demo-table-expand">
|
||||||
<el-form-item label="Name">
|
<el-form-item label="Name">
|
||||||
@@ -36,35 +33,14 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column label="Name" prop="name" sortable />
|
||||||
label="Name"
|
<el-table-column label="Connections" prop="conns" sortable />
|
||||||
prop="name"
|
<el-table-column label="Traffic In" prop="traffic_in" :formatter="formatTrafficIn" sortable />
|
||||||
sortable>
|
<el-table-column label="Traffic Out" prop="traffic_out" :formatter="formatTrafficOut" sortable />
|
||||||
</el-table-column>
|
<el-table-column label="status" prop="status" sortable>
|
||||||
<el-table-column
|
|
||||||
label="Connections"
|
|
||||||
prop="conns"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Traffic In"
|
|
||||||
prop="traffic_in"
|
|
||||||
:formatter="formatTrafficIn"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Traffic Out"
|
|
||||||
prop="traffic_out"
|
|
||||||
:formatter="formatTrafficOut"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="status"
|
|
||||||
prop="status"
|
|
||||||
sortable>
|
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
|
<el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag>
|
||||||
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
|
<el-tag v-else type="danger">{{ scope.row.status }}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -76,16 +52,16 @@
|
|||||||
import Traffic from './Traffic.vue'
|
import Traffic from './Traffic.vue'
|
||||||
import { StcpProxy } from '../utils/proxy.js'
|
import { StcpProxy } from '../utils/proxy.js'
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
'my-traffic-chart': Traffic
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
proxies: null
|
proxies: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
mounted() {
|
||||||
this.fetchData()
|
this.initData()
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'$route': 'fetchData'
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatTrafficIn(row, column) {
|
formatTrafficIn(row, column) {
|
||||||
@@ -94,20 +70,15 @@
|
|||||||
formatTrafficOut(row, column) {
|
formatTrafficOut(row, column) {
|
||||||
return Humanize.fileSize(row.traffic_out)
|
return Humanize.fileSize(row.traffic_out)
|
||||||
},
|
},
|
||||||
fetchData() {
|
async initData() {
|
||||||
fetch('/api/proxy/stcp', {credentials: 'include'})
|
const json = await this.$fetch('proxy/stcp')
|
||||||
.then(res => {
|
if (!json) return
|
||||||
return res.json()
|
|
||||||
}).then(json => {
|
this.proxies = []
|
||||||
this.proxies = new Array()
|
for (const proxyStats of json.proxies) {
|
||||||
for (let proxyStats of json.proxies) {
|
|
||||||
this.proxies.push(new StcpProxy(proxyStats))
|
this.proxies.push(new StcpProxy(proxyStats))
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
components: {
|
|
||||||
'my-traffic-chart': Traffic
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,16 +3,13 @@
|
|||||||
<el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
|
<el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
|
||||||
<el-table-column type="expand">
|
<el-table-column type="expand">
|
||||||
<template slot-scope="props">
|
<template slot-scope="props">
|
||||||
<el-popover
|
<el-popover placement="right" width="600" style="margin-left: 0px" trigger="click">
|
||||||
ref="popover4"
|
<my-traffic-chart :proxy-name="props.row.name" />
|
||||||
placement="right"
|
|
||||||
width="600"
|
|
||||||
style="margin-left:0px"
|
|
||||||
trigger="click">
|
|
||||||
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
|
|
||||||
</el-popover>
|
|
||||||
|
|
||||||
<el-button v-popover:popover4 type="primary" size="small" icon="view" :name="props.row.name" style="margin-bottom:10px" @click="fetchData2">Traffic Statistics</el-button>
|
<el-button slot="reference" type="primary" size="small" icon="view" :name="props.row.name" style="margin-bottom: 10px">
|
||||||
|
Traffic Statistics
|
||||||
|
</el-button>
|
||||||
|
</el-popover>
|
||||||
|
|
||||||
<el-form label-position="left" inline class="demo-table-expand">
|
<el-form label-position="left" inline class="demo-table-expand">
|
||||||
<el-form-item label="Name">
|
<el-form-item label="Name">
|
||||||
@@ -39,40 +36,15 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column label="Name" prop="name" sortable />
|
||||||
label="Name"
|
<el-table-column label="Port" prop="port" sortable />
|
||||||
prop="name"
|
<el-table-column label="Connections" prop="conns" sortable />
|
||||||
sortable>
|
<el-table-column label="Traffic In" prop="traffic_in" :formatter="formatTrafficIn" sortable />
|
||||||
</el-table-column>
|
<el-table-column label="Traffic Out" prop="traffic_out" :formatter="formatTrafficOut" sortable />
|
||||||
<el-table-column
|
<el-table-column label="status" prop="status" sortable>
|
||||||
label="Port"
|
|
||||||
prop="port"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Connections"
|
|
||||||
prop="conns"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Traffic In"
|
|
||||||
prop="traffic_in"
|
|
||||||
:formatter="formatTrafficIn"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Traffic Out"
|
|
||||||
prop="traffic_out"
|
|
||||||
:formatter="formatTrafficOut"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="status"
|
|
||||||
prop="status"
|
|
||||||
sortable>
|
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
|
<el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag>
|
||||||
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
|
<el-tag v-else type="danger">{{ scope.row.status }}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -84,16 +56,16 @@
|
|||||||
import Traffic from './Traffic.vue'
|
import Traffic from './Traffic.vue'
|
||||||
import { TcpProxy } from '../utils/proxy.js'
|
import { TcpProxy } from '../utils/proxy.js'
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
'my-traffic-chart': Traffic
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
proxies: null
|
proxies: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
mounted() {
|
||||||
this.fetchData()
|
this.initData()
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'$route': 'fetchData'
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatTrafficIn(row, column) {
|
formatTrafficIn(row, column) {
|
||||||
@@ -102,20 +74,15 @@
|
|||||||
formatTrafficOut(row, column) {
|
formatTrafficOut(row, column) {
|
||||||
return Humanize.fileSize(row.traffic_out)
|
return Humanize.fileSize(row.traffic_out)
|
||||||
},
|
},
|
||||||
fetchData() {
|
async initData() {
|
||||||
fetch('/api/proxy/tcp', {credentials: 'include'})
|
const json = await this.$fetch('proxy/tcp')
|
||||||
.then(res => {
|
if (!json) return
|
||||||
return res.json()
|
|
||||||
}).then(json => {
|
this.proxies = []
|
||||||
this.proxies = new Array()
|
for (const proxyStats of json.proxies) {
|
||||||
for (let proxyStats of json.proxies) {
|
|
||||||
this.proxies.push(new TcpProxy(proxyStats))
|
this.proxies.push(new TcpProxy(proxyStats))
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
components: {
|
|
||||||
'my-traffic-chart': Traffic
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,13 +3,8 @@
|
|||||||
<el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
|
<el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
|
||||||
<el-table-column type="expand">
|
<el-table-column type="expand">
|
||||||
<template slot-scope="props">
|
<template slot-scope="props">
|
||||||
<el-popover
|
<el-popover ref="popover4" placement="right" width="600" style="margin-left: 0px" trigger="click">
|
||||||
ref="popover4"
|
<my-traffic-chart :proxy-name="props.row.name" />
|
||||||
placement="right"
|
|
||||||
width="600"
|
|
||||||
style="margin-left:0px"
|
|
||||||
trigger="click">
|
|
||||||
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
|
|
||||||
</el-popover>
|
</el-popover>
|
||||||
|
|
||||||
<el-button v-popover:popover4 type="primary" size="small" icon="view" style="margin-bottom: 10px">Traffic Statistics</el-button>
|
<el-button v-popover:popover4 type="primary" size="small" icon="view" style="margin-bottom: 10px">Traffic Statistics</el-button>
|
||||||
@@ -39,40 +34,15 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column label="Name" prop="name" sortable />
|
||||||
label="Name"
|
<el-table-column label="Port" prop="port" sortable />
|
||||||
prop="name"
|
<el-table-column label="Connections" prop="conns" sortable />
|
||||||
sortable>
|
<el-table-column label="Traffic In" prop="traffic_in" :formatter="formatTrafficIn" sortable />
|
||||||
</el-table-column>
|
<el-table-column label="Traffic Out" prop="traffic_out" :formatter="formatTrafficOut" sortable />
|
||||||
<el-table-column
|
<el-table-column label="status" prop="status" sortable>
|
||||||
label="Port"
|
|
||||||
prop="port"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Connections"
|
|
||||||
prop="conns"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Traffic In"
|
|
||||||
prop="traffic_in"
|
|
||||||
:formatter="formatTrafficIn"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Traffic Out"
|
|
||||||
prop="traffic_out"
|
|
||||||
:formatter="formatTrafficOut"
|
|
||||||
sortable>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="status"
|
|
||||||
prop="status"
|
|
||||||
sortable>
|
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
|
<el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag>
|
||||||
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
|
<el-tag v-else type="danger">{{ scope.row.status }}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -80,22 +50,20 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Humanize from 'humanize-plus';
|
import Humanize from 'humanize-plus'
|
||||||
import Traffic from './Traffic.vue'
|
import Traffic from './Traffic.vue'
|
||||||
import {
|
import { UdpProxy } from '../utils/proxy.js'
|
||||||
UdpProxy
|
|
||||||
} from '../utils/proxy.js'
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
'my-traffic-chart': Traffic
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
proxies: null
|
proxies: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
mounted() {
|
||||||
this.fetchData()
|
this.initData()
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'$route': 'fetchData'
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatTrafficIn(row, column) {
|
formatTrafficIn(row, column) {
|
||||||
@@ -104,23 +72,16 @@
|
|||||||
formatTrafficOut(row, column) {
|
formatTrafficOut(row, column) {
|
||||||
return Humanize.fileSize(row.traffic_out)
|
return Humanize.fileSize(row.traffic_out)
|
||||||
},
|
},
|
||||||
fetchData() {
|
async initData() {
|
||||||
fetch('/api/proxy/udp', {credentials: 'include'})
|
const json = await this.$fetch('proxy/udp')
|
||||||
.then(res => {
|
if (!json) return
|
||||||
return res.json()
|
|
||||||
}).then(json => {
|
this.proxies = []
|
||||||
this.proxies = new Array()
|
for (const proxyStats of json.proxies) {
|
||||||
for (let proxyStats of json.proxies) {
|
|
||||||
this.proxies.push(new UdpProxy(proxyStats))
|
this.proxies.push(new UdpProxy(proxyStats))
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
components: {
|
|
||||||
'my-traffic-chart': Traffic
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,36 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :id="proxy_name" style="width: 600px;height:400px;"></div>
|
<div :id="proxyName" style="width: 600px; height: 400px" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { DrawProxyTrafficChart } from '../utils/chart.js'
|
import { DrawProxyTrafficChart } from '../utils/chart.js'
|
||||||
export default {
|
export default {
|
||||||
props: ['proxy_name'],
|
props: {
|
||||||
created() {
|
proxyName: {
|
||||||
this.fetchData()
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initData()
|
||||||
},
|
},
|
||||||
//watch: {
|
|
||||||
//'$route': 'fetchData'
|
|
||||||
//},
|
|
||||||
methods: {
|
methods: {
|
||||||
fetchData() {
|
async initData() {
|
||||||
let url = '/api/traffic/' + this.proxy_name
|
const json = await this.$fetch(`traffic/${this.proxyName}`)
|
||||||
fetch(url, {credentials: 'include'})
|
if (!json) return
|
||||||
.then(res => {
|
|
||||||
return res.json()
|
DrawProxyTrafficChart(this.proxyName, json.traffic_in, json.traffic_out)
|
||||||
}).then(json => {
|
|
||||||
DrawProxyTrafficChart(this.proxy_name, json.traffic_in, json.traffic_out)
|
|
||||||
}).catch( err => {
|
|
||||||
this.$message({
|
|
||||||
showClose: true,
|
|
||||||
message: 'Get server info from frps failed!' + err,
|
|
||||||
type: 'warning'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>frps dashboard</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
<!--<script src="https://code.jquery.com/jquery-3.2.0.min.js"></script>-->
|
|
||||||
<!--<script src="//cdn.bootcss.com/echarts/3.4.0/echarts.min.js"></script>-->
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
+9
-18
@@ -1,19 +1,6 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
// import ElementUI from 'element-ui'
|
// import ElementUI from 'element-ui'
|
||||||
import {
|
import { Button, Form, FormItem, Row, Col, Table, TableColumn, Popover, Menu, Submenu, MenuItem, Tag, Message } from 'element-ui'
|
||||||
Button,
|
|
||||||
Form,
|
|
||||||
FormItem,
|
|
||||||
Row,
|
|
||||||
Col,
|
|
||||||
Table,
|
|
||||||
TableColumn,
|
|
||||||
Popover,
|
|
||||||
Menu,
|
|
||||||
Submenu,
|
|
||||||
MenuItem,
|
|
||||||
Tag
|
|
||||||
} from 'element-ui'
|
|
||||||
import lang from 'element-ui/lib/locale/lang/en'
|
import lang from 'element-ui/lib/locale/lang/en'
|
||||||
import locale from 'element-ui/lib/locale'
|
import locale from 'element-ui/lib/locale'
|
||||||
import 'element-ui/lib/theme-chalk/index.css'
|
import 'element-ui/lib/theme-chalk/index.css'
|
||||||
@@ -21,6 +8,7 @@ import './utils/less/custom.less'
|
|||||||
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
|
import store from '@/store'
|
||||||
import 'whatwg-fetch'
|
import 'whatwg-fetch'
|
||||||
|
|
||||||
locale.use(lang)
|
locale.use(lang)
|
||||||
@@ -37,12 +25,15 @@ Vue.use(Menu)
|
|||||||
Vue.use(Submenu)
|
Vue.use(Submenu)
|
||||||
Vue.use(MenuItem)
|
Vue.use(MenuItem)
|
||||||
Vue.use(Tag)
|
Vue.use(Tag)
|
||||||
|
Vue.prototype.$message = Message
|
||||||
|
|
||||||
|
import fetch from '@/utils/fetch'
|
||||||
|
Vue.prototype.$fetch = fetch
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
el: '#app',
|
|
||||||
router,
|
router,
|
||||||
template: '<App/>',
|
store,
|
||||||
components: { App }
|
render: h => h(App)
|
||||||
})
|
}).$mount('#app')
|
||||||
|
|||||||
@@ -10,29 +10,36 @@ import ProxiesStcp from '../components/ProxiesStcp.vue'
|
|||||||
Vue.use(Router)
|
Vue.use(Router)
|
||||||
|
|
||||||
export default new Router({
|
export default new Router({
|
||||||
routes: [{
|
routes: [
|
||||||
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'Overview',
|
name: 'Overview',
|
||||||
component: Overview
|
component: Overview
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
path: '/proxies/tcp',
|
path: '/proxies/tcp',
|
||||||
name: 'ProxiesTcp',
|
name: 'ProxiesTcp',
|
||||||
component: ProxiesTcp
|
component: ProxiesTcp
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
path: '/proxies/udp',
|
path: '/proxies/udp',
|
||||||
name: 'ProxiesUdp',
|
name: 'ProxiesUdp',
|
||||||
component: ProxiesUdp
|
component: ProxiesUdp
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
path: '/proxies/http',
|
path: '/proxies/http',
|
||||||
name: 'ProxiesHttp',
|
name: 'ProxiesHttp',
|
||||||
component: ProxiesHttp
|
component: ProxiesHttp
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
path: '/proxies/https',
|
path: '/proxies/https',
|
||||||
name: 'ProxiesHttps',
|
name: 'ProxiesHttps',
|
||||||
component: ProxiesHttps
|
component: ProxiesHttps
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
path: '/proxies/stcp',
|
path: '/proxies/stcp',
|
||||||
name: 'ProxiesStcp',
|
name: 'ProxiesStcp',
|
||||||
component: ProxiesStcp
|
component: ProxiesStcp
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
import fetch from '@/utils/fetch'
|
||||||
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
const store = new Vuex.Store({
|
||||||
|
state: {
|
||||||
|
serverInfo: null
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
SET_SERVER_INFO(state, serverInfo) {
|
||||||
|
state.serverInfo = serverInfo
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
async fetchServerInfo({ commit }) {
|
||||||
|
const json = await fetch('serverinfo')
|
||||||
|
commit('SET_SERVER_INFO', json || null)
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default store
|
||||||
+68
-52
@@ -1,17 +1,17 @@
|
|||||||
import Humanize from "humanize-plus"
|
import Humanize from 'humanize-plus'
|
||||||
import echarts from "echarts/lib/echarts"
|
import echarts from 'echarts/lib/echarts'
|
||||||
|
|
||||||
import "echarts/theme/macarons"
|
import 'echarts/theme/macarons'
|
||||||
import "echarts/lib/chart/bar"
|
import 'echarts/lib/chart/bar'
|
||||||
import "echarts/lib/chart/pie"
|
import 'echarts/lib/chart/pie'
|
||||||
import "echarts/lib/component/tooltip"
|
import 'echarts/lib/component/tooltip'
|
||||||
import "echarts/lib/component/title"
|
import 'echarts/lib/component/title'
|
||||||
|
|
||||||
function DrawTrafficChart(elementId, trafficIn, trafficOut) {
|
function DrawTrafficChart(elementId, trafficIn, trafficOut) {
|
||||||
let myChart = echarts.init(document.getElementById(elementId), 'macarons');
|
const myChart = echarts.init(document.getElementById(elementId), 'macarons')
|
||||||
myChart.showLoading()
|
myChart.showLoading()
|
||||||
|
|
||||||
let option = {
|
const option = {
|
||||||
title: {
|
title: {
|
||||||
text: 'Network Traffic',
|
text: 'Network Traffic',
|
||||||
subtext: 'today',
|
subtext: 'today',
|
||||||
@@ -20,20 +20,24 @@ function DrawTrafficChart(elementId, trafficIn, trafficOut) {
|
|||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: function(v) {
|
formatter: function(v) {
|
||||||
return Humanize.fileSize(v.data.value) + " (" + v.percent + "%)"
|
return Humanize.fileSize(v.data.value) + ' (' + v.percent + '%)'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
series: [{
|
series: [
|
||||||
|
{
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: '55%',
|
radius: '55%',
|
||||||
center: ['50%', '60%'],
|
center: ['50%', '60%'],
|
||||||
data: [{
|
data: [
|
||||||
|
{
|
||||||
value: trafficIn,
|
value: trafficIn,
|
||||||
name: 'Traffic In'
|
name: 'Traffic In'
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
value: trafficOut,
|
value: trafficOut,
|
||||||
name: 'Traffic Out'
|
name: 'Traffic Out'
|
||||||
}, ],
|
}
|
||||||
|
],
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
emphasis: {
|
emphasis: {
|
||||||
shadowBlur: 10,
|
shadowBlur: 10,
|
||||||
@@ -41,9 +45,10 @@ function DrawTrafficChart(elementId, trafficIn, trafficOut) {
|
|||||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}]
|
}
|
||||||
};
|
]
|
||||||
myChart.setOption(option);
|
}
|
||||||
|
myChart.setOption(option)
|
||||||
myChart.hideLoading()
|
myChart.hideLoading()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,10 +71,10 @@ function DrawProxyChart(elementId, serverInfo) {
|
|||||||
if (serverInfo.proxy_type_count.xtcp == null) {
|
if (serverInfo.proxy_type_count.xtcp == null) {
|
||||||
serverInfo.proxy_type_count.xtcp = 0
|
serverInfo.proxy_type_count.xtcp = 0
|
||||||
}
|
}
|
||||||
let myChart = echarts.init(document.getElementById(elementId), 'macarons')
|
const myChart = echarts.init(document.getElementById(elementId), 'macarons')
|
||||||
myChart.showLoading()
|
myChart.showLoading()
|
||||||
|
|
||||||
let option = {
|
const option = {
|
||||||
title: {
|
title: {
|
||||||
text: 'Proxies',
|
text: 'Proxies',
|
||||||
subtext: 'now',
|
subtext: 'now',
|
||||||
@@ -81,29 +86,37 @@ function DrawProxyChart(elementId, serverInfo) {
|
|||||||
return v.data.value
|
return v.data.value
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
series: [{
|
series: [
|
||||||
|
{
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: '55%',
|
radius: '55%',
|
||||||
center: ['50%', '60%'],
|
center: ['50%', '60%'],
|
||||||
data: [{
|
data: [
|
||||||
|
{
|
||||||
value: serverInfo.proxy_type_count.tcp,
|
value: serverInfo.proxy_type_count.tcp,
|
||||||
name: 'TCP'
|
name: 'TCP'
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
value: serverInfo.proxy_type_count.udp,
|
value: serverInfo.proxy_type_count.udp,
|
||||||
name: 'UDP'
|
name: 'UDP'
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
value: serverInfo.proxy_type_count.http,
|
value: serverInfo.proxy_type_count.http,
|
||||||
name: 'HTTP'
|
name: 'HTTP'
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
value: serverInfo.proxy_type_count.https,
|
value: serverInfo.proxy_type_count.https,
|
||||||
name: 'HTTPS'
|
name: 'HTTPS'
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
value: serverInfo.proxy_type_count.stcp,
|
value: serverInfo.proxy_type_count.stcp,
|
||||||
name: 'STCP'
|
name: 'STCP'
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
value: serverInfo.proxy_type_count.xtcp,
|
value: serverInfo.proxy_type_count.xtcp,
|
||||||
name: 'XTCP'
|
name: 'XTCP'
|
||||||
}],
|
}
|
||||||
|
],
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
emphasis: {
|
emphasis: {
|
||||||
shadowBlur: 10,
|
shadowBlur: 10,
|
||||||
@@ -111,33 +124,34 @@ function DrawProxyChart(elementId, serverInfo) {
|
|||||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}]
|
}
|
||||||
};
|
]
|
||||||
myChart.setOption(option);
|
}
|
||||||
|
myChart.setOption(option)
|
||||||
myChart.hideLoading()
|
myChart.hideLoading()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7 days
|
// 7 days
|
||||||
function DrawProxyTrafficChart(elementId, trafficInArr, trafficOutArr) {
|
function DrawProxyTrafficChart(elementId, trafficInArr, trafficOutArr) {
|
||||||
let params = {
|
const params = {
|
||||||
width: '600px',
|
width: '600px',
|
||||||
height: '400px'
|
height: '400px'
|
||||||
}
|
}
|
||||||
|
|
||||||
let myChart = echarts.init(document.getElementById(elementId), 'macarons', params);
|
const myChart = echarts.init(document.getElementById(elementId), 'macarons', params)
|
||||||
myChart.showLoading()
|
myChart.showLoading()
|
||||||
|
|
||||||
trafficInArr = trafficInArr.reverse()
|
trafficInArr = trafficInArr.reverse()
|
||||||
trafficOutArr = trafficOutArr.reverse()
|
trafficOutArr = trafficOutArr.reverse()
|
||||||
let now = new Date()
|
let now = new Date()
|
||||||
now = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6)
|
now = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6)
|
||||||
let dates = new Array()
|
const dates = []
|
||||||
for (let i = 0; i < 7; i++) {
|
for (let i = 0; i < 7; i++) {
|
||||||
dates.push(now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate())
|
dates.push(now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate())
|
||||||
now = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)
|
now = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
let option = {
|
const option = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
@@ -148,9 +162,9 @@ function DrawProxyTrafficChart(elementId, trafficInArr, trafficOutArr) {
|
|||||||
if (data.length > 0) {
|
if (data.length > 0) {
|
||||||
html += data[0].name + '<br/>'
|
html += data[0].name + '<br/>'
|
||||||
}
|
}
|
||||||
for (let v of data) {
|
for (const v of data) {
|
||||||
let colorEl = '<span style="display:inline-block;margin-right:5px;' +
|
const colorEl =
|
||||||
'border-radius:10px;width:9px;height:9px;background-color:' + v.color + '"></span>';
|
'<span style="display:inline-block;margin-right:5px;' + 'border-radius:10px;width:9px;height:9px;background-color:' + v.color + '"></span>'
|
||||||
html += colorEl + v.seriesName + ': ' + Humanize.fileSize(v.value) + '<br/>'
|
html += colorEl + v.seriesName + ': ' + Humanize.fileSize(v.value) + '<br/>'
|
||||||
}
|
}
|
||||||
return html
|
return html
|
||||||
@@ -165,35 +179,37 @@ function DrawProxyTrafficChart(elementId, trafficInArr, trafficOutArr) {
|
|||||||
bottom: '3%',
|
bottom: '3%',
|
||||||
containLabel: true
|
containLabel: true
|
||||||
},
|
},
|
||||||
xAxis: [{
|
xAxis: [
|
||||||
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: dates
|
data: dates
|
||||||
}],
|
}
|
||||||
yAxis: [{
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
type: 'value',
|
type: 'value',
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
formatter: function(value) {
|
formatter: function(value) {
|
||||||
return Humanize.fileSize(value)
|
return Humanize.fileSize(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}],
|
}
|
||||||
series: [{
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
name: 'Traffic In',
|
name: 'Traffic In',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: trafficInArr
|
data: trafficInArr
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
name: 'Traffic Out',
|
name: 'Traffic Out',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: trafficOutArr
|
data: trafficOutArr
|
||||||
}]
|
}
|
||||||
};
|
]
|
||||||
myChart.setOption(option);
|
}
|
||||||
|
myChart.setOption(option)
|
||||||
myChart.hideLoading()
|
myChart.hideLoading()
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export { DrawTrafficChart, DrawProxyChart, DrawProxyTrafficChart }
|
||||||
DrawTrafficChart,
|
|
||||||
DrawProxyChart,
|
|
||||||
DrawProxyTrafficChart
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { Message } from 'element-ui'
|
||||||
|
|
||||||
|
export default function(api, init = {}) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
fetch(`/api/${api}`, Object.assign({ credentials: 'include' }, init))
|
||||||
|
.then(res => {
|
||||||
|
if (res.status < 200 || res.status >= 300) {
|
||||||
|
Message.warning('Get server info from frps failed!')
|
||||||
|
resolve()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(res ? res.json() : undefined)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.$message.error(err.message)
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
+25
-25
@@ -5,8 +5,8 @@ class BaseProxy {
|
|||||||
this.encryption = proxyStats.conf.use_encryption
|
this.encryption = proxyStats.conf.use_encryption
|
||||||
this.compression = proxyStats.conf.use_compression
|
this.compression = proxyStats.conf.use_compression
|
||||||
} else {
|
} else {
|
||||||
this.encryption = ""
|
this.encryption = ''
|
||||||
this.compression = ""
|
this.compression = ''
|
||||||
}
|
}
|
||||||
this.conns = proxyStats.cur_conns
|
this.conns = proxyStats.cur_conns
|
||||||
this.traffic_in = proxyStats.today_traffic_in
|
this.traffic_in = proxyStats.today_traffic_in
|
||||||
@@ -20,13 +20,13 @@ class BaseProxy {
|
|||||||
class TcpProxy extends BaseProxy {
|
class TcpProxy extends BaseProxy {
|
||||||
constructor(proxyStats) {
|
constructor(proxyStats) {
|
||||||
super(proxyStats)
|
super(proxyStats)
|
||||||
this.type = "tcp"
|
this.type = 'tcp'
|
||||||
if (proxyStats.conf != null) {
|
if (proxyStats.conf != null) {
|
||||||
this.addr = ":" + proxyStats.conf.remote_port
|
this.addr = ':' + proxyStats.conf.remote_port
|
||||||
this.port = proxyStats.conf.remote_port
|
this.port = proxyStats.conf.remote_port
|
||||||
} else {
|
} else {
|
||||||
this.addr = ""
|
this.addr = ''
|
||||||
this.port = ""
|
this.port = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,13 +34,13 @@ class TcpProxy extends BaseProxy {
|
|||||||
class UdpProxy extends BaseProxy {
|
class UdpProxy extends BaseProxy {
|
||||||
constructor(proxyStats) {
|
constructor(proxyStats) {
|
||||||
super(proxyStats)
|
super(proxyStats)
|
||||||
this.type = "udp"
|
this.type = 'udp'
|
||||||
if (proxyStats.conf != null) {
|
if (proxyStats.conf != null) {
|
||||||
this.addr = ":" + proxyStats.conf.remote_port
|
this.addr = ':' + proxyStats.conf.remote_port
|
||||||
this.port = proxyStats.conf.remote_port
|
this.port = proxyStats.conf.remote_port
|
||||||
} else {
|
} else {
|
||||||
this.addr = ""
|
this.addr = ''
|
||||||
this.port = ""
|
this.port = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,22 +48,22 @@ class UdpProxy extends BaseProxy {
|
|||||||
class HttpProxy extends BaseProxy {
|
class HttpProxy extends BaseProxy {
|
||||||
constructor(proxyStats, port, subdomain_host) {
|
constructor(proxyStats, port, subdomain_host) {
|
||||||
super(proxyStats)
|
super(proxyStats)
|
||||||
this.type = "http"
|
this.type = 'http'
|
||||||
this.port = port
|
this.port = port
|
||||||
if (proxyStats.conf != null) {
|
if (proxyStats.conf != null) {
|
||||||
this.custom_domains = proxyStats.conf.custom_domains
|
this.custom_domains = proxyStats.conf.custom_domains
|
||||||
this.host_header_rewrite = proxyStats.conf.host_header_rewrite
|
this.host_header_rewrite = proxyStats.conf.host_header_rewrite
|
||||||
this.locations = proxyStats.conf.locations
|
this.locations = proxyStats.conf.locations
|
||||||
if (proxyStats.conf.sub_domain != "") {
|
if (proxyStats.conf.sub_domain !== '') {
|
||||||
this.subdomain = proxyStats.conf.sub_domain + "." + subdomain_host
|
this.subdomain = proxyStats.conf.sub_domain + '.' + subdomain_host
|
||||||
} else {
|
} else {
|
||||||
this.subdomain = ""
|
this.subdomain = ''
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.custom_domains = ""
|
this.custom_domains = ''
|
||||||
this.host_header_rewrite = ""
|
this.host_header_rewrite = ''
|
||||||
this.subdomain = ""
|
this.subdomain = ''
|
||||||
this.locations = ""
|
this.locations = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,18 +71,18 @@ class HttpProxy extends BaseProxy {
|
|||||||
class HttpsProxy extends BaseProxy {
|
class HttpsProxy extends BaseProxy {
|
||||||
constructor(proxyStats, port, subdomain_host) {
|
constructor(proxyStats, port, subdomain_host) {
|
||||||
super(proxyStats)
|
super(proxyStats)
|
||||||
this.type = "https"
|
this.type = 'https'
|
||||||
this.port = port
|
this.port = port
|
||||||
if (proxyStats.conf != null) {
|
if (proxyStats.conf != null) {
|
||||||
this.custom_domains = proxyStats.conf.custom_domains
|
this.custom_domains = proxyStats.conf.custom_domains
|
||||||
if (proxyStats.conf.sub_domain != "") {
|
if (proxyStats.conf.sub_domain !== '') {
|
||||||
this.subdomain = proxyStats.conf.sub_domain + "." + subdomain_host
|
this.subdomain = proxyStats.conf.sub_domain + '.' + subdomain_host
|
||||||
} else {
|
} else {
|
||||||
this.subdomain = ""
|
this.subdomain = ''
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.custom_domains = ""
|
this.custom_domains = ''
|
||||||
this.subdomain = ""
|
this.subdomain = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ class HttpsProxy extends BaseProxy {
|
|||||||
class StcpProxy extends BaseProxy {
|
class StcpProxy extends BaseProxy {
|
||||||
constructor(proxyStats) {
|
constructor(proxyStats) {
|
||||||
super(proxyStats)
|
super(proxyStats)
|
||||||
this.type = "stcp"
|
this.type = 'stcp'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
module.exports = {
|
||||||
|
publicPath: './',
|
||||||
|
devServer: {
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 8010,
|
||||||
|
proxy: {
|
||||||
|
'/api/': {
|
||||||
|
target: 'http://127.0.0.1:8080/api',
|
||||||
|
changeOrigin: true,
|
||||||
|
pathRewrite: {
|
||||||
|
'^/api': ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
const path = require('path')
|
|
||||||
var webpack = require('webpack')
|
|
||||||
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
|
||||||
var VueLoaderPlugin = require('vue-loader/lib/plugin')
|
|
||||||
var url = require('url')
|
|
||||||
var publicPath = ''
|
|
||||||
|
|
||||||
module.exports = (options = {}) => ({
|
|
||||||
entry: {
|
|
||||||
vendor: './src/main'
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
path: path.resolve(__dirname, 'dist'),
|
|
||||||
filename: options.dev ? '[name].js' : '[name].js?[chunkhash]',
|
|
||||||
chunkFilename: '[id].js?[chunkhash]',
|
|
||||||
publicPath: options.dev ? '/assets/' : publicPath
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
extensions: ['.js', '.vue', '.json'],
|
|
||||||
alias: {
|
|
||||||
'vue$': 'vue/dist/vue.esm.js',
|
|
||||||
'@': path.resolve(__dirname, 'src'),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [{
|
|
||||||
test: /\.vue$/,
|
|
||||||
loader: 'vue-loader'
|
|
||||||
}, {
|
|
||||||
test: /\.js$/,
|
|
||||||
use: ['babel-loader'],
|
|
||||||
exclude: /node_modules/
|
|
||||||
}, {
|
|
||||||
test: /\.html$/,
|
|
||||||
use: [{
|
|
||||||
loader: 'html-loader',
|
|
||||||
options: {
|
|
||||||
root: path.resolve(__dirname, 'src'),
|
|
||||||
attrs: ['img:src', 'link:href']
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
test: /\.less$/,
|
|
||||||
loader: 'style-loader!css-loader!postcss-loader!less-loader'
|
|
||||||
}, {
|
|
||||||
test: /\.css$/,
|
|
||||||
use: ['style-loader', 'css-loader', 'postcss-loader']
|
|
||||||
}, {
|
|
||||||
test: /favicon\.png$/,
|
|
||||||
use: [{
|
|
||||||
loader: 'file-loader',
|
|
||||||
options: {
|
|
||||||
name: '[name].[ext]?[hash]'
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
|
|
||||||
exclude: /favicon\.png$/,
|
|
||||||
use: [{
|
|
||||||
loader: 'url-loader',
|
|
||||||
options: {
|
|
||||||
limit: 10000
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new webpack.optimize.CommonsChunkPlugin({
|
|
||||||
names: ['vendor', 'manifest']
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
favicon: 'src/assets/favicon.ico',
|
|
||||||
template: 'src/index.html'
|
|
||||||
}),
|
|
||||||
new webpack.NormalModuleReplacementPlugin(/element-ui[\/\\]lib[\/\\]locale[\/\\]lang[\/\\]zh-CN/, 'element-ui/lib/locale/lang/en'),
|
|
||||||
new webpack.DefinePlugin({
|
|
||||||
'process.env': {
|
|
||||||
NODE_ENV: '"production"'
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
new webpack.optimize.UglifyJsPlugin({
|
|
||||||
sourceMap: false,
|
|
||||||
comments: false,
|
|
||||||
compress: {
|
|
||||||
warnings: false
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
new VueLoaderPlugin()
|
|
||||||
],
|
|
||||||
devServer: {
|
|
||||||
host: '127.0.0.1',
|
|
||||||
port: 8010,
|
|
||||||
proxy: {
|
|
||||||
'/api/': {
|
|
||||||
target: 'http://127.0.0.1:8080',
|
|
||||||
changeOrigin: true,
|
|
||||||
pathRewrite: {
|
|
||||||
'^/api': ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
historyApiFallback: {
|
|
||||||
index: url.parse(options.dev ? '/assets/' : publicPath).pathname
|
|
||||||
}
|
|
||||||
}//,
|
|
||||||
//devtool: options.dev ? '#eval-source-map' : '#source-map'
|
|
||||||
})
|
|
||||||
+9478
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user