1
0

xray_setting.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. package controller
  2. import (
  3. "encoding/json"
  4. "github.com/mhsanaei/3x-ui/v2/util/common"
  5. "github.com/mhsanaei/3x-ui/v2/web/service"
  6. "github.com/gin-gonic/gin"
  7. )
  8. // XraySettingController handles Xray configuration and settings operations.
  9. type XraySettingController struct {
  10. XraySettingService service.XraySettingService
  11. SettingService service.SettingService
  12. InboundService service.InboundService
  13. OutboundService service.OutboundService
  14. XrayService service.XrayService
  15. WarpService service.WarpService
  16. NordService service.NordService
  17. }
  18. // NewXraySettingController creates a new XraySettingController and initializes its routes.
  19. func NewXraySettingController(g *gin.RouterGroup) *XraySettingController {
  20. a := &XraySettingController{}
  21. a.initRouter(g)
  22. return a
  23. }
  24. // initRouter sets up the routes for Xray settings management.
  25. func (a *XraySettingController) initRouter(g *gin.RouterGroup) {
  26. g = g.Group("/xray")
  27. g.GET("/getDefaultJsonConfig", a.getDefaultXrayConfig)
  28. g.GET("/getOutboundsTraffic", a.getOutboundsTraffic)
  29. g.GET("/getXrayResult", a.getXrayResult)
  30. g.POST("/", a.getXraySetting)
  31. g.POST("/warp/:action", a.warp)
  32. g.POST("/nord/:action", a.nord)
  33. g.POST("/update", a.updateSetting)
  34. g.POST("/resetOutboundsTraffic", a.resetOutboundsTraffic)
  35. g.POST("/testOutbound", a.testOutbound)
  36. }
  37. // getXraySetting retrieves the Xray configuration template, inbound tags, and outbound test URL.
  38. func (a *XraySettingController) getXraySetting(c *gin.Context) {
  39. xraySetting, err := a.SettingService.GetXrayConfigTemplate()
  40. if err != nil {
  41. jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err)
  42. return
  43. }
  44. // Older versions of this handler embedded the raw DB value as
  45. // `xraySetting` in the response without checking if the value
  46. // already had that wrapper shape. When the frontend saved it
  47. // back through the textarea verbatim, the wrapper got persisted
  48. // and every subsequent save nested another layer, which is what
  49. // eventually produced the blank Xray Settings page in #4059.
  50. // Strip any such wrapper here, and heal the DB if we found one so
  51. // the next read is O(1) instead of climbing the same pile again.
  52. if unwrapped := service.UnwrapXrayTemplateConfig(xraySetting); unwrapped != xraySetting {
  53. if saveErr := a.XraySettingService.SaveXraySetting(unwrapped); saveErr == nil {
  54. xraySetting = unwrapped
  55. } else {
  56. // Don't fail the read — just serve the unwrapped value
  57. // and leave the DB healing for a later save.
  58. xraySetting = unwrapped
  59. }
  60. }
  61. inboundTags, err := a.InboundService.GetInboundTags()
  62. if err != nil {
  63. jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err)
  64. return
  65. }
  66. clientReverseTags, err := a.InboundService.GetClientReverseTags()
  67. if err != nil {
  68. clientReverseTags = "[]"
  69. }
  70. outboundTestUrl, _ := a.SettingService.GetXrayOutboundTestUrl()
  71. if outboundTestUrl == "" {
  72. outboundTestUrl = "https://www.google.com/generate_204"
  73. }
  74. xrayResponse := map[string]any{
  75. "xraySetting": json.RawMessage(xraySetting),
  76. "inboundTags": json.RawMessage(inboundTags),
  77. "clientReverseTags": json.RawMessage(clientReverseTags),
  78. "outboundTestUrl": outboundTestUrl,
  79. }
  80. result, err := json.Marshal(xrayResponse)
  81. if err != nil {
  82. jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err)
  83. return
  84. }
  85. jsonObj(c, string(result), nil)
  86. }
  87. // updateSetting updates the Xray configuration settings.
  88. func (a *XraySettingController) updateSetting(c *gin.Context) {
  89. xraySetting := c.PostForm("xraySetting")
  90. if err := a.XraySettingService.SaveXraySetting(xraySetting); err != nil {
  91. jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifySettings"), err)
  92. return
  93. }
  94. outboundTestUrl := c.PostForm("outboundTestUrl")
  95. if outboundTestUrl == "" {
  96. outboundTestUrl = "https://www.google.com/generate_204"
  97. }
  98. _ = a.SettingService.SetXrayOutboundTestUrl(outboundTestUrl)
  99. jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifySettings"), nil)
  100. }
  101. // getDefaultXrayConfig retrieves the default Xray configuration.
  102. func (a *XraySettingController) getDefaultXrayConfig(c *gin.Context) {
  103. defaultJsonConfig, err := a.SettingService.GetDefaultXrayConfig()
  104. if err != nil {
  105. jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err)
  106. return
  107. }
  108. jsonObj(c, defaultJsonConfig, nil)
  109. }
  110. // getXrayResult retrieves the current Xray service result.
  111. func (a *XraySettingController) getXrayResult(c *gin.Context) {
  112. jsonObj(c, a.XrayService.GetXrayResult(), nil)
  113. }
  114. // warp handles Warp-related operations based on the action parameter.
  115. func (a *XraySettingController) warp(c *gin.Context) {
  116. action := c.Param("action")
  117. var resp string
  118. var err error
  119. switch action {
  120. case "data":
  121. resp, err = a.WarpService.GetWarpData()
  122. case "del":
  123. err = a.WarpService.DelWarpData()
  124. case "config":
  125. resp, err = a.WarpService.GetWarpConfig()
  126. case "reg":
  127. skey := c.PostForm("privateKey")
  128. pkey := c.PostForm("publicKey")
  129. resp, err = a.WarpService.RegWarp(skey, pkey)
  130. case "license":
  131. license := c.PostForm("license")
  132. resp, err = a.WarpService.SetWarpLicense(license)
  133. }
  134. jsonObj(c, resp, err)
  135. }
  136. // nord handles NordVPN-related operations based on the action parameter.
  137. func (a *XraySettingController) nord(c *gin.Context) {
  138. action := c.Param("action")
  139. var resp string
  140. var err error
  141. switch action {
  142. case "countries":
  143. resp, err = a.NordService.GetCountries()
  144. case "servers":
  145. countryId := c.PostForm("countryId")
  146. resp, err = a.NordService.GetServers(countryId)
  147. case "reg":
  148. token := c.PostForm("token")
  149. resp, err = a.NordService.GetCredentials(token)
  150. case "setKey":
  151. key := c.PostForm("key")
  152. resp, err = a.NordService.SetKey(key)
  153. case "data":
  154. resp, err = a.NordService.GetNordData()
  155. case "del":
  156. err = a.NordService.DelNordData()
  157. }
  158. jsonObj(c, resp, err)
  159. }
  160. // getOutboundsTraffic retrieves the traffic statistics for outbounds.
  161. func (a *XraySettingController) getOutboundsTraffic(c *gin.Context) {
  162. outboundsTraffic, err := a.OutboundService.GetOutboundsTraffic()
  163. if err != nil {
  164. jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getOutboundTrafficError"), err)
  165. return
  166. }
  167. jsonObj(c, outboundsTraffic, nil)
  168. }
  169. // resetOutboundsTraffic resets the traffic statistics for the specified outbound tag.
  170. func (a *XraySettingController) resetOutboundsTraffic(c *gin.Context) {
  171. tag := c.PostForm("tag")
  172. err := a.OutboundService.ResetOutboundTraffic(tag)
  173. if err != nil {
  174. jsonMsg(c, I18nWeb(c, "pages.settings.toasts.resetOutboundTrafficError"), err)
  175. return
  176. }
  177. jsonObj(c, "", nil)
  178. }
  179. // testOutbound tests an outbound configuration and returns the delay/response time.
  180. // Optional form "allOutbounds": JSON array of all outbounds; used to resolve sockopt.dialerProxy dependencies.
  181. func (a *XraySettingController) testOutbound(c *gin.Context) {
  182. outboundJSON := c.PostForm("outbound")
  183. allOutboundsJSON := c.PostForm("allOutbounds")
  184. if outboundJSON == "" {
  185. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), common.NewError("outbound parameter is required"))
  186. return
  187. }
  188. // Load the test URL from server settings to prevent SSRF via user-controlled URLs
  189. testURL, _ := a.SettingService.GetXrayOutboundTestUrl()
  190. result, err := a.OutboundService.TestOutbound(outboundJSON, testURL, allOutboundsJSON)
  191. if err != nil {
  192. jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
  193. return
  194. }
  195. jsonObj(c, result, nil)
  196. }