[sub] support optional usage info in Remark #453

This commit is contained in:
Alireza Ahmadi
2023-08-25 23:31:59 +02:00
parent af5d681c22
commit 7adc8755f8
11 changed files with 77 additions and 16 deletions

View File

@@ -27,9 +27,10 @@ func (a *SUBController) initRouter(g *gin.RouterGroup) {
func (a *SUBController) subs(c *gin.Context) { func (a *SUBController) subs(c *gin.Context) {
subEncrypt, _ := a.settingService.GetSubEncrypt() subEncrypt, _ := a.settingService.GetSubEncrypt()
subShowInfo, _ := a.settingService.GetSubShowInfo()
subId := c.Param("subid") subId := c.Param("subid")
host := strings.Split(c.Request.Host, ":")[0] 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 { if err != nil || len(subs) == 0 {
c.String(400, "Error!") c.String(400, "Error!")
} else { } else {

View File

@@ -5,9 +5,11 @@ import (
"fmt" "fmt"
"net/url" "net/url"
"strings" "strings"
"time"
"x-ui/database" "x-ui/database"
"x-ui/database/model" "x-ui/database/model"
"x-ui/logger" "x-ui/logger"
"x-ui/util/common"
"x-ui/web/service" "x-ui/web/service"
"x-ui/xray" "x-ui/xray"
@@ -16,12 +18,14 @@ import (
type SubService struct { type SubService struct {
address string address string
showInfo bool
inboundService service.InboundService inboundService service.InboundService
settingServics service.SettingService 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.address = host
s.showInfo = showInfo
var result []string var result []string
var headers []string var headers []string
var traffic xray.ClientTraffic var traffic xray.ClientTraffic
@@ -139,10 +143,8 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
if inbound.Protocol != model.VMess { if inbound.Protocol != model.VMess {
return "" return ""
} }
remark := fmt.Sprintf("%s-%s", inbound.Remark, email)
obj := map[string]interface{}{ obj := map[string]interface{}{
"v": "2", "v": "2",
"ps": remark,
"add": s.address, "add": s.address,
"port": inbound.Port, "port": inbound.Port,
"type": "none", "type": "none",
@@ -241,7 +243,7 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
links := "" links := ""
for index, d := range domains { for index, d := range domains {
domain := d.(map[string]interface{}) 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) obj["add"] = domain["domain"].(string)
if index > 0 { if index > 0 {
links += "\n" links += "\n"
@@ -252,6 +254,8 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
return links return links
} }
obj["ps"] = s.genRemark(inbound, email, "")
jsonStr, _ := json.MarshalIndent(obj, "", " ") jsonStr, _ := json.MarshalIndent(obj, "", " ")
return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr) return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
} }
@@ -407,13 +411,12 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
// Set the new query values on the URL // Set the new query values on the URL
url.RawQuery = q.Encode() url.RawQuery = q.Encode()
remark := fmt.Sprintf("%s-%s", inbound.Remark, email)
if len(domains) > 0 { if len(domains) > 0 {
links := "" links := ""
for index, d := range domains { for index, d := range domains {
domain := d.(map[string]interface{}) 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) url.Host = fmt.Sprintf("%s:%d", domain["domain"].(string), port)
if index > 0 { if index > 0 {
links += "\n" links += "\n"
@@ -423,7 +426,7 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
return links return links
} }
url.Fragment = remark url.Fragment = s.genRemark(inbound, email, "")
return url.String() return url.String()
} }
@@ -572,13 +575,11 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
// Set the new query values on the URL // Set the new query values on the URL
url.RawQuery = q.Encode() url.RawQuery = q.Encode()
remark := fmt.Sprintf("%s-%s", inbound.Remark, email)
if len(domains) > 0 { if len(domains) > 0 {
links := "" links := ""
for index, d := range domains { for index, d := range domains {
domain := d.(map[string]interface{}) 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) url.Host = fmt.Sprintf("%s:%d", domain["domain"].(string), port)
if index > 0 { if index > 0 {
links += "\n" links += "\n"
@@ -588,7 +589,7 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
return links return links
} }
url.Fragment = remark url.Fragment = s.genRemark(inbound, email, "")
return url.String() return url.String()
} }
@@ -671,12 +672,55 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
// Set the new query values on the URL // Set the new query values on the URL
url.RawQuery = q.Encode() url.RawQuery = q.Encode()
url.Fragment = s.genRemark(inbound, email, "")
remark := fmt.Sprintf("%s-%s", inbound.Remark, clients[clientIndex].Email)
url.Fragment = remark
return url.String() return url.String()
} }
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 {
return inbound.Remark
}
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 "N/A"
}
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) { func searchKey(data interface{}, key string) (interface{}, bool) {
switch val := data.(type) { switch val := data.(type) {
case map[string]interface{}: case map[string]interface{}:

View File

@@ -192,6 +192,7 @@ class AllSetting {
this.subKeyFile = ""; this.subKeyFile = "";
this.subUpdates = 0; this.subUpdates = 0;
this.subEncrypt = true; this.subEncrypt = true;
this.subShowInfo = false;
this.timeLocation = "Asia/Tehran"; this.timeLocation = "Asia/Tehran";

View File

@@ -65,6 +65,7 @@ func (a *SettingController) getDefaultSettings(c *gin.Context) {
"subKeyFile": func() (interface{}, error) { return a.settingService.GetSubKeyFile() }, "subKeyFile": func() (interface{}, error) { return a.settingService.GetSubKeyFile() },
"subCertFile": func() (interface{}, error) { return a.settingService.GetSubCertFile() }, "subCertFile": func() (interface{}, error) { return a.settingService.GetSubCertFile() },
"subEncrypt": func() (interface{}, error) { return a.settingService.GetSubEncrypt() }, "subEncrypt": func() (interface{}, error) { return a.settingService.GetSubEncrypt() },
"subShowInfo": func() (interface{}, error) { return a.settingService.GetSubShowInfo() },
} }
result := make(map[string]interface{}) result := make(map[string]interface{})

View File

@@ -55,6 +55,7 @@ type AllSetting struct {
SubKeyFile string `json:"subKeyFile" form:"subKeyFile"` SubKeyFile string `json:"subKeyFile" form:"subKeyFile"`
SubUpdates int `json:"subUpdates" form:"subUpdates"` SubUpdates int `json:"subUpdates" form:"subUpdates"`
SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"` SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"`
SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"`
} }
func (s *AllSetting) CheckValid() error { func (s *AllSetting) CheckValid() error {

View File

@@ -338,6 +338,7 @@
<a-list item-layout="horizontal" :style="themeSwitcher.textStyle"> <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.subEnable"}}' desc='{{ i18n "pages.settings.subEnableDesc"}}' v-model="allSetting.subEnable"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.settings.subEncrypt"}}' desc='{{ i18n "pages.settings.subEncryptDesc"}}' v-model="allSetting.subEncrypt"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.settings.subEncrypt"}}' desc='{{ i18n "pages.settings.subEncryptDesc"}}' v-model="allSetting.subEncrypt"></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.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="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> <setting-list-item type="number" title='{{ i18n "pages.settings.subPort"}}' desc='{{ i18n "pages.settings.subPortDesc"}}' v-model.number="allSetting.subPort"></setting-list-item>

View File

@@ -51,6 +51,7 @@ var defaultValueMap = map[string]string{
"subKeyFile": "", "subKeyFile": "",
"subUpdates": "12", "subUpdates": "12",
"subEncrypt": "true", "subEncrypt": "true",
"subShowInfo": "false",
} }
type SettingService struct { type SettingService struct {
@@ -377,6 +378,10 @@ func (s *SettingService) GetSubEncrypt() (bool, error) {
return s.getBool("subEncrypt") return s.getBool("subEncrypt")
} }
func (s *SettingService) GetSubShowInfo() (bool, error) {
return s.getBool("subShowInfo")
}
func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error { func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error {
if err := allSetting.CheckValid(); err != nil { if err := allSetting.CheckValid(); err != nil {
return err return err

View File

@@ -267,7 +267,8 @@
"subUpdatesDesc" = "Interval hours between updates in client application" "subUpdatesDesc" = "Interval hours between updates in client application"
"subEncrypt" = "Encrypt configs" "subEncrypt" = "Encrypt configs"
"subEncryptDesc" = "Encrypt the returned configs in subscription" "subEncryptDesc" = "Encrypt the returned configs in subscription"
"subShowInfo" = "Show usage info"
"subShowInfoDesc" = "Show remianed traffic and date after config name"
[pages.settings.templates] [pages.settings.templates]
"title" = "Templates" "title" = "Templates"

View File

@@ -266,6 +266,8 @@
"subUpdatesDesc" = "ساعت های فاصله بین به روز رسانی در برنامه کاربر" "subUpdatesDesc" = "ساعت های فاصله بین به روز رسانی در برنامه کاربر"
"subEncrypt" = "رمزگذاری کانفیگ ها" "subEncrypt" = "رمزگذاری کانفیگ ها"
"subEncryptDesc" = "رمزگذاری کانفیگ های بازگشتی سابسکریپشن" "subEncryptDesc" = "رمزگذاری کانفیگ های بازگشتی سابسکریپشن"
"subShowInfo" = "نمایش اطلاعات مصرف"
"subShowInfoDesc" = "ترافیک و زمان باقیمانده را در هر کانفیگ نمایش میدهد"
[pages.settings.templates] [pages.settings.templates]
"title" = "الگوها" "title" = "الگوها"

View File

@@ -267,6 +267,8 @@
"subUpdatesDesc" = "Часовой интервал между обновлениями в клиентском приложении" "subUpdatesDesc" = "Часовой интервал между обновлениями в клиентском приложении"
"subEncrypt" = "Шифрование конфигураций" "subEncrypt" = "Шифрование конфигураций"
"subEncryptDesc" = "Шифрование возвращаемых конфигураций в подписке" "subEncryptDesc" = "Шифрование возвращаемых конфигураций в подписке"
"subShowInfo" = "Показать информацию об использовании"
"subShowInfoDesc" = "Показывать восстановленный трафик и дату после имени конфигурации"
[pages.settings.templates] [pages.settings.templates]
"title" = "Шаблоны" "title" = "Шаблоны"

View File

@@ -267,6 +267,8 @@
"subUpdatesDesc" = "客户端应用程序更新之间的间隔时间" "subUpdatesDesc" = "客户端应用程序更新之间的间隔时间"
"subEncrypt" = "加密配置" "subEncrypt" = "加密配置"
"subEncryptDesc" = "在订阅中加密返回的配置" "subEncryptDesc" = "在订阅中加密返回的配置"
"subShowInfo" = "显示使用信息"
"subShowInfoDesc" = "在配置名称后显示剩余流量和日期"
[pages.settings.templates] [pages.settings.templates]
"title" = "模板" "title" = "模板"