environment.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package cluster
  2. import (
  3. "context"
  4. "crypto/sha256"
  5. "encoding/hex"
  6. "encoding/json"
  7. "io"
  8. "net/http"
  9. "time"
  10. "github.com/0xJacky/Nginx-UI/api"
  11. "github.com/0xJacky/Nginx-UI/internal/analytic"
  12. "github.com/0xJacky/Nginx-UI/internal/cluster"
  13. "github.com/0xJacky/Nginx-UI/model"
  14. "github.com/0xJacky/Nginx-UI/query"
  15. "github.com/0xJacky/Nginx-UI/settings"
  16. "github.com/gin-gonic/gin"
  17. "github.com/spf13/cast"
  18. "github.com/uozi-tech/cosy"
  19. "gorm.io/gorm"
  20. )
  21. func GetEnvironment(c *gin.Context) {
  22. id := cast.ToUint64(c.Param("id"))
  23. envQuery := query.Environment
  24. env, err := envQuery.FirstByID(id)
  25. if err != nil {
  26. cosy.ErrHandler(c, err)
  27. return
  28. }
  29. c.JSON(http.StatusOK, analytic.GetNode(env))
  30. }
  31. func GetEnvironmentList(c *gin.Context) {
  32. core := cosy.Core[model.Environment](c).
  33. SetFussy("name")
  34. // fix for sqlite
  35. if c.Query("enabled") != "" {
  36. core.GormScope(func(tx *gorm.DB) *gorm.DB {
  37. return tx.Where("enabled = ?", cast.ToInt(cast.ToBool(c.Query("enabled"))))
  38. })
  39. }
  40. core.SetTransformer(func(m *model.Environment) any {
  41. return analytic.GetNode(m)
  42. }).PagingList()
  43. }
  44. func GetAllEnabledEnvironment(c *gin.Context) {
  45. api.SetSSEHeaders(c)
  46. notify := c.Writer.CloseNotify()
  47. interval := 10
  48. type respEnvironment struct {
  49. *model.Environment
  50. Status bool `json:"status"`
  51. }
  52. f := func() (any, bool) {
  53. return cosy.Core[model.Environment](c).
  54. SetFussy("name").
  55. SetTransformer(func(m *model.Environment) any {
  56. resp := respEnvironment{
  57. Environment: m,
  58. Status: analytic.GetNode(m).Status,
  59. }
  60. return resp
  61. }).ListAllData()
  62. }
  63. getHash := func(data any) string {
  64. bytes, _ := json.Marshal(data)
  65. hash := sha256.New()
  66. hash.Write(bytes)
  67. hashSum := hash.Sum(nil)
  68. return hex.EncodeToString(hashSum)
  69. }
  70. dataHash := ""
  71. {
  72. data, ok := f()
  73. if !ok {
  74. return
  75. }
  76. c.Stream(func(w io.Writer) bool {
  77. c.SSEvent("message", data)
  78. dataHash = getHash(data)
  79. return false
  80. })
  81. }
  82. for {
  83. select {
  84. case <-time.After(time.Duration(interval) * time.Second):
  85. data, ok := f()
  86. if !ok {
  87. return
  88. }
  89. // if data is not changed, send heartbeat
  90. if dataHash == getHash(data) {
  91. c.Stream(func(w io.Writer) bool {
  92. c.SSEvent("heartbeat", "")
  93. return false
  94. })
  95. return
  96. }
  97. dataHash = getHash(data)
  98. c.Stream(func(w io.Writer) bool {
  99. c.SSEvent("message", data)
  100. return false
  101. })
  102. case <-time.After(30 * time.Second):
  103. c.Stream(func(w io.Writer) bool {
  104. c.SSEvent("heartbeat", "")
  105. return false
  106. })
  107. case <-notify:
  108. return
  109. }
  110. }
  111. }
  112. func AddEnvironment(c *gin.Context) {
  113. cosy.Core[model.Environment](c).SetValidRules(gin.H{
  114. "name": "required",
  115. "url": "required,url",
  116. "token": "required",
  117. "enabled": "omitempty,boolean",
  118. }).ExecutedHook(func(c *cosy.Ctx[model.Environment]) {
  119. go analytic.RestartRetrieveNodesStatus()
  120. }).Create()
  121. }
  122. func EditEnvironment(c *gin.Context) {
  123. cosy.Core[model.Environment](c).SetValidRules(gin.H{
  124. "name": "required",
  125. "url": "required,url",
  126. "token": "required",
  127. "enabled": "omitempty,boolean",
  128. }).ExecutedHook(func(c *cosy.Ctx[model.Environment]) {
  129. go analytic.RestartRetrieveNodesStatus()
  130. }).Modify()
  131. }
  132. func DeleteEnvironment(c *gin.Context) {
  133. cosy.Core[model.Environment](c).
  134. ExecutedHook(func(c *cosy.Ctx[model.Environment]) {
  135. go analytic.RestartRetrieveNodesStatus()
  136. }).Destroy()
  137. }
  138. func LoadEnvironmentFromSettings(c *gin.Context) {
  139. err := settings.ReloadCluster()
  140. if err != nil {
  141. cosy.ErrHandler(c, err)
  142. return
  143. }
  144. ctx := context.Background()
  145. cluster.RegisterPredefinedNodes(ctx)
  146. go analytic.RestartRetrieveNodesStatus()
  147. c.JSON(http.StatusOK, gin.H{
  148. "message": "ok",
  149. })
  150. }