domain.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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/helper"
  6. "github.com/0xJacky/Nginx-UI/internal/nginx"
  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/logger"
  12. "net/http"
  13. "os"
  14. )
  15. func GetSite(c *gin.Context) {
  16. rewriteName, ok := c.Get("rewriteConfigFileName")
  17. name := c.Param("name")
  18. // for modify filename
  19. if ok {
  20. name = rewriteName.(string)
  21. }
  22. path := nginx.GetConfPath("sites-available", name)
  23. file, err := os.Stat(path)
  24. if os.IsNotExist(err) {
  25. c.JSON(http.StatusNotFound, gin.H{
  26. "message": "file not found",
  27. })
  28. return
  29. }
  30. enabled := true
  31. if _, err := os.Stat(nginx.GetConfPath("sites-enabled", name)); os.IsNotExist(err) {
  32. enabled = false
  33. }
  34. g := query.ChatGPTLog
  35. chatgpt, err := g.Where(g.Name.Eq(path)).FirstOrCreate()
  36. if err != nil {
  37. api.ErrHandler(c, err)
  38. return
  39. }
  40. if chatgpt.Content == nil {
  41. chatgpt.Content = make([]openai.ChatCompletionMessage, 0)
  42. }
  43. s := query.Site
  44. site, err := s.Where(s.Path.Eq(path)).FirstOrInit()
  45. if err != nil {
  46. api.ErrHandler(c, err)
  47. return
  48. }
  49. certModel, err := model.FirstCert(name)
  50. if err != nil {
  51. logger.Warn(err)
  52. }
  53. if site.Advanced {
  54. origContent, err := os.ReadFile(path)
  55. if err != nil {
  56. api.ErrHandler(c, err)
  57. return
  58. }
  59. c.JSON(http.StatusOK, Site{
  60. ModifiedAt: file.ModTime(),
  61. Advanced: site.Advanced,
  62. Enabled: enabled,
  63. Name: name,
  64. Config: string(origContent),
  65. AutoCert: certModel.AutoCert == model.AutoCertEnabled,
  66. ChatGPTMessages: chatgpt.Content,
  67. Filepath: path,
  68. })
  69. return
  70. }
  71. nginxConfig, err := nginx.ParseNgxConfig(path)
  72. if err != nil {
  73. api.ErrHandler(c, err)
  74. return
  75. }
  76. certInfoMap := make(map[int][]*cert.Info)
  77. for serverIdx, server := range nginxConfig.Servers {
  78. for _, directive := range server.Directives {
  79. if directive.Directive == "ssl_certificate" {
  80. pubKey, err := cert.GetCertInfo(directive.Params)
  81. if err != nil {
  82. logger.Error("Failed to get certificate information", err)
  83. continue
  84. }
  85. certInfoMap[serverIdx] = append(certInfoMap[serverIdx], pubKey)
  86. }
  87. }
  88. }
  89. c.JSON(http.StatusOK, Site{
  90. ModifiedAt: file.ModTime(),
  91. Advanced: site.Advanced,
  92. Enabled: enabled,
  93. Name: name,
  94. Config: nginxConfig.FmtCode(),
  95. Tokenized: nginxConfig,
  96. AutoCert: certModel.AutoCert == model.AutoCertEnabled,
  97. CertInfo: certInfoMap,
  98. ChatGPTMessages: chatgpt.Content,
  99. Filepath: path,
  100. })
  101. }
  102. func SaveSite(c *gin.Context) {
  103. name := c.Param("name")
  104. if name == "" {
  105. c.JSON(http.StatusNotAcceptable, gin.H{
  106. "message": "param name is empty",
  107. })
  108. return
  109. }
  110. var json struct {
  111. Name string `json:"name" binding:"required"`
  112. Content string `json:"content" binding:"required"`
  113. Overwrite bool `json:"overwrite"`
  114. }
  115. if !api.BindAndValid(c, &json) {
  116. return
  117. }
  118. path := nginx.GetConfPath("sites-available", name)
  119. if !json.Overwrite && helper.FileExists(path) {
  120. c.JSON(http.StatusNotAcceptable, gin.H{
  121. "message": "File exists",
  122. })
  123. return
  124. }
  125. err := os.WriteFile(path, []byte(json.Content), 0644)
  126. if err != nil {
  127. api.ErrHandler(c, err)
  128. return
  129. }
  130. enabledConfigFilePath := nginx.GetConfPath("sites-enabled", name)
  131. // rename the config file if needed
  132. if name != json.Name {
  133. newPath := nginx.GetConfPath("sites-available", json.Name)
  134. s := query.Site
  135. _, err = s.Where(s.Path.Eq(path)).Update(s.Path, newPath)
  136. // check if dst file exists, do not rename
  137. if helper.FileExists(newPath) {
  138. c.JSON(http.StatusNotAcceptable, gin.H{
  139. "message": "File exists",
  140. })
  141. return
  142. }
  143. // recreate a soft link
  144. if helper.FileExists(enabledConfigFilePath) {
  145. _ = os.Remove(enabledConfigFilePath)
  146. enabledConfigFilePath = nginx.GetConfPath("sites-enabled", json.Name)
  147. err = os.Symlink(newPath, enabledConfigFilePath)
  148. if err != nil {
  149. api.ErrHandler(c, err)
  150. return
  151. }
  152. }
  153. err = os.Rename(path, newPath)
  154. if err != nil {
  155. api.ErrHandler(c, err)
  156. return
  157. }
  158. name = json.Name
  159. c.Set("rewriteConfigFileName", name)
  160. }
  161. enabledConfigFilePath = nginx.GetConfPath("sites-enabled", name)
  162. if helper.FileExists(enabledConfigFilePath) {
  163. // Test nginx configuration
  164. output := nginx.TestConf()
  165. if nginx.GetLogLevel(output) > nginx.Warn {
  166. c.JSON(http.StatusInternalServerError, gin.H{
  167. "message": output,
  168. })
  169. return
  170. }
  171. output = nginx.Reload()
  172. if nginx.GetLogLevel(output) > nginx.Warn {
  173. c.JSON(http.StatusInternalServerError, gin.H{
  174. "message": output,
  175. })
  176. return
  177. }
  178. }
  179. GetSite(c)
  180. }
  181. func EnableSite(c *gin.Context) {
  182. configFilePath := nginx.GetConfPath("sites-available", c.Param("name"))
  183. enabledConfigFilePath := nginx.GetConfPath("sites-enabled", c.Param("name"))
  184. _, err := os.Stat(configFilePath)
  185. if err != nil {
  186. api.ErrHandler(c, err)
  187. return
  188. }
  189. if _, err = os.Stat(enabledConfigFilePath); os.IsNotExist(err) {
  190. err = os.Symlink(configFilePath, enabledConfigFilePath)
  191. if err != nil {
  192. api.ErrHandler(c, err)
  193. return
  194. }
  195. }
  196. // Test nginx config, if not pass, then disable the site.
  197. output := nginx.TestConf()
  198. if nginx.GetLogLevel(output) > nginx.Warn {
  199. _ = os.Remove(enabledConfigFilePath)
  200. c.JSON(http.StatusInternalServerError, gin.H{
  201. "message": output,
  202. })
  203. return
  204. }
  205. output = nginx.Reload()
  206. if nginx.GetLogLevel(output) > nginx.Warn {
  207. c.JSON(http.StatusInternalServerError, gin.H{
  208. "message": output,
  209. })
  210. return
  211. }
  212. c.JSON(http.StatusOK, gin.H{
  213. "message": "ok",
  214. })
  215. }
  216. func DisableSite(c *gin.Context) {
  217. enabledConfigFilePath := nginx.GetConfPath("sites-enabled", c.Param("name"))
  218. _, err := os.Stat(enabledConfigFilePath)
  219. if err != nil {
  220. api.ErrHandler(c, err)
  221. return
  222. }
  223. err = os.Remove(enabledConfigFilePath)
  224. if err != nil {
  225. api.ErrHandler(c, err)
  226. return
  227. }
  228. // delete auto cert record
  229. certModel := model.Cert{Filename: c.Param("name")}
  230. err = certModel.Remove()
  231. if err != nil {
  232. api.ErrHandler(c, err)
  233. return
  234. }
  235. output := nginx.Reload()
  236. if nginx.GetLogLevel(output) > nginx.Warn {
  237. c.JSON(http.StatusInternalServerError, gin.H{
  238. "message": output,
  239. })
  240. return
  241. }
  242. c.JSON(http.StatusOK, gin.H{
  243. "message": "ok",
  244. })
  245. }
  246. func DeleteSite(c *gin.Context) {
  247. var err error
  248. name := c.Param("name")
  249. availablePath := nginx.GetConfPath("sites-available", name)
  250. enabledPath := nginx.GetConfPath("sites-enabled", name)
  251. if _, err = os.Stat(availablePath); os.IsNotExist(err) {
  252. c.JSON(http.StatusNotFound, gin.H{
  253. "message": "site not found",
  254. })
  255. return
  256. }
  257. if _, err = os.Stat(enabledPath); err == nil {
  258. c.JSON(http.StatusNotAcceptable, gin.H{
  259. "message": "site is enabled",
  260. })
  261. return
  262. }
  263. certModel := model.Cert{Filename: name}
  264. _ = certModel.Remove()
  265. err = os.Remove(availablePath)
  266. if err != nil {
  267. api.ErrHandler(c, err)
  268. return
  269. }
  270. c.JSON(http.StatusOK, gin.H{
  271. "message": "ok",
  272. })
  273. }