diff --git a/sub/subService.go b/sub/subService.go index a1cbbb0f..251ef2e8 100644 --- a/sub/subService.go +++ b/sub/subService.go @@ -19,6 +19,7 @@ import ( type SubService struct { address string showInfo bool + remarkModel string inboundService service.InboundService settingService service.SettingService } @@ -34,6 +35,10 @@ func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string if err != nil { return nil, nil, err } + s.remarkModel, err = s.settingService.GetRemarkModel() + if err != nil { + s.remarkModel = "-ieo" + } for _, inbound := range inbounds { clients, err := s.inboundService.GetClients(inbound) if err != nil { @@ -857,17 +862,30 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st } func (s *SubService) genRemark(inbound *model.Inbound, email string, extra string) string { - var remark []string + separationChar := string(s.remarkModel[0]) + orderChars := s.remarkModel[1:] + orders := map[byte]string{ + 'i': "", + 'e': "", + 'o': "", + } if len(email) > 0 { - if len(inbound.Remark) > 0 { - remark = append(remark, inbound.Remark) + orders['e'] = email + } + if len(inbound.Remark) > 0 { + orders['i'] = inbound.Remark + } + if len(extra) > 0 { + orders['e'] = extra + } + + var remark []string + for i := 0; i < len(orderChars); i++ { + char := orderChars[i] + order, exists := orders[char] + if exists && order != "" { + remark = append(remark, order) } - remark = append(remark, email) - if len(extra) > 0 { - remark = append(remark, extra) - } - } else { - return inbound.Remark } if s.showInfo { @@ -884,7 +902,7 @@ func (s *SubService) genRemark(inbound *model.Inbound, email string, extra strin // Get remained days if statsExist { if !stats.Enable { - return fmt.Sprintf("⛔️N/A-%s", strings.Join(remark, "-")) + return fmt.Sprintf("⛔️N/A%s%s", separationChar, strings.Join(remark, separationChar)) } if vol := stats.Total - (stats.Up + stats.Down); vol > 0 { remark = append(remark, fmt.Sprintf("%s%s", common.FormatTraffic(vol), "📊")) @@ -898,7 +916,7 @@ func (s *SubService) genRemark(inbound *model.Inbound, email string, extra strin } } } - return strings.Join(remark, " : ") + return strings.Join(remark, separationChar) } func searchKey(data interface{}, key string) (interface{}, bool) { diff --git a/web/assets/js/model/dbinbound.js b/web/assets/js/model/dbinbound.js index 7f4f0812..a7157f49 100644 --- a/web/assets/js/model/dbinbound.js +++ b/web/assets/js/model/dbinbound.js @@ -137,8 +137,8 @@ class DBInbound { } } - get genInboundLinks() { + genInboundLinks() { const inbound = this.toInbound(); - return inbound.genInboundLinks(this.remark); + return inbound.genInboundLinks(this.remark,remarkModel); } } \ No newline at end of file diff --git a/web/assets/js/model/setting.js b/web/assets/js/model/setting.js index 06f66f81..61b7b532 100644 --- a/web/assets/js/model/setting.js +++ b/web/assets/js/model/setting.js @@ -11,6 +11,7 @@ class AllSetting { this.pageSize = 0; this.expireDiff = ""; this.trafficDiff = ""; + this.remarkModel = "-ieo"; this.tgBotEnable = false; this.tgBotToken = ""; this.tgBotChatId = ""; diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js index ba8b322e..58d3491a 100644 --- a/web/assets/js/model/xray.js +++ b/web/assets/js/model/xray.js @@ -1566,20 +1566,28 @@ class Inbound extends XrayCommonClass { } } - genAllLinks(remark='', client){ + genAllLinks(remark='', remarkModel = '-ieo', client){ let result = []; let email = client ? client.email : ''; let addr = !ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0" ? this.listen : location.hostname; - let port = this.port + let port = this.port; + const separationChar = remarkModel.charAt(0); + const orderChars = remarkModel.slice(1); + let orders = { + 'i': remark, + 'e': client ? client.email : '', + 'o': '', + }; if(ObjectUtil.isArrEmpty(this.stream.externalProxy)){ - let r = [remark, email].filter(x => x.length > 0).join('-'); + let r = orderChars.split('').map(char => orders[char]).filter(x => x.length > 0).join(separationChar); result.push({ remark: r, link: this.genLink(addr, port, 'same', r, client) }); } else { this.stream.externalProxy.forEach((ep) => { - let r = [remark, email, ep.remark].filter(x => x.length > 0).join('-') + orders['o'] = ep.remark; + let r = orderChars.split('').map(char => orders[char]).filter(x => x.length > 0).join(separationChar); result.push({ remark: r, link: this.genLink(ep.dest, ep.port, ep.forceTls, r, client) @@ -1589,11 +1597,11 @@ class Inbound extends XrayCommonClass { return result; } - genInboundLinks(remark = '') { + genInboundLinks(remark = '', remarkModel = '-ieo') { if(this.clients){ let links = []; this.clients.forEach((client) => { - genAllLinks(remark,client).forEach(l => { + genAllLinks(remark,remarkModel,client).forEach(l => { links.push(l.link); }) }); diff --git a/web/entity/entity.go b/web/entity/entity.go index 94f0171f..d00f51e1 100644 --- a/web/entity/entity.go +++ b/web/entity/entity.go @@ -25,6 +25,7 @@ type AllSetting struct { PageSize int `json:"pageSize" form:"pageSize"` ExpireDiff int `json:"expireDiff" form:"expireDiff"` TrafficDiff int `json:"trafficDiff" form:"trafficDiff"` + RemarkModel string `json:"remarkModel" form:"remarkModel"` TgBotEnable bool `json:"tgBotEnable" form:"tgBotEnable"` TgBotToken string `json:"tgBotToken" form:"tgBotToken"` TgBotChatId string `json:"tgBotChatId" form:"tgBotChatId"` diff --git a/web/html/common/qrcode_modal.html b/web/html/common/qrcode_modal.html index 0df369ff..4be7997d 100644 --- a/web/html/common/qrcode_modal.html +++ b/web/html/common/qrcode_modal.html @@ -35,7 +35,7 @@ this.client = client; this.subId = ''; this.qrcodes = []; - this.inbound.genAllLinks(this.dbInbound.remark, client).forEach(l => { + this.inbound.genAllLinks(this.dbInbound.remark, app.remarkModel, client).forEach(l => { this.qrcodes.push({ remark: l.remark, link: l.link diff --git a/web/html/xui/inbound_info_modal.html b/web/html/xui/inbound_info_modal.html index e92fc874..bd1fea72 100644 --- a/web/html/xui/inbound_info_modal.html +++ b/web/html/xui/inbound_info_modal.html @@ -264,7 +264,7 @@ this.clientSettings = this.inbound.clients ? this.inbound.clients[index] : null; this.isExpired = this.inbound.clients ? this.inbound.isExpiry(index): this.dbInbound.isExpiry; this.clientStats = this.inbound.clients ? this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email) : []; - this.links = this.inbound.genAllLinks(this.dbInbound.remark, this.clientSettings); + this.links = this.inbound.genAllLinks(this.dbInbound.remark, app.remarkModel, this.clientSettings); if (this.clientSettings) { if (this.clientSettings.subId) { this.subLink = this.genSubLink(this.clientSettings.subId); diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html index 31595f00..14371631 100644 --- a/web/html/xui/inbounds.html +++ b/web/html/xui/inbounds.html @@ -556,6 +556,7 @@ enable : false, subURI : '' }, + remarkModel: '-ieo', tgBotEnable: false, pageSize: 0, isMobile: window.innerWidth <= 768, @@ -600,6 +601,7 @@ subURI: subURI }; this.pageSize = pageSize; + this.remarkModel = remarkModel; } }, setInbounds(dbInbounds) { @@ -1191,7 +1193,7 @@ exportAllLinks() { let copyText = []; for (const dbInbound of this.dbInbounds) { - copyText.push(dbInbound.genInboundLinks); + copyText.push(dbInbound.genInboundLinks(this.remarkModel)); } txtModal.show('{{ i18n "pages.inbounds.export"}}', copyText.join('\r\n'), 'All-Inbounds'); }, diff --git a/web/html/xui/settings.html b/web/html/xui/settings.html index 24cef2af..f40b20a5 100644 --- a/web/html/xui/settings.html +++ b/web/html/xui/settings.html @@ -105,6 +105,28 @@ + + + + + + + + + + + [[ value ]] + + + [[ key ]] + + + + + @@ -272,6 +294,31 @@ saveBtnDisable: true, user: {}, lang: getLang(), + showAlert: false, + remarkModels: {i:'Inbound',e:'Email',o:'Other'}, + remarkSeparators: [' ','-','_','@',':','~','|',',','.','/'], + remarkSample: '', + get remarkModel() { + rm = this.allSetting.remarkModel; + return rm.length>1 ? rm.substring(1).split('') : []; + }, + set remarkModel(value) { + rs = this.allSetting.remarkModel[0]; + this.allSetting.remarkModel = rs + value.join(''); + this.changeRemarkSample(); + }, + get remarkSeparator() { + return this.allSetting.remarkModel.length > 1 ? this.allSetting.remarkModel.charAt(0) : '-'; + }, + set remarkSeparator(value) { + this.allSetting.remarkModel = value + this.allSetting.remarkModel.substring(1); + this.changeRemarkSample(); + }, + changeRemarkSample(){ + sample = [] + this.remarkModel.forEach(r => sample.push(this.remarkModels[r])); + this.remarkSample = sample.length == 0 ? '' : sample.join(this.remarkSeparator); + } }, methods: { loading(spinning = true) { @@ -284,6 +331,7 @@ if (msg.success) { this.oldAllSetting = new AllSetting(msg.obj); this.allSetting = new AllSetting(msg.obj); + app.changeRemarkSample(); this.saveBtnDisable = true; } await this.fetchUserSecret(); diff --git a/web/service/setting.go b/web/service/setting.go index d6cf193e..8b60d166 100644 --- a/web/service/setting.go +++ b/web/service/setting.go @@ -34,6 +34,7 @@ var defaultValueMap = map[string]string{ "pageSize": "0", "expireDiff": "0", "trafficDiff": "0", + "remarkModel": "-ieo", "timeLocation": "Asia/Tehran", "tgBotEnable": "false", "tgBotToken": "", @@ -311,6 +312,10 @@ func (s *SettingService) GetSessionMaxAge() (int, error) { return s.getInt("sessionMaxAge") } +func (s *SettingService) GetRemarkModel() (string, error) { + return s.getString("remarkModel") +} + func (s *SettingService) GetSecretStatus() (bool, error) { return s.getBool("secretEnable") } @@ -457,6 +462,7 @@ func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) { "tgBotEnable": func() (interface{}, error) { return s.GetTgbotenabled() }, "subEnable": func() (interface{}, error) { return s.GetSubEnable() }, "subURI": func() (interface{}, error) { return s.GetSubURI() }, + "remarkModel": func() (interface{}, error) { return s.GetRemarkModel() }, } result := make(map[string]interface{}) diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index 63b07cfd..e704bf73 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -245,6 +245,8 @@ "panelUrlPathDesc" = "Must start with '/' and end with." "pageSize" = "Pagination size" "pageSizeDesc" = "Define page size for inbounds table. Set 0 to disable" +"remarkModel" = "Remark Model and Seperation charachter" +"sampleRemark" = "Sample remark" "oldUsername" = "Current Username" "currentPassword" = "Current Password" "newUsername" = "New Username" diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index e2dd44f7..b3a53ba4 100644 --- a/web/translation/translate.es_ES.toml +++ b/web/translation/translate.es_ES.toml @@ -245,6 +245,8 @@ "panelUrlPathDesc" = "Debe empezar con '/' y terminar con." "pageSize" = "Tamaño de paginación" "pageSizeDesc" = "Defina el tamaño de página para la tabla de entradas. Establezca 0 para desactivar" +"remarkModel" = "Modelo de observación y carácter de separación" +"sampleRemark" = "Observación de muestra" "oldUsername" = "Nombre de Usuario Actual" "currentPassword" = "Contraseña Actual" "newUsername" = "Nuevo Nombre de Usuario" diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index e63174ef..239ddcae 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -245,6 +245,8 @@ "panelUrlPathDesc" = "باید با '/' شروع شود و با '/' تمام شود" "pageSize" = "اندازه صفحه بندی جدول" "pageSizeDesc" = "اندازه صفحه را برای جدول سرویس ها تعریف کنید. 0: غیرفعال" +"remarkModel" = "نام کانفیگ و جداکننده" +"sampleRemark" = "نمونه نام" "oldUsername" = "نام کاربری فعلی" "currentPassword" = "رمز عبور فعلی" "newUsername" = "نام کاربری جدید" diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index ab12169a..44c4608e 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -245,6 +245,8 @@ "panelUrlPathDesc" = "Должен начинаться с '/' и заканчиваться на" "pageSize" = "Размер нумерации страниц" "pageSizeDesc" = "Определить размер страницы для входящей таблицы. Установите 0, чтобы отключить" +"remarkModel" = "Модель примечания и символ разделения" +"sampleRemark" = "Пример замечания" "oldUsername" = "Текущее имя пользователя" "currentPassword" = "Текущий пароль" "newUsername" = "Новое имя пользователя" diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml index d9426c77..2032e95b 100644 --- a/web/translation/translate.vi_VN.toml +++ b/web/translation/translate.vi_VN.toml @@ -245,6 +245,8 @@ "panelUrlPathDesc" = "Phải bắt đầu bằng '/' và kết thúc bằng." "pageSize" = "Kích thước phân trang" "pageSizeDesc" = "Xác định kích thước trang cho bảng gửi đến. Đặt 0 để tắt" +"remarkModel" = "Ghi chú mô hình và ký tự phân tách" +"sampleRemark" = "Nhận xét mẫu" "oldUsername" = "Tên người dùng hiện tại" "currentPassword" = "Mật khẩu hiện tại" "newUsername" = "Tên người dùng mới" diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml index 47ccba87..8cc05e09 100644 --- a/web/translation/translate.zh_Hans.toml +++ b/web/translation/translate.zh_Hans.toml @@ -245,6 +245,8 @@ "panelUrlPathDesc" = "必须以 '/' 开头,以 '/' 结尾" "pageSize" = "分页大小" "pageSizeDesc" = "定义入站表的页面大小。设置 0 表示禁用" +"remarkModel" = "备注模型和分隔符" +"sampleRemark" = "备注示例" "oldUsername" = "原用户名" "currentPassword" = "原密码" "newUsername" = "新用户名"