site.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. package sites
  2. import (
  3. "net/http"
  4. "os"
  5. "github.com/0xJacky/Nginx-UI/internal/cert"
  6. "github.com/0xJacky/Nginx-UI/internal/nginx"
  7. "github.com/0xJacky/Nginx-UI/internal/site"
  8. "github.com/0xJacky/Nginx-UI/model"
  9. "github.com/0xJacky/Nginx-UI/query"
  10. "github.com/gin-gonic/gin"
  11. "github.com/sashabaranov/go-openai"
  12. "github.com/uozi-tech/cosy"
  13. "github.com/uozi-tech/cosy/logger"
  14. "gorm.io/gorm/clause"
  15. )
  16. func GetSite(c *gin.Context) {
  17. name := c.Param("name")
  18. path := nginx.GetConfPath("sites-available", name)
  19. file, err := os.Stat(path)
  20. if os.IsNotExist(err) {
  21. c.JSON(http.StatusNotFound, gin.H{
  22. "message": "file not found",
  23. })
  24. return
  25. }
  26. enabled := true
  27. if _, err := os.Stat(nginx.GetConfPath("sites-enabled", name)); os.IsNotExist(err) {
  28. enabled = false
  29. }
  30. g := query.ChatGPTLog
  31. chatgpt, err := g.Where(g.Name.Eq(path)).FirstOrCreate()
  32. if err != nil {
  33. cosy.ErrHandler(c, err)
  34. return
  35. }
  36. if chatgpt.Content == nil {
  37. chatgpt.Content = make([]openai.ChatCompletionMessage, 0)
  38. }
  39. s := query.Site
  40. siteModel, err := s.Where(s.Path.Eq(path)).FirstOrCreate()
  41. if err != nil {
  42. cosy.ErrHandler(c, err)
  43. return
  44. }
  45. certModel, err := model.FirstCert(name)
  46. if err != nil {
  47. logger.Warn(err)
  48. }
  49. if siteModel.Advanced {
  50. origContent, err := os.ReadFile(path)
  51. if err != nil {
  52. cosy.ErrHandler(c, err)
  53. return
  54. }
  55. c.JSON(http.StatusOK, Site{
  56. ModifiedAt: file.ModTime(),
  57. Site: siteModel,
  58. Enabled: enabled,
  59. Name: name,
  60. Config: string(origContent),
  61. AutoCert: certModel.AutoCert == model.AutoCertEnabled,
  62. ChatGPTMessages: chatgpt.Content,
  63. Filepath: path,
  64. })
  65. return
  66. }
  67. nginxConfig, err := nginx.ParseNgxConfig(path)
  68. if err != nil {
  69. cosy.ErrHandler(c, err)
  70. return
  71. }
  72. certInfoMap := make(map[int][]*cert.Info)
  73. for serverIdx, server := range nginxConfig.Servers {
  74. for _, directive := range server.Directives {
  75. if directive.Directive == "ssl_certificate" {
  76. pubKey, err := cert.GetCertInfo(directive.Params)
  77. if err != nil {
  78. logger.Error("Failed to get certificate information", err)
  79. continue
  80. }
  81. certInfoMap[serverIdx] = append(certInfoMap[serverIdx], pubKey)
  82. }
  83. }
  84. }
  85. c.JSON(http.StatusOK, Site{
  86. Site: siteModel,
  87. ModifiedAt: file.ModTime(),
  88. Enabled: enabled,
  89. Name: name,
  90. Config: nginxConfig.FmtCode(),
  91. Tokenized: nginxConfig,
  92. AutoCert: certModel.AutoCert == model.AutoCertEnabled,
  93. CertInfo: certInfoMap,
  94. ChatGPTMessages: chatgpt.Content,
  95. Filepath: path,
  96. })
  97. }
  98. func SaveSite(c *gin.Context) {
  99. name := c.Param("name")
  100. var json struct {
  101. Content string `json:"content" binding:"required"`
  102. EnvGroupID uint64 `json:"env_group_id"`
  103. SyncNodeIDs []uint64 `json:"sync_node_ids"`
  104. Overwrite bool `json:"overwrite"`
  105. PostAction string `json:"post_action"`
  106. }
  107. if !cosy.BindAndValid(c, &json) {
  108. return
  109. }
  110. err := site.Save(name, json.Content, json.Overwrite, json.EnvGroupID, json.SyncNodeIDs, json.PostAction)
  111. if err != nil {
  112. cosy.ErrHandler(c, err)
  113. return
  114. }
  115. GetSite(c)
  116. }
  117. func RenameSite(c *gin.Context) {
  118. oldName := c.Param("name")
  119. var json struct {
  120. NewName string `json:"new_name"`
  121. }
  122. if !cosy.BindAndValid(c, &json) {
  123. return
  124. }
  125. err := site.Rename(oldName, json.NewName)
  126. if err != nil {
  127. cosy.ErrHandler(c, err)
  128. return
  129. }
  130. c.JSON(http.StatusOK, gin.H{
  131. "message": "ok",
  132. })
  133. }
  134. func EnableSite(c *gin.Context) {
  135. err := site.Enable(c.Param("name"))
  136. if err != nil {
  137. cosy.ErrHandler(c, err)
  138. return
  139. }
  140. c.JSON(http.StatusOK, gin.H{
  141. "message": "ok",
  142. })
  143. }
  144. func DisableSite(c *gin.Context) {
  145. err := site.Disable(c.Param("name"))
  146. if err != nil {
  147. cosy.ErrHandler(c, err)
  148. return
  149. }
  150. c.JSON(http.StatusOK, gin.H{
  151. "message": "ok",
  152. })
  153. }
  154. func DeleteSite(c *gin.Context) {
  155. err := site.Delete(c.Param("name"))
  156. if err != nil {
  157. cosy.ErrHandler(c, err)
  158. return
  159. }
  160. c.JSON(http.StatusOK, gin.H{
  161. "message": "ok",
  162. })
  163. }
  164. func BatchUpdateSites(c *gin.Context) {
  165. cosy.Core[model.Site](c).SetValidRules(gin.H{
  166. "env_group_id": "required",
  167. }).SetItemKey("path").
  168. BeforeExecuteHook(func(ctx *cosy.Ctx[model.Site]) {
  169. effectedPath := make([]string, len(ctx.BatchEffectedIDs))
  170. var sites []*model.Site
  171. for i, name := range ctx.BatchEffectedIDs {
  172. path := nginx.GetConfPath("sites-available", name)
  173. effectedPath[i] = path
  174. sites = append(sites, &model.Site{
  175. Path: path,
  176. })
  177. }
  178. s := query.Site
  179. err := s.Clauses(clause.OnConflict{
  180. DoNothing: true,
  181. }).Create(sites...)
  182. if err != nil {
  183. ctx.AbortWithError(err)
  184. return
  185. }
  186. ctx.BatchEffectedIDs = effectedPath
  187. }).BatchModify()
  188. }