site.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. g := query.ChatGPTLog
  27. chatgpt, err := g.Where(g.Name.Eq(path)).FirstOrCreate()
  28. if err != nil {
  29. cosy.ErrHandler(c, err)
  30. return
  31. }
  32. if chatgpt.Content == nil {
  33. chatgpt.Content = make([]openai.ChatCompletionMessage, 0)
  34. }
  35. s := query.Site
  36. siteModel, err := s.Where(s.Path.Eq(path)).FirstOrCreate()
  37. if err != nil {
  38. cosy.ErrHandler(c, err)
  39. return
  40. }
  41. certModel, err := model.FirstCert(name)
  42. if err != nil {
  43. logger.Warn(err)
  44. }
  45. if siteModel.Advanced {
  46. origContent, err := os.ReadFile(path)
  47. if err != nil {
  48. cosy.ErrHandler(c, err)
  49. return
  50. }
  51. c.JSON(http.StatusOK, site.Site{
  52. ModifiedAt: file.ModTime(),
  53. Site: siteModel,
  54. Name: name,
  55. Config: string(origContent),
  56. AutoCert: certModel.AutoCert == model.AutoCertEnabled,
  57. ChatGPTMessages: chatgpt.Content,
  58. Filepath: path,
  59. Status: site.GetSiteStatus(name),
  60. })
  61. return
  62. }
  63. nginxConfig, err := nginx.ParseNgxConfig(path)
  64. if err != nil {
  65. cosy.ErrHandler(c, err)
  66. return
  67. }
  68. certInfoMap := make(map[int][]*cert.Info)
  69. for serverIdx, server := range nginxConfig.Servers {
  70. for _, directive := range server.Directives {
  71. if directive.Directive == "ssl_certificate" {
  72. pubKey, err := cert.GetCertInfo(directive.Params)
  73. if err != nil {
  74. logger.Error("Failed to get certificate information", err)
  75. continue
  76. }
  77. certInfoMap[serverIdx] = append(certInfoMap[serverIdx], pubKey)
  78. }
  79. }
  80. }
  81. c.JSON(http.StatusOK, site.Site{
  82. Site: siteModel,
  83. ModifiedAt: file.ModTime(),
  84. Name: name,
  85. Config: nginxConfig.FmtCode(),
  86. Tokenized: nginxConfig,
  87. AutoCert: certModel.AutoCert == model.AutoCertEnabled,
  88. CertInfo: certInfoMap,
  89. ChatGPTMessages: chatgpt.Content,
  90. Filepath: path,
  91. Status: site.GetSiteStatus(name),
  92. })
  93. }
  94. func SaveSite(c *gin.Context) {
  95. name := c.Param("name")
  96. var json struct {
  97. Content string `json:"content" binding:"required"`
  98. EnvGroupID uint64 `json:"env_group_id"`
  99. SyncNodeIDs []uint64 `json:"sync_node_ids"`
  100. Overwrite bool `json:"overwrite"`
  101. PostAction string `json:"post_action"`
  102. }
  103. if !cosy.BindAndValid(c, &json) {
  104. return
  105. }
  106. err := site.Save(name, json.Content, json.Overwrite, json.EnvGroupID, json.SyncNodeIDs, json.PostAction)
  107. if err != nil {
  108. cosy.ErrHandler(c, err)
  109. return
  110. }
  111. GetSite(c)
  112. }
  113. func RenameSite(c *gin.Context) {
  114. oldName := c.Param("name")
  115. var json struct {
  116. NewName string `json:"new_name"`
  117. }
  118. if !cosy.BindAndValid(c, &json) {
  119. return
  120. }
  121. err := site.Rename(oldName, json.NewName)
  122. if err != nil {
  123. cosy.ErrHandler(c, err)
  124. return
  125. }
  126. c.JSON(http.StatusOK, gin.H{
  127. "message": "ok",
  128. })
  129. }
  130. func EnableSite(c *gin.Context) {
  131. name := c.Param("name")
  132. // Check if the site is in maintenance mode, if yes, disable maintenance mode first
  133. maintenanceConfigPath := nginx.GetConfPath("sites-enabled", name+site.MaintenanceSuffix)
  134. if _, err := os.Stat(maintenanceConfigPath); err == nil {
  135. // Site is in maintenance mode, disable it first
  136. err := site.DisableMaintenance(name)
  137. if err != nil {
  138. cosy.ErrHandler(c, err)
  139. return
  140. }
  141. }
  142. // Then enable the site normally
  143. err := site.Enable(name)
  144. if err != nil {
  145. cosy.ErrHandler(c, err)
  146. return
  147. }
  148. c.JSON(http.StatusOK, gin.H{
  149. "message": "ok",
  150. })
  151. }
  152. func DisableSite(c *gin.Context) {
  153. name := c.Param("name")
  154. // Check if the site is in maintenance mode, if yes, disable maintenance mode first
  155. maintenanceConfigPath := nginx.GetConfPath("sites-enabled", name+site.MaintenanceSuffix)
  156. if _, err := os.Stat(maintenanceConfigPath); err == nil {
  157. // Site is in maintenance mode, disable it first
  158. err := site.DisableMaintenance(name)
  159. if err != nil {
  160. cosy.ErrHandler(c, err)
  161. return
  162. }
  163. }
  164. // Then disable the site normally
  165. err := site.Disable(name)
  166. if err != nil {
  167. cosy.ErrHandler(c, err)
  168. return
  169. }
  170. c.JSON(http.StatusOK, gin.H{
  171. "message": "ok",
  172. })
  173. }
  174. func DeleteSite(c *gin.Context) {
  175. err := site.Delete(c.Param("name"))
  176. if err != nil {
  177. cosy.ErrHandler(c, err)
  178. return
  179. }
  180. c.JSON(http.StatusOK, gin.H{
  181. "message": "ok",
  182. })
  183. }
  184. func BatchUpdateSites(c *gin.Context) {
  185. cosy.Core[model.Site](c).SetValidRules(gin.H{
  186. "env_group_id": "required",
  187. }).SetItemKey("path").
  188. BeforeExecuteHook(func(ctx *cosy.Ctx[model.Site]) {
  189. effectedPath := make([]string, len(ctx.BatchEffectedIDs))
  190. var sites []*model.Site
  191. for i, name := range ctx.BatchEffectedIDs {
  192. path := nginx.GetConfPath("sites-available", name)
  193. effectedPath[i] = path
  194. sites = append(sites, &model.Site{
  195. Path: path,
  196. })
  197. }
  198. s := query.Site
  199. err := s.Clauses(clause.OnConflict{
  200. DoNothing: true,
  201. }).Create(sites...)
  202. if err != nil {
  203. ctx.AbortWithError(err)
  204. return
  205. }
  206. ctx.BatchEffectedIDs = effectedPath
  207. }).BatchModify()
  208. }
  209. func EnableMaintenanceSite(c *gin.Context) {
  210. name := c.Param("name")
  211. // If site is already enabled, disable the normal site first
  212. enabledConfigPath := nginx.GetConfPath("sites-enabled", name)
  213. if _, err := os.Stat(enabledConfigPath); err == nil {
  214. // Site is already enabled, disable normal site first
  215. err := site.Disable(name)
  216. if err != nil {
  217. cosy.ErrHandler(c, err)
  218. return
  219. }
  220. }
  221. // Then enable maintenance mode
  222. err := site.EnableMaintenance(name)
  223. if err != nil {
  224. cosy.ErrHandler(c, err)
  225. return
  226. }
  227. c.JSON(http.StatusOK, gin.H{
  228. "message": "ok",
  229. })
  230. }