site.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. package sites
  2. import (
  3. "github.com/0xJacky/Nginx-UI/api"
  4. "github.com/0xJacky/Nginx-UI/internal/cert"
  5. "github.com/0xJacky/Nginx-UI/internal/nginx"
  6. "github.com/0xJacky/Nginx-UI/internal/site"
  7. "github.com/0xJacky/Nginx-UI/model"
  8. "github.com/0xJacky/Nginx-UI/query"
  9. "github.com/gin-gonic/gin"
  10. "github.com/sashabaranov/go-openai"
  11. "github.com/uozi-tech/cosy"
  12. "github.com/uozi-tech/cosy/logger"
  13. "gorm.io/gorm/clause"
  14. "net/http"
  15. "os"
  16. )
  17. func GetSite(c *gin.Context) {
  18. name := c.Param("name")
  19. path := nginx.GetConfPath("sites-available", name)
  20. file, err := os.Stat(path)
  21. if os.IsNotExist(err) {
  22. c.JSON(http.StatusNotFound, gin.H{
  23. "message": "file not found",
  24. })
  25. return
  26. }
  27. enabled := true
  28. if _, err := os.Stat(nginx.GetConfPath("sites-enabled", name)); os.IsNotExist(err) {
  29. enabled = false
  30. }
  31. g := query.ChatGPTLog
  32. chatgpt, err := g.Where(g.Name.Eq(path)).FirstOrCreate()
  33. if err != nil {
  34. api.ErrHandler(c, err)
  35. return
  36. }
  37. if chatgpt.Content == nil {
  38. chatgpt.Content = make([]openai.ChatCompletionMessage, 0)
  39. }
  40. s := query.Site
  41. siteModel, err := s.Where(s.Path.Eq(path)).FirstOrCreate()
  42. if err != nil {
  43. api.ErrHandler(c, err)
  44. return
  45. }
  46. certModel, err := model.FirstCert(name)
  47. if err != nil {
  48. logger.Warn(err)
  49. }
  50. if siteModel.Advanced {
  51. origContent, err := os.ReadFile(path)
  52. if err != nil {
  53. api.ErrHandler(c, err)
  54. return
  55. }
  56. c.JSON(http.StatusOK, Site{
  57. ModifiedAt: file.ModTime(),
  58. Site: siteModel,
  59. Enabled: enabled,
  60. Name: name,
  61. Config: string(origContent),
  62. AutoCert: certModel.AutoCert == model.AutoCertEnabled,
  63. ChatGPTMessages: chatgpt.Content,
  64. Filepath: path,
  65. })
  66. return
  67. }
  68. nginxConfig, err := nginx.ParseNgxConfig(path)
  69. if err != nil {
  70. api.ErrHandler(c, err)
  71. return
  72. }
  73. certInfoMap := make(map[int][]*cert.Info)
  74. for serverIdx, server := range nginxConfig.Servers {
  75. for _, directive := range server.Directives {
  76. if directive.Directive == "ssl_certificate" {
  77. pubKey, err := cert.GetCertInfo(directive.Params)
  78. if err != nil {
  79. logger.Error("Failed to get certificate information", err)
  80. continue
  81. }
  82. certInfoMap[serverIdx] = append(certInfoMap[serverIdx], pubKey)
  83. }
  84. }
  85. }
  86. c.JSON(http.StatusOK, Site{
  87. Site: siteModel,
  88. ModifiedAt: file.ModTime(),
  89. Enabled: enabled,
  90. Name: name,
  91. Config: nginxConfig.FmtCode(),
  92. Tokenized: nginxConfig,
  93. AutoCert: certModel.AutoCert == model.AutoCertEnabled,
  94. CertInfo: certInfoMap,
  95. ChatGPTMessages: chatgpt.Content,
  96. Filepath: path,
  97. })
  98. }
  99. func SaveSite(c *gin.Context) {
  100. name := c.Param("name")
  101. var json struct {
  102. Content string `json:"content" binding:"required"`
  103. SiteCategoryID uint64 `json:"site_category_id"`
  104. SyncNodeIDs []uint64 `json:"sync_node_ids"`
  105. Overwrite bool `json:"overwrite"`
  106. }
  107. if !cosy.BindAndValid(c, &json) {
  108. return
  109. }
  110. err := site.Save(name, json.Content, json.Overwrite, json.SiteCategoryID, json.SyncNodeIDs)
  111. if err != nil {
  112. api.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. api.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. api.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. api.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. api.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. "site_category_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. }