diff --git a/web/controller/xray_setting.go b/web/controller/xray_setting.go index d4deacd9..09e9115f 100644 --- a/web/controller/xray_setting.go +++ b/web/controller/xray_setting.go @@ -26,6 +26,7 @@ func (a *XraySettingController) initRouter(g *gin.RouterGroup) { g.POST("/update", a.updateSetting) g.GET("/getXrayResult", a.getXrayResult) g.GET("/getDefaultJsonConfig", a.getDefaultXrayConfig) + g.POST("/warp/:action", a.warp) } func (a *XraySettingController) getXraySetting(c *gin.Context) { @@ -61,3 +62,25 @@ func (a *XraySettingController) getDefaultXrayConfig(c *gin.Context) { func (a *XraySettingController) getXrayResult(c *gin.Context) { jsonObj(c, a.XrayService.GetXrayResult(), nil) } + +func (a *XraySettingController) warp(c *gin.Context) { + action := c.Param("action") + var resp string + var err error + switch action { + case "data": + resp, err = a.XraySettingService.GetWarp() + case "config": + resp, err = a.XraySettingService.GetWarpConfig() + case "reg": + skey := c.PostForm("privateKey") + pkey := c.PostForm("publicKey") + resp, err = a.XraySettingService.RegWarp(skey, pkey) + case "license": + license := c.PostForm("license") + println(license) + resp, err = a.XraySettingService.SetWarpLicence(license) + } + + jsonObj(c, resp, err) +} diff --git a/web/html/xui/warp_modal.html b/web/html/xui/warp_modal.html new file mode 100644 index 00000000..c9f86603 --- /dev/null +++ b/web/html/xui/warp_modal.html @@ -0,0 +1,204 @@ +{{define "warpModal"}} + + + + + +{{end}} diff --git a/web/html/xui/xray.html b/web/html/xui/xray.html index 51576a96..a8760c4b 100644 --- a/web/html/xui/xray.html +++ b/web/html/xui/xray.html @@ -209,6 +209,23 @@ + + + + + + + + WARP {{ i18n "pages.xray.rules.outbound" }} + {{ i18n "pages.settings.resetDefaultConfig" }} @@ -326,6 +343,7 @@ {{ i18n "pages.xray.outbound.addOutbound" }} + WARP const rulesColumns = [ { title: "#", align: 'center', width: 15, scopedSlots: { customRender: 'action' } }, @@ -519,7 +538,9 @@ "geosite:category-ads-all", "ext:geosite_IR.dat:category-ads-all" ], + openai: ["geosite:openai"], google: ["geosite:google"], + spotify: ["geosite:spotify"], netflix: ["geosite:netflix"], cn: [ "geosite:cn", @@ -701,6 +722,8 @@ break; case Protocols.DNS: return [o.settings.address + ':' + o.settings.port]; + case Protocols.Wireguard: + return o.settings.peers.map(peer => peer.endpoint); default: return null; } @@ -866,6 +889,9 @@ rules = this.templateSettings.routing.rules; rules.splice(index,1); this.routingRuleSettings = JSON.stringify(rules); + }, + showWarp(){ + warpModal.show(); } }, async mounted() { @@ -1041,6 +1067,14 @@ this.syncRulesWithOutbound("IPv4", this.ipv4Settings); } }, + warpDomains: { + get: function () { + return this.templateRuleGetter({ outboundTag: "warp", property: "domain" }); + }, + set: function (newValue) { + this.templateRuleSetter({ outboundTag: "warp", property: "domain", data: newValue }); + } + }, torrentSettings: { get: function () { return doAllItemsExist(this.settingsData.protocols.bittorrent, this.blockedProtocols); @@ -1260,6 +1294,59 @@ } } }, + WarpExist: { + get: function() { + return this.templateSettings ? this.templateSettings.outbounds.findIndex((o) => o.tag == "warp")>=0 : false; + }, + }, + GoogleWARPSettings: { + get: function () { + return doAllItemsExist(this.settingsData.domains.google, this.warpDomains); + }, + set: function (newValue) { + if (newValue) { + this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.google]; + } else { + this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.google.includes(data)); + } + }, + }, + OpenAIWARPSettings: { + get: function () { + return doAllItemsExist(this.settingsData.domains.openai, this.warpDomains); + }, + set: function (newValue) { + if (newValue) { + this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.openai]; + } else { + this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.openai.includes(data)); + } + }, + }, + NetflixWARPSettings: { + get: function () { + return doAllItemsExist(this.settingsData.domains.netflix, this.warpDomains); + }, + set: function (newValue) { + if (newValue) { + this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.netflix]; + } else { + this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.netflix.includes(data)); + } + }, + }, + SpotifyWARPSettings: { + get: function () { + return doAllItemsExist(this.settingsData.domains.spotify, this.warpDomains); + }, + set: function (newValue) { + if (newValue) { + this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.spotify]; + } else { + this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.spotify.includes(data)); + } + }, + }, }, }); diff --git a/web/service/setting.go b/web/service/setting.go index 03c69620..427ee17b 100644 --- a/web/service/setting.go +++ b/web/service/setting.go @@ -55,6 +55,7 @@ var defaultValueMap = map[string]string{ "subEncrypt": "true", "subShowInfo": "false", "subURI": "", + "warp": "", } type SettingService struct { @@ -397,6 +398,13 @@ func (s *SettingService) GetSubURI() (string, error) { return s.getString("subURI") } +func (s *SettingService) GetWarp() (string, error) { + return s.getString("warp") +} +func (s *SettingService) SetWarp(data string) error { + return s.setString("warp", data) +} + func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error { if err := allSetting.CheckValid(); err != nil { return err diff --git a/web/service/xray_setting.go b/web/service/xray_setting.go index 4550bde2..1d2e696e 100644 --- a/web/service/xray_setting.go +++ b/web/service/xray_setting.go @@ -1,8 +1,13 @@ package service import ( + "bytes" _ "embed" "encoding/json" + "fmt" + "net/http" + "os" + "time" "x-ui/util/common" "x-ui/xray" ) @@ -26,3 +31,142 @@ 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 := fmt.Sprintf("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) SetWarpLicence(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/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index b400feb1..8278459f 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -312,6 +312,8 @@ "directCountryConfigsDesc" = "These options will directly forward traffic based on the specific requested country." "ipv4Configs" = "IPv4 Routing" "ipv4ConfigsDesc" = "These options will route requests to destination only via IPv4." +"warpConfigs" = "WARP Routing" +"warpConfigsDesc" = "WARP will route traffic to websites through Cloudflare servers." "Template" = "Advanced Xray Configuration Template" "TemplateDesc" = "The final Xray configuration file will be generated based on this template." "FreedomStrategy" = "Freedom Protocol Strategy" @@ -354,6 +356,14 @@ "GoogleIPv4Desc" = "Routes traffic to Google via IPv4." "NetflixIPv4" = "Netflix" "NetflixIPv4Desc" = "Routes traffic to Netflix via IPv4." +"GoogleWARP" = "Route Google through WARP." +"GoogleWARPDesc" = "Add routing for Google via WARP." +"OpenAIWARP" = "Route OpenAI (ChatGPT) through WARP." +"OpenAIWARPDesc" = "Add routing for OpenAI (ChatGPT) via WARP." +"NetflixWARP" = "Route Netflix through WARP." +"NetflixWARPDesc" = "Add routing for Netflix via WARP." +"SpotifyWARP" = "Route Spotify through WARP." +"SpotifyWARPDesc" = "Add routing for Spotify via WARP." "completeTemplate" = "All" "Inbounds" = "Inbounds" "Outbounds" = "Outbounds" diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index 7bd055b5..c18cee4c 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -311,6 +311,8 @@ "directCountryConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس کشور درخواستی خاص بصورت مستقیم ارسال می‌کند" "ipv4Configs" = "IPv4 مسیریابی" "ipv4ConfigsDesc" = "این گزینه‌ها درخواست‌ها را فقط از طریق آی‌پی‌نسخه4 به مقصد هدایت می‌کند" +"warpConfigs" = "تنظیمات برای وارپ" +"warpConfigsDesc" = ".وارپ ترافیک را از طریق سرورهای کلادفلر به وب سایت ها هدایت می کند" "Template" = "‌پیکربندی پیشرفته الگو ایکس‌ری" "TemplateDesc" = "فایل پیکربندی نهایی ایکس‌ری بر اساس این الگو ایجاد می‌شود" "FreedomStrategy" = "Freedom استراتژی پروتکل" @@ -354,6 +356,14 @@ "NetflixIPv4" = "نتفلیکس" "NetflixIPv4Desc" = "ترافیک را از طریق آی‌پی‌نسخه4 به نتفلیکس هدایت می‌کند" "completeTemplate" = "کامل" +"GoogleWARP" = "مسیردهی گوگل به وارپ" +"GoogleWARPDesc" = "مسیردهی جدید برای اتصال به گوگل به وارپ اضافه میکند" +"OpenAIWARP" = "مسیردهی چت جی‌بی‌تی به وارپ" +"OpenAIWARPDesc" = "مسیردهی جدید برای اتصال به چت جی‌بی‌تی به وارپ اضافه میکند" +"NetflixWARP" = "مسیردهی نتفلیکس به وارپ" +"NetflixWARPDesc" = "مسیردهی جدید برای اتصال به نتفلیکس به وارپ اضافه میکند" +"SpotifyWARP" = "مسیردهی اسپاتیفای به وارپ" +"SpotifyWARPDesc" = "مسیردهی جدید برای اتصال به اسپاتیفای به وارپ اضافه میکند" "Inbounds" = "ورودی‌ها" "Outbounds" = "خروجی‌ها" "Routings" = "قوانین مسیریابی" diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index c465b2e7..a02b5382 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -312,6 +312,8 @@ "directCountryConfigsDesc" = "Эти параметры будут подключать пользователей напрямую к доменам определенной страны." "ipv4Configs" = "Настройки IPv4" "ipv4ConfigsDesc" = "Эти параметры будут маршрутизироваться к целевым доменам только через IPv4" +"warpConfigs" = "Настройки WARP" +"warpConfigsDesc" = "WARP будет направлять трафик на веб-сайты через серверы Cloudflare" "Template" = "Шаблон конфигурации Xray" "TemplateDesc" = "Создание файла конфигурации Xray на основе этого шаблона." "FreedomStrategy" = "Настроить стратегию протокола Freedom" @@ -354,6 +356,14 @@ "GoogleIPv4Desc" = "Применить маршрутизацию Google для подключения к IPv4." "NetflixIPv4" = "Использовать IPv4 для Netflix" "NetflixIPv4Desc" = "Применить маршрутизацию Netflix для подключения к IPv4." +"GoogleWARP" = "Маршрутизация Google через WARP" +"GoogleWARPDesc" = "Добавить маршрутизацию для Google через WARP" +"OpenAIWARP" = "Маршрутизация OpenAI (ChatGPT) через WARP" +"OpenAIWARPDesc" = "Добавить маршрутизацию для OpenAI (ChatGPT) через WARP" +"NetflixWARP" = "Маршрутизация Netflix через WARP" +"NetflixWARPDesc" = "Добавить маршрутизацию для Netflix через WARP" +"SpotifyWARP" = "Маршрутизация Spotify через WARP" +"SpotifyWARPDesc" = "Добавить маршрутизацию для Spotify через WARP" "completeTemplate" = "Все" "Inbounds" = "Входящие" "Outbounds" = "Исходящие" diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml index 02a4d76e..63f340d9 100644 --- a/web/translation/translate.vi_VN.toml +++ b/web/translation/translate.vi_VN.toml @@ -312,6 +312,8 @@ "directCountryConfigsDesc" = "Những tùy chọn này sẽ kết nối người dùng trực tiếp đến các tên miền quốc gia cụ thể." "ipv4Configs" = "Cấu hình IPv4" "ipv4ConfigsDesc" = "Những tùy chọn này sẽ chỉ định kết nối đến các tên miền mục tiêu qua IPv4." +"warpConfigs" = "Cấu hình WARP" +"warpConfigsDesc" = "WARP sẽ định tuyến lưu lượng đến các trang web qua máy chủ Cloudflare." "Template" = "Mẫu cấu hình Xray" "TemplateDesc" = "Tạo tệp cấu hình Xray cuối cùng dựa trên mẫu này." "FreedomStrategy" = "Cấu hình chiến lược cho giao thức tự do" @@ -354,6 +356,14 @@ "GoogleIPv4Desc" = "Thêm định tuyến để Google kết nối với IPv4." "NetflixIPv4" = "Sử dụng IPv4 cho Netflix" "NetflixIPv4Desc" = "Thêm định tuyến cho Netflix để kết nối với IPv4." +"GoogleWARP" = "Định tuyến Google qua WARP." +"GoogleWARPDesc" = "Thêm định tuyến cho Google qua WARP." +"OpenAIWARP" = "Định tuyến OpenAI (ChatGPT) qua WARP." +"OpenAIWARPDesc" = "Thêm định tuyến cho OpenAI (ChatGPT) qua WARP." +"NetflixWARP" = "Định tuyến Netflix qua WARP." +"NetflixWARPDesc" = "Thêm định tuyến cho Netflix qua WARP." +"SpotifyWARP" = "Định tuyến Spotify qua WARP." +"SpotifyWARPDesc" = "Thêm định tuyến cho Spotify qua WARP." "completeTemplate" = "Tất cả" "Inbounds" = "Đầu vào" "Outbounds" = "Đầu ra" diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml index 22f75775..5bb73a88 100644 --- a/web/translation/translate.zh_Hans.toml +++ b/web/translation/translate.zh_Hans.toml @@ -312,6 +312,8 @@ "directCountryConfigsDesc" = "这些选项会将用户直接连接到特定国家/地区的域。" "ipv4Configs" = "IPv4 配置" "ipv4ConfigsDesc" = "此选项将仅通过 IPv4 路由到目标域" +"warpConfigs" = "WARP 配置" +"warpConfigsDesc" = "WARP 将通过 Cloudflare 服务器将流量路由到网站。" "Template" = "Xray 配置模板" "TemplateDesc" = "以该模型为基础生成最终的Xray配置文件,重新启动面板生成效率" "FreedomStrategy" = "配置自由协议的策略" @@ -354,6 +356,14 @@ "GoogleIPv4Desc" = "添加谷歌连接IPv4的路由" "NetflixIPv4" = "为 Netflix 使用 IPv4" "NetflixIPv4Desc" = "添加Netflix连接IPv4的路由" +"GoogleWARP" = "将谷歌路由到 WARP" +"GoogleWARPDesc" = "为谷歌添加路由到WARP" +"OpenAIWARP" = "将 OpenAI (ChatGPT) 路由到 WARP" +"OpenAIWARPDesc" = "将OpenAI(ChatGPT)路由添加到WARP" +"NetflixWARP" = "将 Netflix 路由到 WARP" +"NetflixWARPDesc" = "为Netflix添加路由到WARP" +"SpotifyWARP" = "将 Spotify 路由到 WARP" +"SpotifyWARPDesc" = "为Spotify添加路由到WARP" "completeTemplate" = "全部" "Inbounds" = "界内" "Outbounds" = "出站"