diff --git a/README.md b/README.md index 96952794..60c6c044 100644 --- a/README.md +++ b/README.md @@ -25,10 +25,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install. ## Install Custom Version -To install your desired version, add the version to the end of the installation command. e.g., ver `v2.1.0`: +To install your desired version, add the version to the end of the installation command. e.g., ver `v2.1.1`: ``` -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.1.0 +bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.1.1 ``` ## Manual Install & Upgrade @@ -103,6 +103,15 @@ systemctl restart x-ui ghcr.io/mhsanaei/3x-ui:latest ``` +update to latest version + + ```sh + cd 3x-ui + docker compose down + docker compose pull 3x-ui + docker compose up -d + ``` + @@ -196,26 +205,6 @@ certbot renew --dry-run - -## Xray Configurations - -
- Click for Xray configurations details - -#### Usage - -**1.** Copy & paste into the Advanced Xray Configuration: - -- [traffic](./media/configs/traffic.json) -- [traffic + Block all Iran IP address](./media/configs/traffic+block-iran-ip.json) -- [traffic + Block all Iran Domains](./media/configs/traffic+block-iran-domains.json) -- [traffic + Block Ads + Use IPv4 for Google](./media/configs/traffic+block-ads+ipv4-google.json) -- [traffic + Block Ads + Route Google + Netflix + Spotify + OpenAI (ChatGPT) to WARP](./media/configs/traffic+block-ads+warp.json) - -***Tip:*** *You don't need to do this for a fresh install.* - -
- ## [WARP Configuration](https://gitlab.com/fscarmen/warp)
@@ -223,21 +212,21 @@ certbot renew --dry-run #### Usage -If you want to use routing to WARP follow steps as below: +If you want to use routing to WARP before v2.1.0 follow steps as below: -**1.** If you already installed warp, you can uninstall using below command: - - ```sh - warp u - ``` - -**2.** Install WARP on **SOCKS Proxy Mode**: +**1.** Install WARP on **SOCKS Proxy Mode**: ```sh bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh) ``` -**3.** Turn on the config you need in panel or [Copy and paste this file to Xray Configuration](./media/configs/traffic+block-ads+warp.json) +**2.** If you already installed warp, you can uninstall using below command: + + ```sh + warp u + ``` + +**3.** Turn on the config you need in panel Config Features: diff --git a/config/version b/config/version index eca07e4c..c212a7fe 100644 --- a/config/version +++ b/config/version @@ -1 +1,2 @@ -2.1.2 +2.1.1 + diff --git a/database/model/model.go b/database/model/model.go index c9f9c105..e2d54436 100644 --- a/database/model/model.go +++ b/database/model/model.go @@ -37,7 +37,7 @@ type Inbound struct { // config part Listen string `json:"listen" form:"listen"` - Port int `json:"port" form:"port" gorm:"unique"` + Port int `json:"port" form:"port"` Protocol Protocol `json:"protocol" form:"protocol"` Settings string `json:"settings" form:"settings"` StreamSettings string `json:"streamSettings" form:"streamSettings"` diff --git a/install.sh b/install.sh index d0118c36..8fd42ab8 100644 --- a/install.sh +++ b/install.sh @@ -167,6 +167,7 @@ install_x-ui() { # Check the system's architecture and rename the file accordingly if [[ $(arch3xui) == "armv5" || $(arch3xui) == "armv6" || $(arch3xui) == "armv7" ]]; then + mv bin/xray-linux-$(arch3xui) bin/xray-linux-arm chmod +x bin/xray-linux-arm fi diff --git a/media/configs/traffic+block-ads+ipv4-google.json b/media/configs/traffic+block-ads+ipv4-google.json deleted file mode 100644 index d6cf9171..00000000 --- a/media/configs/traffic+block-ads+ipv4-google.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "log": { - "loglevel": "warning", - "error": "./error.log" - }, - "api": { - "tag": "api", - "services": [ - "HandlerService", - "LoggerService", - "StatsService" - ] - }, - "inbounds": [ - { - "tag": "api", - "listen": "127.0.0.1", - "port": 62789, - "protocol": "dokodemo-door", - "settings": { - "address": "127.0.0.1" - } - } - ], - "outbounds": [ - { - "protocol": "freedom", - "settings": {} - }, - { - "tag": "blocked", - "protocol": "blackhole", - "settings": {} - }, - { - "tag": "IPv4", - "protocol": "freedom", - "settings": { - "domainStrategy": "UseIPv4" - } - } - ], - "policy": { - "levels": { - "0": { - "statsUserDownlink": true, - "statsUserUplink": true - } - }, - "system": { - "statsInboundDownlink": true, - "statsInboundUplink": true - } - }, - "routing": { - "domainStrategy": "IPIfNonMatch", - "rules": [ - { - "type": "field", - "inboundTag": [ - "api" - ], - "outboundTag": "api" - }, - { - "type": "field", - "outboundTag": "blocked", - "ip": [ - "geoip:private" - ] - }, - { - "type": "field", - "outboundTag": "blocked", - "protocol": [ - "bittorrent" - ] - }, - { - "type": "field", - "outboundTag": "blocked", - "domain": [ - "geosite:category-ads-all", - "ext:geosite_IR.dat:category-ads-all" - ] - }, - { - "type": "field", - "outboundTag": "IPv4", - "domain": [ - "geosite:google" - ] - } - ] - }, - "stats": {} -} \ No newline at end of file diff --git a/media/configs/traffic+block-ads+warp.json b/media/configs/traffic+block-ads+warp.json deleted file mode 100644 index c85cd2e9..00000000 --- a/media/configs/traffic+block-ads+warp.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "log": { - "loglevel": "warning", - "error": "./error.log" - }, - "api": { - "tag": "api", - "services": [ - "HandlerService", - "LoggerService", - "StatsService" - ] - }, - "inbounds": [ - { - "tag": "api", - "listen": "127.0.0.1", - "port": 62789, - "protocol": "dokodemo-door", - "settings": { - "address": "127.0.0.1" - } - } - ], - "outbounds": [ - { - "protocol": "freedom", - "settings": {} - }, - { - "tag": "blocked", - "protocol": "blackhole", - "settings": {} - }, - { - "tag": "WARP", - "protocol": "socks", - "settings": { - "servers": [ - { - "address": "127.0.0.1", - "port": 40000 - } - ] - } - } - ], - "policy": { - "levels": { - "0": { - "statsUserDownlink": true, - "statsUserUplink": true - } - }, - "system": { - "statsInboundDownlink": true, - "statsInboundUplink": true - } - }, - "routing": { - "domainStrategy": "IPIfNonMatch", - "rules": [ - { - "type": "field", - "inboundTag": [ - "api" - ], - "outboundTag": "api" - }, - { - "type": "field", - "outboundTag": "blocked", - "ip": [ - "geoip:private" - ] - }, - { - "type": "field", - "outboundTag": "blocked", - "protocol": [ - "bittorrent" - ] - }, - { - "type": "field", - "outboundTag": "blocked", - "domain": [ - "geosite:category-ads-all", - "ext:geosite_IR.dat:category-ads-all" - ] - }, - { - "type": "field", - "outboundTag": "WARP", - "domain": [ - "geosite:spotify", - "geosite:netflix", - "geosite:openai", - "geosite:google" - ] - } - ] - }, - "stats": {} -} \ No newline at end of file diff --git a/media/configs/traffic+block-iran-domains.json b/media/configs/traffic+block-iran-domains.json deleted file mode 100644 index 96de15fd..00000000 --- a/media/configs/traffic+block-iran-domains.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "log": { - "loglevel": "warning", - "error": "./error.log" - }, - "api": { - "tag": "api", - "services": [ - "HandlerService", - "LoggerService", - "StatsService" - ] - }, - "inbounds": [ - { - "tag": "api", - "listen": "127.0.0.1", - "port": 62789, - "protocol": "dokodemo-door", - "settings": { - "address": "127.0.0.1" - } - } - ], - "outbounds": [ - { - "protocol": "freedom", - "settings": {} - }, - { - "tag": "blocked", - "protocol": "blackhole", - "settings": {} - } - ], - "policy": { - "levels": { - "0": { - "statsUserDownlink": true, - "statsUserUplink": true - } - }, - "system": { - "statsInboundDownlink": true, - "statsInboundUplink": true - } - }, - "routing": { - "domainStrategy": "IPIfNonMatch", - "rules": [ - { - "type": "field", - "inboundTag": [ - "api" - ], - "outboundTag": "api" - }, - { - "type": "field", - "outboundTag": "blocked", - "ip": [ - "geoip:private" - ] - }, - { - "type": "field", - "outboundTag": "blocked", - "protocol": [ - "bittorrent" - ] - }, - { - "type": "field", - "outboundTag": "blocked", - "domain": [ - "regexp:.*\\.ir$", - "regexp:.*\\.xn--mgba3a4f16a$", - "ext:geosite_IR.dat:ir" - ] - } - ] - }, - "stats": {} -} \ No newline at end of file diff --git a/media/configs/traffic+block-iran-ip.json b/media/configs/traffic+block-iran-ip.json deleted file mode 100644 index 5e7d3f29..00000000 --- a/media/configs/traffic+block-iran-ip.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "log": { - "loglevel": "warning", - "error": "./error.log" - }, - "api": { - "tag": "api", - "services": [ - "HandlerService", - "LoggerService", - "StatsService" - ] - }, - "inbounds": [ - { - "tag": "api", - "listen": "127.0.0.1", - "port": 62789, - "protocol": "dokodemo-door", - "settings": { - "address": "127.0.0.1" - } - } - ], - "outbounds": [ - { - "protocol": "freedom", - "settings": {} - }, - { - "tag": "blocked", - "protocol": "blackhole", - "settings": {} - } - ], - "policy": { - "levels": { - "0": { - "statsUserDownlink": true, - "statsUserUplink": true - } - }, - "system": { - "statsInboundDownlink": true, - "statsInboundUplink": true - } - }, - "routing": { - "domainStrategy": "IPIfNonMatch", - "rules": [ - { - "type": "field", - "inboundTag": [ - "api" - ], - "outboundTag": "api" - }, - { - "type": "field", - "outboundTag": "blocked", - "ip": [ - "geoip:private", - "ext:geoip_IR.dat:ir" - ] - }, - { - "type": "field", - "outboundTag": "blocked", - "protocol": [ - "bittorrent" - ] - } - ] - }, - "stats": {} -} \ No newline at end of file diff --git a/media/configs/traffic.json b/media/configs/traffic.json deleted file mode 100644 index 63c3537e..00000000 --- a/media/configs/traffic.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "log": { - "loglevel": "warning", - "error": "./error.log" - }, - "api": { - "tag": "api", - "services": ["HandlerService", "LoggerService", "StatsService"] - }, - "inbounds": [ - { - "tag": "api", - "listen": "127.0.0.1", - "port": 62789, - "protocol": "dokodemo-door", - "settings": { - "address": "127.0.0.1" - } - } - ], - "outbounds": [ - { - "protocol": "freedom", - "settings": {} - }, - { - "tag": "blocked", - "protocol": "blackhole", - "settings": {} - } - ], - "policy": { - "levels": { - "0": { - "statsUserDownlink": true, - "statsUserUplink": true - } - }, - "system": { - "statsInboundDownlink": true, - "statsInboundUplink": true - } - }, - "routing": { - "domainStrategy": "IPIfNonMatch", - "rules": [ - { - "type": "field", - "inboundTag": ["api"], - "outboundTag": "api" - }, - { - "type": "field", - "outboundTag": "blocked", - "ip": ["geoip:private"] - }, - { - "type": "field", - "outboundTag": "blocked", - "protocol": ["bittorrent"] - } - ] - }, - "stats": {} -} diff --git a/web/controller/inbound.go b/web/controller/inbound.go index 123c486f..86da9813 100644 --- a/web/controller/inbound.go +++ b/web/controller/inbound.go @@ -85,7 +85,11 @@ func (a *InboundController) addInbound(c *gin.Context) { } user := session.GetLoginUser(c) inbound.UserId = user.Id - inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port) + if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" { + inbound.Tag = fmt.Sprintf("inbound-0.0.0.0:%v", inbound.Port) + } else { + inbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port) + } needRestart := false inbound, needRestart, err = a.inboundService.AddInbound(inbound) @@ -278,7 +282,11 @@ func (a *InboundController) importInbound(c *gin.Context) { user := session.GetLoginUser(c) inbound.Id = 0 inbound.UserId = user.Id - inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port) + if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" { + inbound.Tag = fmt.Sprintf("inbound-0.0.0.0:%v", inbound.Port) + } else { + inbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port) + } for index := range inbound.ClientStats { inbound.ClientStats[index].Id = 0 diff --git a/web/html/xui/index.html b/web/html/xui/index.html index d3357a4d..3c01a29f 100644 --- a/web/html/xui/index.html +++ b/web/html/xui/index.html @@ -93,7 +93,7 @@ {{ i18n "pages.index.xrayStatus" }}: - [[ status.xray.state.toUpperCase() ]] + [[ status.xray.state ]] An error occurred while running Xray diff --git a/web/html/xui/settings.html b/web/html/xui/settings.html index 4be7d2b5..0acbbfec 100644 --- a/web/html/xui/settings.html +++ b/web/html/xui/settings.html @@ -244,7 +244,7 @@ - + diff --git a/web/html/xui/xray.html b/web/html/xui/xray.html index 54d40993..05f5f178 100644 --- a/web/html/xui/xray.html +++ b/web/html/xui/xray.html @@ -1122,26 +1122,6 @@ this.templateRuleSetter({ outboundTag: "warp", property: "domain", data: newValue }); } }, - manualBlockedIPs: { - get: function () { return JSON.stringify(this.blockedIPs, null, 2); }, - set: debounce(function (value) { this.blockedIPs = JSON.parse(value); }, 1000) - }, - manualBlockedDomains: { - get: function () { return JSON.stringify(this.blockedDomains, null, 2); }, - set: debounce(function (value) { this.blockedDomains = JSON.parse(value); }, 1000) - }, - manualDirectIPs: { - get: function () { return JSON.stringify(this.directIPs, null, 2); }, - set: debounce(function (value) { this.directIPs = JSON.parse(value); }, 1000) - }, - manualDirectDomains: { - get: function () { return JSON.stringify(this.directDomains, null, 2); }, - set: debounce(function (value) { this.directDomains = JSON.parse(value); }, 1000) - }, - manualIPv4Domains: { - get: function () { return JSON.stringify(this.ipv4Domains, null, 2); }, - set: debounce(function (value) { this.ipv4Domains = JSON.parse(value); }, 1000) - }, torrentSettings: { get: function () { return doAllItemsExist(this.settingsData.protocols.bittorrent, this.blockedProtocols); diff --git a/web/service/inbound.go b/web/service/inbound.go index 30619791..2a4966ab 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -38,9 +38,25 @@ func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error) { return inbounds, nil } -func (s *InboundService) checkPortExist(port int, ignoreId int) (bool, error) { +func (s *InboundService) checkPortExist(listen string, port int, ignoreId int) (bool, error) { db := database.GetDB() - db = db.Model(model.Inbound{}).Where("port = ?", port) + if listen == "" || listen == "0.0.0.0" || listen == "::" || listen == "::0" { + db = db.Model(model.Inbound{}).Where("port = ?", port) + } else { + db = db.Model(model.Inbound{}). + Where("port = ?", port). + Where( + db.Model(model.Inbound{}).Where( + "listen = ?", listen, + ).Or( + "listen = \"\"", + ).Or( + "listen = \"0.0.0.0\"", + ).Or( + "listen = \"::\"", + ).Or( + "listen = \"::0\"")) + } if ignoreId > 0 { db = db.Where("id != ?", ignoreId) } @@ -135,7 +151,7 @@ func (s *InboundService) checkEmailExistForInbound(inbound *model.Inbound) (stri } func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, bool, error) { - exist, err := s.checkPortExist(inbound.Port, 0) + exist, err := s.checkPortExist(inbound.Listen, inbound.Port, 0) if err != nil { return inbound, false, err } @@ -252,7 +268,7 @@ func (s *InboundService) GetInbound(id int) (*model.Inbound, error) { } func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, bool, error) { - exist, err := s.checkPortExist(inbound.Port, inbound.Id) + exist, err := s.checkPortExist(inbound.Listen, inbound.Port, inbound.Id) if err != nil { return inbound, false, err } @@ -295,7 +311,12 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, oldInbound.Settings = inbound.Settings oldInbound.StreamSettings = inbound.StreamSettings oldInbound.Sniffing = inbound.Sniffing - oldInbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port) + if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" { + oldInbound.Tag = fmt.Sprintf("inbound-0.0.0.0:%v", inbound.Port) + } else { + oldInbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port) + } + needRestart := false s.xrayApi.Init(p.GetAPIPort()) diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index d7c93c71..2f1c0544 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -54,11 +54,11 @@ "security" = "Security" [menu] -"dashboard" = "OVERVIEW" -"inbounds" = "INBOUNDS" -"settings" = "PANEL SETTINGS" -"xray" = "XRAY CONFIGS" -"logout" = "LOG OUT" +"dashboard" = "Overview" +"inbounds" = "Inbounds" +"settings" = "Panel Settings" +"xray" = "Xray Configs" +"logout" = "Log Out" "link" = "Manage" [pages.login] @@ -319,7 +319,7 @@ "ipv4Configs" = "IPv4 Routing" "ipv4ConfigsDesc" = "These options will route traffic based on a specific destination via IPv4." "warpConfigs" = "WARP Routing" -"warpConfigsDesc" = "These options will route traffic based on a specific destination via WARP. (follow the guide on the Panel’s GitHub)" +"warpConfigsDesc" = "These options will route traffic based on a specific destination via WARP." "Template" = "Advanced Xray Configuration Template" "TemplateDesc" = "The final Xray config file will be generated based on this template." "FreedomStrategy" = "Freedom Protocol Strategy" @@ -476,7 +476,6 @@ "helpAdminCommands" = "To search for a client email:\r\n/usage [Email]\r\n\r\nTo search for inbounds (with client stats):\r\n/inbound [Remark]" "helpClientCommands" = "To search for statistics, use the following command:\r\n\r\n/usage [Email]" - [tgbot.messages] "cpuThreshold" = "🔴 CPU Load {{ .Percent }}% exceeds the threshold of {{ .Threshold }}%" "selectUserFailed" = "❌ Error in user selection!" @@ -522,7 +521,6 @@ "yes" = "✅ Yes" "no" = "❌ No" - [tgbot.buttons] "closeKeyboard" = "❌ Close Keyboard" "cancel" = "❌ Cancel" @@ -556,7 +554,6 @@ "limitTraffic" = "🚧 Traffic Limit" "getBanLogs" = "Get Ban Logs" - [tgbot.answers] "successfulOperation" = "✅ Operation successful!" "errorOperation" = "❗ Error in operation." diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index fb22b8a1..42a3a2b2 100644 --- a/web/translation/translate.es_ES.toml +++ b/web/translation/translate.es_ES.toml @@ -1,6 +1,6 @@ "username" = "Nombre de Usuario" "password" = "Contraseña" -"login" = "Acceso" +"login" = "Acceder" "confirm" = "Confirmar" "cancel" = "Cancelar" "close" = "Cerrar" @@ -59,10 +59,10 @@ "settings" = "Configuraciones" "xray" = "Configuración Xray" "logout" = "Cerrar Sesión" -"link" = "Otro" +"link" = "Gestionar" [pages.login] -"title" = "Grata" +"title" = "Bienvenido" "loginAgain" = "El límite de tiempo de inicio de sesión ha expirado. Por favor, inicia sesión nuevamente." [pages.login.toasts] diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index 307ea1d6..3edcb4cb 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -501,8 +501,8 @@ "time" = "⏰ زمان: {{ .Time }}\r\n" "inbound" = "📍 نام‌ورودی: {{ .Remark }}\r\n" "port" = "🔌 پورت: {{ .Port }}\r\n" -"expire" = "📅 تاریخ‌انقضا: {{ .DateTime }}\r\n \r\n" -"expireIn" = "📅 باقی‌مانده‌تاانقضا: {{ .Time }}\r\n \r\n" +"expire" = "📅 تاریخ‌انقضا: {{ .Time }}\r\n\r\n" +"expireIn" = "📅 باقی‌مانده‌تاانقضا: {{ .Time }}\r\n\r\n" "active" = "💡 فعال: {{ .Enable }}\r\n" "enabled" = "🚨 وضعیت: {{ .Enable }}\r\n" "online" = "🌐 وضعیت اتصال: {{ .Status }}\r\n" @@ -515,7 +515,7 @@ "exhaustedCount" = "🚨 تعداد {{ .Type }} به‌اتمام‌رسیده‌است:\r\n" "onlinesCount" = "🌐 کاربران‌آنلاین: {{ .Count }}\r\n" "disabled" = "🛑 غیرفعال: {{ .Disabled }}\r\n" -"depleteSoon" = "🔜 به‌زودی‌به‌پایان‌خواهدرسید: {{ .Deplete }}\r\n \r\n" +"depleteSoon" = "🔜 به‌زودی‌به‌پایان‌خواهدرسید: {{ .Deplete }}\r\n\r\n" "backupTime" = "🗄 زمان‌پشتیبان‌گیری: {{ .Time }}\r\n" "refreshedOn" = "\r\n📋🔄 تازه‌سازی شده در: {{ .Time }}\r\n\r\n" "yes" = "✅ بله" @@ -537,7 +537,6 @@ "clientUsage" = "دریافت آمار کاربر" "onlines" = "کاربران آنلاین" "commands" = "دستورات" - "refresh" = "🔄 تازه‌سازی" "clearIPs" = "❌ پاک‌سازی آدرس‌ها" "removeTGUser" = "❌ حذف کاربر تلگرام" diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index 8d91562c..d8211d8d 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -503,7 +503,7 @@ "port" = "🔌 Порт: {{ .Port }}\r\n" "expire" = "📅 Дата окончания: {{ .Time }}\r\n" "expireIn" = "📅 Окончание через: {{ .Time }}\r\n" -"active" = "💡 Активен: ✅ Да\r\n" +"active" = "💡 Активен: {{ .Enable }}\r\n" "enabled" = "🚨 Включен: {{ .Enable }}\r\n" "online" = "🌐 Статус соединения: {{ .Status }}\r\n" "email" = "📧 Email: {{ .Email }}\r\n" diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml index 7cb0230b..7491a70c 100644 --- a/web/translation/translate.vi_VN.toml +++ b/web/translation/translate.vi_VN.toml @@ -32,7 +32,7 @@ "transmission" = "Truyền tải" "host" = "Máy chủ" "path" = "Đường dẫn" -"camouflage" = "camouflage" +"camouflage" = "Ngụy trang" "status" = "Trạng thái" "enabled" = "Đã kích hoạt" "disabled" = "Đã tắt" @@ -125,8 +125,8 @@ "modifyInbound" = "Chỉnh sửa điểm vào (Inbound)" "deleteInbound" = "Xóa điểm vào (Inbound)" "deleteInboundContent" = "Xác nhận xóa điểm vào? (Inbound)" -"deleteClient" = "Xóa khách hàng" -"deleteClientContent" = "Bạn có chắc chắn muốn xóa ứng dụng khách không?" +"deleteClient" = "Xóa người dùng" +"deleteClientContent" = "Bạn có chắc chắn muốn xóa người dùng không?" "resetTrafficContent" = "Xác nhận đặt lại lưu lượng?" "copyLink" = "Sao chép liên kết" "address" = "Địa chỉ" @@ -145,7 +145,7 @@ "keyPath" = "Đường dẫn khóa riêng tư" "keyContent" = "Nội dung khóa riêng tư" "clickOnQRcode" = "Nhấn vào Mã QR để sao chép" -"client" = "Máy Khách" +"client" = "Người dùng" "export" = "Xuất liên kết" "clone" = "Sao chép" "cloneInbound" = "Sao chép điểm vào (Inbound)" @@ -154,15 +154,15 @@ "resetAllTraffic" = "Đặt lại lưu lượng cho tất cả điểm vào" "resetAllTrafficTitle" = "Đặt lại lưu lượng cho tất cả điểm vào" "resetAllTrafficContent" = "Bạn có chắc chắn muốn đặt lại lưu lượng cho tất cả điểm vào không?" -"resetInboundClientTraffics" = "Đặt lại lưu lượng cho các client của điểm vào" -"resetInboundClientTrafficTitle" = "Đặt lại lưu lượng cho tất cả lưu lượng của client" -"resetInboundClientTrafficContent" = "Bạn có chắc chắn muốn đặt lại tất cả lưu lượng cho các client của điểm vào này không?" -"resetAllClientTraffics" = "Đặt lại lưu lượng cho tất cả client" -"resetAllClientTrafficTitle" = "Đặt lại lưu lượng cho tất cả client" -"resetAllClientTrafficContent" = "Bạn có chắc chắn muốn đặt lại tất cả lưu lượng cho tất cả client không?" -"delDepletedClients" = "Xóa các client đã cạn kiệt" -"delDepletedClientsTitle" = "Xóa các client đã cạn kiệt" -"delDepletedClientsContent" = "Bạn có chắc chắn muốn xóa tất cả các client đã cạn kiệt không?" +"resetInboundClientTraffics" = "Đặt lại lưu lượng toàn bộ người dùng của điểm vào" +"resetInboundClientTrafficTitle" = "Đặt lại lưu lượng cho toàn bộ người dùng của điểm vào" +"resetInboundClientTrafficContent" = "Bạn có chắc chắn muốn đặt lại tất cả lưu lượng cho các người dùng của điểm vào này không?" +"resetAllClientTraffics" = "Đặt lại lưu lượng cho toàn bộ người dùng" +"resetAllClientTrafficTitle" = "Đặt lại lưu lượng cho toàn bộ người dùng" +"resetAllClientTrafficContent" = "Bạn có chắc chắn muốn đặt lại tất cả lưu lượng cho toàn bộ người dùng không?" +"delDepletedClients" = "Xóa các người dùng đã cạn kiệt" +"delDepletedClientsTitle" = "Xóa các người dùng đã cạn kiệt" +"delDepletedClientsContent" = "Bạn có chắc chắn muốn xóa toàn bộ người dùng đã cạn kiệt không?" "email" = "Email" "emailDesc" = "Vui lòng cung cấp một địa chỉ email duy nhất." "IPLimit" = "Giới hạn IP" @@ -174,20 +174,20 @@ "xtlsDesc" = "Xray core cần phiên bản 1.7.5" "realityDesc" = "Xray core cần phiên bản 1.8.0 hoặc cao hơn." "telegramDesc" = "Chỉ sử dụng ID trò chuyện (bạn có thể nhận được nó ở đây @userinfobot hoặc sử dụng lệnh '/id' trong bot)" -"subscriptionDesc" = "Bạn có thể tìm liên kết đăng ký của mình trong Chi tiết, cũng như bạn có thể sử dụng cùng tên cho nhiều cấu hình khác nhau" +"subscriptionDesc" = "Bạn có thể tìm liên kết gói đăng ký của mình trong Chi tiết, cũng như bạn có thể sử dụng cùng tên cho nhiều cấu hình khác nhau" "info" = "Thông tin" "same" = "Giống nhau" "inboundData" = "Dữ liệu gửi đến" "copyToClipboard" = "Sao chép vào bảng nhớ tạm" "import" = "Nhập" -"importInbound" = "Nhập hàng gửi về" +"importInbound" = "Nhập inbound" [pages.client] -"add" = "Thêm Client" -"edit" = "Chỉnh sửa Client" -"submitAdd" = "Thêm Client" +"add" = "Thêm người dùng" +"edit" = "Chỉnh sửa người dùng" +"submitAdd" = "Thêm" "submitEdit" = "Lưu thay đổi" -"clientCount" = "Số lượng Client" +"clientCount" = "Số lượng người dùng" "bulk" = "Thêm hàng loạt" "method" = "Phương pháp" "first" = "Đầu tiên" @@ -212,11 +212,11 @@ [pages.inbounds.stream.tcp] "version" = "Phiên bản" "method" = "Phương pháp" -"path" = "Con đường" +"path" = "Đường dẫn" "status" = "Trạng thái" "statusDescription" = "Tình trạng Mô tả" -"requestHeader" = "Tiêu đề yêu cầu" -"responseHeader" = "Tiêu đề phản hồi" +"requestHeader" = "Header yêu cầu" +"responseHeader" = "Header phản hồi" [pages.inbounds.stream.quic] "encryption" = "Mã hóa" @@ -225,30 +225,30 @@ "title" = "Cài đặt" "save" = "Lưu" "infoDesc" = "Mọi thay đổi được thực hiện ở đây cần phải được lưu. Vui lòng khởi động lại bảng điều khiển để áp dụng các thay đổi." -"restartPanel" = "Khởi động lại Bảng điều khiển" +"restartPanel" = "Khởi động lại bảng điều khiển" "restartPanelDesc" = "Bạn có chắc chắn muốn khởi động lại bảng điều khiển? Nhấn OK để khởi động lại sau 3 giây. Nếu bạn không thể truy cập bảng điều khiển sau khi khởi động lại, vui lòng xem thông tin nhật ký của bảng điều khiển trên máy chủ." "actions" = "Hành động" -"resetDefaultConfig" = "Đặt lại Cấu hình Mặc định" -"panelSettings" = "Cài đặt Bảng điều khiển" -"securitySettings" = "Cài đặt Bảo mật" -"TGBotSettings" = "Cài đặt Bot Telegram" -"panelListeningIP" = "IP Nghe của Bảng điều khiển" +"resetDefaultConfig" = "Đặt lại cấu hình mặc định" +"panelSettings" = "Bảng điều khiển" +"securitySettings" = "Bảo mật" +"TGBotSettings" = "Bot Telegram" +"panelListeningIP" = "IP Nghe của bảng điều khiển" "panelListeningIPDesc" = "Mặc định để trống để nghe tất cả các IP." -"panelListeningDomain" = "Tên miền của nghe Bảng điều khiển" +"panelListeningDomain" = "Tên miền của nghe bảng điều khiển" "panelListeningDomainDesc" = "Mặc định để trống để nghe tất cả các tên miền và IP" -"panelPort" = "Cổng Bảng điều khiển" -"panelPortDesc" = "Cổng được sử dụng để hiển thị bảng điều khiển này" -"publicKeyPath" = "Đường dẫn tập tin khóa công khai Chứng chỉ Bảng điều khiển" -"publicKeyPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với." -"privateKeyPath" = "Đường dẫn tập tin khóa riêng tư Chứng chỉ Bảng điều khiển" -"privateKeyPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với." -"panelUrlPath" = "Đường dẫn gốc URL Bảng điều khiển" -"panelUrlPathDesc" = "Phải bắt đầu bằng '/' và kết thúc bằng." +"panelPort" = "Cổng bảng điều khiển" +"panelPortDesc" = "Cổng được sử dụng để kết nối với bảng điều khiển này" +"publicKeyPath" = "Đường dẫn file chứng chỉ bảng điều khiển" +"publicKeyPathDesc" = "Điền vào đường dẫn đầy đủ (bắt đầu từ '/')" +"privateKeyPath" = "Đường dẫn file khóa của chứng chỉ bảng điều khiển" +"privateKeyPathDesc" = "Điền vào đường dẫn đầy đủ (bắt đầu từ '/')" +"panelUrlPath" = "Đường dẫn gốc URL bảng điều khiển" +"panelUrlPathDesc" = "Phải bắt đầu và kết thúc bằng '/'" "pageSize" = "Kích thước phân trang" "pageSizeDesc" = "Xác định kích thước trang cho bảng gửi đến. Đặt 0 để tắt" "remarkModel" = "Ghi chú mô hình và ký tự phân tách" -"datepicker" = "bảng chọn ngày" -"datepickerDescription" = "Loại lịch chọn chỉ định ngày hết hạn" +"datepicker" = "Kiểu lịch" +"datepickerDescription" = "Tác vụ chạy theo lịch trình sẽ chạy theo kiểu lịch này." "sampleRemark" = "Nhận xét mẫu" "oldUsername" = "Tên người dùng hiện tại" "currentPassword" = "Mật khẩu hiện tại" @@ -268,7 +268,7 @@ "tgNotifyBackupDesc" = "Bao gồm tệp sao lưu cơ sở dữ liệu với thông báo báo cáo." "tgNotifyLogin" = "Thông báo Đăng nhập" "tgNotifyLoginDesc" = "Hiển thị tên người dùng, địa chỉ IP và thời gian khi ai đó cố gắng đăng nhập vào bảng điều khiển của bạn." -"sessionMaxAge" = "Tuổi tối đa của phiên" +"sessionMaxAge" = "Thời gian tối đa của phiên" "sessionMaxAgeDesc" = "Thời gian của phiên đăng nhập (đơn vị: phút)" "expireTimeDiff" = "Ngưỡng hết hạn cho thông báo" "expireTimeDiffDesc" = "Nhận thông báo về việc hết hạn tài khoản trước ngưỡng này (đơn vị: ngày)" @@ -278,29 +278,29 @@ "tgNotifyCpuDesc" = "Nhận thông báo nếu tỷ lệ sử dụng CPU vượt quá ngưỡng này (đơn vị: %)" "timeZone" = "Múi giờ" "timeZoneDesc" = "Các tác vụ được lên lịch chạy theo thời gian trong múi giờ này." -"subSettings" = "Đăng ký" +"subSettings" = "Gói đăng ký" "subEnable" = "Bật dịch vụ" -"subEnableDesc" = "Tính năng đăng ký với cấu hình riêng" +"subEnableDesc" = "Tính năng gói đăng ký với cấu hình riêng" "subListen" = "Listening IP" "subListenDesc" = "Mặc định để trống để nghe tất cả các IP" -"subPort" = "Cổng Đăng ký" +"subPort" = "Cổng gói đăng ký" "subPortDesc" = "Số cổng dịch vụ đăng ký phải chưa được sử dụng trên máy chủ" -"subCertPath" = "Đường dẫn tập tin khóa công khai Chứng chỉ Đăng ký" -"subCertPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với '/'" -"subKeyPath" = "Đường dẫn tập tin khóa riêng tư Chứng chỉ Đăng ký" -"subKeyPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với '/'" -"subPath" = "Đường dẫn gốc URL Đăng ký" -"subPathDesc" = "Phải bắt đầu bằng '/' và kết thúc bằng '/'" +"subCertPath" = "Đường dẫn file chứng chỉ gói đăng ký" +"subCertPathDesc" = "Điền vào đường dẫn đầy đủ (bắt đầu với '/')" +"subKeyPath" = "Đường dẫn file khóa của chứng chỉ gói đăng ký" +"subKeyPathDesc" = "Điền vào đường dẫn đầy đủ (bắt đầu với '/')" +"subPath" = "Đường dẫn gốc URL gói đăng ký" +"subPathDesc" = "Phải bắt đầu và kết thúc bằng '/'" "subDomain" = "Tên miền con" "subDomainDesc" = "Mặc định để trống để nghe tất cả các tên miền và IP" -"subUpdates" = "Khoảng thời gian cập nhật đăng ký" +"subUpdates" = "Khoảng thời gian cập nhật gói đăng ký" "subUpdatesDesc" = "Số giờ giữa các cập nhật trong ứng dụng khách" "subEncrypt" = "Mã hóa cấu hình" -"subEncryptDesc" = "Mã hóa các cấu hình được trả về trong đăng ký" +"subEncryptDesc" = "Mã hóa các cấu hình được trả về trong gói đăng ký" "subShowInfo" = "Hiển thị thông tin sử dụng" "subShowInfoDesc" = "Hiển thị lưu lượng truy cập còn lại và ngày sau tên cấu hình" -"subURI" = "URI proxy ngược" -"subURIDesc" = "Thay đổi URI cơ sở của URL đăng ký để sử dụng ở phía sau proxy" +"subURI" = "URI proxy trung gian" +"subURIDesc" = "Thay đổi URI cơ sở của URL gói đăng ký để sử dụng cho proxy trung gian" [pages.xray] "title" = "Cài đặt Xray" @@ -422,7 +422,7 @@ "intercon" = "Kết nối" [pages.xray.wireguard] -"secretKey" = "Chìa khoá bí mật" +"secretKey" = "Khoá bí mật" "publicKey" = "Khóa công khai" "allowedIPs" = "IP được phép" "endpoint" = "Điểm cuối" @@ -434,8 +434,8 @@ "secret" = "Mã thông báo bí mật" "loginSecurity" = "Bảo mật đăng nhập" "loginSecurityDesc" = "Bật bước bảo mật đăng nhập bổ sung cho người dùng" -"secretToken" = "Mã thông báo bí mật" -"secretTokenDesc" = "Vui lòng sao chép và lưu trữ mã thông báo này một cách an toàn ở nơi an toàn. Mã thông báo này cần thiết để đăng nhập và không thể phục hồi từ công cụ lệnh x-ui." +"secretToken" = "Mã bí mật" +"secretTokenDesc" = "Vui lòng sao chép và lưu trữ mã này một cách an toàn ở nơi an toàn. Mã này cần thiết để đăng nhập và không thể phục hồi từ công cụ lệnh x-ui." [pages.settings.toasts] "modifySettings" = "Chỉnh sửa cài đặt " @@ -460,7 +460,7 @@ "hours" = "Giờ" "unknown" = "Không rõ" "inbounds" = "Vào" -"clients" = "Các khách hàng" +"clients" = "Các người dùng" "offline" = "🔴 Ngoại tuyến" "online" = "🟢 Trực tuyến" diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml index 32765f01..5a46ec73 100644 --- a/web/translation/translate.zh_Hans.toml +++ b/web/translation/translate.zh_Hans.toml @@ -503,7 +503,7 @@ "port" = "🔌 端口:{{ .Port }}\r\n" "expire" = "📅 过期日期:{{ .Time }}\r\n" "expireIn" = "📅 剩余时间:{{ .Time }}\r\n" -"active" = "💡 激活:✅\r\n" +"active" = "💡 激活:{{ .Enable }}\r\n" "enabled" = "🚨 已启用:{{ .Enable }}\r\n" "online" = "🌐 连接状态:{{ .Status }}\r\n" "email" = "📧 邮箱:{{ .Email }}\r\n" diff --git a/x-ui.sh b/x-ui.sh index d245a247..2807bc4f 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -991,6 +991,12 @@ install_iplimit() { echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n" exit 1 ;; esac + + if ! command -v fail2ban-client &>/dev/null; then + echo -e "${red}Fail2ban installation failed.${plain}\n" + exit 1 + fi + echo -e "${green}Fail2ban installed successfully!${plain}\n" else echo -e "${yellow}Fail2ban is already installed.${plain}\n"