site.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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. name := c.Param("name")
  136. // Check if the site is in maintenance mode, if yes, disable maintenance mode first
  137. maintenanceConfigPath := nginx.GetConfPath("sites-enabled", name+site.MaintenanceSuffix)
  138. if _, err := os.Stat(maintenanceConfigPath); err == nil {
  139. // Site is in maintenance mode, disable it first
  140. err := site.DisableMaintenance(name)
  141. if err != nil {
  142. cosy.ErrHandler(c, err)
  143. return
  144. }
  145. }
  146. // Then enable the site normally
  147. err := site.Enable(name)
  148. if err != nil {
  149. cosy.ErrHandler(c, err)
  150. return
  151. }
  152. c.JSON(http.StatusOK, gin.H{
  153. "message": "ok",
  154. })
  155. }
  156. func DisableSite(c *gin.Context) {
  157. name := c.Param("name")
  158. // Check if the site is in maintenance mode, if yes, disable maintenance mode first
  159. maintenanceConfigPath := nginx.GetConfPath("sites-enabled", name+site.MaintenanceSuffix)
  160. if _, err := os.Stat(maintenanceConfigPath); err == nil {
  161. // Site is in maintenance mode, disable it first
  162. err := site.DisableMaintenance(name)
  163. if err != nil {
  164. cosy.ErrHandler(c, err)
  165. return
  166. }
  167. }
  168. // Then disable the site normally
  169. err := site.Disable(name)
  170. if err != nil {
  171. cosy.ErrHandler(c, err)
  172. return
  173. }
  174. c.JSON(http.StatusOK, gin.H{
  175. "message": "ok",
  176. })
  177. }
  178. func DeleteSite(c *gin.Context) {
  179. err := site.Delete(c.Param("name"))
  180. if err != nil {
  181. cosy.ErrHandler(c, err)
  182. return
  183. }
  184. c.JSON(http.StatusOK, gin.H{
  185. "message": "ok",
  186. })
  187. }
  188. func BatchUpdateSites(c *gin.Context) {
  189. cosy.Core[model.Site](c).SetValidRules(gin.H{
  190. "env_group_id": "required",
  191. }).SetItemKey("path").
  192. BeforeExecuteHook(func(ctx *cosy.Ctx[model.Site]) {
  193. effectedPath := make([]string, len(ctx.BatchEffectedIDs))
  194. var sites []*model.Site
  195. for i, name := range ctx.BatchEffectedIDs {
  196. path := nginx.GetConfPath("sites-available", name)
  197. effectedPath[i] = path
  198. sites = append(sites, &model.Site{
  199. Path: path,
  200. })
  201. }
  202. s := query.Site
  203. err := s.Clauses(clause.OnConflict{
  204. DoNothing: true,
  205. }).Create(sites...)
  206. if err != nil {
  207. ctx.AbortWithError(err)
  208. return
  209. }
  210. ctx.BatchEffectedIDs = effectedPath
  211. }).BatchModify()
  212. }
  213. func EnableMaintenanceSite(c *gin.Context) {
  214. name := c.Param("name")
  215. // If site is already enabled, disable the normal site first
  216. enabledConfigPath := nginx.GetConfPath("sites-enabled", name)
  217. if _, err := os.Stat(enabledConfigPath); err == nil {
  218. // Site is already enabled, disable normal site first
  219. err := site.Disable(name)
  220. if err != nil {
  221. cosy.ErrHandler(c, err)
  222. return
  223. }
  224. }
  225. // Then enable maintenance mode
  226. err := site.EnableMaintenance(name)
  227. if err != nil {
  228. cosy.ErrHandler(c, err)
  229. return
  230. }
  231. c.JSON(http.StatusOK, gin.H{
  232. "message": "ok",
  233. })
  234. }