Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sub/subController.go7
-rw-r--r--sub/subService.go169
-rw-r--r--web/assets/js/model/models.js1
-rw-r--r--web/controller/setting.go1
-rw-r--r--web/entity/entity.go1
-rw-r--r--web/html/xui/settings.html1
-rw-r--r--web/service/setting.go5
-rw-r--r--web/translation/translate.en_US.toml2
-rw-r--r--web/translation/translate.fa_IR.toml2
-rw-r--r--web/translation/translate.ru_RU.toml2
-rw-r--r--web/translation/translate.vi_VN.toml2
-rw-r--r--web/translation/translate.zh_Hans.toml2
12 files changed, 89 insertions, 106 deletions
diff --git a/sub/subController.go b/sub/subController.go
index 69d9086d..b0e62ecf 100644
--- a/sub/subController.go
+++ b/sub/subController.go
@@ -3,12 +3,14 @@ package sub
import (
"encoding/base64"
"strings"
+ "x-ui/web/service"
"github.com/gin-gonic/gin"
)
type SUBController struct {
- subService SubService
+ subService SubService
+ settingService service.SettingService
}
func NewSUBController(g *gin.RouterGroup) *SUBController {
@@ -24,9 +26,10 @@ func (a *SUBController) initRouter(g *gin.RouterGroup) {
}
func (a *SUBController) subs(c *gin.Context) {
+ subShowInfo, _ := a.settingService.GetSubShowInfo()
subId := c.Param("subid")
host := strings.Split(c.Request.Host, ":")[0]
- subs, headers, err := a.subService.GetSubs(subId, host)
+ subs, headers, err := a.subService.GetSubs(subId, host, subShowInfo)
if err != nil || len(subs) == 0 {
c.String(400, "Error!")
} else {
diff --git a/sub/subService.go b/sub/subService.go
index d82b8ceb..0565100e 100644
--- a/sub/subService.go
+++ b/sub/subService.go
@@ -18,12 +18,14 @@ import (
type SubService struct {
address string
+ showInfo bool
inboundService service.InboundService
settingServics service.SettingService
}
-func (s *SubService) GetSubs(subId string, host string) ([]string, []string, error) {
+func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string, []string, error) {
s.address = host
+ s.showInfo = showInfo
var result []string
var headers []string
var traffic xray.ClientTraffic
@@ -57,7 +59,7 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, []string, err
}
for _, client := range clients {
if client.Enable && client.SubID == subId {
- link := s.getLink(inbound, client.Email, client.ExpiryTime)
+ link := s.getLink(inbound, client.Email)
result = append(result, link)
clientTraffics = append(clientTraffics, s.getClientTraffics(inbound.ClientStats, client.Email))
}
@@ -123,39 +125,26 @@ func (s *SubService) getFallbackMaster(dest string) (*model.Inbound, error) {
return inbound, nil
}
-func (s *SubService) getLink(inbound *model.Inbound, email string, expiryTime int64) string {
+func (s *SubService) getLink(inbound *model.Inbound, email string) string {
switch inbound.Protocol {
case "vmess":
- return s.genVmessLink(inbound, email, expiryTime)
+ return s.genVmessLink(inbound, email)
case "vless":
- return s.genVlessLink(inbound, email, expiryTime)
+ return s.genVlessLink(inbound, email)
case "trojan":
- return s.genTrojanLink(inbound, email, expiryTime)
+ return s.genTrojanLink(inbound, email)
case "shadowsocks":
- return s.genShadowsocksLink(inbound, email, expiryTime)
+ return s.genShadowsocksLink(inbound, email)
}
return ""
}
-func (s *SubService) genVmessLink(inbound *model.Inbound, email string, expiryTime int64) string {
+func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
if inbound.Protocol != model.VMess {
return ""
}
-
- remainedTraffic := s.getRemainedTraffic(email)
- expiryTimeString := getExpiryTime(expiryTime)
- remark := ""
- isTerminated := strings.Contains(expiryTimeString, "Terminated") || strings.Contains(remainedTraffic, "Terminated")
-
- if isTerminated {
- remark = fmt.Sprintf("%s: %s⛔️", email, "Terminated")
- } else {
- remark = fmt.Sprintf("%s: %s - %s", email, remainedTraffic, expiryTimeString)
- }
-
obj := map[string]interface{}{
"v": "2",
- "ps": remark,
"add": s.address,
"port": inbound.Port,
"type": "none",
@@ -254,7 +243,7 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string, expiryTi
links := ""
for index, d := range domains {
domain := d.(map[string]interface{})
- obj["ps"] = remark + "-" + domain["remark"].(string)
+ obj["ps"] = s.genRemark(inbound, email, domain["remark"].(string))
obj["add"] = domain["domain"].(string)
if index > 0 {
links += "\n"
@@ -265,11 +254,13 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string, expiryTi
return links
}
+ obj["ps"] = s.genRemark(inbound, email, "")
+
jsonStr, _ := json.MarshalIndent(obj, "", " ")
return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
}
-func (s *SubService) genVlessLink(inbound *model.Inbound, email string, expiryTime int64) string {
+func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
address := s.address
if inbound.Protocol != model.VLESS {
return ""
@@ -462,22 +453,11 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string, expiryTi
// Set the new query values on the URL
url.RawQuery = q.Encode()
- remainedTraffic := s.getRemainedTraffic(email)
- expiryTimeString := getExpiryTime(expiryTime)
- remark := ""
- isTerminated := strings.Contains(expiryTimeString, "Terminated") || strings.Contains(remainedTraffic, "Terminated")
-
- if isTerminated {
- remark = fmt.Sprintf("%s: %s⛔️", email, "Terminated")
- } else {
- remark = fmt.Sprintf("%s: %s - %s", email, remainedTraffic, expiryTimeString)
- }
-
if len(domains) > 0 {
links := ""
for index, d := range domains {
domain := d.(map[string]interface{})
- url.Fragment = remark + "-" + domain["remark"].(string)
+ url.Fragment = s.genRemark(inbound, email, domain["remark"].(string))
url.Host = fmt.Sprintf("%s:%d", domain["domain"].(string), port)
if index > 0 {
links += "\n"
@@ -486,11 +466,12 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string, expiryTi
}
return links
}
- url.Fragment = remark
+
+ url.Fragment = s.genRemark(inbound, email, "")
return url.String()
}
-func (s *SubService) genTrojanLink(inbound *model.Inbound, email string, expiryTime int64) string {
+func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string {
address := s.address
if inbound.Protocol != model.Trojan {
return ""
@@ -680,22 +661,11 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string, expiryT
// Set the new query values on the URL
url.RawQuery = q.Encode()
- remainedTraffic := s.getRemainedTraffic(email)
- expiryTimeString := getExpiryTime(expiryTime)
- remark := ""
- isTerminated := strings.Contains(expiryTimeString, "Terminated") || strings.Contains(remainedTraffic, "Terminated")
-
- if isTerminated {
- remark = fmt.Sprintf("%s: %s⛔️", email, "Terminated")
- } else {
- remark = fmt.Sprintf("%s: %s - %s", email, remainedTraffic, expiryTimeString)
- }
-
if len(domains) > 0 {
links := ""
for index, d := range domains {
domain := d.(map[string]interface{})
- url.Fragment = remark + "-" + domain["remark"].(string)
+ url.Fragment = s.genRemark(inbound, email, domain["remark"].(string))
url.Host = fmt.Sprintf("%s:%d", domain["domain"].(string), port)
if index > 0 {
links += "\n"
@@ -705,11 +675,11 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string, expiryT
return links
}
- url.Fragment = remark
+ url.Fragment = s.genRemark(inbound, email, "")
return url.String()
}
-func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string, expiryTime int64) string {
+func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) string {
address := s.address
if inbound.Protocol != model.Shadowsocks {
return ""
@@ -788,20 +758,53 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string, ex
// Set the new query values on the URL
url.RawQuery = q.Encode()
+ url.Fragment = s.genRemark(inbound, email, "")
+ return url.String()
+}
- remainedTraffic := s.getRemainedTraffic(email)
- expiryTimeString := getExpiryTime(expiryTime)
- remark := ""
- isTerminated := strings.Contains(expiryTimeString, "Terminated") || strings.Contains(remainedTraffic, "Terminated")
-
- if isTerminated {
- remark = fmt.Sprintf("%s: %s⛔️", clients[clientIndex].Email, "Terminated")
+func (s *SubService) genRemark(inbound *model.Inbound, email string, extra string) string {
+ var remark []string
+ if len(email) > 0 {
+ if len(inbound.Remark) > 0 {
+ remark = append(remark, inbound.Remark)
+ }
+ remark = append(remark, email)
+ if len(extra) > 0 {
+ remark = append(remark, extra)
+ }
} else {
- remark = fmt.Sprintf("%s: %s - %s", clients[clientIndex].Email, remainedTraffic, expiryTimeString)
+ return inbound.Remark
}
-
- url.Fragment = remark
- return url.String()
+
+ if s.showInfo {
+ statsExist := false
+ var stats xray.ClientTraffic
+ for _, clientStat := range inbound.ClientStats {
+ if clientStat.Email == email {
+ stats = clientStat
+ statsExist = true
+ break
+ }
+ }
+
+ // Get remained days
+ if statsExist {
+ if !stats.Enable {
+ return fmt.Sprintf("⛔️N/A-%s", strings.Join(remark, "-"))
+ }
+ if vol := stats.Total - (stats.Up + stats.Down); vol > 0 {
+ remark = append(remark, fmt.Sprintf("%s%s", common.FormatTraffic(vol), "📊"))
+ }
+ now := time.Now().Unix()
+ switch exp := stats.ExpiryTime / 1000; {
+ case exp > 0:
+ remark = append(remark, fmt.Sprintf("%d%s⏳", (exp-now)/86400, "Days"))
+ case exp < 0:
+ remark = append(remark, fmt.Sprintf("%d%s⏳", exp/-86400, "Days"))
+ }
+ }
+ }
+ return strings.Join(remark, "-")
}
func searchKey(data interface{}, key string) (interface{}, bool) {
@@ -845,45 +848,3 @@ func searchHost(headers interface{}) string {
return ""
}
-
-func getExpiryTime(expiryTime int64) string {
- now := time.Now().Unix()
- expiryString := ""
-
- timeDifference := expiryTime/1000 - now
- isTerminated := timeDifference/3600 <= 0
-
- if expiryTime == 0 {
- expiryString = "♾ ⏳"
- } else if timeDifference > 172800 {
- expiryString = fmt.Sprintf("%d %s⏳", timeDifference/86400, "Days")
- } else if expiryTime < 0 {
- expiryString = fmt.Sprintf("%d %s⏳", expiryTime/-86400000, "Days")
- } else if isTerminated {
- expiryString = fmt.Sprintf("%s⛔️", "Terminated")
- } else {
- expiryString = fmt.Sprintf("%d %s⏳", timeDifference/3600, "Hours")
- }
-
- return expiryString
-}
-
-func (s *SubService) getRemainedTraffic(email string) string {
- traffic, err := s.inboundService.GetClientTrafficByEmail(email)
- if err != nil {
- logger.Warning(err)
- }
-
- remainedTraffic := ""
- isTerminated := traffic.Total-(traffic.Up+traffic.Down) < 0
-
- if traffic.Total == 0 {
- remainedTraffic = "♾ 📊"
- } else if isTerminated {
- remainedTraffic = fmt.Sprintf("%s⛔️", "Terminated")
- } else {
- remainedTraffic = fmt.Sprintf("%s%s", common.FormatTraffic(traffic.Total-(traffic.Up+traffic.Down)), "📊")
- }
-
- return remainedTraffic
-}
diff --git a/web/assets/js/model/models.js b/web/assets/js/model/models.js
index 2c992a1a..762d1174 100644
--- a/web/assets/js/model/models.js
+++ b/web/assets/js/model/models.js
@@ -194,6 +194,7 @@ class AllSetting {
this.subCertFile = "";
this.subKeyFile = "";
this.subUpdates = 0;
+ this.subShowInfo = false;
this.timeLocation = "Asia/Tehran";
diff --git a/web/controller/setting.go b/web/controller/setting.go
index cd509293..d991633a 100644
--- a/web/controller/setting.go
+++ b/web/controller/setting.go
@@ -79,6 +79,7 @@ func (a *SettingController) getDefaultSettings(c *gin.Context) {
"subDomain": func() (interface{}, error) { return a.settingService.GetSubDomain() },
"subKeyFile": func() (interface{}, error) { return a.settingService.GetSubKeyFile() },
"subCertFile": func() (interface{}, error) { return a.settingService.GetSubCertFile() },
+ "subShowInfo": func() (interface{}, error) { return a.settingService.GetSubShowInfo() },
}
result := make(map[string]interface{})
diff --git a/web/entity/entity.go b/web/entity/entity.go
index 328af7b6..10a6a97d 100644
--- a/web/entity/entity.go
+++ b/web/entity/entity.go
@@ -55,6 +55,7 @@ type AllSetting struct {
SubCertFile string `json:"subCertFile" form:"subCertFile"`
SubKeyFile string `json:"subKeyFile" form:"subKeyFile"`
SubUpdates int `json:"subUpdates" form:"subUpdates"`
+ SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"`
}
func (s *AllSetting) CheckValid() error {
diff --git a/web/html/xui/settings.html b/web/html/xui/settings.html
index 76ca8aa1..e1741302 100644
--- a/web/html/xui/settings.html
+++ b/web/html/xui/settings.html
@@ -406,6 +406,7 @@
</a-row>
<a-list item-layout="horizontal" :style="themeSwitcher.textStyle">
<setting-list-item type="switch" title='{{ i18n "pages.settings.subEnable"}}' desc='{{ i18n "pages.settings.subEnableDesc"}}' v-model="allSetting.subEnable"></setting-list-item>
+ <setting-list-item type="switch" title='{{ i18n "pages.settings.subShowInfo"}}' desc='{{ i18n "pages.settings.subShowInfoDesc"}}' v-model="allSetting.subShowInfo"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.settings.subListen"}}' desc='{{ i18n "pages.settings.subListenDesc"}}' v-model="allSetting.subListen"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.settings.subDomain"}}' desc='{{ i18n "pages.settings.subDomainDesc"}}' v-model="allSetting.subDomain"></setting-list-item>
<setting-list-item type="number" title='{{ i18n "pages.settings.subPort"}}' desc='{{ i18n "pages.settings.subPortDesc"}}' v-model.number="allSetting.subPort"></setting-list-item>
diff --git a/web/service/setting.go b/web/service/setting.go
index 9bf74641..585e60c7 100644
--- a/web/service/setting.go
+++ b/web/service/setting.go
@@ -51,6 +51,7 @@ var defaultValueMap = map[string]string{
"subCertFile": "",
"subKeyFile": "",
"subUpdates": "12",
+ "subShowInfo": "false",
}
type SettingService struct {
@@ -396,6 +397,10 @@ func (s *SettingService) GetSubUpdates() (int, error) {
return s.getInt("subUpdates")
}
+func (s *SettingService) GetSubShowInfo() (bool, error) {
+ return s.getBool("subShowInfo")
+}
+
func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error {
if err := allSetting.CheckValid(); err != nil {
return err
diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml
index f760ee0b..5ac63f57 100644
--- a/web/translation/translate.en_US.toml
+++ b/web/translation/translate.en_US.toml
@@ -274,6 +274,8 @@
"subDomainDesc" = "Leave blank by default to monitor all domains and IPs"
"subUpdates" = "Subscription update intervals"
"subUpdatesDesc" = "Interval hours between updates in client application"
+"subShowInfo" = "Show usage info"
+"subShowInfoDesc" = "Show remianed traffic and date after config name"
[pages.settings.templates]
"title" = "Templates"
diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml
index 8557644f..a145965d 100644
--- a/web/translation/translate.fa_IR.toml
+++ b/web/translation/translate.fa_IR.toml
@@ -274,6 +274,8 @@
"subDomainDesc" = "برای نظارت بر همه دامنه ها و آی‌پی ها به طور پیش فرض خالی بگذارید"
"subUpdates" = "فاصله به روز رسانی های سابسکریپشن"
"subUpdatesDesc" = "ساعت های فاصله بین به روز رسانی در برنامه کاربر"
+"subShowInfo" = "نمایش اطلاعات مصرف"
+"subShowInfoDesc" = "ترافیک و زمان باقیمانده را در هر کانفیگ نمایش میدهد"
[pages.settings.templates]
"title" = "الگوها"
diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml
index 9d219129..53b6fedd 100644
--- a/web/translation/translate.ru_RU.toml
+++ b/web/translation/translate.ru_RU.toml
@@ -274,6 +274,8 @@
"subDomainDesc" = "Оставьте пустым по умолчанию, чтобы отслеживать все домены и IP-адреса"
"subUpdates" = "Интервалы обновления подписки"
"subUpdatesDesc" = "Часовой интервал между обновлениями в клиентском приложении"
+"subShowInfo" = "Показать информацию об использовании"
+"subShowInfoDesc" = "Показывать восстановленный трафик и дату после имени конфигурации"
[pages.settings.templates]
"title" = "Шаблоны"
diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml
index fc61909e..8ea0bfd2 100644
--- a/web/translation/translate.vi_VN.toml
+++ b/web/translation/translate.vi_VN.toml
@@ -274,6 +274,8 @@
"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ý"
"subUpdatesDesc" = "Số giờ giữa các cập nhật trong ứng dụng khách"
+"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"
[pages.settings.templates]
"title" = "Mẫu"
diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml
index b39e3ca8..fa9b84a3 100644
--- a/web/translation/translate.zh_Hans.toml
+++ b/web/translation/translate.zh_Hans.toml
@@ -274,6 +274,8 @@
"subDomainDesc" = "留空默认监控所有域名和IP"
"subUpdates" = "订阅更新间隔"
"subUpdatesDesc" = "客户端应用程序更新之间的间隔时间"
+"subShowInfo" = "显示使用信息"
+"subShowInfoDesc" = "在配置名称后显示剩余流量和日期"
[pages.settings.templates]
"title" = "模板"