Files

230 lines
7.6 KiB
Go
Raw Permalink Normal View History

2023-12-04 19:20:46 +01:00
package controller
import (
2026-02-10 00:13:17 +03:30
"encoding/json"
2026-05-10 02:13:42 +02:00
"github.com/mhsanaei/3x-ui/v3/util/common"
"github.com/mhsanaei/3x-ui/v3/web/service"
2023-12-04 19:20:46 +01:00
"github.com/gin-gonic/gin"
)
2025-09-20 09:35:50 +02:00
// XraySettingController handles Xray configuration and settings operations.
2023-12-04 19:20:46 +01:00
type XraySettingController struct {
XraySettingService service.XraySettingService
SettingService service.SettingService
2023-12-05 18:13:36 +01:00
InboundService service.InboundService
OutboundService service.OutboundService
2023-12-10 12:57:39 +01:00
XrayService service.XrayService
2024-07-15 00:21:54 +02:00
WarpService service.WarpService
NordService service.NordService
2023-12-04 19:20:46 +01:00
}
2025-09-20 09:35:50 +02:00
// NewXraySettingController creates a new XraySettingController and initializes its routes.
2023-12-04 19:20:46 +01:00
func NewXraySettingController(g *gin.RouterGroup) *XraySettingController {
a := &XraySettingController{}
a.initRouter(g)
return a
}
2025-09-20 09:35:50 +02:00
// initRouter sets up the routes for Xray settings management.
2023-12-04 19:20:46 +01:00
func (a *XraySettingController) initRouter(g *gin.RouterGroup) {
g = g.Group("/xray")
2025-09-17 01:08:59 +02:00
g.GET("/getDefaultJsonConfig", a.getDefaultXrayConfig)
g.GET("/getOutboundsTraffic", a.getOutboundsTraffic)
g.GET("/getXrayResult", a.getXrayResult)
2023-12-04 19:20:46 +01:00
g.POST("/", a.getXraySetting)
2024-01-11 09:57:21 +03:30
g.POST("/warp/:action", a.warp)
g.POST("/nord/:action", a.nord)
2025-09-17 01:08:59 +02:00
g.POST("/update", a.updateSetting)
2024-02-07 11:25:31 +03:30
g.POST("/resetOutboundsTraffic", a.resetOutboundsTraffic)
2026-02-10 00:13:17 +03:30
g.POST("/testOutbound", a.testOutbound)
2023-12-04 19:20:46 +01:00
}
2026-02-10 00:13:17 +03:30
// getXraySetting retrieves the Xray configuration template, inbound tags, and outbound test URL.
2023-12-04 19:20:46 +01:00
func (a *XraySettingController) getXraySetting(c *gin.Context) {
xraySetting, err := a.SettingService.GetXrayConfigTemplate()
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err)
return
}
// Older versions of this handler embedded the raw DB value as
// `xraySetting` in the response without checking if the value
// already had that wrapper shape. When the frontend saved it
// back through the textarea verbatim, the wrapper got persisted
// and every subsequent save nested another layer, which is what
// eventually produced the blank Xray Settings page in #4059.
// Strip any such wrapper here, and heal the DB if we found one so
// the next read is O(1) instead of climbing the same pile again.
if unwrapped := service.UnwrapXrayTemplateConfig(xraySetting); unwrapped != xraySetting {
if saveErr := a.XraySettingService.SaveXraySetting(unwrapped); saveErr == nil {
xraySetting = unwrapped
} else {
// Don't fail the read — just serve the unwrapped value
// and leave the DB healing for a later save.
xraySetting = unwrapped
}
}
2023-12-05 18:13:36 +01:00
inboundTags, err := a.InboundService.GetInboundTags()
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err)
return
}
2026-05-06 00:43:47 +02:00
clientReverseTags, err := a.InboundService.GetClientReverseTags()
if err != nil {
clientReverseTags = "[]"
}
2026-02-10 00:13:17 +03:30
outboundTestUrl, _ := a.SettingService.GetXrayOutboundTestUrl()
if outboundTestUrl == "" {
outboundTestUrl = "https://www.google.com/generate_204"
}
2026-05-06 00:43:47 +02:00
xrayResponse := map[string]any{
"xraySetting": json.RawMessage(xraySetting),
"inboundTags": json.RawMessage(inboundTags),
"clientReverseTags": json.RawMessage(clientReverseTags),
"outboundTestUrl": outboundTestUrl,
2026-02-09 22:56:21 +01:00
}
result, err := json.Marshal(xrayResponse)
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err)
return
}
jsonObj(c, string(result), nil)
2023-12-04 19:20:46 +01:00
}
2025-09-20 09:35:50 +02:00
// updateSetting updates the Xray configuration settings.
2023-12-04 19:20:46 +01:00
func (a *XraySettingController) updateSetting(c *gin.Context) {
xraySetting := c.PostForm("xraySetting")
2026-02-10 00:13:17 +03:30
if err := a.XraySettingService.SaveXraySetting(xraySetting); err != nil {
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifySettings"), err)
return
}
outboundTestUrl := c.PostForm("outboundTestUrl")
if outboundTestUrl == "" {
outboundTestUrl = "https://www.google.com/generate_204"
}
if err := a.SettingService.SetXrayOutboundTestUrl(outboundTestUrl); err != nil {
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifySettings"), err)
return
}
2026-02-10 00:13:17 +03:30
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifySettings"), nil)
2023-12-04 19:20:46 +01:00
}
2025-09-20 09:35:50 +02:00
// getDefaultXrayConfig retrieves the default Xray configuration.
2023-12-04 19:20:46 +01:00
func (a *XraySettingController) getDefaultXrayConfig(c *gin.Context) {
defaultJsonConfig, err := a.SettingService.GetDefaultXrayConfig()
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err)
return
}
jsonObj(c, defaultJsonConfig, nil)
}
2023-12-10 12:57:39 +01:00
2025-09-20 09:35:50 +02:00
// getXrayResult retrieves the current Xray service result.
2023-12-10 12:57:39 +01:00
func (a *XraySettingController) getXrayResult(c *gin.Context) {
jsonObj(c, a.XrayService.GetXrayResult(), nil)
}
2024-01-11 09:57:21 +03:30
2025-09-20 09:35:50 +02:00
// warp handles Warp-related operations based on the action parameter.
2024-01-11 09:57:21 +03:30
func (a *XraySettingController) warp(c *gin.Context) {
action := c.Param("action")
var resp string
var err error
switch action {
case "data":
2024-07-15 00:21:54 +02:00
resp, err = a.WarpService.GetWarpData()
case "del":
err = a.WarpService.DelWarpData()
2024-01-11 09:57:21 +03:30
case "config":
2024-07-15 00:21:54 +02:00
resp, err = a.WarpService.GetWarpConfig()
2024-01-11 09:57:21 +03:30
case "reg":
skey := c.PostForm("privateKey")
pkey := c.PostForm("publicKey")
2024-07-15 00:21:54 +02:00
resp, err = a.WarpService.RegWarp(skey, pkey)
2024-01-11 09:57:21 +03:30
case "license":
license := c.PostForm("license")
2024-07-15 00:21:54 +02:00
resp, err = a.WarpService.SetWarpLicense(license)
2024-01-11 09:57:21 +03:30
}
jsonObj(c, resp, err)
}
// nord handles NordVPN-related operations based on the action parameter.
func (a *XraySettingController) nord(c *gin.Context) {
action := c.Param("action")
var resp string
var err error
switch action {
case "countries":
resp, err = a.NordService.GetCountries()
case "servers":
countryId := c.PostForm("countryId")
resp, err = a.NordService.GetServers(countryId)
case "reg":
token := c.PostForm("token")
resp, err = a.NordService.GetCredentials(token)
case "setKey":
key := c.PostForm("key")
resp, err = a.NordService.SetKey(key)
case "data":
resp, err = a.NordService.GetNordData()
case "del":
err = a.NordService.DelNordData()
}
jsonObj(c, resp, err)
}
2025-09-20 09:35:50 +02:00
// getOutboundsTraffic retrieves the traffic statistics for outbounds.
func (a *XraySettingController) getOutboundsTraffic(c *gin.Context) {
outboundsTraffic, err := a.OutboundService.GetOutboundsTraffic()
if err != nil {
2025-05-09 10:46:29 +07:00
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getOutboundTrafficError"), err)
return
}
jsonObj(c, outboundsTraffic, nil)
}
2024-02-07 11:25:31 +03:30
2025-09-20 09:35:50 +02:00
// resetOutboundsTraffic resets the traffic statistics for the specified outbound tag.
2024-02-07 11:25:31 +03:30
func (a *XraySettingController) resetOutboundsTraffic(c *gin.Context) {
tag := c.PostForm("tag")
err := a.OutboundService.ResetOutboundTraffic(tag)
if err != nil {
2025-05-09 10:46:29 +07:00
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.resetOutboundTrafficError"), err)
2024-02-07 11:25:31 +03:30
return
}
jsonObj(c, "", nil)
}
2026-02-10 00:13:17 +03:30
// testOutbound tests an outbound configuration and returns the delay/response time.
// Optional form "allOutbounds": JSON array of all outbounds; used to resolve sockopt.dialerProxy dependencies.
// Optional form "mode": "tcp" for a fast dial-only probe (parallel-safe),
// anything else (default) for a full HTTP probe through a temp xray instance.
2026-02-10 00:13:17 +03:30
func (a *XraySettingController) testOutbound(c *gin.Context) {
outboundJSON := c.PostForm("outbound")
allOutboundsJSON := c.PostForm("allOutbounds")
mode := c.PostForm("mode")
2026-02-10 00:13:17 +03:30
if outboundJSON == "" {
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), common.NewError("outbound parameter is required"))
return
}
2026-02-09 22:56:21 +01:00
// Load the test URL from server settings to prevent SSRF via user-controlled URLs
testURL, _ := a.SettingService.GetXrayOutboundTestUrl()
testURL, err := service.SanitizePublicHTTPURL(testURL, false)
if err != nil {
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
return
}
2026-02-09 22:56:21 +01:00
result, err := a.OutboundService.TestOutbound(outboundJSON, testURL, allOutboundsJSON, mode)
2026-02-10 00:13:17 +03:30
if err != nil {
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
return
}
jsonObj(c, result, nil)
}