1
0

node.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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("/probe/:id", a.probe)
  30. g.POST("/updatePanel", a.updatePanel)
  31. g.GET("/history/:id/:metric/:bucket", a.history)
  32. }
  33. func (a *NodeController) list(c *gin.Context) {
  34. nodes, err := a.nodeService.GetAll()
  35. if err != nil {
  36. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.list"), err)
  37. return
  38. }
  39. jsonObj(c, nodes, nil)
  40. }
  41. func (a *NodeController) get(c *gin.Context) {
  42. id, err := strconv.Atoi(c.Param("id"))
  43. if err != nil {
  44. jsonMsg(c, I18nWeb(c, "get"), err)
  45. return
  46. }
  47. n, err := a.nodeService.GetById(id)
  48. if err != nil {
  49. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.obtain"), err)
  50. return
  51. }
  52. jsonObj(c, n, nil)
  53. }
  54. func (a *NodeController) add(c *gin.Context) {
  55. n, ok := middleware.BindAndValidate[model.Node](c)
  56. if !ok {
  57. return
  58. }
  59. if err := a.nodeService.Create(n); err != nil {
  60. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.add"), err)
  61. return
  62. }
  63. jsonMsgObj(c, I18nWeb(c, "pages.nodes.toasts.add"), n, nil)
  64. }
  65. func (a *NodeController) update(c *gin.Context) {
  66. id, err := strconv.Atoi(c.Param("id"))
  67. if err != nil {
  68. jsonMsg(c, I18nWeb(c, "get"), err)
  69. return
  70. }
  71. n, ok := middleware.BindAndValidate[model.Node](c)
  72. if !ok {
  73. return
  74. }
  75. if err := a.nodeService.Update(id, n); err != nil {
  76. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), err)
  77. return
  78. }
  79. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), nil)
  80. }
  81. func (a *NodeController) del(c *gin.Context) {
  82. id, err := strconv.Atoi(c.Param("id"))
  83. if err != nil {
  84. jsonMsg(c, I18nWeb(c, "get"), err)
  85. return
  86. }
  87. if err := a.nodeService.Delete(id); err != nil {
  88. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.delete"), err)
  89. return
  90. }
  91. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.delete"), nil)
  92. }
  93. func (a *NodeController) setEnable(c *gin.Context) {
  94. id, err := strconv.Atoi(c.Param("id"))
  95. if err != nil {
  96. jsonMsg(c, I18nWeb(c, "get"), err)
  97. return
  98. }
  99. body := struct {
  100. Enable bool `json:"enable" form:"enable"`
  101. }{}
  102. if err := c.ShouldBind(&body); err != nil {
  103. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), err)
  104. return
  105. }
  106. if err := a.nodeService.SetEnable(id, body.Enable); err != nil {
  107. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), err)
  108. return
  109. }
  110. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.update"), nil)
  111. }
  112. func (a *NodeController) test(c *gin.Context) {
  113. n := &model.Node{}
  114. if err := c.ShouldBind(n); err != nil {
  115. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.test"), err)
  116. return
  117. }
  118. if n.Scheme == "" {
  119. n.Scheme = "https"
  120. }
  121. if n.BasePath == "" {
  122. n.BasePath = "/"
  123. }
  124. ctx, cancel := context.WithTimeout(c.Request.Context(), 6*time.Second)
  125. defer cancel()
  126. patch, err := a.nodeService.Probe(ctx, n)
  127. jsonObj(c, patch.ToUI(err == nil), nil)
  128. }
  129. func (a *NodeController) probe(c *gin.Context) {
  130. id, err := strconv.Atoi(c.Param("id"))
  131. if err != nil {
  132. jsonMsg(c, I18nWeb(c, "get"), err)
  133. return
  134. }
  135. n, err := a.nodeService.GetById(id)
  136. if err != nil {
  137. jsonMsg(c, I18nWeb(c, "pages.nodes.toasts.obtain"), err)
  138. return
  139. }
  140. ctx, cancel := context.WithTimeout(c.Request.Context(), 6*time.Second)
  141. defer cancel()
  142. patch, probeErr := a.nodeService.Probe(ctx, n)
  143. if probeErr != nil {
  144. patch.Status = "offline"
  145. } else {
  146. patch.Status = "online"
  147. }
  148. _ = a.nodeService.UpdateHeartbeat(id, patch)
  149. jsonObj(c, patch.ToUI(probeErr == nil), nil)
  150. }
  151. func (a *NodeController) updatePanel(c *gin.Context) {
  152. var req struct {
  153. Ids []int `json:"ids"`
  154. }
  155. if err := c.ShouldBindJSON(&req); err != nil {
  156. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  157. return
  158. }
  159. if len(req.Ids) == 0 {
  160. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), fmt.Errorf("no nodes selected"))
  161. return
  162. }
  163. results, err := a.nodeService.UpdatePanels(req.Ids)
  164. jsonMsgObj(c, I18nWeb(c, "pages.nodes.toasts.updateStarted"), results, err)
  165. }
  166. func (a *NodeController) history(c *gin.Context) {
  167. id, err := strconv.Atoi(c.Param("id"))
  168. if err != nil {
  169. jsonMsg(c, I18nWeb(c, "get"), err)
  170. return
  171. }
  172. metric := c.Param("metric")
  173. if !slices.Contains(service.NodeMetricKeys, metric) {
  174. jsonMsg(c, "invalid metric", fmt.Errorf("unknown metric"))
  175. return
  176. }
  177. bucket, err := strconv.Atoi(c.Param("bucket"))
  178. if err != nil || bucket <= 0 || !service.IsAllowedHistoryBucket(bucket) {
  179. jsonMsg(c, "invalid bucket", fmt.Errorf("unsupported bucket"))
  180. return
  181. }
  182. jsonObj(c, a.nodeService.AggregateNodeMetric(id, metric, bucket, 60), nil)
  183. }