1
0

node.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. package controller
  2. import (
  3. "context"
  4. "fmt"
  5. "slices"
  6. "strconv"
  7. "time"
  8. "github.com/mhsanaei/3x-ui/v3/database/model"
  9. "github.com/mhsanaei/3x-ui/v3/web/middleware"
  10. "github.com/mhsanaei/3x-ui/v3/web/service"
  11. "github.com/gin-gonic/gin"
  12. )
  13. type NodeController struct {
  14. nodeService service.NodeService
  15. }
  16. func NewNodeController(g *gin.RouterGroup) *NodeController {
  17. a := &NodeController{}
  18. a.initRouter(g)
  19. return a
  20. }
  21. func (a *NodeController) initRouter(g *gin.RouterGroup) {
  22. g.GET("/list", a.list)
  23. g.GET("/get/:id", a.get)
  24. g.POST("/add", a.add)
  25. g.POST("/update/:id", a.update)
  26. g.POST("/del/:id", a.del)
  27. g.POST("/setEnable/:id", a.setEnable)
  28. g.POST("/test", a.test)
  29. g.POST("/certFingerprint", a.certFingerprint)
  30. g.POST("/probe/:id", a.probe)
  31. g.POST("/updatePanel", a.updatePanel)
  32. g.GET("/history/:id/:metric/:bucket", a.history)
  33. }
  34. func (a *NodeController) list(c *gin.Context) {
  35. nodes, err := a.nodeService.GetAll()
  36. if err != nil {
  37. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.list"), err)
  38. return
  39. }
  40. jsonObj(c, nodes, nil)
  41. }
  42. func (a *NodeController) get(c *gin.Context) {
  43. id, err := strconv.Atoi(c.Param("id"))
  44. if err != nil {
  45. jsonMsg(c, I18nWeb(c, "get"), err)
  46. return
  47. }
  48. n, err := a.nodeService.GetById(id)
  49. if err != nil {
  50. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.obtain"), err)
  51. return
  52. }
  53. jsonObj(c, n, nil)
  54. }
  55. func (a *NodeController) add(c *gin.Context) {
  56. n, ok := middleware.BindAndValidate[model.Node](c)
  57. if !ok {
  58. return
  59. }
  60. if err := a.nodeService.Create(n); err != nil {
  61. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.add"), err)
  62. return
  63. }
  64. jsonMsgObj(c, I18nWeb(c, "pages.nodes.toasts.add"), n, nil)
  65. }
  66. func (a *NodeController) update(c *gin.Context) {
  67. id, err := strconv.Atoi(c.Param("id"))
  68. if err != nil {
  69. jsonMsg(c, I18nWeb(c, "get"), err)
  70. return
  71. }
  72. n, ok := middleware.BindAndValidate[model.Node](c)
  73. if !ok {
  74. return
  75. }
  76. if err := a.nodeService.Update(id, n); err != nil {
  77. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), err)
  78. return
  79. }
  80. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), nil)
  81. }
  82. func (a *NodeController) del(c *gin.Context) {
  83. id, err := strconv.Atoi(c.Param("id"))
  84. if err != nil {
  85. jsonMsg(c, I18nWeb(c, "get"), err)
  86. return
  87. }
  88. if err := a.nodeService.Delete(id); err != nil {
  89. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.delete"), err)
  90. return
  91. }
  92. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.delete"), nil)
  93. }
  94. func (a *NodeController) setEnable(c *gin.Context) {
  95. id, err := strconv.Atoi(c.Param("id"))
  96. if err != nil {
  97. jsonMsg(c, I18nWeb(c, "get"), err)
  98. return
  99. }
  100. body := struct {
  101. Enable bool `json:"enable" form:"enable"`
  102. }{}
  103. if err := c.ShouldBind(&body); err != nil {
  104. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), err)
  105. return
  106. }
  107. if err := a.nodeService.SetEnable(id, body.Enable); err != nil {
  108. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), err)
  109. return
  110. }
  111. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), nil)
  112. }
  113. func (a *NodeController) test(c *gin.Context) {
  114. n := &model.Node{}
  115. if err := c.ShouldBind(n); err != nil {
  116. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.test"), err)
  117. return
  118. }
  119. if n.Scheme == "" {
  120. n.Scheme = "https"
  121. }
  122. if n.BasePath == "" {
  123. n.BasePath = "/"
  124. }
  125. ctx, cancel := context.WithTimeout(c.Request.Context(), 6*time.Second)
  126. defer cancel()
  127. patch, err := a.nodeService.Probe(ctx, n)
  128. jsonObj(c, patch.ToUI(err == nil), nil)
  129. }
  130. func (a *NodeController) certFingerprint(c *gin.Context) {
  131. n := &model.Node{}
  132. if err := c.ShouldBind(n); err != nil {
  133. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.test"), err)
  134. return
  135. }
  136. if n.Scheme == "" {
  137. n.Scheme = "https"
  138. }
  139. if n.BasePath == "" {
  140. n.BasePath = "/"
  141. }
  142. ctx, cancel := context.WithTimeout(c.Request.Context(), 6*time.Second)
  143. defer cancel()
  144. fp, err := a.nodeService.FetchCertFingerprint(ctx, n)
  145. if err != nil {
  146. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.test"), err)
  147. return
  148. }
  149. jsonObj(c, fp, nil)
  150. }
  151. func (a *NodeController) probe(c *gin.Context) {
  152. id, err := strconv.Atoi(c.Param("id"))
  153. if err != nil {
  154. jsonMsg(c, I18nWeb(c, "get"), err)
  155. return
  156. }
  157. n, err := a.nodeService.GetById(id)
  158. if err != nil {
  159. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.obtain"), err)
  160. return
  161. }
  162. ctx, cancel := context.WithTimeout(c.Request.Context(), 6*time.Second)
  163. defer cancel()
  164. patch, probeErr := a.nodeService.Probe(ctx, n)
  165. if probeErr != nil {
  166. patch.Status = "offline"
  167. } else {
  168. patch.Status = "online"
  169. }
  170. _ = a.nodeService.UpdateHeartbeat(id, patch)
  171. jsonObj(c, patch.ToUI(probeErr == nil), nil)
  172. }
  173. func (a *NodeController) updatePanel(c *gin.Context) {
  174. var req struct {
  175. Ids []int `json:"ids"`
  176. }
  177. if err := c.ShouldBindJSON(&req); err != nil {
  178. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  179. return
  180. }
  181. if len(req.Ids) == 0 {
  182. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), fmt.Errorf("no nodes selected"))
  183. return
  184. }
  185. results, err := a.nodeService.UpdatePanels(req.Ids)
  186. jsonMsgObj(c, I18nWeb(c, "pages.nodes.toasts.updateStarted"), results, err)
  187. }
  188. func (a *NodeController) history(c *gin.Context) {
  189. id, err := strconv.Atoi(c.Param("id"))
  190. if err != nil {
  191. jsonMsg(c, I18nWeb(c, "get"), err)
  192. return
  193. }
  194. metric := c.Param("metric")
  195. if !slices.Contains(service.NodeMetricKeys, metric) {
  196. jsonMsg(c, "invalid metric", fmt.Errorf("unknown metric"))
  197. return
  198. }
  199. bucket, err := strconv.Atoi(c.Param("bucket"))
  200. if err != nil || bucket <= 0 || !service.IsAllowedHistoryBucket(bucket) {
  201. jsonMsg(c, "invalid bucket", fmt.Errorf("unsupported bucket"))
  202. return
  203. }
  204. jsonObj(c, a.nodeService.AggregateNodeMetric(id, metric, bucket, 60), nil)
  205. }