diff --git a/README.md b/README.md
index 96952794..d9da9cf5 100644
--- a/README.md
+++ b/README.md
@@ -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/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/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/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())