1
0

dashboard_api.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. // Copyright 2017 fatedier, fatedier@gmail.com
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package server
  15. import (
  16. "encoding/json"
  17. "net/http"
  18. "github.com/gorilla/mux"
  19. "github.com/prometheus/client_golang/prometheus/promhttp"
  20. "github.com/fatedier/frp/pkg/config/types"
  21. v1 "github.com/fatedier/frp/pkg/config/v1"
  22. "github.com/fatedier/frp/pkg/metrics/mem"
  23. httppkg "github.com/fatedier/frp/pkg/util/http"
  24. "github.com/fatedier/frp/pkg/util/log"
  25. netpkg "github.com/fatedier/frp/pkg/util/net"
  26. "github.com/fatedier/frp/pkg/util/version"
  27. )
  28. // TODO(fatedier): add an API to clean status of all offline proxies.
  29. type GeneralResponse struct {
  30. Code int
  31. Msg string
  32. }
  33. func (svr *Service) registerRouteHandlers(helper *httppkg.RouterRegisterHelper) {
  34. helper.Router.HandleFunc("/healthz", svr.healthz)
  35. subRouter := helper.Router.NewRoute().Subrouter()
  36. subRouter.Use(helper.AuthMiddleware.Middleware)
  37. // metrics
  38. if svr.cfg.EnablePrometheus {
  39. subRouter.Handle("/metrics", promhttp.Handler())
  40. }
  41. // apis
  42. subRouter.HandleFunc("/api/serverinfo", svr.apiServerInfo).Methods("GET")
  43. subRouter.HandleFunc("/api/proxy/{type}", svr.apiProxyByType).Methods("GET")
  44. subRouter.HandleFunc("/api/proxy/{type}/{name}", svr.apiProxyByTypeAndName).Methods("GET")
  45. subRouter.HandleFunc("/api/traffic/{name}", svr.apiProxyTraffic).Methods("GET")
  46. // view
  47. subRouter.Handle("/favicon.ico", http.FileServer(helper.AssetsFS)).Methods("GET")
  48. subRouter.PathPrefix("/static/").Handler(
  49. netpkg.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(helper.AssetsFS))),
  50. ).Methods("GET")
  51. subRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  52. http.Redirect(w, r, "/static/", http.StatusMovedPermanently)
  53. })
  54. }
  55. type serverInfoResp struct {
  56. Version string `json:"version"`
  57. BindPort int `json:"bindPort"`
  58. VhostHTTPPort int `json:"vhostHTTPPort"`
  59. VhostHTTPSPort int `json:"vhostHTTPSPort"`
  60. TCPMuxHTTPConnectPort int `json:"tcpmuxHTTPConnectPort"`
  61. KCPBindPort int `json:"kcpBindPort"`
  62. QUICBindPort int `json:"quicBindPort"`
  63. SubdomainHost string `json:"subdomainHost"`
  64. MaxPoolCount int64 `json:"maxPoolCount"`
  65. MaxPortsPerClient int64 `json:"maxPortsPerClient"`
  66. HeartBeatTimeout int64 `json:"heartbeatTimeout"`
  67. AllowPortsStr string `json:"allowPortsStr,omitempty"`
  68. TLSForce bool `json:"tlsForce,omitempty"`
  69. TotalTrafficIn int64 `json:"totalTrafficIn"`
  70. TotalTrafficOut int64 `json:"totalTrafficOut"`
  71. CurConns int64 `json:"curConns"`
  72. ClientCounts int64 `json:"clientCounts"`
  73. ProxyTypeCounts map[string]int64 `json:"proxyTypeCount"`
  74. }
  75. // /healthz
  76. func (svr *Service) healthz(w http.ResponseWriter, _ *http.Request) {
  77. w.WriteHeader(200)
  78. }
  79. // /api/serverinfo
  80. func (svr *Service) apiServerInfo(w http.ResponseWriter, r *http.Request) {
  81. res := GeneralResponse{Code: 200}
  82. defer func() {
  83. log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
  84. w.WriteHeader(res.Code)
  85. if len(res.Msg) > 0 {
  86. _, _ = w.Write([]byte(res.Msg))
  87. }
  88. }()
  89. log.Info("Http request: [%s]", r.URL.Path)
  90. serverStats := mem.StatsCollector.GetServer()
  91. svrResp := serverInfoResp{
  92. Version: version.Full(),
  93. BindPort: svr.cfg.BindPort,
  94. VhostHTTPPort: svr.cfg.VhostHTTPPort,
  95. VhostHTTPSPort: svr.cfg.VhostHTTPSPort,
  96. TCPMuxHTTPConnectPort: svr.cfg.TCPMuxHTTPConnectPort,
  97. KCPBindPort: svr.cfg.KCPBindPort,
  98. QUICBindPort: svr.cfg.QUICBindPort,
  99. SubdomainHost: svr.cfg.SubDomainHost,
  100. MaxPoolCount: svr.cfg.Transport.MaxPoolCount,
  101. MaxPortsPerClient: svr.cfg.MaxPortsPerClient,
  102. HeartBeatTimeout: svr.cfg.Transport.HeartbeatTimeout,
  103. AllowPortsStr: types.PortsRangeSlice(svr.cfg.AllowPorts).String(),
  104. TLSForce: svr.cfg.Transport.TLS.Force,
  105. TotalTrafficIn: serverStats.TotalTrafficIn,
  106. TotalTrafficOut: serverStats.TotalTrafficOut,
  107. CurConns: serverStats.CurConns,
  108. ClientCounts: serverStats.ClientCounts,
  109. ProxyTypeCounts: serverStats.ProxyTypeCounts,
  110. }
  111. buf, _ := json.Marshal(&svrResp)
  112. res.Msg = string(buf)
  113. }
  114. type BaseOutConf struct {
  115. v1.ProxyBaseConfig
  116. }
  117. type TCPOutConf struct {
  118. BaseOutConf
  119. RemotePort int `json:"remotePort"`
  120. }
  121. type TCPMuxOutConf struct {
  122. BaseOutConf
  123. v1.DomainConfig
  124. Multiplexer string `json:"multiplexer"`
  125. }
  126. type UDPOutConf struct {
  127. BaseOutConf
  128. RemotePort int `json:"remotePort"`
  129. }
  130. type HTTPOutConf struct {
  131. BaseOutConf
  132. v1.DomainConfig
  133. Locations []string `json:"locations"`
  134. HostHeaderRewrite string `json:"hostHeaderRewrite"`
  135. }
  136. type HTTPSOutConf struct {
  137. BaseOutConf
  138. v1.DomainConfig
  139. }
  140. type STCPOutConf struct {
  141. BaseOutConf
  142. }
  143. type XTCPOutConf struct {
  144. BaseOutConf
  145. }
  146. func getConfByType(proxyType string) any {
  147. switch v1.ProxyType(proxyType) {
  148. case v1.ProxyTypeTCP:
  149. return &TCPOutConf{}
  150. case v1.ProxyTypeTCPMUX:
  151. return &TCPMuxOutConf{}
  152. case v1.ProxyTypeUDP:
  153. return &UDPOutConf{}
  154. case v1.ProxyTypeHTTP:
  155. return &HTTPOutConf{}
  156. case v1.ProxyTypeHTTPS:
  157. return &HTTPSOutConf{}
  158. case v1.ProxyTypeSTCP:
  159. return &STCPOutConf{}
  160. case v1.ProxyTypeXTCP:
  161. return &XTCPOutConf{}
  162. default:
  163. return nil
  164. }
  165. }
  166. // Get proxy info.
  167. type ProxyStatsInfo struct {
  168. Name string `json:"name"`
  169. Conf interface{} `json:"conf"`
  170. ClientVersion string `json:"clientVersion,omitempty"`
  171. TodayTrafficIn int64 `json:"todayTrafficIn"`
  172. TodayTrafficOut int64 `json:"todayTrafficOut"`
  173. CurConns int64 `json:"curConns"`
  174. LastStartTime string `json:"lastStartTime"`
  175. LastCloseTime string `json:"lastCloseTime"`
  176. Status string `json:"status"`
  177. }
  178. type GetProxyInfoResp struct {
  179. Proxies []*ProxyStatsInfo `json:"proxies"`
  180. }
  181. // /api/proxy/:type
  182. func (svr *Service) apiProxyByType(w http.ResponseWriter, r *http.Request) {
  183. res := GeneralResponse{Code: 200}
  184. params := mux.Vars(r)
  185. proxyType := params["type"]
  186. defer func() {
  187. log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
  188. w.WriteHeader(res.Code)
  189. if len(res.Msg) > 0 {
  190. _, _ = w.Write([]byte(res.Msg))
  191. }
  192. }()
  193. log.Info("Http request: [%s]", r.URL.Path)
  194. proxyInfoResp := GetProxyInfoResp{}
  195. proxyInfoResp.Proxies = svr.getProxyStatsByType(proxyType)
  196. buf, _ := json.Marshal(&proxyInfoResp)
  197. res.Msg = string(buf)
  198. }
  199. func (svr *Service) getProxyStatsByType(proxyType string) (proxyInfos []*ProxyStatsInfo) {
  200. proxyStats := mem.StatsCollector.GetProxiesByType(proxyType)
  201. proxyInfos = make([]*ProxyStatsInfo, 0, len(proxyStats))
  202. for _, ps := range proxyStats {
  203. proxyInfo := &ProxyStatsInfo{}
  204. if pxy, ok := svr.pxyManager.GetByName(ps.Name); ok {
  205. content, err := json.Marshal(pxy.GetConfigurer())
  206. if err != nil {
  207. log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err)
  208. continue
  209. }
  210. proxyInfo.Conf = getConfByType(ps.Type)
  211. if err = json.Unmarshal(content, &proxyInfo.Conf); err != nil {
  212. log.Warn("unmarshal proxy [%s] conf info error: %v", ps.Name, err)
  213. continue
  214. }
  215. proxyInfo.Status = "online"
  216. if pxy.GetLoginMsg() != nil {
  217. proxyInfo.ClientVersion = pxy.GetLoginMsg().Version
  218. }
  219. } else {
  220. proxyInfo.Status = "offline"
  221. }
  222. proxyInfo.Name = ps.Name
  223. proxyInfo.TodayTrafficIn = ps.TodayTrafficIn
  224. proxyInfo.TodayTrafficOut = ps.TodayTrafficOut
  225. proxyInfo.CurConns = ps.CurConns
  226. proxyInfo.LastStartTime = ps.LastStartTime
  227. proxyInfo.LastCloseTime = ps.LastCloseTime
  228. proxyInfos = append(proxyInfos, proxyInfo)
  229. }
  230. return
  231. }
  232. // Get proxy info by name.
  233. type GetProxyStatsResp struct {
  234. Name string `json:"name"`
  235. Conf interface{} `json:"conf"`
  236. TodayTrafficIn int64 `json:"todayTrafficIn"`
  237. TodayTrafficOut int64 `json:"todayTrafficOut"`
  238. CurConns int64 `json:"curConns"`
  239. LastStartTime string `json:"lastStartTime"`
  240. LastCloseTime string `json:"lastCloseTime"`
  241. Status string `json:"status"`
  242. }
  243. // /api/proxy/:type/:name
  244. func (svr *Service) apiProxyByTypeAndName(w http.ResponseWriter, r *http.Request) {
  245. res := GeneralResponse{Code: 200}
  246. params := mux.Vars(r)
  247. proxyType := params["type"]
  248. name := params["name"]
  249. defer func() {
  250. log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
  251. w.WriteHeader(res.Code)
  252. if len(res.Msg) > 0 {
  253. _, _ = w.Write([]byte(res.Msg))
  254. }
  255. }()
  256. log.Info("Http request: [%s]", r.URL.Path)
  257. var proxyStatsResp GetProxyStatsResp
  258. proxyStatsResp, res.Code, res.Msg = svr.getProxyStatsByTypeAndName(proxyType, name)
  259. if res.Code != 200 {
  260. return
  261. }
  262. buf, _ := json.Marshal(&proxyStatsResp)
  263. res.Msg = string(buf)
  264. }
  265. func (svr *Service) getProxyStatsByTypeAndName(proxyType string, proxyName string) (proxyInfo GetProxyStatsResp, code int, msg string) {
  266. proxyInfo.Name = proxyName
  267. ps := mem.StatsCollector.GetProxiesByTypeAndName(proxyType, proxyName)
  268. if ps == nil {
  269. code = 404
  270. msg = "no proxy info found"
  271. } else {
  272. if pxy, ok := svr.pxyManager.GetByName(proxyName); ok {
  273. content, err := json.Marshal(pxy.GetConfigurer())
  274. if err != nil {
  275. log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err)
  276. code = 400
  277. msg = "parse conf error"
  278. return
  279. }
  280. proxyInfo.Conf = getConfByType(ps.Type)
  281. if err = json.Unmarshal(content, &proxyInfo.Conf); err != nil {
  282. log.Warn("unmarshal proxy [%s] conf info error: %v", ps.Name, err)
  283. code = 400
  284. msg = "parse conf error"
  285. return
  286. }
  287. proxyInfo.Status = "online"
  288. } else {
  289. proxyInfo.Status = "offline"
  290. }
  291. proxyInfo.TodayTrafficIn = ps.TodayTrafficIn
  292. proxyInfo.TodayTrafficOut = ps.TodayTrafficOut
  293. proxyInfo.CurConns = ps.CurConns
  294. proxyInfo.LastStartTime = ps.LastStartTime
  295. proxyInfo.LastCloseTime = ps.LastCloseTime
  296. code = 200
  297. }
  298. return
  299. }
  300. // /api/traffic/:name
  301. type GetProxyTrafficResp struct {
  302. Name string `json:"name"`
  303. TrafficIn []int64 `json:"trafficIn"`
  304. TrafficOut []int64 `json:"trafficOut"`
  305. }
  306. func (svr *Service) apiProxyTraffic(w http.ResponseWriter, r *http.Request) {
  307. res := GeneralResponse{Code: 200}
  308. params := mux.Vars(r)
  309. name := params["name"]
  310. defer func() {
  311. log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
  312. w.WriteHeader(res.Code)
  313. if len(res.Msg) > 0 {
  314. _, _ = w.Write([]byte(res.Msg))
  315. }
  316. }()
  317. log.Info("Http request: [%s]", r.URL.Path)
  318. trafficResp := GetProxyTrafficResp{}
  319. trafficResp.Name = name
  320. proxyTrafficInfo := mem.StatsCollector.GetProxyTraffic(name)
  321. if proxyTrafficInfo == nil {
  322. res.Code = 404
  323. res.Msg = "no proxy info found"
  324. return
  325. }
  326. trafficResp.TrafficIn = proxyTrafficInfo.TrafficIn
  327. trafficResp.TrafficOut = proxyTrafficInfo.TrafficOut
  328. buf, _ := json.Marshal(&trafficResp)
  329. res.Msg = string(buf)
  330. }