Flow |
diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html
index f27be794..1aad5d91 100644
--- a/web/html/xui/inbounds.html
+++ b/web/html/xui/inbounds.html
@@ -733,7 +733,7 @@
this.inbounds.push(to_inbound);
this.dbInbounds.push(dbInbound);
if ([Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(inbound.protocol)) {
- if (inbound.protocol === Protocols.SHADOWSOCKS && (!to_inbound.isSSMultiUser)) {
+ if (dbInbound.isSS && (!to_inbound.isSSMultiUser)) {
continue;
}
this.clientCount[inbound.id] = this.getClientCounts(inbound, to_inbound);
@@ -935,6 +935,7 @@
settings: Inbound.Settings.getSettings(baseInbound.protocol).toString(),
streamSettings: baseInbound.stream.toString(),
sniffing: baseInbound.sniffing.toString(),
+ allocate: baseInbound.allocate.toString(),
};
await this.submit('/panel/inbound/add', data, inModal);
},
@@ -980,6 +981,7 @@
};
if (inbound.canEnableStream()) data.streamSettings = inbound.stream.toString();
data.sniffing = inbound.sniffing.toString();
+ data.allocate = inbound.allocate.toString();
await this.submit('/panel/inbound/add', data, inModal);
},
@@ -999,6 +1001,7 @@
};
if (inbound.canEnableStream()) data.streamSettings = inbound.stream.toString();
data.sniffing = inbound.sniffing.toString();
+ data.allocate = inbound.allocate.toString();
await this.submit(`/panel/inbound/update/${dbInbound.id}`, data, inModal);
},
diff --git a/web/html/xui/settings.html b/web/html/xui/settings.html
index a12dfab9..f2462518 100644
--- a/web/html/xui/settings.html
+++ b/web/html/xui/settings.html
@@ -138,7 +138,7 @@
-
+
@@ -296,18 +296,7 @@
-
-
-
-
-
-
-
- [[ p ]]
-
-
-
-
+
@@ -316,20 +305,38 @@
-
- {{ i18n "pages.settings.noiseDesc"}}
+
+ {{ i18n "pages.settings.noisesDesc"}}
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+ updateNoiseType(index, value)">
+
+ [[ p ]]
+
+
+
+
+ updateNoisePacket(index, value)" placeholder="5-10">
+ updateNoiseDelay(index, value)" placeholder="10-20">
+ Remove
+ Add Noise
@@ -373,9 +380,14 @@
-
+
-
+
+
+
+
+
+
@@ -430,15 +442,14 @@
}
}
},
- defaultNoise: {
- tag: "noise",
+ defaultNoises: {
+ tag: "noises",
protocol: "freedom",
settings: {
domainStrategy: "AsIs",
- noise: {
- packet: "rand:5-10",
- delay: "10-20",
- }
+ noises: [
+ { type: "rand", packet: "10-20", delay: "10-16" },
+ ],
},
},
defaultMux: {
@@ -452,8 +463,7 @@
type: "field",
outboundTag: "direct",
domain: [
- "geosite:category-ir",
- "geosite:cn"
+ "geosite:category-ir"
],
"enabled": true
},
@@ -462,17 +472,30 @@
outboundTag: "direct",
ip: [
"geoip:private",
- "geoip:ir",
- "geoip:cn"
+ "geoip:ir"
],
enabled: true
},
],
- countryOptions: [
- { label: 'Private IP/Domain', value: 'private' },
+ geoIPOptions: [
+ { label: 'Private IP', value: 'private' },
{ label: '🇮🇷 Iran', value: 'ir' },
{ label: '🇨🇳 China', value: 'cn' },
{ label: '🇷🇺 Russia', value: 'ru' },
+ { label: '🇻🇳 Vietnam', value: 'vn' },
+ { label: '🇪🇸 Spain', value: 'es' },
+ { label: '🇮🇩 Indonesia', value: 'id' },
+ { label: '🇺🇦 Ukraine', value: 'ua' },
+ { label: '🇹🇷 Türkiye', value: 'tr' },
+ { label: '🇧🇷 Brazil', value: 'br' },
+ ],
+ geoSiteOptions: [
+ { label: '🇮🇷 Iran', value: 'ir' },
+ { label: '🇨🇳 China', value: 'cn' },
+ { label: '🇷🇺 Russia', value: 'ru' },
+ { label: 'Apple', value: 'apple' },
+ { label: 'Meta', value: 'meta' },
+ { label: 'Google', value: 'google' },
],
get remarkModel() {
rm = this.allSetting.remarkModel;
@@ -602,6 +625,30 @@
this.user.loginSecret = "";
}
},
+ addNoise() {
+ const newNoise = { type: "rand", packet: "10-20", delay: "10-16" };
+ this.noisesArray = [...this.noisesArray, newNoise];
+ },
+ removeNoise(index) {
+ const newNoises = [...this.noisesArray];
+ newNoises.splice(index, 1);
+ this.noisesArray = newNoises;
+ },
+ updateNoiseType(index, value) {
+ const updatedNoises = [...this.noisesArray];
+ updatedNoises[index] = { ...updatedNoises[index], type: value };
+ this.noisesArray = updatedNoises;
+ },
+ updateNoisePacket(index, value) {
+ const updatedNoises = [...this.noisesArray];
+ updatedNoises[index] = { ...updatedNoises[index], packet: value };
+ this.noisesArray = updatedNoises;
+ },
+ updateNoiseDelay(index, value) {
+ const updatedNoises = [...this.noisesArray];
+ updatedNoises[index] = { ...updatedNoises[index], delay: value };
+ this.noisesArray = updatedNoises;
+ },
},
computed: {
fragment: {
@@ -640,29 +687,27 @@
}
}
},
- noise: {
- get: function () { return this.allSetting?.subJsonNoise != ""; },
- set: function (v) {
- this.allSetting.subJsonNoise = v ? JSON.stringify(this.defaultNoise) : "";
- }
- },
- noisePacket: {
- get: function () { return this.noise ? JSON.parse(this.allSetting.subJsonNoise).settings.noise.packet : ""; },
- set: function (v) {
- if (v != "") {
- newNoise = JSON.parse(this.allSetting.subJsonNoise);
- newNoise.settings.noise.packet = v;
- this.allSetting.subJsonNoise = JSON.stringify(newNoise);
+ noises: {
+ get() {
+ return this.allSetting?.subJsonNoises != "";
+ },
+ set(v) {
+ if (v) {
+ this.allSetting.subJsonNoises = JSON.stringify(this.defaultNoises);
+ } else {
+ this.allSetting.subJsonNoises = "";
}
}
},
- noiseDelay: {
- get: function () { return this.noise ? JSON.parse(this.allSetting.subJsonNoise).settings.noise.delay : ""; },
- set: function (v) {
- if (v != "") {
- newNoise = JSON.parse(this.allSetting.subJsonNoise);
- newNoise.settings.noise.delay = v;
- this.allSetting.subJsonNoise = JSON.stringify(newNoise);
+ noisesArray: {
+ get() {
+ return this.noises ? JSON.parse(this.allSetting.subJsonNoises).settings.noises : [];
+ },
+ set(value) {
+ if (this.noises) {
+ const newNoises = JSON.parse(this.allSetting.subJsonNoises);
+ newNoises.settings.noises = value;
+ this.allSetting.subJsonNoises = JSON.stringify(newNoises);
}
}
},
@@ -702,28 +747,49 @@
this.allSetting.subJsonRules = v ? JSON.stringify(this.defaultRules) : "";
}
},
- directCountries: {
+ geoIP: {
get: function () {
if (!this.enableDirect) return [];
- rules = JSON.parse(this.allSetting.subJsonRules);
+ const rules = JSON.parse(this.allSetting.subJsonRules);
return Array.isArray(rules) ? rules[1].ip.map(d => d.replace("geoip:", "")) : [];
},
set: function (v) {
- rules = JSON.parse(this.allSetting.subJsonRules);
+ const rules = JSON.parse(this.allSetting.subJsonRules);
if (!Array.isArray(rules)) return;
- rules[0].domain = [];
+
rules[1].ip = [];
+ v.forEach(d => {
+ rules[1].ip.push("geoip:" + d);
+ });
+ this.allSetting.subJsonRules = JSON.stringify(rules);
+ }
+ },
+ geoSite: {
+ get: function () {
+ if (!this.enableDirect) return [];
+ const rules = JSON.parse(this.allSetting.subJsonRules);
+ return Array.isArray(rules) ?
+ rules[0].domain.map(d => {
+ if (d.startsWith("geosite:category-")) {
+ return d.replace("geosite:category-", "");
+ }
+ return d.replace("geosite:", "");
+ })
+ : [];
+ },
+ set: function (v) {
+ const rules = JSON.parse(this.allSetting.subJsonRules);
+ if (!Array.isArray(rules)) return;
+
+ rules[0].domain = [];
v.forEach(d => {
let category = '';
- if (["cn", "private"].includes(d)) {
+ if (["cn", "apple", "meta", "google"].includes(d)) {
category = "";
- } else if (d === 'ru') {
- category = "category-gov-";
- } else {
+ } else if (["ru", "ir"].includes(d)) {
category = "category-";
}
rules[0].domain.push("geosite:" + category + d);
- rules[1].ip.push("geoip:" + d);
});
this.allSetting.subJsonRules = JSON.stringify(rules);
}
diff --git a/web/html/xui/xray.html b/web/html/xui/xray.html
index 2a4989a7..39010f28 100644
--- a/web/html/xui/xray.html
+++ b/web/html/xui/xray.html
@@ -163,8 +163,8 @@
-
- [[ s ]]
+
+ [[ s ]]
@@ -178,7 +178,8 @@
- [[ s ]]
+ Empty
+ [[ s ]]
@@ -192,11 +193,28 @@
- [[ s ]]
+ Empty
+ [[ s ]]
+
+
+
+
+
+
+
+
+ Empty
+ [[ s ]]
+
+
+
+
+
@@ -791,9 +809,13 @@
protocol: "freedom"
},
routingDomainStrategies: ["AsIs", "IPIfNonMatch", "IPOnDemand"],
- logLevel: ["none" , "debug" , "info" , "warning", "error"],
- access: [],
- error: [],
+ log: {
+ loglevel: ["none", "debug", "info", "warning", "error"],
+ access: ["none", "./access.log"],
+ error: ["none", "./error.log"],
+ dnsLog: false,
+ maskAddress: ["quarter", "half", "full"],
+ },
settingsData: {
protocols: {
bittorrent: ["bittorrent"],
@@ -828,10 +850,11 @@
"regexp:.*\\.cn$"
],
ru: [
- "geosite:category-gov-ru",
+ "geosite:category-ru", //https://github.com/v2fly/domain-list-community/blob/master/data/category-ru
"regexp:.*\\.ru$"
],
ir: [
+ "geosite:category-ir", // https://github.com/v2fly/domain-list-community/blob/master/data/category-ir
"regexp:.*\\.ir$",
"regexp:.*\\.xn--mgba3a4f16a$", // .ایران
"ext:geosite_IR.dat:ir"
@@ -1519,27 +1542,11 @@
templateSettings: {
get: function () {
const parsedSettings = this.xraySetting ? JSON.parse(this.xraySetting) : null;
- let accessLogPath = "./access.log";
- let errorLogPath = "./error.log";
-
- if (parsedSettings && parsedSettings.log) {
- if (parsedSettings.log.access && parsedSettings.log.access !== "none") {
- accessLogPath = parsedSettings.log.access;
- }
- if (parsedSettings.log.error && parsedSettings.log.error !== "none") {
- errorLogPath = parsedSettings.log.error;
- }
- }
-
- this.access = ["none", accessLogPath];
- this.error = ["none", errorLogPath];
return parsedSettings;
},
set: function (newValue) {
- if (newValue && newValue.log) {
+ if (newValue) {
this.xraySetting = JSON.stringify(newValue, null, 2);
- this.access = ["none", newValue.log.access || "./access.log"];
- this.error = ["none", newValue.log.error || "./error.log"];
}
},
},
@@ -1688,7 +1695,7 @@
this.templateSettings = newTemplateSettings;
}
},
- setLogLevel: {
+ logLevel: {
get: function () {
if (!this.templateSettings || !this.templateSettings.log || !this.templateSettings.log.loglevel) return "warning";
return this.templateSettings.log.loglevel;
@@ -1721,6 +1728,28 @@
this.templateSettings = newTemplateSettings;
}
},
+ dnslog: {
+ get: function () {
+ if (!this.templateSettings || !this.templateSettings.log || !this.templateSettings.log.dnsLog) return false;
+ return this.templateSettings.log.dnsLog;
+ },
+ set: function (newValue) {
+ newTemplateSettings = this.templateSettings;
+ newTemplateSettings.log.dnsLog = newValue;
+ this.templateSettings = newTemplateSettings;
+ }
+ },
+ maskAddressLog: {
+ get: function () {
+ if (!this.templateSettings || !this.templateSettings.log || !this.templateSettings.log.maskAddress) return "";
+ return this.templateSettings.log.maskAddress;
+ },
+ set: function (newValue) {
+ newTemplateSettings = this.templateSettings;
+ newTemplateSettings.log.maskAddress = newValue;
+ this.templateSettings = newTemplateSettings;
+ }
+ },
blockedIPs: {
get: function () {
return this.templateRuleGetter({ outboundTag: "blocked", property: "ip" });
diff --git a/web/job/check_client_ip_job.go b/web/job/check_client_ip_job.go
index e5e51c24..c38550b7 100644
--- a/web/job/check_client_ip_job.go
+++ b/web/job/check_client_ip_job.go
@@ -36,20 +36,15 @@ func (j *CheckClientIpJob) Run() {
}
shouldClearAccessLog := false
- f2bInstalled := j.checkFail2BanInstalled()
- isAccessLogAvailable := j.checkAccessLogAvailable(f2bInstalled)
+ iplimitActive := j.hasLimitIp()
+ f2bInstalled := j.checkFail2BanInstalled(iplimitActive)
+ isAccessLogAvailable := j.checkAccessLogAvailable(iplimitActive)
- if j.hasLimitIp() {
- if f2bInstalled && isAccessLogAvailable {
- shouldClearAccessLog = j.processLogFile()
- } else {
- if !f2bInstalled {
- logger.Warning("[iplimit] fail2ban is not installed. IP limiting may not work properly.")
- }
- }
+ if iplimitActive && f2bInstalled && isAccessLogAvailable {
+ shouldClearAccessLog = j.processLogFile()
}
- if shouldClearAccessLog || isAccessLogAvailable && time.Now().Unix()-j.lastClear > 3600 {
+ if shouldClearAccessLog || (isAccessLogAvailable && time.Now().Unix()-j.lastClear > 3600) {
j.clearAccessLog()
}
}
@@ -122,13 +117,13 @@ func (j *CheckClientIpJob) processLogFile() bool {
for scanner.Scan() {
line := scanner.Text()
- ipRegx, _ := regexp.Compile(`(\d+\.\d+\.\d+\.\d+).* accepted`)
- emailRegx, _ := regexp.Compile(`email:.+`)
+ ipRegx, _ := regexp.Compile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`)
+ emailRegx, _ := regexp.Compile(`email: (\S+)$`)
matches := ipRegx.FindStringSubmatch(line)
if len(matches) > 1 {
ip := matches[1]
- if ip == "127.0.0.1" {
+ if ip == "127.0.0.1" || ip == "::1" {
continue
}
@@ -136,7 +131,7 @@ func (j *CheckClientIpJob) processLogFile() bool {
if matchesEmail == "" {
continue
}
- matchesEmail = strings.TrimSpace(strings.Split(matchesEmail, "email: ")[1])
+ matchesEmail = strings.Split(matchesEmail, "email: ")[1]
if InboundClientIps[matchesEmail] != nil {
if j.contains(InboundClientIps[matchesEmail], ip) {
@@ -167,35 +162,33 @@ func (j *CheckClientIpJob) processLogFile() bool {
return shouldCleanLog
}
-func (j *CheckClientIpJob) checkFail2BanInstalled() bool {
+func (j *CheckClientIpJob) checkFail2BanInstalled(iplimitActive bool) bool {
cmd := "fail2ban-client"
args := []string{"-h"}
err := exec.Command(cmd, args...).Run()
- return err == nil
+
+ if iplimitActive && err != nil {
+ logger.Warning("[LimitIP] Fail2Ban is not installed, Please install Fail2Ban from the x-ui bash menu.")
+ return false
+ }
+
+ return true
}
-func (j *CheckClientIpJob) checkAccessLogAvailable(handleWarning bool) bool {
- isAvailable := true
- warningMsg := ""
+func (j *CheckClientIpJob) checkAccessLogAvailable(iplimitActive bool) bool {
accessLogPath, err := xray.GetAccessLogPath()
if err != nil {
return false
}
- // access log is not available if it is set to 'none' or an empty string
- switch accessLogPath {
- case "none":
- warningMsg = "Access log is set to 'none', check your Xray Configs"
- isAvailable = false
- case "":
- warningMsg = "Access log doesn't exist in your Xray Configs"
- isAvailable = false
+ if accessLogPath == "none" || accessLogPath == "" {
+ if iplimitActive {
+ logger.Warning("[LimitIP] Access log path is not set, Please configure the access log path in Xray configs.")
+ }
+ return false
}
- if handleWarning && warningMsg != "" {
- logger.Warning(warningMsg)
- }
- return isAvailable
+ return true
}
func (j *CheckClientIpJob) checkError(e error) {
diff --git a/web/service/config.json b/web/service/config.json
index 3f7fbc3a..122963fe 100644
--- a/web/service/config.json
+++ b/web/service/config.json
@@ -2,8 +2,9 @@
"log": {
"access": "none",
"dnsLog": false,
- "error": "./error.log",
- "loglevel": "warning"
+ "error": "",
+ "loglevel": "warning",
+ "maskAddress": ""
},
"api": {
"tag": "api",
diff --git a/web/service/inbound.go b/web/service/inbound.go
index baf7616f..5213fce6 100644
--- a/web/service/inbound.go
+++ b/web/service/inbound.go
@@ -2,7 +2,9 @@ package service
import (
"encoding/json"
+ "errors"
"fmt"
+ "regexp"
"strconv"
"strings"
"time"
@@ -329,6 +331,7 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
oldInbound.Settings = inbound.Settings
oldInbound.StreamSettings = inbound.StreamSettings
oldInbound.Sniffing = inbound.Sniffing
+ oldInbound.Allocate = inbound.Allocate
if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
oldInbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
} else {
@@ -411,6 +414,12 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) {
return false, err
}
+ email := clients[0].Email
+ valid, err := validateEmail(email)
+ if !valid {
+ return false, err
+ }
+
var settings map[string]interface{}
err = json.Unmarshal([]byte(data.Settings), &settings)
if err != nil {
@@ -534,11 +543,13 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
interfaceClients := settings["clients"].([]interface{})
var newClients []interface{}
+ needApiDel := false
for _, client := range interfaceClients {
c := client.(map[string]interface{})
c_id := c[client_key].(string)
if c_id == clientId {
- email = c["email"].(string)
+ email, _ = c["email"].(string)
+ needApiDel, _ = c["enable"].(bool)
} else {
newClients = append(newClients, client)
}
@@ -557,11 +568,6 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
oldInbound.Settings = string(newSettings)
db := database.GetDB()
- err = s.DelClientStat(db, email)
- if err != nil {
- logger.Error("Delete stats Data Error")
- return false, err
- }
err = s.DelClientIPs(db, email)
if err != nil {
@@ -569,17 +575,31 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
return false, err
}
needRestart := false
+
if len(email) > 0 {
- s.xrayApi.Init(p.GetAPIPort())
- err1 := s.xrayApi.RemoveUser(oldInbound.Tag, email)
- if err1 == nil {
- logger.Debug("Client deleted by api:", email)
- needRestart = false
- } else {
- logger.Debug("Unable to del client by api:", err1)
- needRestart = true
+ notDepleted := true
+ err = db.Model(xray.ClientTraffic{}).Select("enable").Where("email = ?", email).First(¬Depleted).Error
+ if err != nil {
+ logger.Error("Get stats error")
+ return false, err
+ }
+ err = s.DelClientStat(db, email)
+ if err != nil {
+ logger.Error("Delete stats Data Error")
+ return false, err
+ }
+ if needApiDel && notDepleted {
+ s.xrayApi.Init(p.GetAPIPort())
+ err1 := s.xrayApi.RemoveUser(oldInbound.Tag, email)
+ if err1 == nil {
+ logger.Debug("Client deleted by api:", email)
+ needRestart = false
+ } else {
+ logger.Debug("Unable to del client by api:", err1)
+ needRestart = true
+ }
+ s.xrayApi.Close()
}
- s.xrayApi.Close()
}
return needRestart, db.Save(oldInbound).Error
}
@@ -590,6 +610,12 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
return false, err
}
+ email := clients[0].Email
+ valid, err := validateEmail(email)
+ if !valid {
+ return false, err
+ }
+
var settings map[string]interface{}
err = json.Unmarshal([]byte(data.Settings), &settings)
if err != nil {
@@ -697,12 +723,14 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
needRestart := false
if len(oldEmail) > 0 {
s.xrayApi.Init(p.GetAPIPort())
- err1 := s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail)
- if err1 == nil {
- logger.Debug("Old client deleted by api:", clients[0].Email)
- } else {
- logger.Debug("Error in deleting client by api:", err1)
- needRestart = true
+ if oldClients[clientIndex].Enable {
+ err1 := s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail)
+ if err1 == nil {
+ logger.Debug("Old client deleted by api:", clients[0].Email)
+ } else {
+ logger.Debug("Error in deleting client by api:", err1)
+ needRestart = true
+ }
}
if clients[0].Enable {
cipher := ""
@@ -1994,3 +2022,20 @@ func (s *InboundService) MigrateDB() {
func (s *InboundService) GetOnlineClients() []string {
return p.GetOnlineClients()
}
+
+func validateEmail(email string) (bool, error) {
+ if strings.Contains(email, " ") {
+ return false, errors.New("email contains spaces, please remove them")
+ }
+
+ if email != strings.ToLower(email) {
+ return false, errors.New("email contains uppercase letters, please convert to lowercase")
+ }
+
+ emailPattern := `^[a-z0-9@._-]+$`
+ if !regexp.MustCompile(emailPattern).MatchString(email) {
+ return false, errors.New("email contains invalid characters, please use only lowercase letters, digits, and @._-")
+ }
+
+ return true, nil
+}
diff --git a/web/service/setting.go b/web/service/setting.go
index bddcacce..90bf8fc4 100644
--- a/web/service/setting.go
+++ b/web/service/setting.go
@@ -32,7 +32,7 @@ var defaultValueMap = map[string]string{
"webKeyFile": "",
"secret": random.Seq(32),
"webBasePath": "/",
- "sessionMaxAge": "0",
+ "sessionMaxAge": "60",
"pageSize": "50",
"expireDiff": "0",
"trafficDiff": "0",
@@ -62,7 +62,7 @@ var defaultValueMap = map[string]string{
"subJsonPath": "/json/",
"subJsonURI": "",
"subJsonFragment": "",
- "subJsonNoise": "",
+ "subJsonNoises": "",
"subJsonMux": "",
"subJsonRules": "",
"datepicker": "gregorian",
@@ -459,8 +459,8 @@ func (s *SettingService) GetSubJsonFragment() (string, error) {
return s.getString("subJsonFragment")
}
-func (s *SettingService) GetSubJsonNoise() (string, error) {
- return s.getString("subJsonNoise")
+func (s *SettingService) GetSubJsonNoises() (string, error) {
+ return s.getString("subJsonNoises")
}
func (s *SettingService) GetSubJsonMux() (string, error) {
diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml
index 290feae4..0978e84e 100644
--- a/web/translation/translate.en_US.toml
+++ b/web/translation/translate.en_US.toml
@@ -225,9 +225,6 @@
"requestHeader" = "Request Header"
"responseHeader" = "Response Header"
-[pages.inbounds.stream.quic]
-"encryption" = "Encryption"
-
[pages.settings]
"title" = "Panel Settings"
"save" = "Save"
@@ -312,14 +309,14 @@
"fragment" = "Fragmentation"
"fragmentDesc" = "Enable fragmentation for TLS hello packet."
"fragmentSett" = "Fragmentation Settings"
-"noiseDesc" = "Enable Noise."
-"noiseSett" = "Noise Settings"
+"noisesDesc" = "Enable Noises."
+"noisesSett" = "Noises Settings"
"mux" = "Mux"
"muxDesc" = "Transmit multiple independent data streams within an established data stream."
"muxSett" = "Mux Settings"
"direct" = "Direct Connection"
"directDesc" = "Directly establishes connections with domains or IP ranges of a specific country."
-"directSett" = "Direct Connection Options"
+
[pages.xray]
"title" = "Xray Configs"
@@ -425,6 +422,10 @@
"accessLogDesc" = "The file path for the access log. The special value 'none' disabled access logs"
"errorLog" = "Error Log"
"errorLogDesc" = "The file path for the error log. The special value 'none' disabled error logs"
+"dnsLog" = "DNS Log"
+"dnsLogDesc" = "Whether to enable DNS query logs"
+"maskAddress" = "Mask Address"
+"maskAddressDesc" = "IP address mask, when enabled, will automatically replace the IP address that appears in the log."
[pages.xray.rules]
"first" = "First"
diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml
index adb52271..1997bd82 100644
--- a/web/translation/translate.es_ES.toml
+++ b/web/translation/translate.es_ES.toml
@@ -225,9 +225,6 @@
"requestHeader" = "Encabezado de solicitud"
"responseHeader" = "Encabezado de respuesta"
-[pages.inbounds.stream.quic]
-"encryption" = "Cifrado"
-
[pages.settings]
"title" = "Configuraciones"
"save" = "Guardar"
@@ -312,14 +309,14 @@
"fragment" = "Fragmentación"
"fragmentDesc" = "Habilitar la fragmentación para el paquete de saludo de TLS"
"fragmentSett" = "Configuración de Fragmentación"
-"noiseDesc" = "Activar Noise."
-"noiseSett" = "Configuración de Noise"
+"noisesDesc" = "Activar Noises."
+"noisesSett" = "Configuración de Noises"
"mux" = "Mux"
"muxDesc" = "Transmite múltiples flujos de datos independientes dentro de un flujo de datos establecido."
"muxSett" = "Configuración Mux"
"direct" = "Conexión Directa"
"directDesc" = "Establece conexiones directas con dominios o rangos de IP de un país específico."
-"directSett" = "Opciones de Conexión Directa"
+
[pages.xray]
"title" = "Xray Configuración"
@@ -425,6 +422,10 @@
"accessLogDesc" = "La ruta del archivo para el registro de acceso. El valor especial 'ninguno' deshabilita los registros de acceso"
"errorLog" = "Registro de Errores"
"errorLogDesc" = "La ruta del archivo para el registro de errores. El valor especial 'none' desactiva los registros de errores."
+"dnsLog" = "Registro DNS"
+"dnsLogDesc" = "Si habilitar los registros de consulta DNS"
+"maskAddress" = "Enmascarar Dirección"
+"maskAddressDesc" = "Máscara de dirección IP, cuando se habilita, reemplazará automáticamente la dirección IP que aparece en el registro."
[pages.xray.rules]
"first" = "Primero"
diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml
index 1258ba41..dac201f2 100644
--- a/web/translation/translate.fa_IR.toml
+++ b/web/translation/translate.fa_IR.toml
@@ -225,9 +225,6 @@
"requestHeader" = "سربرگ درخواست"
"responseHeader" = "سربرگ پاسخ"
-[pages.inbounds.stream.quic]
-"encryption" = "رمزنگاری"
-
[pages.settings]
"title" = "تنظیمات پنل"
"save" = "ذخیره"
@@ -312,14 +309,14 @@
"fragment" = "فرگمنت"
"fragmentDesc" = "فعال کردن فرگمنت برای بستهی نخست تیالاس"
"fragmentSett" = "تنظیمات فرگمنت"
-"noiseDesc" = "فعال کردن Noise."
-"noiseSett" = "تنظیمات Noise"
+"noisesDesc" = "فعال کردن Noises."
+"noisesSett" = "تنظیمات Noises"
"mux" = "ماکس"
"muxDesc" = "چندین جریان داده مستقل را در یک جریان داده ثابت منتقل می کند"
"muxSett" = "تنظیمات ماکس"
"direct" = "اتصال مستقیم"
"directDesc" = "به طور مستقیم با دامنه ها یا محدوده آیپی یک کشور خاص ارتباط برقرار می کند"
-"directSett" = "گزینه های اتصال مستقیم"
+
[pages.xray]
"title" = "پیکربندی ایکسری"
@@ -425,6 +422,10 @@
"accessLogDesc" = "مسیر فایل برای گزارش دسترسی. مقدار ویژه «هیچ» گزارشهای دسترسی را غیرفعال میکند."
"errorLog" = "گزارش خطا"
"errorLogDesc" = "مسیر فایل برای ورود به سیستم خطا. مقدار ویژه «هیچ» گزارش های خطا را غیرفعال میکند"
+"dnsLog" = "گزارش DNS"
+"dnsLogDesc" = "آیا ثبتهای درخواست DNS را فعال کنید"
+"maskAddress" = "پنهان کردن آدرس"
+"maskAddressDesc" = "پوشش آدرس IP، هنگامی که فعال میشود، به طور خودکار آدرس IP که در لاگ ظاهر میشود را جایگزین میکند."
[pages.xray.rules]
"first" = "اولین"
diff --git a/web/translation/translate.id_ID.toml b/web/translation/translate.id_ID.toml
index f88610c1..565b10a7 100644
--- a/web/translation/translate.id_ID.toml
+++ b/web/translation/translate.id_ID.toml
@@ -225,9 +225,6 @@
"requestHeader" = "Header Permintaan"
"responseHeader" = "Header Respons"
-[pages.inbounds.stream.quic]
-"encryption" = "Enkripsi"
-
[pages.settings]
"title" = "Pengaturan Panel"
"save" = "Simpan"
@@ -312,14 +309,14 @@
"fragment" = "Fragmentasi"
"fragmentDesc" = "Aktifkan fragmentasi untuk paket hello TLS"
"fragmentSett" = "Pengaturan Fragmentasi"
-"noiseDesc" = "Aktifkan Noise."
-"noiseSett" = "Pengaturan Noise"
+"noisesDesc" = "Aktifkan Noises."
+"noisesSett" = "Pengaturan Noises"
"mux" = "Mux"
"muxDesc" = "Mengirimkan beberapa aliran data independen dalam aliran data yang sudah ada."
"muxSett" = "Pengaturan Mux"
"direct" = "Koneksi langsung"
"directDesc" = "Secara langsung membuat koneksi dengan domain atau rentang IP negara tertentu."
-"directSett" = "Opsi Koneksi Langsung"
+
[pages.xray]
"title" = "Konfigurasi Xray"
@@ -425,6 +422,10 @@
"accessLogDesc" = "Jalur file untuk log akses. Nilai khusus 'tidak ada' menonaktifkan log akses"
"errorLog" = "Catatan eror"
"errorLogDesc" = "Jalur file untuk log kesalahan. Nilai khusus 'tidak ada' menonaktifkan log kesalahan"
+"dnsLog" = "Log DNS"
+"dnsLogDesc" = "Apakah akan mengaktifkan log kueri DNS"
+"maskAddress" = "Alamat Masker"
+"maskAddressDesc" = "Masker alamat IP, ketika diaktifkan, akan secara otomatis mengganti alamat IP yang muncul di log."
[pages.xray.rules]
"first" = "Pertama"
diff --git a/web/translation/translate.pt_BR.toml b/web/translation/translate.pt_BR.toml
new file mode 100644
index 00000000..106bc9b9
--- /dev/null
+++ b/web/translation/translate.pt_BR.toml
@@ -0,0 +1,647 @@
+"username" = "Nome de Usuário"
+"password" = "Senha"
+"login" = "Entrar"
+"confirm" = "Confirmar"
+"cancel" = "Cancelar"
+"close" = "Fechar"
+"copy" = "Copiar"
+"copied" = "Copiado"
+"download" = "Baixar"
+"remark" = "Observação"
+"enable" = "Ativado"
+"protocol" = "Protocolo"
+"search" = "Pesquisar"
+"filter" = "Filtrar"
+"loading" = "Carregando..."
+"second" = "Segundo"
+"minute" = "Minuto"
+"hour" = "Hora"
+"day" = "Dia"
+"check" = "Verificar"
+"indefinite" = "Indeterminado"
+"unlimited" = "Ilimitado"
+"none" = "Nada"
+"qrCode" = "Código QR"
+"info" = "Mais Informações"
+"edit" = "Editar"
+"delete" = "Excluir"
+"reset" = "Redefinir"
+"copySuccess" = "Copiado com Sucesso"
+"sure" = "Certo"
+"encryption" = "Criptografia"
+"transmission" = "Transmissão"
+"host" = "Servidor"
+"path" = "Caminho"
+"camouflage" = "Ofuscação"
+"status" = "Status"
+"enabled" = "Ativado"
+"disabled" = "Desativado"
+"depleted" = "Encerrado"
+"depletingSoon" = "Esgotando"
+"offline" = "Offline"
+"online" = "Online"
+"domainName" = "Nome de Domínio"
+"monitor" = "IP de Escuta"
+"certificate" = "Certificado Digital"
+"fail" = " Falhou"
+"success" = " Com Sucesso"
+"getVersion" = "Obter Versão"
+"install" = "Instalar"
+"clients" = "Clientes"
+"usage" = "Uso"
+"secretToken" = "Token Secreto"
+"remained" = "Restante"
+"security" = "Segurança"
+"secAlertTitle" = "Alerta de Segurança"
+"secAlertSsl" = "Esta conexão não é segura. Evite inserir informações confidenciais até que o TLS seja ativado para proteção de dados."
+"secAlertConf" = "Algumas configurações estão vulneráveis a ataques. Recomenda-se reforçar os protocolos de segurança para evitar possíveis violações."
+"secAlertSSL" = "O painel não possui uma conexão segura. Instale o certificado TLS para proteção de dados."
+"secAlertPanelPort" = "A porta padrão do painel é vulnerável. Configure uma porta aleatória ou específica."
+"secAlertPanelURI" = "O caminho URI padrão do painel não é seguro. Configure um caminho URI complexo."
+"secAlertSubURI" = "O caminho URI padrão de inscrição não é seguro. Configure um caminho URI complexo."
+"secAlertSubJsonURI" = "O caminho URI JSON de inscrição padrão não é seguro. Configure um caminho URI complexo."
+
+[menu]
+"dashboard" = "Visão Geral"
+"inbounds" = "Inbounds"
+"settings" = "Panel Settings"
+"xray" = "Xray Configs"
+"logout" = "Sair"
+"link" = "Gerenciar"
+
+[pages.login]
+"hello" = "Olá"
+"title" = "Bem-vindo"
+"loginAgain" = "Sua sessão expirou, faça login novamente"
+
+[pages.login.toasts]
+"invalidFormData" = "O formato dos dados de entrada é inválido."
+"emptyUsername" = "Nome de usuário é obrigatório"
+"emptyPassword" = "Senha é obrigatória"
+"wrongUsernameOrPassword" = "Nome de usuário, senha ou segredo inválidos."
+"successLogin" = "Login realizado com sucesso"
+
+[pages.index]
+"title" = "Visão Geral"
+"memory" = "Memória RAM"
+"hard" = "Disco"
+"xrayStatus" = "Xray"
+"stopXray" = "Parar"
+"restartXray" = "Reiniciar"
+"xraySwitch" = "Versão"
+"xraySwitchClick" = "Escolha a versão para a qual deseja alternar."
+"xraySwitchClickDesk" = "Escolha com cuidado, pois versões mais antigas podem não ser compatíveis com as configurações atuais."
+"operationHours" = "Tempo de Atividade"
+"systemLoad" = "Carga do Sistema"
+"systemLoadDesc" = "Média de carga do sistema nos últimos 1, 5 e 15 minutos"
+"connectionTcpCountDesc" = "Total de conexões TCP no sistema"
+"connectionUdpCountDesc" = "Total de conexões UDP no sistema"
+"connectionCount" = "Estatísticas de Conexão"
+"upSpeed" = "Velocidade total de upload no sistema"
+"downSpeed" = "Velocidade total de download no sistema"
+"totalSent" = "Dados totais enviados desde a inicialização do sistema"
+"totalReceive" = "Dados totais recebidos desde a inicialização do sistema"
+"xraySwitchVersionDialog" = "Alterar Versão do Xray"
+"xraySwitchVersionDialogDesc" = "Tem certeza de que deseja alterar a versão do Xray para"
+"dontRefresh" = "Instalação em andamento, por favor não atualize a página"
+"logs" = "Logs"
+"config" = "Configuração"
+"backup" = "Backup e Restauração"
+"backupTitle" = "Backup e Restauração do Banco de Dados"
+"backupDescription" = "É recomendado fazer um backup antes de restaurar o banco de dados."
+"exportDatabase" = "Fazer Backup"
+"importDatabase" = "Restaurar"
+
+[pages.inbounds]
+"title" = "Inbounds"
+"totalDownUp" = "Total Enviado/Recebido"
+"totalUsage" = "Uso Total"
+"inboundCount" = "Total de Inbounds"
+"operate" = "Menu"
+"enable" = "Ativado"
+"remark" = "Observação"
+"protocol" = "Protocolo"
+"port" = "Porta"
+"traffic" = "Tráfego"
+"details" = "Detalhes"
+"transportConfig" = "Transporte"
+"expireDate" = "Duração"
+"resetTraffic" = "Redefinir Tráfego"
+"addInbound" = "Adicionar Inbound"
+"generalActions" = "Ações Gerais"
+"create" = "Criar"
+"update" = "Atualizar"
+"modifyInbound" = "Modificar Inbound"
+"deleteInbound" = "Excluir Inbound"
+"deleteInboundContent" = "Tem certeza de que deseja excluir o inbound?"
+"deleteClient" = "Excluir Cliente"
+"deleteClientContent" = "Tem certeza de que deseja excluir o cliente?"
+"resetTrafficContent" = "Tem certeza de que deseja redefinir o tráfego?"
+"copyLink" = "Copiar URL"
+"address" = "Endereço"
+"network" = "Rede"
+"destinationPort" = "Porta de Destino"
+"targetAddress" = "Endereço de Destino"
+"monitorDesc" = "Deixe em branco para ouvir todos os IPs"
+"meansNoLimit" = " = Ilimitado. (unidade: GB)"
+"totalFlow" = "Fluxo Total"
+"leaveBlankToNeverExpire" = "Deixe em branco para nunca expirar"
+"noRecommendKeepDefault" = "Recomenda-se manter o padrão"
+"certificatePath" = "Caminho"
+"certificateContent" = "Conteúdo"
+"publicKey" = "Chave Pública"
+"privatekey" = "Chave Privada"
+"clickOnQRcode" = "Clique no Código QR para Copiar"
+"client" = "Cliente"
+"export" = "Exportar Todos os URLs"
+"clone" = "Clonar"
+"cloneInbound" = "Clonar"
+"cloneInboundContent" = "Todas as configurações deste inbound, exceto Porta, IP de Escuta e Clientes, serão aplicadas ao clone."
+"cloneInboundOk" = "Clonar"
+"resetAllTraffic" = "Redefinir Tráfego de Todos os Inbounds"
+"resetAllTrafficTitle" = "Redefinir Tráfego de Todos os Inbounds"
+"resetAllTrafficContent" = "Tem certeza de que deseja redefinir o tráfego de todos os inbounds?"
+"resetInboundClientTraffics" = "Redefinir Tráfego dos Clientes"
+"resetInboundClientTrafficTitle" = "Redefinir Tráfego dos Clientes"
+"resetInboundClientTrafficContent" = "Tem certeza de que deseja redefinir o tráfego dos clientes deste inbound?"
+"resetAllClientTraffics" = "Redefinir Tráfego de Todos os Clientes"
+"resetAllClientTrafficTitle" = "Redefinir Tráfego de Todos os Clientes"
+"resetAllClientTrafficContent" = "Tem certeza de que deseja redefinir o tráfego de todos os clientes?"
+"delDepletedClients" = "Excluir Clientes Esgotados"
+"delDepletedClientsTitle" = "Excluir Clientes Esgotados"
+"delDepletedClientsContent" = "Tem certeza de que deseja excluir todos os clientes esgotados?"
+"email" = "Email"
+"emailDesc" = "Por favor, forneça um endereço de e-mail único."
+"IPLimit" = "Limite de IP"
+"IPLimitDesc" = "Desativa o inbound se o número ultrapassar o valor definido. (0 = desativar)"
+"IPLimitlog" = "Log de IP"
+"IPLimitlogDesc" = "O histórico de IPs. (para ativar o inbound após a desativação, limpe o log)"
+"IPLimitlogclear" = "Limpar o Log"
+"setDefaultCert" = "Definir Certificado pelo Painel"
+"xtlsDesc" = "O Xray deve ser v1.7.5"
+"realityDesc" = "O Xray deve ser v1.8.0+"
+"telegramDesc" = "Por favor, forneça o ID do Chat do Telegram. (use o comando '/id' no bot) ou (@userinfobot)"
+"subscriptionDesc" = "Para encontrar seu URL de assinatura, navegue até 'Detalhes'. Além disso, você pode usar o mesmo nome para vários clientes."
+"info" = "Informações"
+"same" = "Igual"
+"inboundData" = "Dados do Inbound"
+"exportInbound" = "Exportar Inbound"
+"import" = "Importar"
+"importInbound" = "Importar um Inbound"
+
+[pages.client]
+"add" = "Adicionar Cliente"
+"edit" = "Editar Cliente"
+"submitAdd" = "Adicionar Cliente"
+"submitEdit" = "Salvar Alterações"
+"clientCount" = "Número de Clientes"
+"bulk" = "Adicionar Vários"
+"method" = "Método"
+"first" = "Primeiro"
+"last" = "Último"
+"prefix" = "Prefixo"
+"postfix" = "Sufixo"
+"delayedStart" = "Iniciar Após Primeiro Uso"
+"expireDays" = "Duração"
+"days" = "Dia(s)"
+"renew" = "Renovação Automática"
+"renewDesc" = "Renovação automática após expiração. (0 = desativado)(unidade: dia)"
+
+[pages.inbounds.toasts]
+"obtain" = "Obter"
+
+[pages.inbounds.stream.general]
+"request" = "Requisição"
+"response" = "Resposta"
+"name" = "Nome"
+"value" = "Valor"
+
+[pages.inbounds.stream.tcp]
+"version" = "Versão"
+"method" = "Método"
+"path" = "Caminho"
+"status" = "Status"
+"statusDescription" = "Descrição do Status"
+"requestHeader" = "Cabeçalho da Requisição"
+"responseHeader" = "Cabeçalho da Resposta"
+
+[pages.settings]
+"title" = "Configurações do Painel"
+"save" = "Salvar"
+"infoDesc" = "Toda alteração feita aqui precisa ser salva. Reinicie o painel para aplicar as alterações."
+"restartPanel" = "Reiniciar Painel"
+"restartPanelDesc" = "Tem certeza de que deseja reiniciar o painel? Se não conseguir acessar o painel após reiniciar, consulte os logs do painel no servidor."
+"actions" = "Ações"
+"resetDefaultConfig" = "Redefinir para Padrão"
+"panelSettings" = "Geral"
+"securitySettings" = "Autenticação"
+"TGBotSettings" = "Bot do Telegram"
+"panelListeningIP" = "IP de Escuta"
+"panelListeningIPDesc" = "O endereço IP para o painel web. (deixe em branco para escutar em todos os IPs)"
+"panelListeningDomain" = "Domínio de Escuta"
+"panelListeningDomainDesc" = "O nome de domínio para o painel web. (deixe em branco para escutar em todos os domínios e IPs)"
+"panelPort" = "Porta de Escuta"
+"panelPortDesc" = "O número da porta para o painel web. (deve ser uma porta não usada)"
+"publicKeyPath" = "Caminho da Chave Pública"
+"publicKeyPathDesc" = "O caminho do arquivo de chave pública para o painel web. (começa com ‘/‘)"
+"privateKeyPath" = "Caminho da Chave Privada"
+"privateKeyPathDesc" = "O caminho do arquivo de chave privada para o painel web. (começa com ‘/‘)"
+"panelUrlPath" = "Caminho URI"
+"panelUrlPathDesc" = "O caminho URI para o painel web. (começa com ‘/‘ e termina com ‘/‘)"
+"pageSize" = "Tamanho da Paginação"
+"pageSizeDesc" = "Definir o tamanho da página para a tabela de entradas. (0 = desativado)"
+"remarkModel" = "Modelo de Observação & Caractere de Separação"
+"datepicker" = "Tipo de Calendário"
+"datepickerPlaceholder" = "Selecionar data"
+"datepickerDescription" = "Tarefas agendadas serão executadas com base neste calendário."
+"sampleRemark" = "Exemplo de Observação"
+"oldUsername" = "Nome de Usuário Atual"
+"currentPassword" = "Senha Atual"
+"newUsername" = "Novo Nome de Usuário"
+"newPassword" = "Nova Senha"
+"telegramBotEnable" = "Ativar Bot do Telegram"
+"telegramBotEnableDesc" = "Ativa o bot do Telegram."
+"telegramToken" = "Token do Telegram"
+"telegramTokenDesc" = "O token do bot do Telegram obtido de '@BotFather'."
+"telegramProxy" = "Proxy SOCKS"
+"telegramProxyDesc" = "Ativa o proxy SOCKS5 para conectar ao Telegram. (ajuste as configurações conforme o guia)"
+"telegramChatId" = "ID de Chat do Administrador"
+"telegramChatIdDesc" = "O(s) ID(s) de Chat do Administrador no Telegram. (separado por vírgulas)(obtenha aqui @userinfobot) ou (use o comando '/id' no bot)"
+"telegramNotifyTime" = "Hora da Notificação"
+"telegramNotifyTimeDesc" = "O horário de notificação do bot do Telegram configurado para relatórios periódicos. (use o formato de tempo do crontab)"
+"tgNotifyBackup" = "Backup do Banco de Dados"
+"tgNotifyBackupDesc" = "Enviar arquivo de backup do banco de dados junto com o relatório."
+"tgNotifyLogin" = "Notificação de Login"
+"tgNotifyLoginDesc" = "Receba notificações sobre o nome de usuário, endereço IP e horário sempre que alguém tentar fazer login no seu painel web."
+"sessionMaxAge" = "Duração da Sessão"
+"sessionMaxAgeDesc" = "A duração pela qual você pode permanecer logado. (unidade: minuto)"
+"expireTimeDiff" = "Notificação de Expiração"
+"expireTimeDiffDesc" = "Receba notificações sobre a data de expiração ao atingir esse limite. (unidade: dia)"
+"trafficDiff" = "Notificação de Limite de Tráfego"
+"trafficDiffDesc" = "Receba notificações sobre o limite de tráfego ao atingir esse limite. (unidade: GB)"
+"tgNotifyCpu" = "Notificação de Carga da CPU"
+"tgNotifyCpuDesc" = "Receba notificações se a carga da CPU ultrapassar esse limite. (unidade: %)"
+"timeZone" = "Fuso Horário"
+"timeZoneDesc" = "As tarefas agendadas serão executadas com base nesse fuso horário."
+"subSettings" = "Assinatura"
+"subEnable" = "Ativar Serviço de Assinatura"
+"subEnableDesc" = "Ativa o serviço de assinatura."
+"subListen" = "IP de Escuta"
+"subListenDesc" = "O endereço IP para o serviço de assinatura. (deixe em branco para escutar em todos os IPs)"
+"subPort" = "Porta de Escuta"
+"subPortDesc" = "O número da porta para o serviço de assinatura. (deve ser uma porta não usada)"
+"subCertPath" = "Caminho da Chave Pública"
+"subCertPathDesc" = "O caminho do arquivo de chave pública para o serviço de assinatura. (começa com ‘/‘)"
+"subKeyPath" = "Caminho da Chave Privada"
+"subKeyPathDesc" = "O caminho do arquivo de chave privada para o serviço de assinatura. (começa com ‘/‘)"
+"subPath" = "Caminho URI"
+"subPathDesc" = "O caminho URI para o serviço de assinatura. (começa com ‘/‘ e termina com ‘/‘)"
+"subDomain" = "Domínio de Escuta"
+"subDomainDesc" = "O nome de domínio para o serviço de assinatura. (deixe em branco para escutar em todos os domínios e IPs)"
+"subUpdates" = "Intervalos de Atualização"
+"subUpdatesDesc" = "Os intervalos de atualização da URL de assinatura nos aplicativos de cliente. (unidade: hora)"
+"subEncrypt" = "Codificar"
+"subEncryptDesc" = "O conteúdo retornado pelo serviço de assinatura será codificado em Base64."
+"subShowInfo" = "Mostrar Informações de Uso"
+"subShowInfoDesc" = "O tráfego restante e a data serão exibidos nos aplicativos de cliente."
+"subURI" = "URI de Proxy Reverso"
+"subURIDesc" = "O caminho URI da URL de assinatura para uso por trás de proxies."
+"fragment" = "Fragmentação"
+"fragmentDesc" = "Ativa a fragmentação para o pacote TLS hello."
+"fragmentSett" = "Configurações de Fragmentação"
+"noisesDesc" = "Ativar Noises."
+"noisesSett" = "Configurações de Noises"
+"mux" = "Mux"
+"muxDesc" = "Transmitir múltiplos fluxos de dados independentes dentro de um fluxo de dados estabelecido."
+"muxSett" = "Configurações de Mux"
+"direct" = "Conexão Direta"
+"directDesc" = "Estabelece conexões diretamente com domínios ou intervalos de IP de um país específico."
+
+
+[pages.xray]
+"title" = "Configurações Xray"
+"save" = "Salvar"
+"restart" = "Reiniciar Xray"
+"basicTemplate" = "Básico"
+"advancedTemplate" = "Avançado"
+"generalConfigs" = "Geral"
+"generalConfigsDesc" = "Essas opções determinam ajustes gerais."
+"logConfigs" = "Log"
+"logConfigsDesc" = "Os logs podem afetar a eficiência do servidor. É recomendável habilitá-los com sabedoria apenas se necessário."
+"blockConfigs" = "Escudo de Proteção"
+"blockConfigsDesc" = "Essas opções bloqueiam tráfego com base em protocolos e sites específicos solicitados."
+"blockCountryConfigs" = "Bloquear País"
+"blockCountryConfigsDesc" = "Essas opções bloqueiam tráfego com base no país solicitado."
+"directCountryConfigs" = "País Direto"
+"directCountryConfigsDesc" = "Uma conexão direta garante que o tráfego específico não seja roteado por outro servidor."
+"ipv4Configs" = "Roteamento IPv4"
+"ipv4ConfigsDesc" = "Essas opções roteam o tráfego para um destino específico via IPv4."
+"warpConfigs" = "Roteamento WARP"
+"warpConfigsDesc" = "Essas opções roteam o tráfego para um destino específico via WARP."
+"Template" = "Modelo de Configuração Avançada do Xray"
+"TemplateDesc" = "O arquivo final de configuração do Xray será gerado com base neste modelo."
+"FreedomStrategy" = "Estratégia do Protocolo Freedom"
+"FreedomStrategyDesc" = "Definir a estratégia de saída para a rede no Protocolo Freedom."
+"RoutingStrategy" = "Estratégia Geral de Roteamento"
+"RoutingStrategyDesc" = "Definir a estratégia geral de roteamento de tráfego para resolver todas as solicitações."
+"Torrent" = "Bloquear Protocolo BitTorrent"
+"TorrentDesc" = "Bloqueia o protocolo BitTorrent."
+"PrivateIp" = "Bloquear Conexão para IPs Privados"
+"PrivateIpDesc" = "Bloqueia a conexão com faixas de IP privadas."
+"Ads" = "Bloquear Anúncios"
+"AdsDesc" = "Bloqueia sites de publicidade."
+"Family" = "Proteção Familiar"
+"FamilyDesc" = "Bloqueia conteúdo adulto e sites maliciosos."
+"Security" = "Escudo de Segurança"
+"SecurityDesc" = "Bloqueia sites de malware, phishing e mineradores de criptomoedas."
+"Speedtest" = "Bloquear Speedtest"
+"SpeedtestDesc" = "Bloqueia a conexão com sites de teste de velocidade."
+"IRIp" = "Bloquear Conexão para IPs do Irã"
+"IRIpDesc" = "Bloqueia a conexão com faixas de IP do Irã."
+"IRDomain" = "Bloquear Conexão para Domínios do Irã"
+"IRDomainDesc" = "Bloqueia a conexão com domínios do Irã."
+"ChinaIp" = "Bloquear Conexão para IPs da China"
+"ChinaIpDesc" = "Bloqueia a conexão com faixas de IP da China."
+"ChinaDomain" = "Bloquear Conexão para Domínios da China"
+"ChinaDomainDesc" = "Bloqueia a conexão com domínios da China."
+"RussiaIp" = "Bloquear Conexão para IPs da Rússia"
+"RussiaIpDesc" = "Bloqueia a conexão com faixas de IP da Rússia."
+"RussiaDomain" = "Bloquear Conexão para Domínios da Rússia"
+"RussiaDomainDesc" = "Bloqueia a conexão com domínios da Rússia."
+"VNIp" = "Bloquear Conexão para IPs do Vietnã"
+"VNIpDesc" = "Bloqueia a conexão com faixas de IP do Vietnã."
+"VNDomain" = "Bloquear Conexão para Domínios do Vietnã"
+"VNDomainDesc" = "Bloqueia a conexão com domínios do Vietnã."
+"DirectIRIp" = "Conexão Direta para IPs do Irã"
+"DirectIRIpDesc" = "Estabelece conexão diretamente com faixas de IP do Irã."
+"DirectIRDomain" = "Conexão Direta para Domínios do Irã"
+"DirectIRDomainDesc" = "Estabelece conexão diretamente com domínios do Irã."
+"DirectChinaIp" = "Conexão Direta para IPs da China"
+"DirectChinaIpDesc" = "Estabelece conexão diretamente com faixas de IP da China."
+"DirectChinaDomain" = "Conexão Direta para Domínios da China"
+"DirectChinaDomainDesc" = "Estabelece conexão diretamente com domínios da China."
+"DirectRussiaIp" = "Conexão Direta para IPs da Rússia"
+"DirectRussiaIpDesc" = "Estabelece conexão diretamente com faixas de IP da Rússia."
+"DirectRussiaDomain" = "Conexão Direta para Domínios da Rússia"
+"DirectRussiaDomainDesc" = "Estabelece conexão diretamente com domínios da Rússia."
+"DirectVNIp" = "Conexão Direta para IPs do Vietnã"
+"DirectVNIpDesc" = "Estabelece conexão diretamente com faixas de IP do Vietnã."
+"DirectVNDomain" = "Conexão Direta para Domínios do Vietnã"
+"DirectVNDomainDesc" = "Estabelece conexão diretamente com domínios do Vietnã."
+"GoogleIPv4" = "Google"
+"GoogleIPv4Desc" = "Roteia tráfego para o Google via IPv4."
+"NetflixIPv4" = "Netflix"
+"NetflixIPv4Desc" = "Roteia tráfego para a Netflix via IPv4."
+"GoogleWARP" = "Google"
+"GoogleWARPDesc" = "Adiciona roteamento para o Google via WARP."
+"OpenAIWARP" = "ChatGPT"
+"OpenAIWARPDesc" = "Roteia tráfego para o ChatGPT via WARP."
+"NetflixWARP" = "Netflix"
+"NetflixWARPDesc" = "Roteia tráfego para a Netflix via WARP."
+"MetaWARP" = "Meta"
+"MetaWARPDesc" = "Roteia tráfego para Meta (Instagram, Facebook, WhatsApp, Threads,...) via WARP."
+"AppleWARP" = "Apple"
+"AppleWARPDesc" = "Roteia tráfego para a Apple via WARP."
+"RedditWARP" = "Reddit"
+"RedditWARPDesc" = "Roteia tráfego para o Reddit via WARP."
+"SpotifyWARP" = "Spotify"
+"SpotifyWARPDesc" = "Roteia tráfego para o Spotify via WARP."
+"IRWARP" = "Domínios do Irã"
+"IRWARPDesc" = "Roteia tráfego para domínios do Irã via WARP."
+"Inbounds" = "Inbounds"
+"InboundsDesc" = "Aceitar clientes específicos."
+"Outbounds" = "Outbounds"
+"Balancers" = "Balanceadores"
+"OutboundsDesc" = "Definir o caminho de saída do tráfego."
+"Routings" = "Regras de Roteamento"
+"RoutingsDesc" = "A prioridade de cada regra é importante!"
+"completeTemplate" = "Todos"
+"logLevel" = "Nível de Log"
+"logLevelDesc" = "O nível de log para erros, indicando a informação que precisa ser registrada."
+"accessLog" = "Log de Acesso"
+"accessLogDesc" = "O caminho do arquivo para o log de acesso. O valor especial 'none' desativa os logs de acesso."
+"errorLog" = "Log de Erros"
+"errorLogDesc" = "O caminho do arquivo para o log de erros. O valor especial 'none' desativa os logs de erro."
+"dnsLog" = "Log DNS"
+"dnsLogDesc" = "Se ativar logs de consulta DNS"
+"maskAddress" = "Mascarar Endereço"
+"maskAddressDesc" = "Máscara de endereço IP, quando ativado, substitui automaticamente o endereço IP que aparece no log."
+
+[pages.xray.rules]
+"first" = "Primeiro"
+"last" = "Último"
+"up" = "Cima"
+"down" = "Baixo"
+"source" = "Fonte"
+"dest" = "Destino"
+"inbound" = "Entrada"
+"outbound" = "Saída"
+"balancer" = "Balanceador"
+"info" = "Info"
+"add" = "Adicionar Regra"
+"edit" = "Editar Regra"
+"useComma" = "Itens separados por vírgula"
+
+[pages.xray.outbound]
+"addOutbound" = "Adicionar Saída"
+"addReverse" = "Adicionar Reverso"
+"editOutbound" = "Editar Saída"
+"editReverse" = "Editar Reverso"
+"tag" = "Tag"
+"tagDesc" = "Tag Única"
+"address" = "Endereço"
+"reverse" = "Reverso"
+"domain" = "Domínio"
+"type" = "Tipo"
+"bridge" = "Ponte"
+"portal" = "Portal"
+"intercon" = "Interconexão"
+"settings" = "Configurações"
+"accountInfo" = "Informações da Conta"
+"outboundStatus" = "Status de Saída"
+"sendThrough" = "Enviar Através de"
+
+[pages.xray.balancer]
+"addBalancer" = "Adicionar Balanceador"
+"editBalancer" = "Editar Balanceador"
+"balancerStrategy" = "Estratégia"
+"balancerSelectors" = "Seletores"
+"tag" = "Tag"
+"tagDesc" = "Tag Única"
+"balancerDesc" = "Não é possível usar balancerTag e outboundTag ao mesmo tempo. Se usados simultaneamente, apenas outboundTag funcionará."
+
+[pages.xray.wireguard]
+"secretKey" = "Chave Secreta"
+"publicKey" = "Chave Pública"
+"allowedIPs" = "IPs Permitidos"
+"endpoint" = "Ponto Final"
+"psk" = "Chave Pré-Compartilhada"
+"domainStrategy" = "Estratégia de Domínio"
+
+[pages.xray.dns]
+"enable" = "Ativar DNS"
+"enableDesc" = "Ativar o servidor DNS integrado"
+"tag" = "Tag de Entrada DNS"
+"tagDesc" = "Esta tag estará disponível como uma tag de Entrada nas regras de roteamento."
+"strategy" = "Estratégia de Consulta"
+"strategyDesc" = "Estratégia geral para resolver nomes de domínio"
+"add" = "Adicionar Servidor"
+"edit" = "Editar Servidor"
+"domains" = "Domínios"
+
+[pages.xray.fakedns]
+"add" = "Adicionar Fake DNS"
+"edit" = "Editar Fake DNS"
+"ipPool" = "Sub-rede do Pool de IP"
+"poolSize" = "Tamanho do Pool"
+
+[pages.settings.security]
+"admin" = "Admin"
+"secret" = "Token Secreto"
+"loginSecurity" = "Login Seguro"
+"loginSecurityDesc" = "Adiciona uma camada extra de autenticação para fornecer mais segurança."
+"secretToken" = "Token Secreto"
+"secretTokenDesc" = "Por favor, armazene este token em um local seguro. Este token é necessário para o login e não pode ser recuperado."
+
+[pages.settings.toasts]
+"modifySettings" = "Modificar Configurações"
+"getSettings" = "Obter Configurações"
+"modifyUser" = "Modificar Admin"
+"originalUserPassIncorrect" = "O nome de usuário ou senha atual é inválido"
+"userPassMustBeNotEmpty" = "O novo nome de usuário e senha não podem estar vazios"
+
+[tgbot]
+"keyboardClosed" = "❌ Teclado personalizado fechado!"
+"noResult" = "❗ Nenhum resultado!"
+"noQuery" = "❌ Consulta não encontrada! Por favor, use o comando novamente!"
+"wentWrong" = "❌ Algo deu errado!"
+"noIpRecord" = "❗ Nenhum registro de IP!"
+"noInbounds" = "❗ Nenhuma entrada encontrada!"
+"unlimited" = "♾ Ilimitado (Reiniciar)"
+"add" = "Adicionar"
+"month" = "Mês"
+"months" = "Meses"
+"day" = "Dia"
+"days" = "Dias"
+"hours" = "Horas"
+"unknown" = "Desconhecido"
+"inbounds" = "Entradas"
+"clients" = "Clientes"
+"offline" = "🔴 Offline"
+"online" = "🟢 Online"
+
+[tgbot.commands]
+"unknown" = "❗ Comando desconhecido."
+"pleaseChoose" = "👇 Escolha:\r\n"
+"help" = "🤖 Bem-vindo a este bot! Ele foi projetado para oferecer dados específicos do painel da web e permite que você faça as modificações necessárias.\r\n\r\n"
+"start" = "👋 Olá {{ .Firstname }}.\r\n"
+"welcome" = "🤖 Bem-vindo ao bot de gerenciamento do {{ .Hostname }}.\r\n"
+"status" = "✅ Bot está OK!"
+"usage" = "❗ Por favor, forneça um texto para pesquisar!"
+"getID" = "🆔 Seu ID: {{ .ID }} "
+"helpAdminCommands" = "Para pesquisar por um email de cliente:\r\n/usage [Email] \r\n\r\nPara pesquisar por inbounds (com estatísticas do cliente):\r\n/inbound [Remark] \r\n\r\nTelegram Chat ID:\r\n/id "
+"helpClientCommands" = "Para pesquisar por estatísticas, use o seguinte comando:\r\n\r\n/usage [Email] \r\n\r\nTelegram Chat ID:\r\n/id "
+
+[tgbot.messages]
+"cpuThreshold" = "🔴 A carga da CPU {{ .Percent }}% excede o limite de {{ .Threshold }}%"
+"selectUserFailed" = "❌ Erro na seleção do usuário!"
+"userSaved" = "✅ Usuário do Telegram salvo."
+"loginSuccess" = "✅ Conectado ao painel com sucesso.\r\n"
+"loginFailed" = "❗️Tentativa de login no painel falhou.\r\n"
+"report" = "🕰 Relatórios agendados: {{ .RunTime }}\r\n"
+"datetime" = "⏰ Data&Hora: {{ .DateTime }}\r\n"
+"hostname" = "💻 Host: {{ .Hostname }}\r\n"
+"version" = "🚀 Versão 3X-UI: {{ .Version }}\r\n"
+"xrayVersion" = "📡 Versão Xray: {{ .XrayVersion }}\r\n"
+"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
+"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
+"ip" = "🌐 IP: {{ .IP }}\r\n"
+"ips" = "🔢 IPs:\r\n{{ .IPs }}\r\n"
+"serverUpTime" = "⏳ Tempo de atividade: {{ .UpTime }} {{ .Unit }}\r\n"
+"serverLoad" = "📈 Carga do sistema: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
+"serverMemory" = "📋 RAM: {{ .Current }}/{{ .Total }}\r\n"
+"tcpCount" = "🔹 TCP: {{ .Count }}\r\n"
+"udpCount" = "🔸 UDP: {{ .Count }}\r\n"
+"traffic" = "🚦 Tráfego: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
+"xrayStatus" = "ℹ️ Status: {{ .State }}\r\n"
+"username" = "👤 Nome de usuário: {{ .Username }}\r\n"
+"password" = "👤 Senha: {{ .Password }}\r\n"
+"time" = "⏰ Hora: {{ .Time }}\r\n"
+"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
+"port" = "🔌 Porta: {{ .Port }}\r\n"
+"expire" = "📅 Data de expiração: {{ .Time }}\r\n"
+"expireIn" = "📅 Expira em: {{ .Time }}\r\n"
+"active" = "💡 Ativo: {{ .Enable }}\r\n"
+"enabled" = "🚨 Ativado: {{ .Enable }}\r\n"
+"online" = "🌐 Status da conexão: {{ .Status }}\r\n"
+"email" = "📧 Email: {{ .Email }}\r\n"
+"upload" = "🔼 Upload: ↑{{ .Upload }}\r\n"
+"download" = "🔽 Download: ↓{{ .Download }}\r\n"
+"total" = "📊 Total: ↑↓{{ .UpDown }} / {{ .Total }}\r\n"
+"TGUser" = "👤 Usuário do Telegram: {{ .TelegramID }}\r\n"
+"exhaustedMsg" = "🚨 {{ .Type }} esgotado:\r\n"
+"exhaustedCount" = "🚨 Contagem de {{ .Type }} esgotado:\r\n"
+"onlinesCount" = "🌐 Clientes online: {{ .Count }}\r\n"
+"disabled" = "🛑 Desativado: {{ .Disabled }}\r\n"
+"depleteSoon" = "🔜 Esgotar em breve: {{ .Deplete }}\r\n\r\n"
+"backupTime" = "🗄 Hora do backup: {{ .Time }}\r\n"
+"refreshedOn" = "\r\n📋🔄 Atualizado em: {{ .Time }}\r\n\r\n"
+"yes" = "✅ Sim"
+"no" = "❌ Não"
+
+[tgbot.buttons]
+"closeKeyboard" = "❌ Fechar teclado"
+"cancel" = "❌ Cancelar"
+"cancelReset" = "❌ Cancelar redefinição"
+"cancelIpLimit" = "❌ Cancelar limite de IP"
+"confirmResetTraffic" = "✅ Confirmar redefinição de tráfego?"
+"confirmClearIps" = "✅ Confirmar limpar IPs?"
+"confirmRemoveTGUser" = "✅ Confirmar remover usuário do Telegram?"
+"confirmToggle" = "✅ Confirmar ativar/desativar usuário?"
+"dbBackup" = "Obter backup do DB"
+"serverUsage" = "Uso do servidor"
+"getInbounds" = "Obter Inbounds"
+"depleteSoon" = "Esgotar em breve"
+"clientUsage" = "Obter uso"
+"onlines" = "Clientes online"
+"commands" = "Comandos"
+"refresh" = "🔄 Atualizar"
+"clearIPs" = "❌ Limpar IPs"
+"removeTGUser" = "❌ Remover usuário do Telegram"
+"selectTGUser" = "👤 Selecionar usuário do Telegram"
+"selectOneTGUser" = "👤 Selecione um usuário do Telegram:"
+"resetTraffic" = "📈 Redefinir tráfego"
+"resetExpire" = "📅 Alterar data de expiração"
+"ipLog" = "🔢 Log de IP"
+"ipLimit" = "🔢 Limite de IP"
+"setTGUser" = "👤 Definir usuário do Telegram"
+"toggle" = "🔘 Ativar / Desativar"
+"custom" = "🔢 Personalizado"
+"confirmNumber" = "✅ Confirmar: {{ .Num }}"
+"confirmNumberAdd" = "✅ Confirmar adicionar: {{ .Num }}"
+"limitTraffic" = "🚧 Limite de tráfego"
+"getBanLogs" = "Obter logs de banimento"
+"allClients" = "Todos os clientes"
+
+[tgbot.answers]
+"successfulOperation" = "✅ Operação bem-sucedida!"
+"errorOperation" = "❗ Erro na operação."
+"getInboundsFailed" = "❌ Falha ao obter inbounds."
+"getClientsFailed" = "❌ Falha ao obter clientes."
+"canceled" = "❌ {{ .Email }}: Operação cancelada."
+"clientRefreshSuccess" = "✅ {{ .Email }}: Cliente atualizado com sucesso."
+"IpRefreshSuccess" = "✅ {{ .Email }}: IPs atualizados com sucesso."
+"TGIdRefreshSuccess" = "✅ {{ .Email }}: Usuário do Telegram do cliente atualizado com sucesso."
+"resetTrafficSuccess" = "✅ {{ .Email }}: Tráfego redefinido com sucesso."
+"setTrafficLimitSuccess" = "✅ {{ .Email }}: Limite de tráfego salvo com sucesso."
+"expireResetSuccess" = "✅ {{ .Email }}: Dias de expiração redefinidos com sucesso."
+"resetIpSuccess" = "✅ {{ .Email }}: Limite de IP {{ .Count }} salvo com sucesso."
+"clearIpSuccess" = "✅ {{ .Email }}: IPs limpos com sucesso."
+"getIpLog" = "✅ {{ .Email }}: Obter log de IP."
+"getUserInfo" = "✅ {{ .Email }}: Obter informações do usuário do Telegram."
+"removedTGUserSuccess" = "✅ {{ .Email }}: Usuário do Telegram removido com sucesso."
+"enableSuccess" = "✅ {{ .Email }}: Ativado com sucesso."
+"disableSuccess" = "✅ {{ .Email }}: Desativado com sucesso."
+"askToAddUserId" = "Sua configuração não foi encontrada!\r\nPeça ao seu administrador para usar seu Telegram ChatID em suas configurações.\r\n\r\nSeu ChatID: {{ .TgUserID }} "
+"chooseClient" = "Escolha um cliente para Inbound {{ .Inbound }}"
+"chooseInbound" = "Escolha um Inbound"
diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml
index 93d8b3fe..7ddc95c8 100644
--- a/web/translation/translate.ru_RU.toml
+++ b/web/translation/translate.ru_RU.toml
@@ -225,9 +225,6 @@
"requestHeader" = "Заголовок запроса"
"responseHeader" = "Заголовок ответа"
-[pages.inbounds.stream.quic]
-"encryption" = "Шифрование"
-
[pages.settings]
"title" = "Настройки"
"save" = "Сохранить"
@@ -312,14 +309,14 @@
"fragment" = "Фрагментация"
"fragmentDesc" = "Включить фрагментацию для пакета приветствия TLS"
"fragmentSett" = "Настройки фрагментации"
-"noiseDesc" = "Включить Noise."
-"noiseSett" = "Настройки Noise"
+"noisesDesc" = "Включить Noises."
+"noisesSett" = "Настройки Noises"
"mux" = "Mux"
"muxDesc" = "Передача нескольких независимых потоков данных в рамках установленного потока данных."
"muxSett" = "Mux Настройки"
"direct" = "Прямая связь"
"directDesc" = "Напрямую устанавливает соединения с доменами или диапазонами IP конкретной страны."
-"directSett" = "Варианты прямого подключения"
+
[pages.xray]
"title" = "Настройки Xray"
@@ -425,6 +422,10 @@
"accessLogDesc" = "Путь к файлу журнала доступа. Специальное значение «none» отключило журналы доступа."
"errorLog" = "Журнал ошибок"
"errorLogDesc" = "Путь к файлу журнала ошибок. Специальное значение «none» отключает журналы ошибок."
+"dnsLog" = "DNS Журнал"
+"dnsLogDesc" = "Включить логи запросов DNS"
+"maskAddress" = "Маскировать Адрес"
+"maskAddressDesc" = "Маска IP-адреса, при активации автоматически заменяет IP-адрес, который появляется в логе."
[pages.xray.rules]
"first" = "Первый"
diff --git a/web/translation/translate.tr_TR.toml b/web/translation/translate.tr_TR.toml
index 7006462f..f21a2927 100644
--- a/web/translation/translate.tr_TR.toml
+++ b/web/translation/translate.tr_TR.toml
@@ -225,9 +225,6 @@
"requestHeader" = "İstek Başlığı"
"responseHeader" = "Yanıt Başlığı"
-[pages.inbounds.stream.quic]
-"encryption" = "Şifreleme"
-
[pages.settings]
"title" = "Panel Ayarları"
"save" = "Kaydet"
@@ -312,14 +309,14 @@
"fragment" = "Parçalama"
"fragmentDesc" = "TLS merhaba paketinin parçalanmasını etkinleştir."
"fragmentSett" = "Parçalama Ayarları"
-"noiseDesc" = "Noise'i Etkinleştir."
-"noiseSett" = "Noise Ayarları"
+"noisesDesc" = "Noises'i Etkinleştir."
+"noisesSett" = "Noises Ayarları"
"mux" = "Mux"
"muxDesc" = "Kurulmuş bir veri akışında birden çok bağımsız veri akışını iletir."
"muxSett" = "Mux Ayarları"
"direct" = "Doğrudan Bağlantı"
"directDesc" = "Belirli bir ülkenin alan adları veya IP aralıkları ile doğrudan bağlantı kurar."
-"directSett" = "Doğrudan Bağlantı Seçenekleri"
+
[pages.xray]
"title" = "Xray Yapılandırmaları"
@@ -425,6 +422,10 @@
"accessLogDesc" = "Erişim günlüğü için dosya yolu. 'none' özel değeri erişim günlüklerini devre dışı bırakır"
"errorLog" = "Hata Günlüğü"
"errorLogDesc" = "Hata günlüğü için dosya yolu. 'none' özel değeri hata günlüklerini devre dışı bırakır"
+"dnsLog" = "DNS Günlüğü"
+"dnsLogDesc" = "DNS sorgu günlüklerini etkinleştirin"
+"maskAddress" = "Adres Maskesi"
+"maskAddressDesc" = "IP adresi maskesi, etkinleştirildiğinde, günlükte görünen IP adresini otomatik olarak değiştirecektir."
[pages.xray.rules]
"first" = "İlk"
diff --git a/web/translation/translate.uk_UA.toml b/web/translation/translate.uk_UA.toml
index f15a36bb..1ed68eab 100644
--- a/web/translation/translate.uk_UA.toml
+++ b/web/translation/translate.uk_UA.toml
@@ -225,9 +225,6 @@
"requestHeader" = "Заголовок запиту"
"responseHeader" = "Заголовок відповіді"
-[pages.inbounds.stream.quic]
-"encryption" = "Шифрування"
-
[pages.settings]
"title" = "Параметри панелі"
"save" = "Зберегти"
@@ -312,14 +309,14 @@
"fragment" = "Фрагментація"
"fragmentDesc" = "Увімкнути фрагментацію для пакету привітання TLS"
"fragmentSett" = "Параметри фрагментації"
-"noiseDesc" = "Увімкнути Noise."
-"noiseSett" = "Налаштування Noise"
+"noisesDesc" = "Увімкнути Noises."
+"noisesSett" = "Налаштування Noises"
"mux" = "Mux"
"muxDesc" = "Передавати кілька незалежних потоків даних у межах встановленого потоку даних."
"muxSett" = "Налаштування Mux"
"direct" = "Пряме підключення"
"directDesc" = "Безпосередньо встановлює з’єднання з доменами або діапазонами IP певної країни."
-"directSett" = "Параметри прямого підключення"
+
[pages.xray]
"title" = "Xray конфігурації"
@@ -425,6 +422,10 @@
"accessLogDesc" = "Шлях до файлу журналу доступу. Спеціальне значення 'none' вимикає журнали доступу"
"errorLog" = "Журнал помилок"
"errorLogDesc" = "Шлях до файлу журналу помилок. Спеціальне значення 'none' вимикає журнали помилок"
+"dnsLog" = "Журнал DNS"
+"dnsLogDesc" = "Чи включити журнали запитів DNS"
+"maskAddress" = "Маскувати Адресу"
+"maskAddressDesc" = "Маска IP-адреси, при активації автоматично замінює IP-адресу, яка з'являється у журналі."
[pages.xray.rules]
"first" = "Перший"
diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml
index 6024fe2b..edcf56bb 100644
--- a/web/translation/translate.vi_VN.toml
+++ b/web/translation/translate.vi_VN.toml
@@ -225,9 +225,6 @@
"requestHeader" = "Header yêu cầu"
"responseHeader" = "Header phản hồi"
-[pages.inbounds.stream.quic]
-"encryption" = "Mã hóa"
-
[pages.settings]
"title" = "Cài đặt"
"save" = "Lưu"
@@ -312,14 +309,14 @@
"fragment" = "Sự phân mảnh"
"fragmentDesc" = "Kích hoạt phân mảnh cho gói TLS hello"
"fragmentSett" = "Cài đặt phân mảnh"
-"noiseDesc" = "Bật Noise."
-"noiseSett" = "Cài đặt Noise"
+"noisesDesc" = "Bật Noises."
+"noisesSett" = "Cài đặt Noises"
"mux" = "Mux"
"muxDesc" = "Truyền nhiều luồng dữ liệu độc lập trong luồng dữ liệu đã thiết lập."
"muxSett" = "Mux Cài đặt"
"direct" = "Kết nối trực tiếp"
"directDesc" = "Trực tiếp thiết lập kết nối với tên miền hoặc dải IP của một quốc gia cụ thể."
-"directSett" = "Tùy chọn kết nối trực tiếp"
+
[pages.xray]
"title" = "Cài đặt Xray"
@@ -425,6 +422,10 @@
"accessLogDesc" = "Đường dẫn tệp cho nhật ký truy cập. Nhật ký truy cập bị vô hiệu hóa có giá trị đặc biệt 'không'"
"errorLog" = "Nhật ký lỗi"
"errorLogDesc" = "Đường dẫn tệp cho nhật ký lỗi. Nhật ký lỗi bị vô hiệu hóa có giá trị đặc biệt 'không'"
+"dnsLog" = "Nhật ký DNS"
+"dnsLogDesc" = "Có bật nhật ký truy vấn DNS không"
+"maskAddress" = "Ẩn Địa Chỉ"
+"maskAddressDesc" = "Mặt nạ địa chỉ IP, khi được bật, sẽ tự động thay thế địa chỉ IP xuất hiện trong nhật ký."
[pages.xray.rules]
"first" = "Đầu tiên"
diff --git a/web/translation/translate.zh_CN.toml b/web/translation/translate.zh_CN.toml
index 480dec95..65a2a6ea 100644
--- a/web/translation/translate.zh_CN.toml
+++ b/web/translation/translate.zh_CN.toml
@@ -225,9 +225,6 @@
"requestHeader" = "请求头"
"responseHeader" = "响应头"
-[pages.inbounds.stream.quic]
-"encryption" = "加密"
-
[pages.settings]
"title" = "面板设置"
"save" = "保存"
@@ -312,14 +309,14 @@
"fragment" = "分片"
"fragmentDesc" = "启用 TLS hello 数据包分片"
"fragmentSett" = "设置"
-"noiseDesc" = "启用 Noise."
-"noiseSett" = "Noise 设置"
+"noisesDesc" = "启用 Noises."
+"noisesSett" = "Noises 设置"
"mux" = "多路复用器"
"muxDesc" = "在已建立的数据流内传输多个独立的数据流"
"muxSett" = "复用器设置"
"direct" = "直接连接"
"directDesc" = "直接与特定国家的域或IP范围建立连接"
-"directSett" = "直接连接选项"
+
[pages.xray]
"title" = "Xray 配置"
@@ -425,6 +422,10 @@
"accessLogDesc" = "访问日志的文件路径。特殊值 'none' 禁用访问日志"
"errorLog" = "错误日志"
"errorLogDesc" = "错误日志的文件路径。特殊值 'none' 禁用错误日志"
+"dnsLog" = "DNS 日志"
+"dnsLogDesc" = "是否启用 DNS 查询日志"
+"maskAddress" = "隐藏地址"
+"maskAddressDesc" = "IP 地址掩码,启用时会自动替换日志中出现的 IP 地址。"
[pages.xray.rules]
"first" = "置顶"
diff --git a/x-ui.sh b/x-ui.sh
index 2ffb7049..120c83a0 100644
--- a/x-ui.sh
+++ b/x-ui.sh
@@ -36,12 +36,12 @@ fi
echo "The OS release is: $release"
os_version=""
-os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1)
+os_version=$(grep "^VERSION_ID" /etc/os-release | cut -d '=' -f2 | tr -d '"' | tr -d '.')
if [[ "${release}" == "arch" ]]; then
echo "Your OS is Arch Linux"
elif [[ "${release}" == "parch" ]]; then
- echo "Your OS is Parch linux"
+ echo "Your OS is Parch Linux"
elif [[ "${release}" == "manjaro" ]]; then
echo "Your OS is Manjaro"
elif [[ "${release}" == "armbian" ]]; then
@@ -53,24 +53,28 @@ elif [[ "${release}" == "centos" ]]; then
echo -e "${red} Please use CentOS 8 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "ubuntu" ]]; then
- if [[ ${os_version} -lt 20 ]]; then
+ if [[ ${os_version} -lt 2004 ]]; then
echo -e "${red} Please use Ubuntu 20 or higher version!${plain}\n" && exit 1
fi
elif [[ "${release}" == "fedora" ]]; then
if [[ ${os_version} -lt 36 ]]; then
echo -e "${red} Please use Fedora 36 or higher version!${plain}\n" && exit 1
fi
+elif [[ "${release}" == "amzn" ]]; then
+ if [[ ${os_version} != "2023" ]]; then
+ echo -e "${red} Please use Amazon Linux 2023!${plain}\n" && exit 1
+ fi
elif [[ "${release}" == "debian" ]]; then
if [[ ${os_version} -lt 11 ]]; then
echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "almalinux" ]]; then
- if [[ ${os_version} -lt 9 ]]; then
- echo -e "${red} Please use AlmaLinux 9 or higher ${plain}\n" && exit 1
+ if [[ ${os_version} -lt 80 ]]; then
+ echo -e "${red} Please use AlmaLinux 8.0 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "rocky" ]]; then
- if [[ ${os_version} -lt 9 ]]; then
- echo -e "${red} Please use Rocky Linux 9 or higher ${plain}\n" && exit 1
+ if [[ ${os_version} -lt 8 ]]; then
+ echo -e "${red} Please use Rocky Linux 8 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "oracle" ]]; then
if [[ ${os_version} -lt 8 ]]; then
@@ -87,12 +91,12 @@ else
echo "- Parch Linux"
echo "- Manjaro"
echo "- Armbian"
- echo "- AlmaLinux 9+"
- echo "- Rocky Linux 9+"
+ echo "- AlmaLinux 8.0+"
+ echo "- Rocky Linux 8+"
echo "- Oracle Linux 8+"
echo "- OpenSUSE Tumbleweed"
+ echo "- Amazon Linux 2023"
exit 1
-
fi
# Declare Variables
@@ -978,33 +982,6 @@ ssl_cert_issue_CF() {
fi
}
-warp_cloudflare() {
- echo -e "${green}\t1.${plain} Install WARP socks5 proxy"
- echo -e "${green}\t2.${plain} Account Type (free, plus, team)"
- echo -e "${green}\t3.${plain} Turn on/off WireProxy"
- echo -e "${green}\t4.${plain} Uninstall WARP"
- echo -e "${green}\t0.${plain} Back to Main Menu"
- read -p "Choose an option: " choice
- case "$choice" in
- 0)
- show_menu
- ;;
- 1)
- bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh)
- ;;
- 2)
- warp a
- ;;
- 3)
- warp y
- ;;
- 4)
- warp u
- ;;
- *) echo "Invalid choice" ;;
- esac
-}
-
run_speedtest() {
# Check if Speedtest is already installed
if ! command -v speedtest &>/dev/null; then
@@ -1349,15 +1326,14 @@ show_menu() {
${green}18.${plain} SSL Certificate Management
${green}19.${plain} Cloudflare SSL Certificate
${green}20.${plain} IP Limit Management
- ${green}21.${plain} WARP Management
- ${green}22.${plain} Firewall Management
+ ${green}21.${plain} Firewall Management
————————————————
- ${green}23.${plain} Enable BBR
- ${green}24.${plain} Update Geo Files
- ${green}25.${plain} Speedtest by Ookla
+ ${green}22.${plain} Enable BBR
+ ${green}23.${plain} Update Geo Files
+ ${green}24.${plain} Speedtest by Ookla
"
show_status
- echo && read -p "Please enter your selection [0-25]: " num
+ echo && read -p "Please enter your selection [0-24]: " num
case "${num}" in
0)
@@ -1424,22 +1400,19 @@ show_menu() {
iplimit_main
;;
21)
- warp_cloudflare
- ;;
- 22)
firewall_menu
;;
- 23)
+ 22)
bbr_menu
;;
- 24)
+ 23)
update_geo
;;
- 25)
+ 24)
run_speedtest
;;
*)
- LOGE "Please enter the correct number [0-25]"
+ LOGE "Please enter the correct number [0-24]"
;;
esac
}
diff --git a/xray/inbound.go b/xray/inbound.go
index ea11449d..c74b07ba 100644
--- a/xray/inbound.go
+++ b/xray/inbound.go
@@ -14,6 +14,7 @@ type InboundConfig struct {
StreamSettings json_util.RawMessage `json:"streamSettings"`
Tag string `json:"tag"`
Sniffing json_util.RawMessage `json:"sniffing"`
+ Allocate json_util.RawMessage `json:"allocate"`
}
func (c *InboundConfig) Equals(other *InboundConfig) bool {
@@ -38,5 +39,8 @@ func (c *InboundConfig) Equals(other *InboundConfig) bool {
if !bytes.Equal(c.Sniffing, other.Sniffing) {
return false
}
+ if !bytes.Equal(c.Allocate, other.Allocate) {
+ return false
+ }
return true
}
|