site.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  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. SiteCategoryID uint64 `json:"site_category_id"`
  103. SyncNodeIDs []uint64 `json:"sync_node_ids"`
  104. Overwrite bool `json:"overwrite"`
  105. }
  106. if !cosy.BindAndValid(c, &json) {
  107. return
  108. }
  109. err := site.Save(name, json.Content, json.Overwrite, json.SiteCategoryID, json.SyncNodeIDs)
  110. if err != nil {
  111. cosy.ErrHandler(c, err)
  112. return
  113. }
  114. GetSite(c)
  115. }
  116. func RenameSite(c *gin.Context) {
  117. oldName := c.Param("name")
  118. var json struct {
  119. NewName string `json:"new_name"`
  120. }
  121. if !cosy.BindAndValid(c, &json) {
  122. return
  123. }
  124. err := site.Rename(oldName, json.NewName)
  125. if err != nil {
  126. cosy.ErrHandler(c, err)
  127. return
  128. }
  129. c.JSON(http.StatusOK, gin.H{
  130. "message": "ok",
  131. })
  132. }
  133. func EnableSite(c *gin.Context) {
  134. err := site.Enable(c.Param("name"))
  135. if err != nil {
  136. cosy.ErrHandler(c, err)
  137. return
  138. }
  139. c.JSON(http.StatusOK, gin.H{
  140. "message": "ok",
  141. })
  142. }
  143. func DisableSite(c *gin.Context) {
  144. err := site.Disable(c.Param("name"))
  145. if err != nil {
  146. cosy.ErrHandler(c, err)
  147. return
  148. }
  149. c.JSON(http.StatusOK, gin.H{
  150. "message": "ok",
  151. })
  152. }
  153. func DeleteSite(c *gin.Context) {
  154. err := site.Delete(c.Param("name"))
  155. if err != nil {
  156. cosy.ErrHandler(c, err)
  157. return
  158. }
  159. c.JSON(http.StatusOK, gin.H{
  160. "message": "ok",
  161. })
  162. }
  163. func BatchUpdateSites(c *gin.Context) {
  164. cosy.Core[model.Site](c).SetValidRules(gin.H{
  165. "site_category_id": "required",
  166. }).SetItemKey("path").
  167. BeforeExecuteHook(func(ctx *cosy.Ctx[model.Site]) {
  168. effectedPath := make([]string, len(ctx.BatchEffectedIDs))
  169. var sites []*model.Site
  170. for i, name := range ctx.BatchEffectedIDs {
  171. path := nginx.GetConfPath("sites-available", name)
  172. effectedPath[i] = path
  173. sites = append(sites, &model.Site{
  174. Path: path,
  175. })
  176. }
  177. s := query.Site
  178. err := s.Clauses(clause.OnConflict{
  179. DoNothing: true,
  180. }).Create(sites...)
  181. if err != nil {
  182. ctx.AbortWithError(err)
  183. return
  184. }
  185. ctx.BatchEffectedIDs = effectedPath
  186. }).BatchModify()
  187. }