1
0

node.go 4.4 KB

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