mirror of https://github.com/fatedier/frp
commit
65d8fe37c5
12
Makefile
12
Makefile
|
@ -6,11 +6,12 @@ build: frps frpc
|
||||||
|
|
||||||
# compile assets into binary file
|
# compile assets into binary file
|
||||||
file:
|
file:
|
||||||
rm -rf ./assets/static/*
|
rm -rf ./assets/frps/static/*
|
||||||
cp -rf ./web/frps/dist/* ./assets/static
|
rm -rf ./assets/frpc/static/*
|
||||||
go get -d github.com/rakyll/statik
|
cp -rf ./web/frps/dist/* ./assets/frps/static
|
||||||
go install github.com/rakyll/statik
|
cp -rf ./web/frpc/dist/* ./assets/frpc/static
|
||||||
rm -rf ./assets/statik
|
rm -rf ./assets/frps/statik
|
||||||
|
rm -rf ./assets/frpc/statik
|
||||||
go generate ./assets/...
|
go generate ./assets/...
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
|
@ -18,7 +19,6 @@ fmt:
|
||||||
|
|
||||||
frps:
|
frps:
|
||||||
go build -o bin/frps ./cmd/frps
|
go build -o bin/frps ./cmd/frps
|
||||||
@cp -rf ./assets/static ./bin
|
|
||||||
|
|
||||||
frpc:
|
frpc:
|
||||||
go build -o bin/frpc ./cmd/frpc
|
go build -o bin/frpc ./cmd/frpc
|
||||||
|
|
|
@ -14,8 +14,10 @@
|
||||||
|
|
||||||
package assets
|
package assets
|
||||||
|
|
||||||
//go:generate statik -src=./static
|
//go:generate statik -src=./frps/static -dest=./frps
|
||||||
//go:generate go fmt statik/statik.go
|
//go:generate statik -src=./frpc/static -dest=./frpc
|
||||||
|
//go:generate go fmt ./frps/statik/statik.go
|
||||||
|
//go:generate go fmt ./frpc/statik/statik.go
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -24,8 +26,6 @@ import (
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/rakyll/statik/fs"
|
"github.com/rakyll/statik/fs"
|
||||||
|
|
||||||
_ "github.com/fatedier/frp/assets/statik"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>frp client admin UI</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?d2cd6337d30c7b22e836"></script><script type="text/javascript" src="vendor.js?edb271e1d9c81f857840"></script></body> </html>
|
|
@ -0,0 +1 @@
|
||||||
|
!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:"edb271e1d9c81f857840"}[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
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -20,6 +20,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/assets"
|
||||||
"github.com/fatedier/frp/g"
|
"github.com/fatedier/frp/g"
|
||||||
frpNet "github.com/fatedier/frp/utils/net"
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
|
|
||||||
|
@ -41,6 +42,15 @@ func (svr *Service) RunAdminServer(addr string, port int) (err error) {
|
||||||
// api, see dashboard_api.go
|
// api, see dashboard_api.go
|
||||||
router.HandleFunc("/api/reload", svr.apiReload).Methods("GET")
|
router.HandleFunc("/api/reload", svr.apiReload).Methods("GET")
|
||||||
router.HandleFunc("/api/status", svr.apiStatus).Methods("GET")
|
router.HandleFunc("/api/status", svr.apiStatus).Methods("GET")
|
||||||
|
router.HandleFunc("/api/config", svr.apiGetConfig).Methods("GET")
|
||||||
|
router.HandleFunc("/api/config", svr.apiPutConfig).Methods("PUT")
|
||||||
|
|
||||||
|
// view
|
||||||
|
router.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET")
|
||||||
|
router.PathPrefix("/static/").Handler(frpNet.MakeHttpGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET")
|
||||||
|
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.Redirect(w, r, "/static/", http.StatusMovedPermanently)
|
||||||
|
})
|
||||||
|
|
||||||
address := fmt.Sprintf("%s:%d", addr, port)
|
address := fmt.Sprintf("%s:%d", addr, port)
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
|
|
|
@ -17,6 +17,7 @@ package client
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -28,57 +29,53 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type GeneralResponse struct {
|
type GeneralResponse struct {
|
||||||
Code int64 `json:"code"`
|
Code int
|
||||||
Msg string `json:"msg"`
|
Msg string
|
||||||
}
|
}
|
||||||
|
|
||||||
// api/reload
|
// GET api/reload
|
||||||
type ReloadResp struct {
|
|
||||||
GeneralResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
|
func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
res := GeneralResponse{Code: 200}
|
||||||
buf []byte
|
|
||||||
res ReloadResp
|
|
||||||
)
|
|
||||||
defer func() {
|
|
||||||
log.Info("Http response [/api/reload]: code [%d]", res.Code)
|
|
||||||
buf, _ = json.Marshal(&res)
|
|
||||||
w.Write(buf)
|
|
||||||
}()
|
|
||||||
|
|
||||||
log.Info("Http request: [/api/reload]")
|
log.Info("Http request [/api/reload]")
|
||||||
|
defer func() {
|
||||||
|
log.Info("Http response [/api/reload], code [%d]", res.Code)
|
||||||
|
w.WriteHeader(res.Code)
|
||||||
|
if len(res.Msg) > 0 {
|
||||||
|
w.Write([]byte(res.Msg))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
content, err := config.GetRenderedConfFromFile(g.GlbClientCfg.CfgFile)
|
content, err := config.GetRenderedConfFromFile(g.GlbClientCfg.CfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Code = 1
|
res.Code = 400
|
||||||
res.Msg = err.Error()
|
res.Msg = err.Error()
|
||||||
log.Error("reload frpc config file error: %v", err)
|
log.Warn("reload frpc config file error: %s", res.Msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
newCommonCfg, err := config.UnmarshalClientConfFromIni(nil, content)
|
newCommonCfg, err := config.UnmarshalClientConfFromIni(nil, content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Code = 2
|
res.Code = 400
|
||||||
res.Msg = err.Error()
|
res.Msg = err.Error()
|
||||||
log.Error("reload frpc common section error: %v", err)
|
log.Warn("reload frpc common section error: %s", res.Msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(g.GlbClientCfg.User, content, newCommonCfg.Start)
|
pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(g.GlbClientCfg.User, content, newCommonCfg.Start)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Code = 3
|
res.Code = 400
|
||||||
res.Msg = err.Error()
|
res.Msg = err.Error()
|
||||||
log.Error("reload frpc proxy config error: %v", err)
|
log.Warn("reload frpc proxy config error: %s", res.Msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = svr.ReloadConf(pxyCfgs, visitorCfgs)
|
err = svr.ReloadConf(pxyCfgs, visitorCfgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Code = 4
|
res.Code = 500
|
||||||
res.Msg = err.Error()
|
res.Msg = err.Error()
|
||||||
log.Error("reload frpc proxy config error: %v", err)
|
log.Warn("reload frpc proxy config error: %s", res.Msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Info("success reload conf")
|
log.Info("success reload conf")
|
||||||
|
@ -163,7 +160,7 @@ func NewProxyStatusResp(status *proxy.ProxyStatus) ProxyStatusResp {
|
||||||
return psr
|
return psr
|
||||||
}
|
}
|
||||||
|
|
||||||
// api/status
|
// GET api/status
|
||||||
func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
|
func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
buf []byte
|
buf []byte
|
||||||
|
@ -175,14 +172,14 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
res.Https = make([]ProxyStatusResp, 0)
|
res.Https = make([]ProxyStatusResp, 0)
|
||||||
res.Stcp = make([]ProxyStatusResp, 0)
|
res.Stcp = make([]ProxyStatusResp, 0)
|
||||||
res.Xtcp = make([]ProxyStatusResp, 0)
|
res.Xtcp = make([]ProxyStatusResp, 0)
|
||||||
|
|
||||||
|
log.Info("Http request [/api/status]")
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Info("Http response [/api/status]")
|
log.Info("Http response [/api/status]")
|
||||||
buf, _ = json.Marshal(&res)
|
buf, _ = json.Marshal(&res)
|
||||||
w.Write(buf)
|
w.Write(buf)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.Info("Http request: [/api/status]")
|
|
||||||
|
|
||||||
ps := svr.ctl.pm.GetAllProxyStatus()
|
ps := svr.ctl.pm.GetAllProxyStatus()
|
||||||
for _, status := range ps {
|
for _, status := range ps {
|
||||||
switch status.Type {
|
switch status.Type {
|
||||||
|
@ -208,3 +205,120 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
sort.Sort(ByProxyStatusResp(res.Xtcp))
|
sort.Sort(ByProxyStatusResp(res.Xtcp))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET api/config
|
||||||
|
func (svr *Service) apiGetConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
|
res := GeneralResponse{Code: 200}
|
||||||
|
|
||||||
|
log.Info("Http get request [/api/config]")
|
||||||
|
defer func() {
|
||||||
|
log.Info("Http get response [/api/config], code [%d]", res.Code)
|
||||||
|
w.WriteHeader(res.Code)
|
||||||
|
if len(res.Msg) > 0 {
|
||||||
|
w.Write([]byte(res.Msg))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if g.GlbClientCfg.CfgFile == "" {
|
||||||
|
res.Code = 400
|
||||||
|
res.Msg = "frpc has no config file path"
|
||||||
|
log.Warn("%s", res.Msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := config.GetRenderedConfFromFile(g.GlbClientCfg.CfgFile)
|
||||||
|
if err != nil {
|
||||||
|
res.Code = 400
|
||||||
|
res.Msg = err.Error()
|
||||||
|
log.Warn("load frpc config file error: %s", res.Msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := strings.Split(content, "\n")
|
||||||
|
newRows := make([]string, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
row = strings.TrimSpace(row)
|
||||||
|
if strings.HasPrefix(row, "token") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newRows = append(newRows, row)
|
||||||
|
}
|
||||||
|
res.Msg = strings.Join(newRows, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT api/config
|
||||||
|
func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
|
res := GeneralResponse{Code: 200}
|
||||||
|
|
||||||
|
log.Info("Http put request [/api/config]")
|
||||||
|
defer func() {
|
||||||
|
log.Info("Http put response [/api/config], code [%d]", res.Code)
|
||||||
|
w.WriteHeader(res.Code)
|
||||||
|
if len(res.Msg) > 0 {
|
||||||
|
w.Write([]byte(res.Msg))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// get new config content
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
res.Code = 400
|
||||||
|
res.Msg = fmt.Sprintf("read request body error: %v", err)
|
||||||
|
log.Warn("%s", res.Msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(body) == 0 {
|
||||||
|
res.Code = 400
|
||||||
|
res.Msg = "body can't be empty"
|
||||||
|
log.Warn("%s", res.Msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get token from origin content
|
||||||
|
token := ""
|
||||||
|
b, err := ioutil.ReadFile(g.GlbClientCfg.CfgFile)
|
||||||
|
if err != nil {
|
||||||
|
res.Code = 400
|
||||||
|
res.Msg = err.Error()
|
||||||
|
log.Warn("load frpc config file error: %s", res.Msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
content := string(b)
|
||||||
|
|
||||||
|
for _, row := range strings.Split(content, "\n") {
|
||||||
|
row = strings.TrimSpace(row)
|
||||||
|
if strings.HasPrefix(row, "token") {
|
||||||
|
token = row
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpRows := make([]string, 0)
|
||||||
|
for _, row := range strings.Split(string(body), "\n") {
|
||||||
|
row = strings.TrimSpace(row)
|
||||||
|
if strings.HasPrefix(row, "token") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tmpRows = append(tmpRows, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
newRows := make([]string, 0)
|
||||||
|
if token != "" {
|
||||||
|
for _, row := range tmpRows {
|
||||||
|
newRows = append(newRows, row)
|
||||||
|
if strings.HasPrefix(row, "[common]") {
|
||||||
|
newRows = append(newRows, token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content = strings.Join(newRows, "\n")
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(g.GlbClientCfg.CfgFile, []byte(content), 0644)
|
||||||
|
if err != nil {
|
||||||
|
res.Code = 500
|
||||||
|
res.Msg = fmt.Sprintf("write content to frpc config file error: %v", err)
|
||||||
|
log.Warn("%s", res.Msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -53,32 +53,32 @@ func NewProxy(pxyConf config.ProxyConf) (pxy Proxy) {
|
||||||
switch cfg := pxyConf.(type) {
|
switch cfg := pxyConf.(type) {
|
||||||
case *config.TcpProxyConf:
|
case *config.TcpProxyConf:
|
||||||
pxy = &TcpProxy{
|
pxy = &TcpProxy{
|
||||||
BaseProxy: baseProxy,
|
BaseProxy: &baseProxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.UdpProxyConf:
|
case *config.UdpProxyConf:
|
||||||
pxy = &UdpProxy{
|
pxy = &UdpProxy{
|
||||||
BaseProxy: baseProxy,
|
BaseProxy: &baseProxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.HttpProxyConf:
|
case *config.HttpProxyConf:
|
||||||
pxy = &HttpProxy{
|
pxy = &HttpProxy{
|
||||||
BaseProxy: baseProxy,
|
BaseProxy: &baseProxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.HttpsProxyConf:
|
case *config.HttpsProxyConf:
|
||||||
pxy = &HttpsProxy{
|
pxy = &HttpsProxy{
|
||||||
BaseProxy: baseProxy,
|
BaseProxy: &baseProxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.StcpProxyConf:
|
case *config.StcpProxyConf:
|
||||||
pxy = &StcpProxy{
|
pxy = &StcpProxy{
|
||||||
BaseProxy: baseProxy,
|
BaseProxy: &baseProxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.XtcpProxyConf:
|
case *config.XtcpProxyConf:
|
||||||
pxy = &XtcpProxy{
|
pxy = &XtcpProxy{
|
||||||
BaseProxy: baseProxy,
|
BaseProxy: &baseProxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ type BaseProxy struct {
|
||||||
|
|
||||||
// TCP
|
// TCP
|
||||||
type TcpProxy struct {
|
type TcpProxy struct {
|
||||||
BaseProxy
|
*BaseProxy
|
||||||
|
|
||||||
cfg *config.TcpProxyConf
|
cfg *config.TcpProxyConf
|
||||||
proxyPlugin plugin.Plugin
|
proxyPlugin plugin.Plugin
|
||||||
|
@ -122,7 +122,7 @@ func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
|
|
||||||
// HTTP
|
// HTTP
|
||||||
type HttpProxy struct {
|
type HttpProxy struct {
|
||||||
BaseProxy
|
*BaseProxy
|
||||||
|
|
||||||
cfg *config.HttpProxyConf
|
cfg *config.HttpProxyConf
|
||||||
proxyPlugin plugin.Plugin
|
proxyPlugin plugin.Plugin
|
||||||
|
@ -151,7 +151,7 @@ func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
|
|
||||||
// HTTPS
|
// HTTPS
|
||||||
type HttpsProxy struct {
|
type HttpsProxy struct {
|
||||||
BaseProxy
|
*BaseProxy
|
||||||
|
|
||||||
cfg *config.HttpsProxyConf
|
cfg *config.HttpsProxyConf
|
||||||
proxyPlugin plugin.Plugin
|
proxyPlugin plugin.Plugin
|
||||||
|
@ -180,7 +180,7 @@ func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
|
|
||||||
// STCP
|
// STCP
|
||||||
type StcpProxy struct {
|
type StcpProxy struct {
|
||||||
BaseProxy
|
*BaseProxy
|
||||||
|
|
||||||
cfg *config.StcpProxyConf
|
cfg *config.StcpProxyConf
|
||||||
proxyPlugin plugin.Plugin
|
proxyPlugin plugin.Plugin
|
||||||
|
@ -209,7 +209,7 @@ func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
|
|
||||||
// XTCP
|
// XTCP
|
||||||
type XtcpProxy struct {
|
type XtcpProxy struct {
|
||||||
BaseProxy
|
*BaseProxy
|
||||||
|
|
||||||
cfg *config.XtcpProxyConf
|
cfg *config.XtcpProxyConf
|
||||||
proxyPlugin plugin.Plugin
|
proxyPlugin plugin.Plugin
|
||||||
|
@ -308,7 +308,7 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
|
|
||||||
// UDP
|
// UDP
|
||||||
type UdpProxy struct {
|
type UdpProxy struct {
|
||||||
BaseProxy
|
*BaseProxy
|
||||||
|
|
||||||
cfg *config.UdpProxyConf
|
cfg *config.UdpProxyConf
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/assets"
|
||||||
"github.com/fatedier/frp/g"
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
|
@ -49,7 +50,14 @@ type Service struct {
|
||||||
closedCh chan int
|
closedCh chan int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) (svr *Service) {
|
func NewService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) (svr *Service, err error) {
|
||||||
|
// Init assets
|
||||||
|
err = assets.Load("")
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Load assets error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
svr = &Service{
|
svr = &Service{
|
||||||
pxyCfgs: pxyCfgs,
|
pxyCfgs: pxyCfgs,
|
||||||
visitorCfgs: visitorCfgs,
|
visitorCfgs: visitorCfgs,
|
||||||
|
|
|
@ -52,12 +52,12 @@ func NewVisitor(ctl *Control, cfg config.VisitorConf) (visitor Visitor) {
|
||||||
switch cfg := cfg.(type) {
|
switch cfg := cfg.(type) {
|
||||||
case *config.StcpVisitorConf:
|
case *config.StcpVisitorConf:
|
||||||
visitor = &StcpVisitor{
|
visitor = &StcpVisitor{
|
||||||
BaseVisitor: baseVisitor,
|
BaseVisitor: &baseVisitor,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.XtcpVisitorConf:
|
case *config.XtcpVisitorConf:
|
||||||
visitor = &XtcpVisitor{
|
visitor = &XtcpVisitor{
|
||||||
BaseVisitor: baseVisitor,
|
BaseVisitor: &baseVisitor,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ type BaseVisitor struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type StcpVisitor struct {
|
type StcpVisitor struct {
|
||||||
BaseVisitor
|
*BaseVisitor
|
||||||
|
|
||||||
cfg *config.StcpVisitorConf
|
cfg *config.StcpVisitorConf
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,7 @@ func (sv *StcpVisitor) handleConn(userConn frpNet.Conn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type XtcpVisitor struct {
|
type XtcpVisitor struct {
|
||||||
BaseVisitor
|
*BaseVisitor
|
||||||
|
|
||||||
cfg *config.XtcpVisitorConf
|
cfg *config.XtcpVisitorConf
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,10 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package main // "github.com/fatedier/frp/cmd/frpc"
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "github.com/fatedier/frp/assets/frpc/statik"
|
||||||
"github.com/fatedier/frp/cmd/frpc/sub"
|
"github.com/fatedier/frp/cmd/frpc/sub"
|
||||||
|
|
||||||
"github.com/fatedier/golib/crypto"
|
"github.com/fatedier/golib/crypto"
|
||||||
|
|
|
@ -16,7 +16,6 @@ package sub
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -25,7 +24,6 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/client"
|
|
||||||
"github.com/fatedier/frp/g"
|
"github.com/fatedier/frp/g"
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
)
|
)
|
||||||
|
@ -79,21 +77,16 @@ func reload() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode == 200 {
|
||||||
return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
res := &client.GeneralResponse{}
|
return fmt.Errorf("code [%d], %s", resp.StatusCode, strings.TrimSpace(string(body)))
|
||||||
err = json.Unmarshal(body, &res)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
|
||||||
} else if res.Code != 0 {
|
|
||||||
return fmt.Errorf(res.Msg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,7 +205,11 @@ func startService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]co
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
svr := client.NewService(pxyCfgs, visitorCfgs)
|
svr, errRet := client.NewService(pxyCfgs, visitorCfgs)
|
||||||
|
if errRet != nil {
|
||||||
|
err = errRet
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Capture the exit signal if we use kcp.
|
// Capture the exit signal if we use kcp.
|
||||||
if g.GlbClientCfg.Protocol == "kcp" {
|
if g.GlbClientCfg.Protocol == "kcp" {
|
||||||
|
|
|
@ -12,10 +12,12 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package main // "github.com/fatedier/frp/cmd/frps"
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fatedier/golib/crypto"
|
"github.com/fatedier/golib/crypto"
|
||||||
|
|
||||||
|
_ "github.com/fatedier/frp/assets/frps/statik"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
@ -51,7 +51,7 @@ type ServerCommonConf struct {
|
||||||
VhostHttpPort int `json:"vhost_http_port"`
|
VhostHttpPort int `json:"vhost_http_port"`
|
||||||
|
|
||||||
// if VhostHttpsPort equals 0, don't listen a public port for https protocol
|
// if VhostHttpsPort equals 0, don't listen a public port for https protocol
|
||||||
VhostHttpsPort int `json:"vhost_http_port"`
|
VhostHttpsPort int `json:"vhost_https_port"`
|
||||||
|
|
||||||
VhostHttpTimeout int64 `json:"vhost_http_timeout"`
|
VhostHttpTimeout int64 `json:"vhost_http_timeout"`
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,6 @@ func ForwardUserConn(udpConn *net.UDPConn, readCh <-chan *msg.UdpPacket, sendCh
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UdpPacket, sendCh chan<- msg.Message) {
|
func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UdpPacket, sendCh chan<- msg.Message) {
|
||||||
|
|
|
@ -28,13 +28,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type GeneralResponse struct {
|
type GeneralResponse struct {
|
||||||
Code int64 `json:"code"`
|
Code int
|
||||||
Msg string `json:"msg"`
|
Msg string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerInfoResp struct {
|
type ServerInfoResp struct {
|
||||||
GeneralResponse
|
|
||||||
|
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
BindPort int `json:"bind_port"`
|
BindPort int `json:"bind_port"`
|
||||||
BindUdpPort int `json:"bind_udp_port"`
|
BindUdpPort int `json:"bind_udp_port"`
|
||||||
|
@ -55,18 +53,19 @@ type ServerInfoResp struct {
|
||||||
|
|
||||||
// api/serverinfo
|
// api/serverinfo
|
||||||
func (svr *Service) ApiServerInfo(w http.ResponseWriter, r *http.Request) {
|
func (svr *Service) ApiServerInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
res := GeneralResponse{Code: 200}
|
||||||
buf []byte
|
|
||||||
res ServerInfoResp
|
|
||||||
)
|
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
|
log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
|
||||||
|
w.WriteHeader(res.Code)
|
||||||
|
if len(res.Msg) > 0 {
|
||||||
|
w.Write([]byte(res.Msg))
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.Info("Http request: [%s]", r.URL.Path)
|
log.Info("Http request: [%s]", r.URL.Path)
|
||||||
cfg := &g.GlbServerCfg.ServerCommonConf
|
cfg := &g.GlbServerCfg.ServerCommonConf
|
||||||
serverStats := svr.statsCollector.GetServer()
|
serverStats := svr.statsCollector.GetServer()
|
||||||
res = ServerInfoResp{
|
svrResp := ServerInfoResp{
|
||||||
Version: version.Full(),
|
Version: version.Full(),
|
||||||
BindPort: cfg.BindPort,
|
BindPort: cfg.BindPort,
|
||||||
BindUdpPort: cfg.BindUdpPort,
|
BindUdpPort: cfg.BindUdpPort,
|
||||||
|
@ -85,8 +84,8 @@ func (svr *Service) ApiServerInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
ProxyTypeCounts: serverStats.ProxyTypeCounts,
|
ProxyTypeCounts: serverStats.ProxyTypeCounts,
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, _ = json.Marshal(&res)
|
buf, _ := json.Marshal(&svrResp)
|
||||||
w.Write(buf)
|
res.Msg = string(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseOutConf struct {
|
type BaseOutConf struct {
|
||||||
|
@ -155,31 +154,29 @@ type ProxyStatsInfo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetProxyInfoResp struct {
|
type GetProxyInfoResp struct {
|
||||||
GeneralResponse
|
|
||||||
Proxies []*ProxyStatsInfo `json:"proxies"`
|
Proxies []*ProxyStatsInfo `json:"proxies"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// api/proxy/:type
|
// api/proxy/:type
|
||||||
func (svr *Service) ApiProxyByType(w http.ResponseWriter, r *http.Request) {
|
func (svr *Service) ApiProxyByType(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
res := GeneralResponse{Code: 200}
|
||||||
buf []byte
|
|
||||||
res GetProxyInfoResp
|
|
||||||
)
|
|
||||||
params := mux.Vars(r)
|
params := mux.Vars(r)
|
||||||
proxyType := params["type"]
|
proxyType := params["type"]
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
|
log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
|
||||||
log.Info(r.URL.Path)
|
w.WriteHeader(res.Code)
|
||||||
log.Info(r.URL.RawPath)
|
if len(res.Msg) > 0 {
|
||||||
|
w.Write([]byte(res.Msg))
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
log.Info("Http request: [%s]", r.URL.Path)
|
log.Info("Http request: [%s]", r.URL.Path)
|
||||||
|
|
||||||
res.Proxies = svr.getProxyStatsByType(proxyType)
|
proxyInfoResp := GetProxyInfoResp{}
|
||||||
|
proxyInfoResp.Proxies = svr.getProxyStatsByType(proxyType)
|
||||||
buf, _ = json.Marshal(&res)
|
|
||||||
w.Write(buf)
|
|
||||||
|
|
||||||
|
buf, _ := json.Marshal(&proxyInfoResp)
|
||||||
|
res.Msg = string(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (svr *Service) getProxyStatsByType(proxyType string) (proxyInfos []*ProxyStatsInfo) {
|
func (svr *Service) getProxyStatsByType(proxyType string) (proxyInfos []*ProxyStatsInfo) {
|
||||||
|
@ -215,8 +212,6 @@ func (svr *Service) getProxyStatsByType(proxyType string) (proxyInfos []*ProxySt
|
||||||
|
|
||||||
// Get proxy info by name.
|
// Get proxy info by name.
|
||||||
type GetProxyStatsResp struct {
|
type GetProxyStatsResp struct {
|
||||||
GeneralResponse
|
|
||||||
|
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Conf interface{} `json:"conf"`
|
Conf interface{} `json:"conf"`
|
||||||
TodayTrafficIn int64 `json:"today_traffic_in"`
|
TodayTrafficIn int64 `json:"today_traffic_in"`
|
||||||
|
@ -229,45 +224,50 @@ type GetProxyStatsResp struct {
|
||||||
|
|
||||||
// api/proxy/:type/:name
|
// api/proxy/:type/:name
|
||||||
func (svr *Service) ApiProxyByTypeAndName(w http.ResponseWriter, r *http.Request) {
|
func (svr *Service) ApiProxyByTypeAndName(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
res := GeneralResponse{Code: 200}
|
||||||
buf []byte
|
|
||||||
res GetProxyStatsResp
|
|
||||||
)
|
|
||||||
params := mux.Vars(r)
|
params := mux.Vars(r)
|
||||||
proxyType := params["type"]
|
proxyType := params["type"]
|
||||||
name := params["name"]
|
name := params["name"]
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
|
log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
|
||||||
|
w.WriteHeader(res.Code)
|
||||||
|
if len(res.Msg) > 0 {
|
||||||
|
w.Write([]byte(res.Msg))
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
log.Info("Http request: [%s]", r.URL.Path)
|
log.Info("Http request: [%s]", r.URL.Path)
|
||||||
|
|
||||||
res = svr.getProxyStatsByTypeAndName(proxyType, name)
|
proxyStatsResp := GetProxyStatsResp{}
|
||||||
|
proxyStatsResp, res.Code, res.Msg = svr.getProxyStatsByTypeAndName(proxyType, name)
|
||||||
|
if res.Code != 200 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
buf, _ = json.Marshal(&res)
|
buf, _ := json.Marshal(&proxyStatsResp)
|
||||||
w.Write(buf)
|
res.Msg = string(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (svr *Service) getProxyStatsByTypeAndName(proxyType string, proxyName string) (proxyInfo GetProxyStatsResp) {
|
func (svr *Service) getProxyStatsByTypeAndName(proxyType string, proxyName string) (proxyInfo GetProxyStatsResp, code int, msg string) {
|
||||||
proxyInfo.Name = proxyName
|
proxyInfo.Name = proxyName
|
||||||
ps := svr.statsCollector.GetProxiesByTypeAndName(proxyType, proxyName)
|
ps := svr.statsCollector.GetProxiesByTypeAndName(proxyType, proxyName)
|
||||||
if ps == nil {
|
if ps == nil {
|
||||||
proxyInfo.Code = 1
|
code = 404
|
||||||
proxyInfo.Msg = "no proxy info found"
|
msg = "no proxy info found"
|
||||||
} else {
|
} else {
|
||||||
if pxy, ok := svr.pxyManager.GetByName(proxyName); ok {
|
if pxy, ok := svr.pxyManager.GetByName(proxyName); ok {
|
||||||
content, err := json.Marshal(pxy.GetConf())
|
content, err := json.Marshal(pxy.GetConf())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err)
|
log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err)
|
||||||
proxyInfo.Code = 2
|
code = 400
|
||||||
proxyInfo.Msg = "parse conf error"
|
msg = "parse conf error"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
proxyInfo.Conf = getConfByType(ps.Type)
|
proxyInfo.Conf = getConfByType(ps.Type)
|
||||||
if err = json.Unmarshal(content, &proxyInfo.Conf); err != nil {
|
if err = json.Unmarshal(content, &proxyInfo.Conf); err != nil {
|
||||||
log.Warn("unmarshal proxy [%s] conf info error: %v", ps.Name, err)
|
log.Warn("unmarshal proxy [%s] conf info error: %v", ps.Name, err)
|
||||||
proxyInfo.Code = 2
|
code = 400
|
||||||
proxyInfo.Msg = "parse conf error"
|
msg = "parse conf error"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
proxyInfo.Status = consts.Online
|
proxyInfo.Status = consts.Online
|
||||||
|
@ -286,36 +286,38 @@ func (svr *Service) getProxyStatsByTypeAndName(proxyType string, proxyName strin
|
||||||
|
|
||||||
// api/traffic/:name
|
// api/traffic/:name
|
||||||
type GetProxyTrafficResp struct {
|
type GetProxyTrafficResp struct {
|
||||||
GeneralResponse
|
|
||||||
|
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
TrafficIn []int64 `json:"traffic_in"`
|
TrafficIn []int64 `json:"traffic_in"`
|
||||||
TrafficOut []int64 `json:"traffic_out"`
|
TrafficOut []int64 `json:"traffic_out"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (svr *Service) ApiProxyTraffic(w http.ResponseWriter, r *http.Request) {
|
func (svr *Service) ApiProxyTraffic(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
res := GeneralResponse{Code: 200}
|
||||||
buf []byte
|
|
||||||
res GetProxyTrafficResp
|
|
||||||
)
|
|
||||||
params := mux.Vars(r)
|
params := mux.Vars(r)
|
||||||
name := params["name"]
|
name := params["name"]
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
|
log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
|
||||||
|
w.WriteHeader(res.Code)
|
||||||
|
if len(res.Msg) > 0 {
|
||||||
|
w.Write([]byte(res.Msg))
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
log.Info("Http request: [%s]", r.URL.Path)
|
log.Info("Http request: [%s]", r.URL.Path)
|
||||||
|
|
||||||
res.Name = name
|
trafficResp := GetProxyTrafficResp{}
|
||||||
|
trafficResp.Name = name
|
||||||
proxyTrafficInfo := svr.statsCollector.GetProxyTraffic(name)
|
proxyTrafficInfo := svr.statsCollector.GetProxyTraffic(name)
|
||||||
|
|
||||||
if proxyTrafficInfo == nil {
|
if proxyTrafficInfo == nil {
|
||||||
res.Code = 1
|
res.Code = 404
|
||||||
res.Msg = "no proxy info found"
|
res.Msg = "no proxy info found"
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
res.TrafficIn = proxyTrafficInfo.TrafficIn
|
trafficResp.TrafficIn = proxyTrafficInfo.TrafficIn
|
||||||
res.TrafficOut = proxyTrafficInfo.TrafficOut
|
trafficResp.TrafficOut = proxyTrafficInfo.TrafficOut
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, _ = json.Marshal(&res)
|
buf, _ := json.Marshal(&trafficResp)
|
||||||
w.Write(buf)
|
res.Msg = string(buf)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type HttpProxy struct {
|
type HttpProxy struct {
|
||||||
BaseProxy
|
*BaseProxy
|
||||||
cfg *config.HttpProxyConf
|
cfg *config.HttpProxyConf
|
||||||
|
|
||||||
closeFuncs []func()
|
closeFuncs []func()
|
||||||
|
|
|
@ -24,7 +24,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type HttpsProxy struct {
|
type HttpsProxy struct {
|
||||||
BaseProxy
|
*BaseProxy
|
||||||
cfg *config.HttpsProxyConf
|
cfg *config.HttpsProxyConf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,33 +135,33 @@ func NewProxy(runId string, rc *controller.ResourceController, statsCollector st
|
||||||
case *config.TcpProxyConf:
|
case *config.TcpProxyConf:
|
||||||
basePxy.usedPortsNum = 1
|
basePxy.usedPortsNum = 1
|
||||||
pxy = &TcpProxy{
|
pxy = &TcpProxy{
|
||||||
BaseProxy: basePxy,
|
BaseProxy: &basePxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.HttpProxyConf:
|
case *config.HttpProxyConf:
|
||||||
pxy = &HttpProxy{
|
pxy = &HttpProxy{
|
||||||
BaseProxy: basePxy,
|
BaseProxy: &basePxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.HttpsProxyConf:
|
case *config.HttpsProxyConf:
|
||||||
pxy = &HttpsProxy{
|
pxy = &HttpsProxy{
|
||||||
BaseProxy: basePxy,
|
BaseProxy: &basePxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.UdpProxyConf:
|
case *config.UdpProxyConf:
|
||||||
basePxy.usedPortsNum = 1
|
basePxy.usedPortsNum = 1
|
||||||
pxy = &UdpProxy{
|
pxy = &UdpProxy{
|
||||||
BaseProxy: basePxy,
|
BaseProxy: &basePxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.StcpProxyConf:
|
case *config.StcpProxyConf:
|
||||||
pxy = &StcpProxy{
|
pxy = &StcpProxy{
|
||||||
BaseProxy: basePxy,
|
BaseProxy: &basePxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.XtcpProxyConf:
|
case *config.XtcpProxyConf:
|
||||||
pxy = &XtcpProxy{
|
pxy = &XtcpProxy{
|
||||||
BaseProxy: basePxy,
|
BaseProxy: &basePxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type StcpProxy struct {
|
type StcpProxy struct {
|
||||||
BaseProxy
|
*BaseProxy
|
||||||
cfg *config.StcpProxyConf
|
cfg *config.StcpProxyConf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type TcpProxy struct {
|
type TcpProxy struct {
|
||||||
BaseProxy
|
*BaseProxy
|
||||||
cfg *config.TcpProxyConf
|
cfg *config.TcpProxyConf
|
||||||
|
|
||||||
realPort int
|
realPort int
|
||||||
|
|
|
@ -30,7 +30,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type UdpProxy struct {
|
type UdpProxy struct {
|
||||||
BaseProxy
|
*BaseProxy
|
||||||
cfg *config.UdpProxyConf
|
cfg *config.UdpProxyConf
|
||||||
|
|
||||||
realPort int
|
realPort int
|
||||||
|
|
|
@ -24,7 +24,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type XtcpProxy struct {
|
type XtcpProxy struct {
|
||||||
BaseProxy
|
*BaseProxy
|
||||||
cfg *config.XtcpProxyConf
|
cfg *config.XtcpProxyConf
|
||||||
|
|
||||||
closeCh chan struct{}
|
closeCh chan struct{}
|
||||||
|
|
|
@ -89,7 +89,7 @@ func NewService() (svr *Service, err error) {
|
||||||
// Init group controller
|
// Init group controller
|
||||||
svr.rc.TcpGroupCtl = group.NewTcpGroupCtl(svr.rc.TcpPortManager)
|
svr.rc.TcpGroupCtl = group.NewTcpGroupCtl(svr.rc.TcpPortManager)
|
||||||
|
|
||||||
// Init assets.
|
// Init assets
|
||||||
err = assets.Load(cfg.AssetsDir)
|
err = assets.Load(cfg.AssetsDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Load assets error: %v", err)
|
err = fmt.Errorf("Load assets error: %v", err)
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/fatedier/beego/logs"
|
"github.com/fatedier/beego/logs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Log is the under log object
|
||||||
var Log *logs.BeeLogger
|
var Log *logs.BeeLogger
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -33,6 +34,7 @@ func InitLog(logWay string, logFile string, logLevel string, maxdays int64) {
|
||||||
SetLogLevel(logLevel)
|
SetLogLevel(logLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLogFile to configure log params
|
||||||
// logWay: file or console
|
// logWay: file or console
|
||||||
func SetLogFile(logWay string, logFile string, maxdays int64) {
|
func SetLogFile(logWay string, logFile string, maxdays int64) {
|
||||||
if logWay == "console" {
|
if logWay == "console" {
|
||||||
|
@ -43,6 +45,7 @@ func SetLogFile(logWay string, logFile string, maxdays int64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLogLevel set log level, default is warning
|
||||||
// value: error, warning, info, debug, trace
|
// value: error, warning, info, debug, trace
|
||||||
func SetLogLevel(logLevel string) {
|
func SetLogLevel(logLevel string) {
|
||||||
level := 4 // warning
|
level := 4 // warning
|
||||||
|
@ -85,7 +88,7 @@ func Trace(format string, v ...interface{}) {
|
||||||
Log.Trace(format, v...)
|
Log.Trace(format, v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logger
|
// Logger is the log interface
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
AddLogPrefix(string)
|
AddLogPrefix(string)
|
||||||
GetPrefixStr() string
|
GetPrefixStr() string
|
||||||
|
|
|
@ -31,6 +31,7 @@ type WebsocketListener struct {
|
||||||
httpMutex *http.ServeMux
|
httpMutex *http.ServeMux
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewWebsocketListener to handle websocket connections
|
||||||
// ln: tcp listener for websocket connections
|
// ln: tcp listener for websocket connections
|
||||||
func NewWebsocketListener(ln net.Listener) (wl *WebsocketListener) {
|
func NewWebsocketListener(ln net.Listener) (wl *WebsocketListener) {
|
||||||
wl = &WebsocketListener{
|
wl = &WebsocketListener{
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string = "0.23.3"
|
var version string = "0.24.0"
|
||||||
|
|
||||||
func Full() string {
|
func Full() string {
|
||||||
return version
|
return version
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
["es2015", { "modules": false }]
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
[
|
||||||
|
"component",
|
||||||
|
{
|
||||||
|
"libraryName": "element-ui",
|
||||||
|
"styleLibraryName": "theme-chalk"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
.DS_Store
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
npm-debug.log
|
||||||
|
.idea
|
||||||
|
.vscode/settings.json
|
|
@ -0,0 +1,6 @@
|
||||||
|
.PHONY: dist build
|
||||||
|
build:
|
||||||
|
@npm run build
|
||||||
|
|
||||||
|
dev:
|
||||||
|
@npm run dev
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"name": "frpc-web",
|
||||||
|
"description": "An admin web ui for frp client.",
|
||||||
|
"author": "fatedier",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "webpack-dev-server -d --inline --hot --env.dev",
|
||||||
|
"build": "rimraf dist && webpack -p --progress --hide-modules"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"element-ui": "^2.5.3",
|
||||||
|
"vue": "^2.5.22",
|
||||||
|
"vue-resource": "^1.5.1",
|
||||||
|
"vue-router": "^3.0.2",
|
||||||
|
"whatwg-fetch": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"autoprefixer": "^9.4.7",
|
||||||
|
"babel-core": "^6.26.3",
|
||||||
|
"babel-eslint": "^10.0.1",
|
||||||
|
"babel-loader": "^7.1.5",
|
||||||
|
"babel-plugin-component": "^1.1.1",
|
||||||
|
"babel-preset-es2015": "^6.24.1",
|
||||||
|
"css-loader": "^2.1.0",
|
||||||
|
"eslint": "^5.12.1",
|
||||||
|
"eslint-config-enough": "^0.3.4",
|
||||||
|
"eslint-loader": "^2.1.1",
|
||||||
|
"file-loader": "^3.0.1",
|
||||||
|
"html-loader": "^0.5.5",
|
||||||
|
"html-webpack-plugin": "^2.24.1",
|
||||||
|
"less": "^3.9.0",
|
||||||
|
"less-loader": "^4.1.0",
|
||||||
|
"postcss-loader": "^3.0.0",
|
||||||
|
"rimraf": "^2.6.3",
|
||||||
|
"style-loader": "^0.23.1",
|
||||||
|
"url-loader": "^1.1.2",
|
||||||
|
"vue-loader": "^15.6.2",
|
||||||
|
"vue-template-compiler": "^2.5.22",
|
||||||
|
"webpack": "^2.7.0",
|
||||||
|
"webpack-cli": "^3.2.1",
|
||||||
|
"webpack-dev-server": "^3.1.14"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
require('autoprefixer')()
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<header class="grid-content header-color">
|
||||||
|
<el-row>
|
||||||
|
<a class="brand" href="#">frp client</a>
|
||||||
|
</el-row>
|
||||||
|
</header>
|
||||||
|
<section>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col id="side-nav" :xs="24" :md="4">
|
||||||
|
<el-menu default-active="1" mode="vertical" theme="light" router="false" @select="handleSelect">
|
||||||
|
<el-menu-item index="/">Overview</el-menu-item>
|
||||||
|
<el-menu-item index="/configure">Configure</el-menu-item>
|
||||||
|
<el-menu-item index="">Help</el-menu-item>
|
||||||
|
</el-menu>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :xs="24" :md="20">
|
||||||
|
<div id="content">
|
||||||
|
<router-view></router-view>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</section>
|
||||||
|
<footer></footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
handleSelect(key, path) {
|
||||||
|
if (key == '') {
|
||||||
|
window.open("https://github.com/fatedier/frp")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #fafafa;
|
||||||
|
margin: 0px;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
</style>
|
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
|
@ -0,0 +1,93 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-row id="head">
|
||||||
|
<el-button type="primary" @click="fetchData">Refresh</el-button>
|
||||||
|
<el-button type="primary" @click="uploadConfig">Upload</el-button>
|
||||||
|
</el-row>
|
||||||
|
<el-input type="textarea" autosize v-model="textarea" placeholder="frpc configrue file, can not be empty..."></el-input>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
textarea: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route': 'fetchData'
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData() {
|
||||||
|
fetch('/api/config', {credentials: 'include'})
|
||||||
|
.then(res => {
|
||||||
|
return res.text()
|
||||||
|
}).then(text => {
|
||||||
|
this.textarea= text
|
||||||
|
}).catch( err => {
|
||||||
|
this.$message({
|
||||||
|
showClose: true,
|
||||||
|
message: 'Get configure content from frpc failed!',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
uploadConfig() {
|
||||||
|
this.$confirm('This operation will upload your frpc configure file content and hot reload it, do you want to continue?', 'Notice', {
|
||||||
|
confirmButtonText: 'Yes',
|
||||||
|
cancelButtonText: 'No',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
if (this.textarea == "") {
|
||||||
|
this.$message({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Configure content can not be empty!'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('/api/config', {
|
||||||
|
credentials: 'include',
|
||||||
|
method: 'PUT',
|
||||||
|
body: this.textarea,
|
||||||
|
}).then(() => {
|
||||||
|
fetch('/api/reload', {credentials: 'include'})
|
||||||
|
.then(() => {
|
||||||
|
this.$message({
|
||||||
|
type: 'success',
|
||||||
|
message: 'Success'
|
||||||
|
})
|
||||||
|
}).catch(err => {
|
||||||
|
this.$message({
|
||||||
|
showClose: true,
|
||||||
|
message: 'Reload frpc configure file error, ' + err,
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}).catch(err => {
|
||||||
|
this.$message({
|
||||||
|
showClose: true,
|
||||||
|
message: 'Put config to frpc and hot reload failed!',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
this.$message({
|
||||||
|
type: 'info',
|
||||||
|
message: 'Canceled'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#head {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,72 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-row>
|
||||||
|
<el-col :md="24">
|
||||||
|
<div>
|
||||||
|
<el-table :data="status" stripe style="width: 100%" :default-sort="{prop: 'type', order: 'ascending'}">
|
||||||
|
<el-table-column prop="name" label="name"></el-table-column>
|
||||||
|
<el-table-column prop="type" label="type" width="150"></el-table-column>
|
||||||
|
<el-table-column prop="local_addr" label="local address" width="200"></el-table-column>
|
||||||
|
<el-table-column prop="plugin" label="plugin" width="200"></el-table-column>
|
||||||
|
<el-table-column prop="remote_addr" label="remote address"></el-table-column>
|
||||||
|
<el-table-column prop="status" label="status" width="150"></el-table-column>
|
||||||
|
<el-table-column prop="err" label="info"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
status: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route': 'fetchData'
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData() {
|
||||||
|
fetch('/api/status', {credentials: 'include'})
|
||||||
|
.then(res => {
|
||||||
|
return res.json()
|
||||||
|
}).then(json => {
|
||||||
|
this.status = new Array()
|
||||||
|
for (let s of json.tcp) {
|
||||||
|
this.status.push(s)
|
||||||
|
}
|
||||||
|
for (let s of json.udp) {
|
||||||
|
this.status.push(s)
|
||||||
|
}
|
||||||
|
for (let s of json.http) {
|
||||||
|
this.status.push(s)
|
||||||
|
}
|
||||||
|
for (let s of json.https) {
|
||||||
|
this.status.push(s)
|
||||||
|
}
|
||||||
|
for (let s of json.stcp) {
|
||||||
|
this.status.push(s)
|
||||||
|
}
|
||||||
|
for (let s of json.xtcp) {
|
||||||
|
this.status.push(s)
|
||||||
|
}
|
||||||
|
}).catch( err => {
|
||||||
|
this.$message({
|
||||||
|
showClose: true,
|
||||||
|
message: 'Get status info from frpc failed!',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>frp client admin UI</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>
|
|
@ -0,0 +1,52 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
// import ElementUI from 'element-ui'
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Form,
|
||||||
|
FormItem,
|
||||||
|
Row,
|
||||||
|
Col,
|
||||||
|
Table,
|
||||||
|
TableColumn,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
MessageBox,
|
||||||
|
Message,
|
||||||
|
Input
|
||||||
|
} from 'element-ui'
|
||||||
|
import lang from 'element-ui/lib/locale/lang/en'
|
||||||
|
import locale from 'element-ui/lib/locale'
|
||||||
|
import 'element-ui/lib/theme-chalk/index.css'
|
||||||
|
import './utils/less/custom.less'
|
||||||
|
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
import 'whatwg-fetch'
|
||||||
|
|
||||||
|
locale.use(lang)
|
||||||
|
|
||||||
|
Vue.use(Button)
|
||||||
|
Vue.use(Form)
|
||||||
|
Vue.use(FormItem)
|
||||||
|
Vue.use(Row)
|
||||||
|
Vue.use(Col)
|
||||||
|
Vue.use(Table)
|
||||||
|
Vue.use(TableColumn)
|
||||||
|
Vue.use(Menu)
|
||||||
|
Vue.use(MenuItem)
|
||||||
|
Vue.use(Input)
|
||||||
|
|
||||||
|
Vue.prototype.$msgbox = MessageBox;
|
||||||
|
Vue.prototype.$confirm = MessageBox.confirm
|
||||||
|
Vue.prototype.$message = Message
|
||||||
|
|
||||||
|
//Vue.use(ElementUI)
|
||||||
|
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
el: '#app',
|
||||||
|
router,
|
||||||
|
template: '<App/>',
|
||||||
|
components: { App }
|
||||||
|
})
|
|
@ -0,0 +1,18 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import Router from 'vue-router'
|
||||||
|
import Overview from '../components/Overview.vue'
|
||||||
|
import Configure from '../components/Configure.vue'
|
||||||
|
|
||||||
|
Vue.use(Router)
|
||||||
|
|
||||||
|
export default new Router({
|
||||||
|
routes: [{
|
||||||
|
path: '/',
|
||||||
|
name: 'Overview',
|
||||||
|
component: Overview
|
||||||
|
},{
|
||||||
|
path: '/configure',
|
||||||
|
name: 'Configure',
|
||||||
|
component: Configure,
|
||||||
|
}]
|
||||||
|
})
|
|
@ -0,0 +1,22 @@
|
||||||
|
@color: red;
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
span {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-table-expand {
|
||||||
|
font-size: 0;
|
||||||
|
|
||||||
|
label {
|
||||||
|
width: 90px;
|
||||||
|
color: #99a9bf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
class ProxyStatus {
|
||||||
|
constructor(status) {
|
||||||
|
this.name = status.name
|
||||||
|
this.type = status.type
|
||||||
|
this.status = status.status
|
||||||
|
this.err = status.err
|
||||||
|
this.local_addr = status.local_addr
|
||||||
|
this.plugin = status.plugin
|
||||||
|
this.remote_addr = status.remote_addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {ProxyStatus}
|
|
@ -0,0 +1,107 @@
|
||||||
|
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'
|
||||||
|
})
|
File diff suppressed because it is too large
Load Diff
|
@ -1,9 +1,7 @@
|
||||||
.PHONY: dist build
|
.PHONY: dist build
|
||||||
install:
|
|
||||||
@npm install
|
build:
|
||||||
|
@npm run build
|
||||||
|
|
||||||
dev: install
|
dev: install
|
||||||
@npm run dev
|
@npm run dev
|
||||||
|
|
||||||
build:
|
|
||||||
@npm run build
|
|
Loading…
Reference in New Issue