environment.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. })
  43. data, ok := core.ListAllData()
  44. if !ok {
  45. return
  46. }
  47. c.JSON(http.StatusOK, model.DataList{
  48. Data: data,
  49. })
  50. }
  51. func GetAllEnabledEnvironment(c *gin.Context) {
  52. api.SetSSEHeaders(c)
  53. notify := c.Writer.CloseNotify()
  54. interval := 10
  55. type respEnvironment struct {
  56. *model.Environment
  57. Status bool `json:"status"`
  58. }
  59. f := func() (any, bool) {
  60. return cosy.Core[model.Environment](c).
  61. SetFussy("name").
  62. SetTransformer(func(m *model.Environment) any {
  63. resp := respEnvironment{
  64. Environment: m,
  65. Status: analytic.GetNode(m).Status,
  66. }
  67. return resp
  68. }).ListAllData()
  69. }
  70. getHash := func(data any) string {
  71. bytes, _ := json.Marshal(data)
  72. hash := sha256.New()
  73. hash.Write(bytes)
  74. hashSum := hash.Sum(nil)
  75. return hex.EncodeToString(hashSum)
  76. }
  77. dataHash := ""
  78. {
  79. data, ok := f()
  80. if !ok {
  81. return
  82. }
  83. c.Stream(func(w io.Writer) bool {
  84. c.SSEvent("message", data)
  85. dataHash = getHash(data)
  86. return false
  87. })
  88. }
  89. for {
  90. select {
  91. case <-time.After(time.Duration(interval) * time.Second):
  92. data, ok := f()
  93. if !ok {
  94. return
  95. }
  96. // if data is not changed, send heartbeat
  97. if dataHash == getHash(data) {
  98. c.Stream(func(w io.Writer) bool {
  99. c.SSEvent("heartbeat", "")
  100. return false
  101. })
  102. return
  103. }
  104. dataHash = getHash(data)
  105. c.Stream(func(w io.Writer) bool {
  106. c.SSEvent("message", data)
  107. return false
  108. })
  109. case <-time.After(30 * time.Second):
  110. c.Stream(func(w io.Writer) bool {
  111. c.SSEvent("heartbeat", "")
  112. return false
  113. })
  114. case <-notify:
  115. return
  116. }
  117. }
  118. }
  119. func AddEnvironment(c *gin.Context) {
  120. cosy.Core[model.Environment](c).SetValidRules(gin.H{
  121. "name": "required",
  122. "url": "required,url",
  123. "token": "required",
  124. "enabled": "omitempty,boolean",
  125. }).ExecutedHook(func(c *cosy.Ctx[model.Environment]) {
  126. go analytic.RestartRetrieveNodesStatus()
  127. }).Create()
  128. }
  129. func EditEnvironment(c *gin.Context) {
  130. cosy.Core[model.Environment](c).SetValidRules(gin.H{
  131. "name": "required",
  132. "url": "required,url",
  133. "token": "required",
  134. "enabled": "omitempty,boolean",
  135. }).ExecutedHook(func(c *cosy.Ctx[model.Environment]) {
  136. go analytic.RestartRetrieveNodesStatus()
  137. }).Modify()
  138. }
  139. func DeleteEnvironment(c *gin.Context) {
  140. cosy.Core[model.Environment](c).
  141. ExecutedHook(func(c *cosy.Ctx[model.Environment]) {
  142. go analytic.RestartRetrieveNodesStatus()
  143. }).Destroy()
  144. }
  145. func LoadEnvironmentFromSettings(c *gin.Context) {
  146. err := settings.ReloadCluster()
  147. if err != nil {
  148. cosy.ErrHandler(c, err)
  149. return
  150. }
  151. ctx := context.Background()
  152. cluster.RegisterPredefinedNodes(ctx)
  153. go analytic.RestartRetrieveNodesStatus()
  154. c.JSON(http.StatusOK, gin.H{
  155. "message": "ok",
  156. })
  157. }