domain.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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. Site: site,
  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. Site: site,
  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. SiteCategoryID uint64 `json:"site_category_id"`
  114. Overwrite bool `json:"overwrite"`
  115. }
  116. if !api.BindAndValid(c, &json) {
  117. return
  118. }
  119. path := nginx.GetConfPath("sites-available", name)
  120. if !json.Overwrite && helper.FileExists(path) {
  121. c.JSON(http.StatusNotAcceptable, gin.H{
  122. "message": "File exists",
  123. })
  124. return
  125. }
  126. err := os.WriteFile(path, []byte(json.Content), 0644)
  127. if err != nil {
  128. api.ErrHandler(c, err)
  129. return
  130. }
  131. enabledConfigFilePath := nginx.GetConfPath("sites-enabled", name)
  132. s := query.Site
  133. _, err = s.Where(s.Path.Eq(path)).Update(s.SiteCategoryID, json.SiteCategoryID)
  134. if err != nil {
  135. api.ErrHandler(c, err)
  136. return
  137. }
  138. // rename the config file if needed
  139. if name != json.Name {
  140. newPath := nginx.GetConfPath("sites-available", json.Name)
  141. _, _ = s.Where(s.Path.Eq(path)).Update(s.Path, newPath)
  142. // check if dst file exists, do not rename
  143. if helper.FileExists(newPath) {
  144. c.JSON(http.StatusNotAcceptable, gin.H{
  145. "message": "File exists",
  146. })
  147. return
  148. }
  149. // recreate a soft link
  150. if helper.FileExists(enabledConfigFilePath) {
  151. _ = os.Remove(enabledConfigFilePath)
  152. enabledConfigFilePath = nginx.GetConfPath("sites-enabled", json.Name)
  153. err = os.Symlink(newPath, enabledConfigFilePath)
  154. if err != nil {
  155. api.ErrHandler(c, err)
  156. return
  157. }
  158. }
  159. err = os.Rename(path, newPath)
  160. if err != nil {
  161. api.ErrHandler(c, err)
  162. return
  163. }
  164. name = json.Name
  165. c.Set("rewriteConfigFileName", name)
  166. }
  167. enabledConfigFilePath = nginx.GetConfPath("sites-enabled", name)
  168. if helper.FileExists(enabledConfigFilePath) {
  169. // Test nginx configuration
  170. output := nginx.TestConf()
  171. if nginx.GetLogLevel(output) > nginx.Warn {
  172. c.JSON(http.StatusInternalServerError, gin.H{
  173. "message": output,
  174. })
  175. return
  176. }
  177. output = nginx.Reload()
  178. if nginx.GetLogLevel(output) > nginx.Warn {
  179. c.JSON(http.StatusInternalServerError, gin.H{
  180. "message": output,
  181. })
  182. return
  183. }
  184. }
  185. GetSite(c)
  186. }
  187. func EnableSite(c *gin.Context) {
  188. configFilePath := nginx.GetConfPath("sites-available", c.Param("name"))
  189. enabledConfigFilePath := nginx.GetConfPath("sites-enabled", c.Param("name"))
  190. _, err := os.Stat(configFilePath)
  191. if err != nil {
  192. api.ErrHandler(c, err)
  193. return
  194. }
  195. if _, err = os.Stat(enabledConfigFilePath); os.IsNotExist(err) {
  196. err = os.Symlink(configFilePath, enabledConfigFilePath)
  197. if err != nil {
  198. api.ErrHandler(c, err)
  199. return
  200. }
  201. }
  202. // Test nginx config, if not pass, then disable the site.
  203. output := nginx.TestConf()
  204. if nginx.GetLogLevel(output) > nginx.Warn {
  205. _ = os.Remove(enabledConfigFilePath)
  206. c.JSON(http.StatusInternalServerError, gin.H{
  207. "message": output,
  208. })
  209. return
  210. }
  211. output = nginx.Reload()
  212. if nginx.GetLogLevel(output) > nginx.Warn {
  213. c.JSON(http.StatusInternalServerError, gin.H{
  214. "message": output,
  215. })
  216. return
  217. }
  218. c.JSON(http.StatusOK, gin.H{
  219. "message": "ok",
  220. })
  221. }
  222. func DisableSite(c *gin.Context) {
  223. enabledConfigFilePath := nginx.GetConfPath("sites-enabled", c.Param("name"))
  224. _, err := os.Stat(enabledConfigFilePath)
  225. if err != nil {
  226. api.ErrHandler(c, err)
  227. return
  228. }
  229. err = os.Remove(enabledConfigFilePath)
  230. if err != nil {
  231. api.ErrHandler(c, err)
  232. return
  233. }
  234. // delete auto cert record
  235. certModel := model.Cert{Filename: c.Param("name")}
  236. err = certModel.Remove()
  237. if err != nil {
  238. api.ErrHandler(c, err)
  239. return
  240. }
  241. output := nginx.Reload()
  242. if nginx.GetLogLevel(output) > nginx.Warn {
  243. c.JSON(http.StatusInternalServerError, gin.H{
  244. "message": output,
  245. })
  246. return
  247. }
  248. c.JSON(http.StatusOK, gin.H{
  249. "message": "ok",
  250. })
  251. }
  252. func DeleteSite(c *gin.Context) {
  253. var err error
  254. name := c.Param("name")
  255. availablePath := nginx.GetConfPath("sites-available", name)
  256. enabledPath := nginx.GetConfPath("sites-enabled", name)
  257. if _, err = os.Stat(availablePath); os.IsNotExist(err) {
  258. c.JSON(http.StatusNotFound, gin.H{
  259. "message": "site not found",
  260. })
  261. return
  262. }
  263. if _, err = os.Stat(enabledPath); err == nil {
  264. c.JSON(http.StatusNotAcceptable, gin.H{
  265. "message": "site is enabled",
  266. })
  267. return
  268. }
  269. certModel := model.Cert{Filename: name}
  270. _ = certModel.Remove()
  271. err = os.Remove(availablePath)
  272. if err != nil {
  273. api.ErrHandler(c, err)
  274. return
  275. }
  276. c.JSON(http.StatusOK, gin.H{
  277. "message": "ok",
  278. })
  279. }