From 43001690415614d43d54d68b77c61b6f926cdf09 Mon Sep 17 00:00:00 2001 From: fatedier <fatedier@gmail.com> Date: Wed, 10 Aug 2016 20:18:36 +0800 Subject: [PATCH] assets: optimize static files archetucture --- Makefile | 1 + assets/{static => css}/bootstrap.min.css | 6 -- assets/{static => css}/font-awesome.min.css | 0 assets/css/iconfont.css | 18 ++++ assets/{static => font}/iconfont.eot | Bin assets/{static => font}/iconfont.svg | 0 assets/{static => font}/iconfont.ttf | Bin assets/{static => font}/iconfont.woff | Bin assets/index.html | 89 +++++++++----------- assets/{static => js}/angular.min.js | 1 - assets/{static => js}/bootstrap.min.js | 0 assets/{static => js}/echarts.min.js | 0 assets/{static => js}/jquery.min.js | 0 conf/frps.ini | 2 + src/frp/models/server/config.go | 10 +++ src/frp/models/server/dashboard.go | 71 +++++++--------- src/frp/models/server/dashboard_api.go | 17 ++-- src/frp/models/server/dashboard_view.go | 35 ++++++++ src/frp/models/server/server.go | 8 +- src/frp/utils/vhost/vhost.go | 1 - 20 files changed, 153 insertions(+), 106 deletions(-) rename assets/{static => css}/bootstrap.min.css (99%) rename assets/{static => css}/font-awesome.min.css (100%) create mode 100644 assets/css/iconfont.css rename assets/{static => font}/iconfont.eot (100%) rename assets/{static => font}/iconfont.svg (100%) rename assets/{static => font}/iconfont.ttf (100%) rename assets/{static => font}/iconfont.woff (100%) rename assets/{static => js}/angular.min.js (99%) rename assets/{static => js}/bootstrap.min.js (100%) rename assets/{static => js}/echarts.min.js (100%) rename assets/{static => js}/jquery.min.js (100%) create mode 100644 src/frp/models/server/dashboard_view.go diff --git a/Makefile b/Makefile index 21362383..366cd882 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ fmt: frps: go build -o bin/frps ./src/frp/cmd/frps + cp -rf ./assets ./bin frpc: go build -o bin/frpc ./src/frp/cmd/frpc diff --git a/assets/static/bootstrap.min.css b/assets/css/bootstrap.min.css similarity index 99% rename from assets/static/bootstrap.min.css rename to assets/css/bootstrap.min.css index c2897e22..9b785e10 100644 --- a/assets/static/bootstrap.min.css +++ b/assets/css/bootstrap.min.css @@ -1,10 +1,4 @@ /*! - * bootswatch v3.3.6 - * Homepage: http://bootswatch.com - * Copyright 2012-2015 Thomas Park - * Licensed under MIT - * Based on Bootstrap -*//*! * Bootstrap v3.3.6 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) diff --git a/assets/static/font-awesome.min.css b/assets/css/font-awesome.min.css similarity index 100% rename from assets/static/font-awesome.min.css rename to assets/css/font-awesome.min.css diff --git a/assets/css/iconfont.css b/assets/css/iconfont.css new file mode 100644 index 00000000..f59c1186 --- /dev/null +++ b/assets/css/iconfont.css @@ -0,0 +1,18 @@ + +@font-face {font-family: "iconfont"; + src: url('/static/font/iconfont.eot'); /* IE9*/ + src: url('/static/font/iconfont.eot#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('/static/font/iconfont.woff') format('woff'), /* chrome, firefox */ + url('/static/font/iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ + url('/static/font/iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */ +} + +.iconfont { + font-family:"iconfont" !important; + font-size:16px; + font-style:normal; + -webkit-font-smoothing: antialiased; + -webkit-text-stroke-width: 0.2px; + -moz-osx-font-smoothing: grayscale; +} +.icon-sort:before { content: "\e66d"; } diff --git a/assets/static/iconfont.eot b/assets/font/iconfont.eot similarity index 100% rename from assets/static/iconfont.eot rename to assets/font/iconfont.eot diff --git a/assets/static/iconfont.svg b/assets/font/iconfont.svg similarity index 100% rename from assets/static/iconfont.svg rename to assets/font/iconfont.svg diff --git a/assets/static/iconfont.ttf b/assets/font/iconfont.ttf similarity index 100% rename from assets/static/iconfont.ttf rename to assets/font/iconfont.ttf diff --git a/assets/static/iconfont.woff b/assets/font/iconfont.woff similarity index 100% rename from assets/static/iconfont.woff rename to assets/font/iconfont.woff diff --git a/assets/index.html b/assets/index.html index fe5b4dc5..f5bd7cd4 100644 --- a/assets/index.html +++ b/assets/index.html @@ -3,19 +3,14 @@ <head> <title>frp</title> - <link href="static/bootstrap.min.css" rel="stylesheet"> - <script src="static/jquery.min.js"></script> - <script src="static/bootstrap.min.js"></script> - <link href="static/iconfont.css" rel="stylesheet"> + <link href="static/css/bootstrap.min.css" rel="stylesheet"> + <link href="static/css/iconfont.css" rel="stylesheet"> + <script src="static/js/jquery.min.js"></script> + <script src="static/js/bootstrap.min.js"></script> </head> <body> - <div class="container-fluid"> - <!--div class="row"> - <div class="col-sm-12 text-center"> - <h1 class="logo">frp</h1> - </div> - </div--> + <div class="container-fluid" style="margin-top: 80px"> <div class="row"> <div class="col-md-5 col-sm-offset-1"> <div class="panel panel-default"> @@ -57,23 +52,23 @@ </div> </div> </div> - <script src="static/angular.min.js"></script> - <script type="text/javascript" src="static/echarts.min.js"></script> + <script src="static/js/angular.min.js"></script> + <script type="text/javascript" src="static/js/echarts.min.js"></script> <script> var alldata = new Array(); var index = null; - <<< range .proxies >>> - alldata["<<< .name >>>"] = { - name: "<<< .name >>>", - type: "<<< .type >>>", - bind_addr: "<<< .bind_addr >>>", - listen_port: "<<< .listen_port >>>", - current_conns: <<< .current_conns >>> , - domains: [ <<< range.custom_domains >>> "<<< . >>>", <<< end >>> ], - stat: "<<< .status >>>", - use_encryption: "<<< .use_encryption >>>", - use_gzip: "<<< .use_gzip >>>", - privilege_mode: "<<< .privilege_mode >>>", + <<< range .>>> + alldata["<<< .Name >>>"] = { + name: "<<< .Name >>>", + type: "<<< .Type >>>", + bind_addr: "<<< .BindAddr >>>", + listen_port: "<<< .ListenPort >>>", + current_conns: <<< .CurrentConns >>> , + domains: [ <<< range.CustomDomains >>> "<<< . >>>", <<< end >>> ], + stat: "<<< .Status >>>", + use_encryption: "<<< .UseEncryption >>>", + use_gzip: "<<< .UseGzip >>>", + privilege_mode: "<<< .PrivilegeMode >>>", times: [], ins: [], outs: [], @@ -93,7 +88,6 @@ var step = 1; function reloadview() { - console.log("in reloadview index:", index); window.maxval = 0; window.dw = " B"; window.step = 1; @@ -154,12 +148,12 @@ }, series: [{ name: 'flow_in', - type: 'line', + type: 'bar', stack: '总量', data: alldata[index].ins }, { name: 'flow_out', - type: 'line', + type: 'bar', stack: '总量', data: alldata[index].outs }] @@ -196,7 +190,7 @@ }, series: [{ name: 'total_accept_conns', - type: 'line', + type: 'bar', stack: '总量', data: alldata[index].conns }] @@ -244,34 +238,33 @@ { var ttdy = new Date(); var today = ttdy.getFullYear() * 10000 + (1 + ttdy.getMonth()) * 100 + ttdy.getDate(); - for (var inx in newproxies.proxies) { - console.log("now inx is ", inx); - if (newproxies.proxies[inx].current_conns == undefined) { - newproxies.proxies[inx].current_conns = 0; - alldata[newproxies.proxies[inx].name].current_conns = 0; + for (var inx in newproxies) { + if (newproxies[inx].current_conns == undefined) { + newproxies[inx].current_conns = 0; + alldata[newproxies[inx].name].current_conns = 0; } - if (newproxies.proxies[inx].daily == undefined ) { - newproxies.proxies[inx].daily = []; + if (newproxies[inx].daily == undefined ) { + newproxies[inx].daily = []; } - newproxies.proxies[inx].daily.sort(function (a, b) { + newproxies[inx].daily.sort(function (a, b) { return a.time > b.time; }); - for (var iinnx in newproxies.proxies[inx].daily) { - alldata[newproxies.proxies[inx].name].times.push(newproxies.proxies[inx].daily[iinnx].time); - alldata[newproxies.proxies[inx].name].ins.push(newproxies.proxies[inx].daily[iinnx].flow_in); - alldata[newproxies.proxies[inx].name].outs.push(newproxies.proxies[inx].daily[iinnx].flow_out); - alldata[newproxies.proxies[inx].name].conns.push(newproxies.proxies[inx].daily[iinnx].total_accept_conns); + for (var iinnx in newproxies[inx].daily) { + alldata[newproxies[inx].name].times.push(newproxies[inx].daily[iinnx].time); + alldata[newproxies[inx].name].ins.push(newproxies[inx].daily[iinnx].flow_in); + alldata[newproxies[inx].name].outs.push(newproxies[inx].daily[iinnx].flow_out); + alldata[newproxies[inx].name].conns.push(newproxies[inx].daily[iinnx].total_accept_conns); } - if (newproxies.proxies[inx].daily.length == 0 || newproxies.proxies[inx].daily[0].time != today) { - alldata[newproxies.proxies[inx].name].times.push(today); - alldata[newproxies.proxies[inx].name].ins.push(0); - alldata[newproxies.proxies[inx].name].outs.push(0); - alldata[newproxies.proxies[inx].name].conns.push(0); - newproxies.proxies[inx].daily.push({ + if (newproxies[inx].daily.length == 0 || newproxies[inx].daily[0].time != today) { + alldata[newproxies[inx].name].times.push(today); + alldata[newproxies[inx].name].ins.push(0); + alldata[newproxies[inx].name].outs.push(0); + alldata[newproxies[inx].name].conns.push(0); + newproxies[inx].daily.push({ time: today, flow_in: 0, flow_out: 0, @@ -286,7 +279,7 @@ app.controller('myCtrl', function($scope) { $scope.col = 'name'; $scope.desc = 0; - $scope.proxies = newproxies.proxies; + $scope.proxies = newproxies; }); $(".tab_info").hover( diff --git a/assets/static/angular.min.js b/assets/js/angular.min.js similarity index 99% rename from assets/static/angular.min.js rename to assets/js/angular.min.js index bf50a288..4cbd7c33 100644 --- a/assets/static/angular.min.js +++ b/assets/js/angular.min.js @@ -315,4 +315,3 @@ y(e)||e.test(b)}}}}},Kc=function(){return{restrict:"A",require:"?ngModel",link:f C.console&&console.log("WARNING: Tried to load angular more than once."):(je(),le(ca),ca.module("ngLocale",[],["$provide",function(a){function b(a){a+="";var b=a.indexOf(".");return-1==b?0:a.length-b-1}a.value("$locale",{DATETIME_FORMATS:{AMPMS:["AM","PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"],FIRSTDAYOFWEEK:6,MONTH:"January February March April May June July August September October November December".split(" "), SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),STANDALONEMONTH:"January February March April May June July August September October November December".split(" "),WEEKENDRANGE:[5,6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a","short":"M/d/yy h:mm a",shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",", PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3,maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",localeID:"en_US",pluralCat:function(a,c){var e=a|0,f=c;void 0===f&&(f=Math.min(b(a),3));Math.pow(10,f);return 1==e&&0==f?"one":"other"}})}]),F(C.document).ready(function(){fe(C.document,Bc)}))})(window);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>'); -//# sourceMappingURL=angular.min.js.map diff --git a/assets/static/bootstrap.min.js b/assets/js/bootstrap.min.js similarity index 100% rename from assets/static/bootstrap.min.js rename to assets/js/bootstrap.min.js diff --git a/assets/static/echarts.min.js b/assets/js/echarts.min.js similarity index 100% rename from assets/static/echarts.min.js rename to assets/js/echarts.min.js diff --git a/assets/static/jquery.min.js b/assets/js/jquery.min.js similarity index 100% rename from assets/static/jquery.min.js rename to assets/js/jquery.min.js diff --git a/conf/frps.ini b/conf/frps.ini index 95a10100..c124fb1f 100644 --- a/conf/frps.ini +++ b/conf/frps.ini @@ -7,6 +7,8 @@ vhost_http_port = 80 vhost_https_port = 443 # if you want to configure or reload frps by dashboard, dashboard_port must be set dashboard_port = 7500 +# dashboard assets directory(only for debug mode) +assets_dir = ./assets # console or real logFile path like ./frps.log log_file = ./frps.log # debug, info, warn, error diff --git a/src/frp/models/server/config.go b/src/frp/models/server/config.go index 7de55bce..9ac11ae3 100644 --- a/src/frp/models/server/config.go +++ b/src/frp/models/server/config.go @@ -36,6 +36,7 @@ var ( VhostHttpPort int64 = 0 // if VhostHttpPort equals 0, don't listen a public port for http protocol VhostHttpsPort int64 = 0 // if VhostHttpsPort equals 0, don't listen a public port for https protocol DashboardPort int64 = 0 // if DashboardPort equals 0, dashboard is not available + AssetsDir string = "" LogFile string = "console" LogWay string = "console" // console or file LogLevel string = "info" @@ -118,6 +119,11 @@ func loadCommonConf(confFile string) error { DashboardPort = 0 } + tmpStr, ok = conf.Get("common", "assets_dir") + if ok { + AssetsDir = tmpStr + } + tmpStr, ok = conf.Get("common", "log_file") if ok { LogFile = tmpStr @@ -252,6 +258,8 @@ func loadProxyConf(confFile string) (proxyServers map[string]*ProxyServer, err e } } else if proxyServer.Type == "http" { // for http + proxyServer.ListenPort = VhostHttpPort + domainStr, ok := section["custom_domains"] if ok { proxyServer.CustomDomains = strings.Split(domainStr, ",") @@ -266,6 +274,8 @@ func loadProxyConf(confFile string) (proxyServers map[string]*ProxyServer, err e } } else if proxyServer.Type == "https" { // for https + proxyServer.ListenPort = VhostHttpsPort + domainStr, ok := section["custom_domains"] if ok { proxyServer.CustomDomains = strings.Split(domainStr, ",") diff --git a/src/frp/models/server/dashboard.go b/src/frp/models/server/dashboard.go index 62e247af..8579337b 100644 --- a/src/frp/models/server/dashboard.go +++ b/src/frp/models/server/dashboard.go @@ -16,55 +16,42 @@ package server import ( "fmt" - "frp/models/metric" - "html/template" + "net" "net/http" - - "github.com/gin-gonic/gin" + "time" ) -func index(w http.ResponseWriter, r *http.Request) { - serinfo := metric.GetAllProxyMetrics() - t := template.Must(template.New("index.html").Delims("<<<", ">>>").ParseFiles("index.html")) - - err := t.Execute(w, serinfo) - if err != nil { - fmt.Println(err.Error()) - } -} +var ( + httpServerReadTimeout = 10 * time.Second + httpServerWriteTimeout = 10 * time.Second +) func RunDashboardServer(addr string, port int64) (err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("%v", r) - } - }() - gin.SetMode(gin.ReleaseMode) - router := gin.New() - //router.LoadHTMLGlob("assets/*") - router.GET("/api/reload", apiReload) - router.GET("/api/proxies", apiProxies) - go router.Run(fmt.Sprintf("%s:%d", addr, port)) - return -} - -func RunDashboardServer2(addr string, port int64) (err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("%v", r) - } - }() - - http.HandleFunc("/", index) - - fs := http.FileServer(http.Dir("static")) - http.Handle("/static/", http.StripPrefix("/static/", fs)) - - newPort := fmt.Sprintf(":%d", port) - err = http.ListenAndServe(newPort, nil) + // url router + mux := http.NewServeMux() + // api, see dashboard_api.go + mux.HandleFunc("/api/reload", apiReload) + mux.HandleFunc("/api/proxies", apiProxies) + + // view see dashboard_view.go + mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./assets")))) + mux.HandleFunc("/", viewDashboard) + + address := fmt.Sprintf("%s:%d", addr, port) + server := &http.Server{ + Addr: address, + Handler: mux, + ReadTimeout: httpServerReadTimeout, + WriteTimeout: httpServerWriteTimeout, + } + if address == "" { + address = ":http" + } + ln, err := net.Listen("tcp", address) if err != nil { return err } - return nil + go server.Serve(ln) + return } diff --git a/src/frp/models/server/dashboard_api.go b/src/frp/models/server/dashboard_api.go index 6a07a558..db21eb1a 100644 --- a/src/frp/models/server/dashboard_api.go +++ b/src/frp/models/server/dashboard_api.go @@ -17,8 +17,7 @@ package server import ( "encoding/json" "fmt" - - "github.com/gin-gonic/gin" + "net/http" "frp/models/metric" "frp/utils/log" @@ -29,10 +28,10 @@ type GeneralResponse struct { Msg string `json:"msg"` } -func apiReload(c *gin.Context) { +func apiReload(w http.ResponseWriter, r *http.Request) { + var buf []byte res := &GeneralResponse{} defer func() { - buf, _ := json.Marshal(res) log.Info("Http response [/api/reload]: %s", string(buf)) }() @@ -43,7 +42,9 @@ func apiReload(c *gin.Context) { res.Msg = fmt.Sprintf("%v", err) log.Error("frps reload error: %v", err) } - c.JSON(200, res) + + buf, _ = json.Marshal(res) + w.Write(buf) } type ProxiesResponse struct { @@ -52,7 +53,8 @@ type ProxiesResponse struct { Proxies []*metric.ServerMetric `json:"proxies"` } -func apiProxies(c *gin.Context) { +func apiProxies(w http.ResponseWriter, r *http.Request) { + var buf []byte res := &ProxiesResponse{} defer func() { log.Info("Http response [/api/proxies]: code [%d]", res.Code) @@ -60,5 +62,6 @@ func apiProxies(c *gin.Context) { log.Info("Http request: [/api/proxies]") res.Proxies = metric.GetAllProxyMetrics() - c.JSON(200, res) + buf, _ = json.Marshal(res) + w.Write(buf) } diff --git a/src/frp/models/server/dashboard_view.go b/src/frp/models/server/dashboard_view.go new file mode 100644 index 00000000..e1dd9404 --- /dev/null +++ b/src/frp/models/server/dashboard_view.go @@ -0,0 +1,35 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "html/template" + "net/http" + "path" + + "frp/models/metric" + "frp/utils/log" +) + +func viewDashboard(w http.ResponseWriter, r *http.Request) { + metrics := metric.GetAllProxyMetrics() + t := template.Must(template.New("index.html").Delims("<<<", ">>>").ParseFiles(path.Join(AssetsDir, "index.html"))) + + err := t.Execute(w, metrics) + if err != nil { + log.Warn("parse template file [index.html] error: %v", err) + http.Error(w, "parse template file error", http.StatusInternalServerError) + } +} diff --git a/src/frp/models/server/server.go b/src/frp/models/server/server.go index 4a84dca5..5713cc19 100644 --- a/src/frp/models/server/server.go +++ b/src/frp/models/server/server.go @@ -63,7 +63,13 @@ func NewProxyServerFromCtlMsg(req *msg.ControlReq) (p *ProxyServer) { p.PrivilegeMode = req.PrivilegeMode p.PrivilegeToken = PrivilegeToken p.BindAddr = BindAddr - p.ListenPort = req.RemotePort + if p.Type == "tcp" { + p.ListenPort = req.RemotePort + } else if p.Type == "http" { + p.ListenPort = VhostHttpPort + } else if p.Type == "https" { + p.ListenPort = VhostHttpsPort + } p.CustomDomains = req.CustomDomains p.HostHeaderRewrite = req.HostHeaderRewrite return diff --git a/src/frp/utils/vhost/vhost.go b/src/frp/utils/vhost/vhost.go index 18c6d5dd..7de2d101 100644 --- a/src/frp/utils/vhost/vhost.go +++ b/src/frp/utils/vhost/vhost.go @@ -131,7 +131,6 @@ func (l *Listener) Accept() (*conn.Conn, error) { // if rewriteFunc is exist and rewriteHost is set // rewrite http requests with a modified host header if l.mux.rewriteFunc != nil && l.rewriteHost != "" { - fmt.Printf("host rewrite: %s\n", l.rewriteHost) sConn, err := l.mux.rewriteFunc(conn, l.rewriteHost) if err != nil { return nil, fmt.Errorf("http host header rewrite failed")