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 (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"strings"
|
"strings"
|
||||||
|
"x-ui/web/service"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SUBController struct {
|
type SUBController struct {
|
||||||
subService SubService
|
subService SubService
|
||||||
|
settingService service.SettingService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSUBController(g *gin.RouterGroup) *SUBController {
|
func NewSUBController(g *gin.RouterGroup) *SUBController {
|
||||||
|
@ -24,9 +26,10 @@ func (a *SUBController) initRouter(g *gin.RouterGroup) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *SUBController) subs(c *gin.Context) {
|
func (a *SUBController) subs(c *gin.Context) {
|
||||||
|
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 {
|
||||||
|
|
|
@ -18,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
|
||||||
|
@ -57,7 +59,7 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, []string, err
|
||||||
}
|
}
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
if client.Enable && client.SubID == subId {
|
if client.Enable && client.SubID == subId {
|
||||||
link := s.getLink(inbound, client.Email, client.ExpiryTime)
|
link := s.getLink(inbound, client.Email)
|
||||||
result = append(result, link)
|
result = append(result, link)
|
||||||
clientTraffics = append(clientTraffics, s.getClientTraffics(inbound.ClientStats, client.Email))
|
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
|
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 {
|
switch inbound.Protocol {
|
||||||
case "vmess":
|
case "vmess":
|
||||||
return s.genVmessLink(inbound, email, expiryTime)
|
return s.genVmessLink(inbound, email)
|
||||||
case "vless":
|
case "vless":
|
||||||
return s.genVlessLink(inbound, email, expiryTime)
|
return s.genVlessLink(inbound, email)
|
||||||
case "trojan":
|
case "trojan":
|
||||||
return s.genTrojanLink(inbound, email, expiryTime)
|
return s.genTrojanLink(inbound, email)
|
||||||
case "shadowsocks":
|
case "shadowsocks":
|
||||||
return s.genShadowsocksLink(inbound, email, expiryTime)
|
return s.genShadowsocksLink(inbound, email)
|
||||||
}
|
}
|
||||||
return ""
|
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 {
|
if inbound.Protocol != model.VMess {
|
||||||
return ""
|
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{}{
|
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",
|
||||||
|
@ -254,7 +243,7 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string, expiryTi
|
||||||
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"
|
||||||
|
@ -265,11 +254,13 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string, expiryTi
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
address := s.address
|
||||||
if inbound.Protocol != model.VLESS {
|
if inbound.Protocol != model.VLESS {
|
||||||
return ""
|
return ""
|
||||||
|
@ -462,22 +453,11 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string, expiryTi
|
||||||
// Set the new query values on the URL
|
// Set the new query values on the URL
|
||||||
url.RawQuery = q.Encode()
|
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 {
|
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"
|
||||||
|
@ -486,11 +466,12 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string, expiryTi
|
||||||
}
|
}
|
||||||
return links
|
return links
|
||||||
}
|
}
|
||||||
url.Fragment = remark
|
|
||||||
|
url.Fragment = s.genRemark(inbound, email, "")
|
||||||
return url.String()
|
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
|
address := s.address
|
||||||
if inbound.Protocol != model.Trojan {
|
if inbound.Protocol != model.Trojan {
|
||||||
return ""
|
return ""
|
||||||
|
@ -680,22 +661,11 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string, expiryT
|
||||||
// Set the new query values on the URL
|
// Set the new query values on the URL
|
||||||
url.RawQuery = q.Encode()
|
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 {
|
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"
|
||||||
|
@ -705,11 +675,11 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string, expiryT
|
||||||
return links
|
return links
|
||||||
}
|
}
|
||||||
|
|
||||||
url.Fragment = remark
|
url.Fragment = s.genRemark(inbound, email, "")
|
||||||
return url.String()
|
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
|
address := s.address
|
||||||
if inbound.Protocol != model.Shadowsocks {
|
if inbound.Protocol != model.Shadowsocks {
|
||||||
return ""
|
return ""
|
||||||
|
@ -788,20 +758,53 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string, ex
|
||||||
|
|
||||||
// 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, "")
|
||||||
remainedTraffic := s.getRemainedTraffic(email)
|
return url.String()
|
||||||
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
|
func (s *SubService) genRemark(inbound *model.Inbound, email string, extra string) string {
|
||||||
return url.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) {
|
func searchKey(data interface{}, key string) (interface{}, bool) {
|
||||||
|
@ -845,45 +848,3 @@ func searchHost(headers interface{}) string {
|
||||||
|
|
||||||
return ""
|
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.subCertFile = "";
|
||||||
this.subKeyFile = "";
|
this.subKeyFile = "";
|
||||||
this.subUpdates = 0;
|
this.subUpdates = 0;
|
||||||
|
this.subShowInfo = false;
|
||||||
|
|
||||||
this.timeLocation = "Asia/Tehran";
|
this.timeLocation = "Asia/Tehran";
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,7 @@ func (a *SettingController) getDefaultSettings(c *gin.Context) {
|
||||||
"subDomain": func() (interface{}, error) { return a.settingService.GetSubDomain() },
|
"subDomain": func() (interface{}, error) { return a.settingService.GetSubDomain() },
|
||||||
"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() },
|
||||||
|
"subShowInfo": func() (interface{}, error) { return a.settingService.GetSubShowInfo() },
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make(map[string]interface{})
|
result := make(map[string]interface{})
|
||||||
|
|
|
@ -55,6 +55,7 @@ type AllSetting struct {
|
||||||
SubCertFile string `json:"subCertFile" form:"subCertFile"`
|
SubCertFile string `json:"subCertFile" form:"subCertFile"`
|
||||||
SubKeyFile string `json:"subKeyFile" form:"subKeyFile"`
|
SubKeyFile string `json:"subKeyFile" form:"subKeyFile"`
|
||||||
SubUpdates int `json:"subUpdates" form:"subUpdates"`
|
SubUpdates int `json:"subUpdates" form:"subUpdates"`
|
||||||
|
SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AllSetting) CheckValid() error {
|
func (s *AllSetting) CheckValid() error {
|
||||||
|
|
|
@ -406,6 +406,7 @@
|
||||||
</a-row>
|
</a-row>
|
||||||
<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.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>
|
||||||
|
|
|
@ -51,6 +51,7 @@ var defaultValueMap = map[string]string{
|
||||||
"subCertFile": "",
|
"subCertFile": "",
|
||||||
"subKeyFile": "",
|
"subKeyFile": "",
|
||||||
"subUpdates": "12",
|
"subUpdates": "12",
|
||||||
|
"subShowInfo": "false",
|
||||||
}
|
}
|
||||||
|
|
||||||
type SettingService struct {
|
type SettingService struct {
|
||||||
|
@ -396,6 +397,10 @@ func (s *SettingService) GetSubUpdates() (int, error) {
|
||||||
return s.getInt("subUpdates")
|
return s.getInt("subUpdates")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
@ -274,6 +274,8 @@
|
||||||
"subDomainDesc" = "Leave blank by default to monitor all domains and IPs"
|
"subDomainDesc" = "Leave blank by default to monitor all domains and IPs"
|
||||||
"subUpdates" = "Subscription update intervals"
|
"subUpdates" = "Subscription update intervals"
|
||||||
"subUpdatesDesc" = "Interval hours between updates in client application"
|
"subUpdatesDesc" = "Interval hours between updates in client application"
|
||||||
|
"subShowInfo" = "Show usage info"
|
||||||
|
"subShowInfoDesc" = "Show remianed traffic and date after config name"
|
||||||
|
|
||||||
[pages.settings.templates]
|
[pages.settings.templates]
|
||||||
"title" = "Templates"
|
"title" = "Templates"
|
||||||
|
|
|
@ -274,6 +274,8 @@
|
||||||
"subDomainDesc" = "برای نظارت بر همه دامنه ها و آیپی ها به طور پیش فرض خالی بگذارید"
|
"subDomainDesc" = "برای نظارت بر همه دامنه ها و آیپی ها به طور پیش فرض خالی بگذارید"
|
||||||
"subUpdates" = "فاصله به روز رسانی های سابسکریپشن"
|
"subUpdates" = "فاصله به روز رسانی های سابسکریپشن"
|
||||||
"subUpdatesDesc" = "ساعت های فاصله بین به روز رسانی در برنامه کاربر"
|
"subUpdatesDesc" = "ساعت های فاصله بین به روز رسانی در برنامه کاربر"
|
||||||
|
"subShowInfo" = "نمایش اطلاعات مصرف"
|
||||||
|
"subShowInfoDesc" = "ترافیک و زمان باقیمانده را در هر کانفیگ نمایش میدهد"
|
||||||
|
|
||||||
[pages.settings.templates]
|
[pages.settings.templates]
|
||||||
"title" = "الگوها"
|
"title" = "الگوها"
|
||||||
|
|
|
@ -274,6 +274,8 @@
|
||||||
"subDomainDesc" = "Оставьте пустым по умолчанию, чтобы отслеживать все домены и IP-адреса"
|
"subDomainDesc" = "Оставьте пустым по умолчанию, чтобы отслеживать все домены и IP-адреса"
|
||||||
"subUpdates" = "Интервалы обновления подписки"
|
"subUpdates" = "Интервалы обновления подписки"
|
||||||
"subUpdatesDesc" = "Часовой интервал между обновлениями в клиентском приложении"
|
"subUpdatesDesc" = "Часовой интервал между обновлениями в клиентском приложении"
|
||||||
|
"subShowInfo" = "Показать информацию об использовании"
|
||||||
|
"subShowInfoDesc" = "Показывать восстановленный трафик и дату после имени конфигурации"
|
||||||
|
|
||||||
[pages.settings.templates]
|
[pages.settings.templates]
|
||||||
"title" = "Шаблоны"
|
"title" = "Шаблоны"
|
||||||
|
|
|
@ -274,6 +274,8 @@
|
||||||
"subDomainDesc" = "Mặc định để trống để nghe tất cả các tên miền và IP"
|
"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ý"
|
"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"
|
"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]
|
[pages.settings.templates]
|
||||||
"title" = "Mẫu"
|
"title" = "Mẫu"
|
||||||
|
|
|
@ -274,6 +274,8 @@
|
||||||
"subDomainDesc" = "留空默认监控所有域名和IP"
|
"subDomainDesc" = "留空默认监控所有域名和IP"
|
||||||
"subUpdates" = "订阅更新间隔"
|
"subUpdates" = "订阅更新间隔"
|
||||||
"subUpdatesDesc" = "客户端应用程序更新之间的间隔时间"
|
"subUpdatesDesc" = "客户端应用程序更新之间的间隔时间"
|
||||||
|
"subShowInfo" = "显示使用信息"
|
||||||
|
"subShowInfoDesc" = "在配置名称后显示剩余流量和日期"
|
||||||
|
|
||||||
[pages.settings.templates]
|
[pages.settings.templates]
|
||||||
"title" = "模板"
|
"title" = "模板"
|
||||||
|
|
Loading…
Reference in a new issue