1
0

admin_api.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  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. "encoding/json"
  17. "fmt"
  18. "io"
  19. "net"
  20. "net/http"
  21. "os"
  22. "sort"
  23. "strconv"
  24. "strings"
  25. "github.com/samber/lo"
  26. "github.com/fatedier/frp/client/proxy"
  27. "github.com/fatedier/frp/pkg/config"
  28. "github.com/fatedier/frp/pkg/util/log"
  29. )
  30. type GeneralResponse struct {
  31. Code int
  32. Msg string
  33. }
  34. // /healthz
  35. func (svr *Service) healthz(w http.ResponseWriter, r *http.Request) {
  36. w.WriteHeader(200)
  37. }
  38. // GET api/reload
  39. func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
  40. res := GeneralResponse{Code: 200}
  41. log.Info("api request [/api/reload]")
  42. defer func() {
  43. log.Info("api response [/api/reload], code [%d]", res.Code)
  44. w.WriteHeader(res.Code)
  45. if len(res.Msg) > 0 {
  46. _, _ = w.Write([]byte(res.Msg))
  47. }
  48. }()
  49. _, pxyCfgs, visitorCfgs, err := config.ParseClientConfig(svr.cfgFile)
  50. if err != nil {
  51. res.Code = 400
  52. res.Msg = err.Error()
  53. log.Warn("reload frpc proxy config error: %s", res.Msg)
  54. return
  55. }
  56. if err = svr.ReloadConf(pxyCfgs, visitorCfgs); err != nil {
  57. res.Code = 500
  58. res.Msg = err.Error()
  59. log.Warn("reload frpc proxy config error: %s", res.Msg)
  60. return
  61. }
  62. log.Info("success reload conf")
  63. }
  64. type StatusResp map[string][]ProxyStatusResp
  65. type ProxyStatusResp struct {
  66. Name string `json:"name"`
  67. Type string `json:"type"`
  68. Status string `json:"status"`
  69. Err string `json:"err"`
  70. LocalAddr string `json:"local_addr"`
  71. Plugin string `json:"plugin"`
  72. RemoteAddr string `json:"remote_addr"`
  73. }
  74. func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxyStatusResp {
  75. psr := ProxyStatusResp{
  76. Name: status.Name,
  77. Type: status.Type,
  78. Status: status.Phase,
  79. Err: status.Err,
  80. }
  81. baseCfg := status.Cfg.GetBaseInfo()
  82. if baseCfg.LocalPort != 0 {
  83. psr.LocalAddr = net.JoinHostPort(baseCfg.LocalIP, strconv.Itoa(baseCfg.LocalPort))
  84. }
  85. psr.Plugin = baseCfg.Plugin
  86. if status.Err == "" {
  87. psr.RemoteAddr = status.RemoteAddr
  88. if lo.Contains([]string{"tcp", "udp"}, status.Type) {
  89. psr.RemoteAddr = serverAddr + psr.RemoteAddr
  90. }
  91. }
  92. return psr
  93. }
  94. // GET api/status
  95. func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
  96. var (
  97. buf []byte
  98. res StatusResp = make(map[string][]ProxyStatusResp)
  99. )
  100. log.Info("Http request [/api/status]")
  101. defer func() {
  102. log.Info("Http response [/api/status]")
  103. buf, _ = json.Marshal(&res)
  104. _, _ = w.Write(buf)
  105. }()
  106. ps := svr.ctl.pm.GetAllProxyStatus()
  107. for _, status := range ps {
  108. res[status.Type] = append(res[status.Type], NewProxyStatusResp(status, svr.cfg.ServerAddr))
  109. }
  110. for _, arrs := range res {
  111. if len(arrs) <= 1 {
  112. continue
  113. }
  114. sort.Slice(arrs, func(i, j int) bool {
  115. return strings.Compare(arrs[i].Name, arrs[j].Name) < 0
  116. })
  117. }
  118. }
  119. // GET api/config
  120. func (svr *Service) apiGetConfig(w http.ResponseWriter, r *http.Request) {
  121. res := GeneralResponse{Code: 200}
  122. log.Info("Http get request [/api/config]")
  123. defer func() {
  124. log.Info("Http get response [/api/config], code [%d]", res.Code)
  125. w.WriteHeader(res.Code)
  126. if len(res.Msg) > 0 {
  127. _, _ = w.Write([]byte(res.Msg))
  128. }
  129. }()
  130. if svr.cfgFile == "" {
  131. res.Code = 400
  132. res.Msg = "frpc has no config file path"
  133. log.Warn("%s", res.Msg)
  134. return
  135. }
  136. content, err := config.GetRenderedConfFromFile(svr.cfgFile)
  137. if err != nil {
  138. res.Code = 400
  139. res.Msg = err.Error()
  140. log.Warn("load frpc config file error: %s", res.Msg)
  141. return
  142. }
  143. rows := strings.Split(string(content), "\n")
  144. newRows := make([]string, 0, len(rows))
  145. for _, row := range rows {
  146. row = strings.TrimSpace(row)
  147. if strings.HasPrefix(row, "token") {
  148. continue
  149. }
  150. newRows = append(newRows, row)
  151. }
  152. res.Msg = strings.Join(newRows, "\n")
  153. }
  154. // PUT api/config
  155. func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
  156. res := GeneralResponse{Code: 200}
  157. log.Info("Http put request [/api/config]")
  158. defer func() {
  159. log.Info("Http put response [/api/config], code [%d]", res.Code)
  160. w.WriteHeader(res.Code)
  161. if len(res.Msg) > 0 {
  162. _, _ = w.Write([]byte(res.Msg))
  163. }
  164. }()
  165. // get new config content
  166. body, err := io.ReadAll(r.Body)
  167. if err != nil {
  168. res.Code = 400
  169. res.Msg = fmt.Sprintf("read request body error: %v", err)
  170. log.Warn("%s", res.Msg)
  171. return
  172. }
  173. if len(body) == 0 {
  174. res.Code = 400
  175. res.Msg = "body can't be empty"
  176. log.Warn("%s", res.Msg)
  177. return
  178. }
  179. // get token from origin content
  180. token := ""
  181. b, err := os.ReadFile(svr.cfgFile)
  182. if err != nil {
  183. res.Code = 400
  184. res.Msg = err.Error()
  185. log.Warn("load frpc config file error: %s", res.Msg)
  186. return
  187. }
  188. content := string(b)
  189. for _, row := range strings.Split(content, "\n") {
  190. row = strings.TrimSpace(row)
  191. if strings.HasPrefix(row, "token") {
  192. token = row
  193. break
  194. }
  195. }
  196. tmpRows := make([]string, 0)
  197. for _, row := range strings.Split(string(body), "\n") {
  198. row = strings.TrimSpace(row)
  199. if strings.HasPrefix(row, "token") {
  200. continue
  201. }
  202. tmpRows = append(tmpRows, row)
  203. }
  204. newRows := make([]string, 0)
  205. if token != "" {
  206. for _, row := range tmpRows {
  207. newRows = append(newRows, row)
  208. if strings.HasPrefix(row, "[common]") {
  209. newRows = append(newRows, token)
  210. }
  211. }
  212. } else {
  213. newRows = tmpRows
  214. }
  215. content = strings.Join(newRows, "\n")
  216. err = os.WriteFile(svr.cfgFile, []byte(content), 0o644)
  217. if err != nil {
  218. res.Code = 500
  219. res.Msg = fmt.Sprintf("write content to frpc config file error: %v", err)
  220. log.Warn("%s", res.Msg)
  221. return
  222. }
  223. }