admin_api.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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 client
  15. import (
  16. "cmp"
  17. "encoding/json"
  18. "fmt"
  19. "io"
  20. "net"
  21. "net/http"
  22. "os"
  23. "slices"
  24. "strconv"
  25. "time"
  26. "github.com/fatedier/frp/client/proxy"
  27. "github.com/fatedier/frp/pkg/config"
  28. "github.com/fatedier/frp/pkg/config/v1/validation"
  29. httppkg "github.com/fatedier/frp/pkg/util/http"
  30. "github.com/fatedier/frp/pkg/util/log"
  31. netpkg "github.com/fatedier/frp/pkg/util/net"
  32. )
  33. type GeneralResponse struct {
  34. Code int
  35. Msg string
  36. }
  37. func (svr *Service) registerRouteHandlers(helper *httppkg.RouterRegisterHelper) {
  38. helper.Router.HandleFunc("/healthz", svr.healthz)
  39. subRouter := helper.Router.NewRoute().Subrouter()
  40. subRouter.Use(helper.AuthMiddleware.Middleware)
  41. // api, see admin_api.go
  42. subRouter.HandleFunc("/api/reload", svr.apiReload).Methods("GET")
  43. subRouter.HandleFunc("/api/stop", svr.apiStop).Methods("POST")
  44. subRouter.HandleFunc("/api/status", svr.apiStatus).Methods("GET")
  45. subRouter.HandleFunc("/api/config", svr.apiGetConfig).Methods("GET")
  46. subRouter.HandleFunc("/api/config", svr.apiPutConfig).Methods("PUT")
  47. // view
  48. subRouter.Handle("/favicon.ico", http.FileServer(helper.AssetsFS)).Methods("GET")
  49. subRouter.PathPrefix("/static/").Handler(
  50. netpkg.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(helper.AssetsFS))),
  51. ).Methods("GET")
  52. subRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  53. http.Redirect(w, r, "/static/", http.StatusMovedPermanently)
  54. })
  55. }
  56. // /healthz
  57. func (svr *Service) healthz(w http.ResponseWriter, _ *http.Request) {
  58. w.WriteHeader(200)
  59. }
  60. // GET /api/reload
  61. func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
  62. res := GeneralResponse{Code: 200}
  63. strictConfigMode := false
  64. strictStr := r.URL.Query().Get("strictConfig")
  65. if strictStr != "" {
  66. strictConfigMode, _ = strconv.ParseBool(strictStr)
  67. }
  68. log.Infof("api request [/api/reload]")
  69. defer func() {
  70. log.Infof("api response [/api/reload], code [%d]", res.Code)
  71. w.WriteHeader(res.Code)
  72. if len(res.Msg) > 0 {
  73. _, _ = w.Write([]byte(res.Msg))
  74. }
  75. }()
  76. cliCfg, proxyCfgs, visitorCfgs, _, err := config.LoadClientConfig(svr.configFilePath, strictConfigMode)
  77. if err != nil {
  78. res.Code = 400
  79. res.Msg = err.Error()
  80. log.Warnf("reload frpc proxy config error: %s", res.Msg)
  81. return
  82. }
  83. if _, err := validation.ValidateAllClientConfig(cliCfg, proxyCfgs, visitorCfgs); err != nil {
  84. res.Code = 400
  85. res.Msg = err.Error()
  86. log.Warnf("reload frpc proxy config error: %s", res.Msg)
  87. return
  88. }
  89. if err := svr.UpdateAllConfigurer(proxyCfgs, visitorCfgs); err != nil {
  90. res.Code = 500
  91. res.Msg = err.Error()
  92. log.Warnf("reload frpc proxy config error: %s", res.Msg)
  93. return
  94. }
  95. log.Infof("success reload conf")
  96. }
  97. // POST /api/stop
  98. func (svr *Service) apiStop(w http.ResponseWriter, _ *http.Request) {
  99. res := GeneralResponse{Code: 200}
  100. log.Infof("api request [/api/stop]")
  101. defer func() {
  102. log.Infof("api response [/api/stop], code [%d]", res.Code)
  103. w.WriteHeader(res.Code)
  104. if len(res.Msg) > 0 {
  105. _, _ = w.Write([]byte(res.Msg))
  106. }
  107. }()
  108. go svr.GracefulClose(100 * time.Millisecond)
  109. }
  110. type StatusResp map[string][]ProxyStatusResp
  111. type ProxyStatusResp struct {
  112. Name string `json:"name"`
  113. Type string `json:"type"`
  114. Status string `json:"status"`
  115. Err string `json:"err"`
  116. LocalAddr string `json:"local_addr"`
  117. Plugin string `json:"plugin"`
  118. RemoteAddr string `json:"remote_addr"`
  119. }
  120. func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxyStatusResp {
  121. psr := ProxyStatusResp{
  122. Name: status.Name,
  123. Type: status.Type,
  124. Status: status.Phase,
  125. Err: status.Err,
  126. }
  127. baseCfg := status.Cfg.GetBaseConfig()
  128. if baseCfg.LocalPort != 0 {
  129. psr.LocalAddr = net.JoinHostPort(baseCfg.LocalIP, strconv.Itoa(baseCfg.LocalPort))
  130. }
  131. psr.Plugin = baseCfg.Plugin.Type
  132. if status.Err == "" {
  133. psr.RemoteAddr = status.RemoteAddr
  134. if slices.Contains([]string{"tcp", "udp"}, status.Type) {
  135. psr.RemoteAddr = serverAddr + psr.RemoteAddr
  136. }
  137. }
  138. return psr
  139. }
  140. // GET /api/status
  141. func (svr *Service) apiStatus(w http.ResponseWriter, _ *http.Request) {
  142. var (
  143. buf []byte
  144. res StatusResp = make(map[string][]ProxyStatusResp)
  145. )
  146. log.Infof("Http request [/api/status]")
  147. defer func() {
  148. log.Infof("Http response [/api/status]")
  149. buf, _ = json.Marshal(&res)
  150. _, _ = w.Write(buf)
  151. }()
  152. svr.ctlMu.RLock()
  153. ctl := svr.ctl
  154. svr.ctlMu.RUnlock()
  155. if ctl == nil {
  156. return
  157. }
  158. ps := ctl.pm.GetAllProxyStatus()
  159. for _, status := range ps {
  160. res[status.Type] = append(res[status.Type], NewProxyStatusResp(status, svr.common.ServerAddr))
  161. }
  162. for _, arrs := range res {
  163. if len(arrs) <= 1 {
  164. continue
  165. }
  166. slices.SortFunc(arrs, func(a, b ProxyStatusResp) int {
  167. return cmp.Compare(a.Name, b.Name)
  168. })
  169. }
  170. }
  171. // GET /api/config
  172. func (svr *Service) apiGetConfig(w http.ResponseWriter, _ *http.Request) {
  173. res := GeneralResponse{Code: 200}
  174. log.Infof("Http get request [/api/config]")
  175. defer func() {
  176. log.Infof("Http get response [/api/config], code [%d]", res.Code)
  177. w.WriteHeader(res.Code)
  178. if len(res.Msg) > 0 {
  179. _, _ = w.Write([]byte(res.Msg))
  180. }
  181. }()
  182. if svr.configFilePath == "" {
  183. res.Code = 400
  184. res.Msg = "frpc has no config file path"
  185. log.Warnf("%s", res.Msg)
  186. return
  187. }
  188. content, err := os.ReadFile(svr.configFilePath)
  189. if err != nil {
  190. res.Code = 400
  191. res.Msg = err.Error()
  192. log.Warnf("load frpc config file error: %s", res.Msg)
  193. return
  194. }
  195. res.Msg = string(content)
  196. }
  197. // PUT /api/config
  198. func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
  199. res := GeneralResponse{Code: 200}
  200. log.Infof("Http put request [/api/config]")
  201. defer func() {
  202. log.Infof("Http put response [/api/config], code [%d]", res.Code)
  203. w.WriteHeader(res.Code)
  204. if len(res.Msg) > 0 {
  205. _, _ = w.Write([]byte(res.Msg))
  206. }
  207. }()
  208. // get new config content
  209. body, err := io.ReadAll(r.Body)
  210. if err != nil {
  211. res.Code = 400
  212. res.Msg = fmt.Sprintf("read request body error: %v", err)
  213. log.Warnf("%s", res.Msg)
  214. return
  215. }
  216. if len(body) == 0 {
  217. res.Code = 400
  218. res.Msg = "body can't be empty"
  219. log.Warnf("%s", res.Msg)
  220. return
  221. }
  222. if err := os.WriteFile(svr.configFilePath, body, 0o600); err != nil {
  223. res.Code = 500
  224. res.Msg = fmt.Sprintf("write content to frpc config file error: %v", err)
  225. log.Warnf("%s", res.Msg)
  226. return
  227. }
  228. }