From bb9b9100a8c924538f1df9ee5e2843cf1e780a64 Mon Sep 17 00:00:00 2001 From: mhsanaei Date: Mon, 15 Jul 2024 00:21:54 +0200 Subject: [PATCH] [warp] enhanced + delete option Co-Authored-By: Alireza Ahmadi --- web/controller/xray_setting.go | 11 ++- web/html/xui/warp_modal.html | 136 +++++++++++++++++---------- web/service/warp.go | 162 +++++++++++++++++++++++++++++++++ web/service/xray_setting.go | 144 ----------------------------- 4 files changed, 258 insertions(+), 195 deletions(-) create mode 100644 web/service/warp.go diff --git a/web/controller/xray_setting.go b/web/controller/xray_setting.go index d0fe9ca3..3391ad3b 100644 --- a/web/controller/xray_setting.go +++ b/web/controller/xray_setting.go @@ -12,6 +12,7 @@ type XraySettingController struct { InboundService service.InboundService OutboundService service.OutboundService XrayService service.XrayService + WarpService service.WarpService } func NewXraySettingController(g *gin.RouterGroup) *XraySettingController { @@ -72,16 +73,18 @@ func (a *XraySettingController) warp(c *gin.Context) { var err error switch action { case "data": - resp, err = a.XraySettingService.GetWarp() + resp, err = a.WarpService.GetWarpData() + case "del": + err = a.WarpService.DelWarpData() case "config": - resp, err = a.XraySettingService.GetWarpConfig() + resp, err = a.WarpService.GetWarpConfig() case "reg": skey := c.PostForm("privateKey") pkey := c.PostForm("publicKey") - resp, err = a.XraySettingService.RegWarp(skey, pkey) + resp, err = a.WarpService.RegWarp(skey, pkey) case "license": license := c.PostForm("license") - resp, err = a.XraySettingService.SetWarpLicense(license) + resp, err = a.WarpService.SetWarpLicense(license) } jsonObj(c, resp, err) diff --git a/web/html/xui/warp_modal.html b/web/html/xui/warp_modal.html index b349ce43..17f035ca 100644 --- a/web/html/xui/warp_modal.html +++ b/web/html/xui/warp_modal.html @@ -24,19 +24,22 @@ [[ warpModal.warpData.private_key ]] + {{ i18n "delete" }} {{ i18n "pages.xray.outbound.settings" }} - {{ i18n "pages.inbounds.update" }} + {{ i18n "pages.inbounds.update" }} {{ i18n "pages.xray.outbound.accountInfo" }} - {{ i18n "info" }} + {{ i18n "info" }} @@ -101,21 +104,20 @@ this.visible = true; this.warpConfig = null; this.getData(); - }, close() { this.visible = false; this.loading(false); }, - loading(loading=true) { + loading(loading = true) { this.confirmLoading = loading; }, - async getData(){ + async getData() { this.loading(true); const msg = await HttpUtil.post('/panel/xray/warp/data'); this.loading(false); if (msg.success) { - this.warpData = msg.obj.length>0 ? JSON.parse(msg.obj): null; + this.warpData = msg.obj.length > 0 ? JSON.parse(msg.obj) : null; } }, }; @@ -131,14 +133,15 @@ collectConfig() { config = warpModal.warpConfig.config; peer = config.peers[0]; - if(config){ + if (config) { warpModal.warpOutbound = Outbound.fromJson({ tag: 'warp', protocol: Protocols.Wireguard, settings: { mtu: 1420, secretKey: warpModal.warpData.private_key, - address: Object.values(config.interface.addresses), + address: this.getAddresses(config.interface.addresses), + reserved: this.getResolved(config.client_id), domainStrategy: 'ForceIP', peers: [{ publicKey: peer.public_key, @@ -149,10 +152,32 @@ }); } }, - async register(){ + getAddresses(addrs) { + let addresses = []; + if (addrs.v4) addresses.push(addrs.v4 + "/32"); + if (addrs.v6) addresses.push(addrs.v6 + "/128"); + return addresses; + }, + getResolved(client_id) { + let reserved = []; + let decoded = atob(client_id); + let hexString = ''; + for (let i = 0; i < decoded.length; i++) { + let hex = decoded.charCodeAt(i).toString(16); + hexString += (hex.length === 1 ? '0' : '') + hex; + } + + for (let i = 0; i < hexString.length; i += 2) { + let hexByte = hexString.slice(i, i + 2); + let decValue = parseInt(hexByte, 16); + reserved.push(decValue); + } + return reserved; + }, + async register() { warpModal.loading(true); keys = Wireguard.generateKeypair(); - const msg = await HttpUtil.post('/panel/xray/warp/reg',keys); + const msg = await HttpUtil.post('/panel/xray/warp/reg', keys); if (msg.success) { resp = JSON.parse(msg.obj); warpModal.warpData = resp.data; @@ -161,9 +186,9 @@ } warpModal.loading(false); }, - async updateLicense(l){ + async updateLicense(l) { warpModal.loading(true); - const msg = await HttpUtil.post('/panel/xray/warp/license',{license: l}); + const msg = await HttpUtil.post('/panel/xray/warp/license', { license: l }); if (msg.success) { warpModal.warpData = JSON.parse(msg.obj); warpModal.warpConfig = null; @@ -171,7 +196,7 @@ } warpModal.loading(false); }, - async getConfig(){ + async getConfig() { warpModal.loading(true); const msg = await HttpUtil.post('/panel/xray/warp/config'); warpModal.loading(false); @@ -180,20 +205,37 @@ this.collectConfig(); } }, - addOutbound(){ + async delConfig() { + warpModal.loading(true); + const msg = await HttpUtil.post('/panel/xray/warp/del'); + warpModal.loading(false); + if (msg.success) { + warpModal.warpData = null; + warpModal.warpConfig = null; + this.delOutbound(); + } + }, + addOutbound() { app.templateSettings.outbounds.push(warpModal.warpOutbound.toJson()); app.outboundSettings = JSON.stringify(app.templateSettings.outbounds); warpModal.close(); }, - resetOutbound(){ + resetOutbound() { app.templateSettings.outbounds[this.warpOutboundIndex] = warpModal.warpOutbound.toJson(); app.outboundSettings = JSON.stringify(app.templateSettings.outbounds); warpModal.close(); + }, + delOutbound() { + if (this.warpOutboundIndex != -1) { + app.templateSettings.outbounds.splice(this.warpOutboundIndex, 1); + app.outboundSettings = JSON.stringify(app.templateSettings.outbounds); + } + warpModal.close(); } }, computed: { warpOutboundIndex: { - get: function() { + get: function () { return app.templateSettings ? app.templateSettings.outbounds.findIndex((o) => o.tag == 'warp') : -1; } } @@ -201,4 +243,4 @@ }); -{{end}} +{{end}} \ No newline at end of file diff --git a/web/service/warp.go b/web/service/warp.go new file mode 100644 index 00000000..a691d3c1 --- /dev/null +++ b/web/service/warp.go @@ -0,0 +1,162 @@ +package service + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "os" + "time" + "x-ui/logger" +) + +type WarpService struct { + SettingService +} + +func (s *WarpService) GetWarpData() (string, error) { + warp, err := s.SettingService.GetWarp() + if err != nil { + return "", err + } + return warp, nil +} + +func (s *WarpService) DelWarpData() error { + err := s.SettingService.SetWarp("") + if err != nil { + return err + } + return nil +} + +func (s *WarpService) GetWarpConfig() (string, error) { + var warpData map[string]string + warp, err := s.SettingService.GetWarp() + if err != nil { + return "", err + } + err = json.Unmarshal([]byte(warp), &warpData) + if err != nil { + return "", err + } + + url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg/%s", warpData["device_id"]) + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return "", err + } + req.Header.Set("Authorization", "Bearer "+warpData["access_token"]) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + buffer := bytes.NewBuffer(make([]byte, 8192)) + buffer.Reset() + _, err = buffer.ReadFrom(resp.Body) + if err != nil { + return "", err + } + + return buffer.String(), nil +} + +func (s *WarpService) RegWarp(secretKey string, publicKey string) (string, error) { + tos := time.Now().UTC().Format("2006-01-02T15:04:05.000Z") + hostName, _ := os.Hostname() + data := fmt.Sprintf(`{"key":"%s","tos":"%s","type": "PC","model": "x-ui", "name": "%s"}`, publicKey, tos, hostName) + + url := "https://api.cloudflareclient.com/v0a2158/reg" + + req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(data))) + if err != nil { + return "", err + } + + req.Header.Add("CF-Client-Version", "a-7.21-0721") + req.Header.Add("Content-Type", "application/json") + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + buffer := bytes.NewBuffer(make([]byte, 8192)) + buffer.Reset() + _, err = buffer.ReadFrom(resp.Body) + if err != nil { + return "", err + } + + var rspData map[string]interface{} + err = json.Unmarshal(buffer.Bytes(), &rspData) + if err != nil { + return "", err + } + + deviceId := rspData["id"].(string) + token := rspData["token"].(string) + license, ok := rspData["account"].(map[string]interface{})["license"].(string) + if !ok { + logger.Debug("Error accessing license value.") + return "", err + } + + warpData := fmt.Sprintf("{\n \"access_token\": \"%s\",\n \"device_id\": \"%s\",", token, deviceId) + warpData += fmt.Sprintf("\n \"license_key\": \"%s\",\n \"private_key\": \"%s\"\n}", license, secretKey) + + s.SettingService.SetWarp(warpData) + + result := fmt.Sprintf("{\n \"data\": %s,\n \"config\": %s\n}", warpData, buffer.String()) + + return result, nil +} + +func (s *WarpService) SetWarpLicense(license string) (string, error) { + var warpData map[string]string + warp, err := s.SettingService.GetWarp() + if err != nil { + return "", err + } + err = json.Unmarshal([]byte(warp), &warpData) + if err != nil { + return "", err + } + + url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg/%s/account", warpData["device_id"]) + data := fmt.Sprintf(`{"license": "%s"}`, license) + + req, err := http.NewRequest("PUT", url, bytes.NewBuffer([]byte(data))) + if err != nil { + return "", err + } + req.Header.Set("Authorization", "Bearer "+warpData["access_token"]) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + buffer := bytes.NewBuffer(make([]byte, 8192)) + buffer.Reset() + _, err = buffer.ReadFrom(resp.Body) + if err != nil { + return "", err + } + + warpData["license_key"] = license + newWarpData, err := json.MarshalIndent(warpData, "", " ") + if err != nil { + return "", err + } + s.SettingService.SetWarp(string(newWarpData)) + println(string(newWarpData)) + + return string(newWarpData), nil +} diff --git a/web/service/xray_setting.go b/web/service/xray_setting.go index 43d143db..f497bf84 100644 --- a/web/service/xray_setting.go +++ b/web/service/xray_setting.go @@ -1,13 +1,8 @@ package service import ( - "bytes" _ "embed" "encoding/json" - "fmt" - "net/http" - "os" - "time" "x-ui/util/common" "x-ui/xray" @@ -32,142 +27,3 @@ func (s *XraySettingService) CheckXrayConfig(XrayTemplateConfig string) error { } return nil } - -func (s *XraySettingService) GetWarpData() (string, error) { - warp, err := s.SettingService.GetWarp() - if err != nil { - return "", err - } - return warp, nil -} - -func (s *XraySettingService) GetWarpConfig() (string, error) { - var warpData map[string]string - warp, err := s.SettingService.GetWarp() - if err != nil { - return "", err - } - err = json.Unmarshal([]byte(warp), &warpData) - if err != nil { - return "", err - } - - url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg/%s", warpData["device_id"]) - - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return "", err - } - req.Header.Set("Authorization", "Bearer "+warpData["access_token"]) - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() - buffer := bytes.NewBuffer(make([]byte, 8192)) - buffer.Reset() - _, err = buffer.ReadFrom(resp.Body) - if err != nil { - return "", err - } - - return buffer.String(), nil -} - -func (s *XraySettingService) RegWarp(secretKey string, publicKey string) (string, error) { - tos := time.Now().UTC().Format("2006-01-02T15:04:05.000Z") - hostName, _ := os.Hostname() - data := fmt.Sprintf(`{"key":"%s","tos":"%s","type": "PC","model": "x-ui", "name": "%s"}`, publicKey, tos, hostName) - - url := "https://api.cloudflareclient.com/v0a2158/reg" - - req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(data))) - if err != nil { - return "", err - } - - req.Header.Add("CF-Client-Version", "a-7.21-0721") - req.Header.Add("Content-Type", "application/json") - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() - buffer := bytes.NewBuffer(make([]byte, 8192)) - buffer.Reset() - _, err = buffer.ReadFrom(resp.Body) - if err != nil { - return "", err - } - - var rspData map[string]interface{} - err = json.Unmarshal(buffer.Bytes(), &rspData) - if err != nil { - return "", err - } - - deviceId := rspData["id"].(string) - token := rspData["token"].(string) - license, ok := rspData["account"].(map[string]interface{})["license"].(string) - if !ok { - fmt.Println("Error accessing license value.") - return "", err - } - - warpData := fmt.Sprintf("{\n \"access_token\": \"%s\",\n \"device_id\": \"%s\",", token, deviceId) - warpData += fmt.Sprintf("\n \"license_key\": \"%s\",\n \"private_key\": \"%s\"\n}", license, secretKey) - - s.SettingService.SetWarp(warpData) - - result := fmt.Sprintf("{\n \"data\": %s,\n \"config\": %s\n}", warpData, buffer.String()) - - return result, nil -} - -func (s *XraySettingService) SetWarpLicense(license string) (string, error) { - var warpData map[string]string - warp, err := s.SettingService.GetWarp() - if err != nil { - return "", err - } - err = json.Unmarshal([]byte(warp), &warpData) - if err != nil { - return "", err - } - - url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg/%s/account", warpData["device_id"]) - data := fmt.Sprintf(`{"license": "%s"}`, license) - - req, err := http.NewRequest("PUT", url, bytes.NewBuffer([]byte(data))) - if err != nil { - return "", err - } - req.Header.Set("Authorization", "Bearer "+warpData["access_token"]) - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() - buffer := bytes.NewBuffer(make([]byte, 8192)) - buffer.Reset() - _, err = buffer.ReadFrom(resp.Body) - if err != nil { - return "", err - } - - warpData["license_key"] = license - newWarpData, err := json.MarshalIndent(warpData, "", " ") - if err != nil { - return "", err - } - s.SettingService.SetWarp(string(newWarpData)) - println(string(newWarpData)) - - return string(newWarpData), nil -}