mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-04-19 21:42:24 +00:00
[sub] improve usage info in Remark
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
This commit is contained in:
parent
71c1a05386
commit
38e1d0f94e
12 changed files with 91 additions and 108 deletions
|
@ -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 {
|
||||
|
|
|
@ -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,22 +758,55 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string, ex
|
|||
|
||||
// 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⛔️", clients[clientIndex].Email, "Terminated")
|
||||
} else {
|
||||
remark = fmt.Sprintf("%s: %s - %s", clients[clientIndex].Email, remainedTraffic, expiryTimeString)
|
||||
}
|
||||
|
||||
url.Fragment = remark
|
||||
url.Fragment = s.genRemark(inbound, email, "")
|
||||
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 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) {
|
||||
switch val := data.(type) {
|
||||
case map[string]interface{}:
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -194,6 +194,7 @@ class AllSetting {
|
|||
this.subCertFile = "";
|
||||
this.subKeyFile = "";
|
||||
this.subUpdates = 0;
|
||||
this.subShowInfo = false;
|
||||
|
||||
this.timeLocation = "Asia/Tehran";
|
||||
|
||||
|
|
|
@ -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{})
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -274,6 +274,8 @@
|
|||
"subDomainDesc" = "برای نظارت بر همه دامنه ها و آیپی ها به طور پیش فرض خالی بگذارید"
|
||||
"subUpdates" = "فاصله به روز رسانی های سابسکریپشن"
|
||||
"subUpdatesDesc" = "ساعت های فاصله بین به روز رسانی در برنامه کاربر"
|
||||
"subShowInfo" = "نمایش اطلاعات مصرف"
|
||||
"subShowInfoDesc" = "ترافیک و زمان باقیمانده را در هر کانفیگ نمایش میدهد"
|
||||
|
||||
[pages.settings.templates]
|
||||
"title" = "الگوها"
|
||||
|
|
|
@ -274,6 +274,8 @@
|
|||
"subDomainDesc" = "Оставьте пустым по умолчанию, чтобы отслеживать все домены и IP-адреса"
|
||||
"subUpdates" = "Интервалы обновления подписки"
|
||||
"subUpdatesDesc" = "Часовой интервал между обновлениями в клиентском приложении"
|
||||
"subShowInfo" = "Показать информацию об использовании"
|
||||
"subShowInfoDesc" = "Показывать восстановленный трафик и дату после имени конфигурации"
|
||||
|
||||
[pages.settings.templates]
|
||||
"title" = "Шаблоны"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -274,6 +274,8 @@
|
|||
"subDomainDesc" = "留空默认监控所有域名和IP"
|
||||
"subUpdates" = "订阅更新间隔"
|
||||
"subUpdatesDesc" = "客户端应用程序更新之间的间隔时间"
|
||||
"subShowInfo" = "显示使用信息"
|
||||
"subShowInfoDesc" = "在配置名称后显示剩余流量和日期"
|
||||
|
||||
[pages.settings.templates]
|
||||
"title" = "模板"
|
||||
|
|
Loading…
Reference in a new issue