dashboard_api.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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/fatedier/frp/pkg/config/types"
  20. v1 "github.com/fatedier/frp/pkg/config/v1"
  21. "github.com/fatedier/frp/pkg/metrics/mem"
  22. "github.com/fatedier/frp/pkg/util/log"
  23. "github.com/fatedier/frp/pkg/util/version"
  24. )
  25. type GeneralResponse struct {
  26. Code int
  27. Msg string
  28. }
  29. type serverInfoResp struct {
  30. Version string `json:"version"`
  31. BindPort int `json:"bindPort"`
  32. VhostHTTPPort int `json:"vhostHTTPPort"`
  33. VhostHTTPSPort int `json:"vhostHTTPSPort"`
  34. TCPMuxHTTPConnectPort int `json:"tcpmuxHTTPConnectPort"`
  35. KCPBindPort int `json:"kcpBindPort"`
  36. QUICBindPort int `json:"quicBindPort"`
  37. SubdomainHost string `json:"subdomainHost"`
  38. MaxPoolCount int64 `json:"maxPoolCount"`
  39. MaxPortsPerClient int64 `json:"maxPortsPerClient"`
  40. HeartBeatTimeout int64 `json:"heartbeatTimeout"`
  41. AllowPortsStr string `json:"allowPortsStr,omitempty"`
  42. TLSForce bool `json:"tlsForce,omitempty"`
  43. TotalTrafficIn int64 `json:"totalTrafficIn"`
  44. TotalTrafficOut int64 `json:"totalTrafficOut"`
  45. CurConns int64 `json:"curConns"`
  46. ClientCounts int64 `json:"clientCounts"`
  47. ProxyTypeCounts map[string]int64 `json:"proxyTypeCount"`
  48. }
  49. // /healthz
  50. func (svr *Service) Healthz(w http.ResponseWriter, _ *http.Request) {
  51. w.WriteHeader(200)
  52. }
  53. // /api/serverinfo
  54. func (svr *Service) APIServerInfo(w http.ResponseWriter, r *http.Request) {
  55. res := GeneralResponse{Code: 200}
  56. defer func() {
  57. log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
  58. w.WriteHeader(res.Code)
  59. if len(res.Msg) > 0 {
  60. _, _ = w.Write([]byte(res.Msg))
  61. }
  62. }()
  63. log.Info("Http request: [%s]", r.URL.Path)
  64. serverStats := mem.StatsCollector.GetServer()
  65. svrResp := serverInfoResp{
  66. Version: version.Full(),
  67. BindPort: svr.cfg.BindPort,
  68. VhostHTTPPort: svr.cfg.VhostHTTPPort,
  69. VhostHTTPSPort: svr.cfg.VhostHTTPSPort,
  70. TCPMuxHTTPConnectPort: svr.cfg.TCPMuxHTTPConnectPort,
  71. KCPBindPort: svr.cfg.KCPBindPort,
  72. QUICBindPort: svr.cfg.QUICBindPort,
  73. SubdomainHost: svr.cfg.SubDomainHost,
  74. MaxPoolCount: svr.cfg.Transport.MaxPoolCount,
  75. MaxPortsPerClient: svr.cfg.MaxPortsPerClient,
  76. HeartBeatTimeout: svr.cfg.Transport.HeartbeatTimeout,
  77. AllowPortsStr: types.PortsRangeSlice(svr.cfg.AllowPorts).String(),
  78. TLSForce: svr.cfg.Transport.TLS.Force,
  79. TotalTrafficIn: serverStats.TotalTrafficIn,
  80. TotalTrafficOut: serverStats.TotalTrafficOut,
  81. CurConns: serverStats.CurConns,
  82. ClientCounts: serverStats.ClientCounts,
  83. ProxyTypeCounts: serverStats.ProxyTypeCounts,
  84. }
  85. buf, _ := json.Marshal(&svrResp)
  86. res.Msg = string(buf)
  87. }
  88. type BaseOutConf struct {
  89. v1.ProxyBaseConfig
  90. }
  91. type TCPOutConf struct {
  92. BaseOutConf
  93. RemotePort int `json:"remotePort"`
  94. }
  95. type TCPMuxOutConf struct {
  96. BaseOutConf
  97. v1.DomainConfig
  98. Multiplexer string `json:"multiplexer"`
  99. }
  100. type UDPOutConf struct {
  101. BaseOutConf
  102. RemotePort int `json:"remotePort"`
  103. }
  104. type HTTPOutConf struct {
  105. BaseOutConf
  106. v1.DomainConfig
  107. Locations []string `json:"locations"`
  108. HostHeaderRewrite string `json:"hostHeaderRewrite"`
  109. }
  110. type HTTPSOutConf struct {
  111. BaseOutConf
  112. v1.DomainConfig
  113. }
  114. type STCPOutConf struct {
  115. BaseOutConf
  116. }
  117. type XTCPOutConf struct {
  118. BaseOutConf
  119. }
  120. func getConfByType(proxyType string) any {
  121. switch v1.ProxyType(proxyType) {
  122. case v1.ProxyTypeTCP:
  123. return &TCPOutConf{}
  124. case v1.ProxyTypeTCPMUX:
  125. return &TCPMuxOutConf{}
  126. case v1.ProxyTypeUDP:
  127. return &UDPOutConf{}
  128. case v1.ProxyTypeHTTP:
  129. return &HTTPOutConf{}
  130. case v1.ProxyTypeHTTPS:
  131. return &HTTPSOutConf{}
  132. case v1.ProxyTypeSTCP:
  133. return &STCPOutConf{}
  134. case v1.ProxyTypeXTCP:
  135. return &XTCPOutConf{}
  136. default:
  137. return nil
  138. }
  139. }
  140. // Get proxy info.
  141. type ProxyStatsInfo struct {
  142. Name string `json:"name"`
  143. Conf interface{} `json:"conf"`
  144. ClientVersion string `json:"clientVersion,omitempty"`
  145. TodayTrafficIn int64 `json:"todayTrafficIn"`
  146. TodayTrafficOut int64 `json:"todayTrafficOut"`
  147. CurConns int64 `json:"curConns"`
  148. LastStartTime string `json:"lastStartTime"`
  149. LastCloseTime string `json:"lastCloseTime"`
  150. Status string `json:"status"`
  151. }
  152. type GetProxyInfoResp struct {
  153. Proxies []*ProxyStatsInfo `json:"proxies"`
  154. }
  155. // /api/proxy/:type
  156. func (svr *Service) APIProxyByType(w http.ResponseWriter, r *http.Request) {
  157. res := GeneralResponse{Code: 200}
  158. params := mux.Vars(r)
  159. proxyType := params["type"]
  160. defer func() {
  161. log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
  162. w.WriteHeader(res.Code)
  163. if len(res.Msg) > 0 {
  164. _, _ = w.Write([]byte(res.Msg))
  165. }
  166. }()
  167. log.Info("Http request: [%s]", r.URL.Path)
  168. proxyInfoResp := GetProxyInfoResp{}
  169. proxyInfoResp.Proxies = svr.getProxyStatsByType(proxyType)
  170. buf, _ := json.Marshal(&proxyInfoResp)
  171. res.Msg = string(buf)
  172. }
  173. func (svr *Service) getProxyStatsByType(proxyType string) (proxyInfos []*ProxyStatsInfo) {
  174. proxyStats := mem.StatsCollector.GetProxiesByType(proxyType)
  175. proxyInfos = make([]*ProxyStatsInfo, 0, len(proxyStats))
  176. for _, ps := range proxyStats {
  177. proxyInfo := &ProxyStatsInfo{}
  178. if pxy, ok := svr.pxyManager.GetByName(ps.Name); ok {
  179. content, err := json.Marshal(pxy.GetConfigurer())
  180. if err != nil {
  181. log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err)
  182. continue
  183. }
  184. proxyInfo.Conf = getConfByType(ps.Type)
  185. if err = json.Unmarshal(content, &proxyInfo.Conf); err != nil {
  186. log.Warn("unmarshal proxy [%s] conf info error: %v", ps.Name, err)
  187. continue
  188. }
  189. proxyInfo.Status = "online"
  190. if pxy.GetLoginMsg() != nil {
  191. proxyInfo.ClientVersion = pxy.GetLoginMsg().Version
  192. }
  193. } else {
  194. proxyInfo.Status = "offline"
  195. }
  196. proxyInfo.Name = ps.Name
  197. proxyInfo.TodayTrafficIn = ps.TodayTrafficIn
  198. proxyInfo.TodayTrafficOut = ps.TodayTrafficOut
  199. proxyInfo.CurConns = ps.CurConns
  200. proxyInfo.LastStartTime = ps.LastStartTime
  201. proxyInfo.LastCloseTime = ps.LastCloseTime
  202. proxyInfos = append(proxyInfos, proxyInfo)
  203. }
  204. return
  205. }
  206. // Get proxy info by name.
  207. type GetProxyStatsResp struct {
  208. Name string `json:"name"`
  209. Conf interface{} `json:"conf"`
  210. TodayTrafficIn int64 `json:"todayTrafficIn"`
  211. TodayTrafficOut int64 `json:"todayTrafficOut"`
  212. CurConns int64 `json:"curConns"`
  213. LastStartTime string `json:"lastStartTime"`
  214. LastCloseTime string `json:"lastCloseTime"`
  215. Status string `json:"status"`
  216. }
  217. // /api/proxy/:type/:name
  218. func (svr *Service) APIProxyByTypeAndName(w http.ResponseWriter, r *http.Request) {
  219. res := GeneralResponse{Code: 200}
  220. params := mux.Vars(r)
  221. proxyType := params["type"]
  222. name := params["name"]
  223. defer func() {
  224. log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
  225. w.WriteHeader(res.Code)
  226. if len(res.Msg) > 0 {
  227. _, _ = w.Write([]byte(res.Msg))
  228. }
  229. }()
  230. log.Info("Http request: [%s]", r.URL.Path)
  231. var proxyStatsResp GetProxyStatsResp
  232. proxyStatsResp, res.Code, res.Msg = svr.getProxyStatsByTypeAndName(proxyType, name)
  233. if res.Code != 200 {
  234. return
  235. }
  236. buf, _ := json.Marshal(&proxyStatsResp)
  237. res.Msg = string(buf)
  238. }
  239. func (svr *Service) getProxyStatsByTypeAndName(proxyType string, proxyName string) (proxyInfo GetProxyStatsResp, code int, msg string) {
  240. proxyInfo.Name = proxyName
  241. ps := mem.StatsCollector.GetProxiesByTypeAndName(proxyType, proxyName)
  242. if ps == nil {
  243. code = 404
  244. msg = "no proxy info found"
  245. } else {
  246. if pxy, ok := svr.pxyManager.GetByName(proxyName); ok {
  247. content, err := json.Marshal(pxy.GetConfigurer())
  248. if err != nil {
  249. log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err)
  250. code = 400
  251. msg = "parse conf error"
  252. return
  253. }
  254. proxyInfo.Conf = getConfByType(ps.Type)
  255. if err = json.Unmarshal(content, &proxyInfo.Conf); err != nil {
  256. log.Warn("unmarshal proxy [%s] conf info error: %v", ps.Name, err)
  257. code = 400
  258. msg = "parse conf error"
  259. return
  260. }
  261. proxyInfo.Status = "online"
  262. } else {
  263. proxyInfo.Status = "offline"
  264. }
  265. proxyInfo.TodayTrafficIn = ps.TodayTrafficIn
  266. proxyInfo.TodayTrafficOut = ps.TodayTrafficOut
  267. proxyInfo.CurConns = ps.CurConns
  268. proxyInfo.LastStartTime = ps.LastStartTime
  269. proxyInfo.LastCloseTime = ps.LastCloseTime
  270. code = 200
  271. }
  272. return
  273. }
  274. // /api/traffic/:name
  275. type GetProxyTrafficResp struct {
  276. Name string `json:"name"`
  277. TrafficIn []int64 `json:"trafficIn"`
  278. TrafficOut []int64 `json:"trafficOut"`
  279. }
  280. func (svr *Service) APIProxyTraffic(w http.ResponseWriter, r *http.Request) {
  281. res := GeneralResponse{Code: 200}
  282. params := mux.Vars(r)
  283. name := params["name"]
  284. defer func() {
  285. log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
  286. w.WriteHeader(res.Code)
  287. if len(res.Msg) > 0 {
  288. _, _ = w.Write([]byte(res.Msg))
  289. }
  290. }()
  291. log.Info("Http request: [%s]", r.URL.Path)
  292. trafficResp := GetProxyTrafficResp{}
  293. trafficResp.Name = name
  294. proxyTrafficInfo := mem.StatsCollector.GetProxyTraffic(name)
  295. if proxyTrafficInfo == nil {
  296. res.Code = 404
  297. res.Msg = "no proxy info found"
  298. return
  299. }
  300. trafficResp.TrafficIn = proxyTrafficInfo.TrafficIn
  301. trafficResp.TrafficOut = proxyTrafficInfo.TrafficOut
  302. buf, _ := json.Marshal(&trafficResp)
  303. res.Msg = string(buf)
  304. }