diff options
| author | Ho3ein <ho3ein.sanaei@gmail.com> | 2023-05-31 09:17:02 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-31 09:17:02 +0300 |
| commit | 94fad02737d82817ca69f1f05872b49e769a0cb4 (patch) | |
| tree | 55e7ead217c5a3e82791c0edae6ad44de1ba524f /web | |
| parent | 8442836512d82b705e404bc1749e3000115ba550 (diff) | |
| parent | d694e6eafccad246c63264714897316f671d6428 (diff) | |
Merge pull request #545 from hamid-gh98/main
🔀 New Feature + Fix URLs + Some Improvements 🛠️🌐
Diffstat (limited to 'web')
| -rw-r--r-- | web/assets/js/model/models.js | 3 | ||||
| -rw-r--r-- | web/assets/js/util/common.js | 18 | ||||
| -rw-r--r-- | web/controller/inbound.go | 2 | ||||
| -rw-r--r-- | web/controller/setting.go | 97 | ||||
| -rw-r--r-- | web/entity/entity.go | 1 | ||||
| -rw-r--r-- | web/global/hashStorage.go | 2 | ||||
| -rw-r--r-- | web/html/common/qrcode_modal.html | 30 | ||||
| -rw-r--r-- | web/html/xui/inbound_info_modal.html | 8 | ||||
| -rw-r--r-- | web/html/xui/inbound_modal.html | 2 | ||||
| -rw-r--r-- | web/html/xui/inbounds.html | 2 | ||||
| -rw-r--r-- | web/html/xui/settings.html | 59 | ||||
| -rw-r--r-- | web/middleware/domainValidator.go | 21 | ||||
| -rw-r--r-- | web/middleware/redirect.go | 34 | ||||
| -rw-r--r-- | web/service/inbound.go | 1 | ||||
| -rw-r--r-- | web/service/setting.go | 7 | ||||
| -rw-r--r-- | web/service/tgbot.go | 47 | ||||
| -rw-r--r-- | web/translation/translate.en_US.toml | 7 | ||||
| -rw-r--r-- | web/translation/translate.fa_IR.toml | 7 | ||||
| -rw-r--r-- | web/translation/translate.ru_RU.toml | 7 | ||||
| -rw-r--r-- | web/translation/translate.zh_Hans.toml | 7 | ||||
| -rw-r--r-- | web/web.go | 34 |
21 files changed, 231 insertions, 165 deletions
diff --git a/web/assets/js/model/models.js b/web/assets/js/model/models.js index 9a5dcc85..e1a766dc 100644 --- a/web/assets/js/model/models.js +++ b/web/assets/js/model/models.js @@ -168,6 +168,7 @@ class AllSetting { constructor(data) { this.webListen = ""; + this.webDomain = ""; this.webPort = 2053; this.webCertFile = ""; this.webKeyFile = ""; @@ -187,7 +188,7 @@ class AllSetting { this.subEnable = false; this.subListen = ""; this.subPort = "2096"; - this.subPath = "sub/"; + this.subPath = "/sub/"; this.subDomain = ""; this.subCertFile = ""; this.subKeyFile = ""; diff --git a/web/assets/js/util/common.js b/web/assets/js/util/common.js index 563bfd45..8e30bce7 100644 --- a/web/assets/js/util/common.js +++ b/web/assets/js/util/common.js @@ -135,3 +135,21 @@ function doAllItemsExist(array1, array2) { } return true; } + +function buildURL({ host, port, isTLS, base, path }) { + if (!host || host.length === 0) host = window.location.hostname; + if (!port || port.length === 0) port = window.location.port; + + if (isTLS === undefined) isTLS = window.location.protocol === "https:"; + + const protocol = isTLS ? "https:" : "http:"; + + port = String(port); + if (port === "" || (isTLS && port === "443") || (!isTLS && port === "80")) { + port = ""; + } else { + port = `:${port}`; + } + + return `${protocol}//${host}${port}${base}${path}`; +} diff --git a/web/controller/inbound.go b/web/controller/inbound.go index 5ce58d53..815f1788 100644 --- a/web/controller/inbound.go +++ b/web/controller/inbound.go @@ -65,6 +65,7 @@ func (a *InboundController) getInbounds(c *gin.Context) { } jsonObj(c, inbounds, nil) } + func (a *InboundController) getInbound(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { @@ -168,6 +169,7 @@ func (a *InboundController) clearClientIps(c *gin.Context) { } jsonMsg(c, "Log Cleared", nil) } + func (a *InboundController) addInboundClient(c *gin.Context) { data := &model.Inbound{} err := c.ShouldBind(data) diff --git a/web/controller/setting.go b/web/controller/setting.go index 0292c46a..cd509293 100644 --- a/web/controller/setting.go +++ b/web/controller/setting.go @@ -65,77 +65,42 @@ func (a *SettingController) getDefaultJsonConfig(c *gin.Context) { } func (a *SettingController) getDefaultSettings(c *gin.Context) { - expireDiff, err := a.settingService.GetExpireDiff() - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) - return - } - trafficDiff, err := a.settingService.GetTrafficDiff() - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) - return - } - defaultCert, err := a.settingService.GetCertFile() - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) - return - } - defaultKey, err := a.settingService.GetKeyFile() - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) - return - } - tgBotEnable, err := a.settingService.GetTgbotenabled() - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) - return - } - subEnable, err := a.settingService.GetSubEnable() - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) - return - } - subPort, err := a.settingService.GetSubPort() - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) - return - } - subPath, err := a.settingService.GetSubPath() - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) - return - } - subDomain, err := a.settingService.GetSubDomain() - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) - return - } - subKeyFile, err := a.settingService.GetSubKeyFile() - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) - return + type settingFunc func() (interface{}, error) + + settings := map[string]settingFunc{ + "expireDiff": func() (interface{}, error) { return a.settingService.GetExpireDiff() }, + "trafficDiff": func() (interface{}, error) { return a.settingService.GetTrafficDiff() }, + "defaultCert": func() (interface{}, error) { return a.settingService.GetCertFile() }, + "defaultKey": func() (interface{}, error) { return a.settingService.GetKeyFile() }, + "tgBotEnable": func() (interface{}, error) { return a.settingService.GetTgbotenabled() }, + "subEnable": func() (interface{}, error) { return a.settingService.GetSubEnable() }, + "subPort": func() (interface{}, error) { return a.settingService.GetSubPort() }, + "subPath": func() (interface{}, error) { return a.settingService.GetSubPath() }, + "subDomain": func() (interface{}, error) { return a.settingService.GetSubDomain() }, + "subKeyFile": func() (interface{}, error) { return a.settingService.GetSubKeyFile() }, + "subCertFile": func() (interface{}, error) { return a.settingService.GetSubCertFile() }, } - subCertFile, err := a.settingService.GetSubCertFile() - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) - return + + result := make(map[string]interface{}) + + for key, fn := range settings { + value, err := fn() + if err != nil { + jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) + return + } + result[key] = value } + subTLS := false - if subKeyFile != "" || subCertFile != "" { + if result["subKeyFile"] != "" || result["subCertFile"] != "" { subTLS = true } - result := map[string]interface{}{ - "expireDiff": expireDiff, - "trafficDiff": trafficDiff, - "defaultCert": defaultCert, - "defaultKey": defaultKey, - "tgBotEnable": tgBotEnable, - "subEnable": subEnable, - "subPort": subPort, - "subPath": subPath, - "subDomain": subDomain, - "subTLS": subTLS, - } + result["subTLS"] = subTLS + + delete(result, "subKeyFile") + delete(result, "subCertFile") + jsonObj(c, result, nil) } diff --git a/web/entity/entity.go b/web/entity/entity.go index 0bfbfd2a..d5e90108 100644 --- a/web/entity/entity.go +++ b/web/entity/entity.go @@ -28,6 +28,7 @@ type Pager struct { type AllSetting struct { WebListen string `json:"webListen" form:"webListen"` + WebDomain string `json:"webDomain" form:"webDomain"` WebPort int `json:"webPort" form:"webPort"` WebCertFile string `json:"webCertFile" form:"webCertFile"` WebKeyFile string `json:"webKeyFile" form:"webKeyFile"` diff --git a/web/global/hashStorage.go b/web/global/hashStorage.go index 9dfea169..5d8135ee 100644 --- a/web/global/hashStorage.go +++ b/web/global/hashStorage.go @@ -18,7 +18,6 @@ type HashStorage struct { sync.RWMutex Data map[string]HashEntry Expiration time.Duration - } func NewHashStorage(expiration time.Duration) *HashStorage { @@ -46,7 +45,6 @@ func (h *HashStorage) SaveHash(query string) string { return md5HashString } - func (h *HashStorage) GetValue(hash string) (string, bool) { h.RLock() defer h.RUnlock() diff --git a/web/html/common/qrcode_modal.html b/web/html/common/qrcode_modal.html index e6b7b998..8edfa2de 100644 --- a/web/html/common/qrcode_modal.html +++ b/web/html/common/qrcode_modal.html @@ -68,8 +68,8 @@ qrModal: qrModal, }, methods: { - copyToClipboard(elmentId,content) { - this.qrModal.clipboard = new ClipboardJS('#'+elmentId, { + copyToClipboard(elmentId, content) { + this.qrModal.clipboard = new ClipboardJS('#' + elmentId, { text: () => content, }); this.qrModal.clipboard.on('success', () => { @@ -77,29 +77,25 @@ this.qrModal.clipboard.destroy(); }); }, - setQrCode(elmentId,content) { + setQrCode(elmentId, content) { new QRious({ - element: document.querySelector('#'+elmentId), - size: 260, - value: content, - }); + element: document.querySelector('#' + elmentId), + size: 260, + value: content, + }); }, genSubLink(subID) { - protocol = app.subSettings.tls ? "https://" : "http://"; - hostName = app.subSettings.domain === "" ? window.location.hostname : app.subSettings.domain; - subPort = app.subSettings.port; - port = (subPort === 443 && app.subSettings.tls) || (subPort === 80 && !app.subSettings.tls) ? "" : ":" + String(subPort); - subPath = app.subSettings.path; - return protocol + hostName + port + subPath + subID; + const { domain: host, port, tls: isTLS, path: base } = app.subSettings; + return buildURL({ host, port, isTLS, base, path: subID }); } }, updated() { - if (qrModal.client.subId){ + if (qrModal.client && qrModal.client.subId) { qrModal.subId = qrModal.client.subId; - this.setQrCode("qrCode-sub",this.genSubLink(qrModal.subId)); + this.setQrCode("qrCode-sub", this.genSubLink(qrModal.subId)); } - qrModal.qrcodes.forEach((element,index) => { - this.setQrCode("qrCode-"+index, element.link); + qrModal.qrcodes.forEach((element, index) => { + this.setQrCode("qrCode-" + index, element.link); }); } }); diff --git a/web/html/xui/inbound_info_modal.html b/web/html/xui/inbound_info_modal.html index b7b3436b..23bd5af1 100644 --- a/web/html/xui/inbound_info_modal.html +++ b/web/html/xui/inbound_info_modal.html @@ -253,12 +253,8 @@ infoModal.visible = false; }, genSubLink(subID) { - protocol = app.subSettings.tls ? "https://" : "http://"; - hostName = app.subSettings.domain === "" ? window.location.hostname : app.subSettings.domain; - subPort = app.subSettings.port; - port = (subPort === 443 && app.subSettings.tls) || (subPort === 80 && !app.subSettings.tls) ? "" : ":" + String(subPort); - subPath = app.subSettings.path; - return protocol + hostName + port + subPath + subID; + const { domain: host, port, tls: isTLS, path: base } = app.subSettings; + return buildURL({ host, port, isTLS, base, path: subID }); } }; diff --git a/web/html/xui/inbound_modal.html b/web/html/xui/inbound_modal.html index 25e19473..11e6020c 100644 --- a/web/html/xui/inbound_modal.html +++ b/web/html/xui/inbound_modal.html @@ -96,7 +96,7 @@ set multiDomain(value) { if (value) { inModal.inbound.stream.tls.server = ""; - inModal.inbound.stream.tls.settings.domains = [{remark: "", domain: window.location.host.split(":")[0]}]; + inModal.inbound.stream.tls.settings.domains = [{ remark: "", domain: window.location.hostname }]; } else { inModal.inbound.stream.tls.server = ""; inModal.inbound.stream.tls.settings.domains = []; diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html index 7b9ba207..329f0f46 100644 --- a/web/html/xui/inbounds.html +++ b/web/html/xui/inbounds.html @@ -311,7 +311,7 @@ { title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } }, { title: '{{ i18n "pages.inbounds.traffic" }}↑|↓', width: 120, scopedSlots: { customRender: 'traffic' } }, { title: '{{ i18n "pages.inbounds.expireDate" }}', width: 70, scopedSlots: { customRender: 'expiryTime' } }, - { title: 'UID', width: 120, dataIndex: "id" }, + { title: 'UUID', width: 120, dataIndex: "id" }, ]; const innerTrojanColumns = [ diff --git a/web/html/xui/settings.html b/web/html/xui/settings.html index d78533a1..745959a2 100644 --- a/web/html/xui/settings.html +++ b/web/html/xui/settings.html @@ -91,6 +91,7 @@ </a-row> <a-list item-layout="horizontal" :style="themeSwitcher.textStyle"> <setting-list-item type="text" title='{{ i18n "pages.settings.panelListeningIP"}}' desc='{{ i18n "pages.settings.panelListeningIPDesc"}}' v-model="allSetting.webListen"></setting-list-item> + <setting-list-item type="text" title='{{ i18n "pages.settings.panelListeningDomain"}}' desc='{{ i18n "pages.settings.panelListeningDomainDesc"}}' v-model="allSetting.webDomain"></setting-list-item> <setting-list-item type="number" title='{{ i18n "pages.settings.panelPort"}}' desc='{{ i18n "pages.settings.panelPortDesc"}}' v-model="allSetting.webPort" :min="0"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.publicKeyPath"}}' desc='{{ i18n "pages.settings.publicKeyPathDesc"}}' v-model="allSetting.webCertFile"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.privateKeyPath"}}' desc='{{ i18n "pages.settings.privateKeyPathDesc"}}' v-model="allSetting.webKeyFile"></setting-list-item> @@ -306,23 +307,37 @@ <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigNetflixWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigNetflixWARPDesc"}}' v-model="NetflixWARPSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigSpotifyWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigSpotifyWARPDesc"}}' v-model="SpotifyWARPSettings"></setting-list-item> </a-collapse-panel> - <a-collapse-panel header='{{ i18n "pages.settings.templates.manualLists"}}'> - <a-row :xs="24" :sm="24" :lg="12"> - <h2 class="collapse-title"> - <a-icon type="warning"></a-icon> - {{ i18n "pages.settings.templates.manualListsDesc" }} - </h2> - </a-row> - <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualBlockedIPs"}}' v-model="manualBlockedIPs"></setting-list-item> - <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualBlockedDomains"}}' v-model="manualBlockedDomains"></setting-list-item> - <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualDirectIPs"}}' v-model="manualDirectIPs"></setting-list-item> - <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualDirectDomains"}}' v-model="manualDirectDomains"></setting-list-item> - <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualIPv4Domains"}}' v-model="manualIPv4Domains"></setting-list-item> - <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualWARPDomains"}}' v-model="manualWARPDomains"></setting-list-item> + </a-collapse> + </a-tab-pane> + <a-tab-pane key="tpl-2" tab='{{ i18n "pages.settings.templates.manualLists"}}' style="padding-top: 20px;"> + <a-row :xs="24" :sm="24" :lg="12"> + <h2 class="collapse-title"> + <a-icon type="warning"></a-icon> + {{ i18n "pages.settings.templates.manualListsDesc" }} + </h2> + </a-row> + <a-collapse> + <a-collapse-panel header='{{ i18n "pages.settings.templates.manualBlockedIPs"}}'> + <setting-list-item type="textarea" v-model="manualBlockedIPs"></setting-list-item> + </a-collapse-panel> + <a-collapse-panel header='{{ i18n "pages.settings.templates.manualBlockedDomains"}}'> + <setting-list-item type="textarea" v-model="manualBlockedDomains"></setting-list-item> + </a-collapse-panel> + <a-collapse-panel header='{{ i18n "pages.settings.templates.manualDirectIPs"}}'> + <setting-list-item type="textarea" v-model="manualDirectIPs"></setting-list-item> + </a-collapse-panel> + <a-collapse-panel header='{{ i18n "pages.settings.templates.manualDirectDomains"}}'> + <setting-list-item type="textarea" v-model="manualDirectDomains"></setting-list-item> + </a-collapse-panel> + <a-collapse-panel header='{{ i18n "pages.settings.templates.manualIPv4Domains"}}'> + <setting-list-item type="textarea" v-model="manualIPv4Domains"></setting-list-item> + </a-collapse-panel> + <a-collapse-panel header='{{ i18n "pages.settings.templates.manualWARPDomains"}}'> + <setting-list-item type="textarea" v-model="manualWARPDomains"></setting-list-item> </a-collapse-panel> </a-collapse> </a-tab-pane> - <a-tab-pane key="tpl-2" tab='{{ i18n "pages.settings.templates.advancedTemplate"}}' style="padding-top: 20px;"> + <a-tab-pane key="tpl-3" tab='{{ i18n "pages.settings.templates.advancedTemplate"}}' style="padding-top: 20px;"> <a-collapse> <a-collapse-panel header='{{ i18n "pages.settings.templates.xrayConfigInbounds"}}'> <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigInbounds"}}' desc='{{ i18n "pages.settings.templates.xrayConfigInboundsDesc"}}' v-model="inboundSettings"></setting-list-item> @@ -335,7 +350,7 @@ </a-collapse-panel> </a-collapse> </a-tab-pane> - <a-tab-pane key="tpl-3" tab='{{ i18n "pages.settings.templates.completeTemplate"}}' style="padding-top: 20px;"> + <a-tab-pane key="tpl-4" tab='{{ i18n "pages.settings.templates.completeTemplate"}}' style="padding-top: 20px;"> <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigTemplate"}}' desc='{{ i18n "pages.settings.templates.xrayConfigTemplateDesc"}}' v-model="allSetting.xrayTemplateConfig"></setting-list-item> </a-tab-pane> </a-tabs> @@ -391,9 +406,9 @@ <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="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> <setting-list-item type="text" title='{{ i18n "pages.settings.subPath"}}' desc='{{ i18n "pages.settings.subPathDesc"}}' v-model="allSetting.subPath"></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.subCertPath"}}' desc='{{ i18n "pages.settings.subCertPathDesc"}}' v-model="allSetting.subCertFile"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.subKeyPath"}}' desc='{{ i18n "pages.settings.subKeyPathDesc"}}' v-model="allSetting.subKeyFile"></setting-list-item> <setting-list-item type="number" title='{{ i18n "pages.settings.subUpdates"}}' desc='{{ i18n "pages.settings.subUpdatesDesc"}}' v-model="allSetting.subUpdates"></setting-list-item> @@ -522,7 +537,7 @@ this.loading(false); if (msg.success) { this.user = {}; - window.location.replace(basePath + "logout") + window.location.replace(basePath + "logout"); } }, async restartPanel() { @@ -541,12 +556,10 @@ if (msg.success) { this.loading(true); await PromiseUtil.sleep(5000); - let protocol = "http://"; - if (this.allSetting.webCertFile !== "") { - protocol = "https://"; - } - const { host } = window.location; - window.location.replace(protocol + host + this.allSetting.webBasePath + "panel/settings"); + const { webCertFile, webKeyFile, webDomain: host, webPort: port, webBasePath: base } = this.allSetting; + const isTLS = webCertFile !== "" || webKeyFile !== ""; + const url = buildURL({ host, port, isTLS, base, path: "panel/settings" }); + window.location.replace(url); } }, async fetchUserSecret() { diff --git a/web/middleware/domainValidator.go b/web/middleware/domainValidator.go new file mode 100644 index 00000000..3adb0f0f --- /dev/null +++ b/web/middleware/domainValidator.go @@ -0,0 +1,21 @@ +package middleware + +import ( + "net/http" + "strings" + + "github.com/gin-gonic/gin" +) + +func DomainValidatorMiddleware(domain string) gin.HandlerFunc { + return func(c *gin.Context) { + host := strings.Split(c.Request.Host, ":")[0] + + if host != domain { + c.AbortWithStatus(http.StatusForbidden) + return + } + + c.Next() + } +} diff --git a/web/middleware/redirect.go b/web/middleware/redirect.go new file mode 100644 index 00000000..e3dc8ada --- /dev/null +++ b/web/middleware/redirect.go @@ -0,0 +1,34 @@ +package middleware + +import ( + "net/http" + "strings" + + "github.com/gin-gonic/gin" +) + +func RedirectMiddleware(basePath string) gin.HandlerFunc { + return func(c *gin.Context) { + // Redirect from old '/xui' path to '/panel' + redirects := map[string]string{ + "panel/API": "panel/api", + "xui/API": "panel/api", + "xui": "panel", + } + + path := c.Request.URL.Path + for from, to := range redirects { + from, to = basePath+from, basePath+to + + if strings.HasPrefix(path, from) { + newPath := to + path[len(from):] + + c.Redirect(http.StatusMovedPermanently, newPath) + c.Abort() + return + } + } + + c.Next() + } +} diff --git a/web/service/inbound.go b/web/service/inbound.go index 6a182fcf..11522ad2 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -1185,6 +1185,7 @@ func (s *InboundService) GetInboundClientIps(clientEmail string) (string, error) } return InboundClientIps.Ips, nil } + func (s *InboundService) ClearClientIps(clientEmail string) error { db := database.GetDB() diff --git a/web/service/setting.go b/web/service/setting.go index 593b23be..677ccbb2 100644 --- a/web/service/setting.go +++ b/web/service/setting.go @@ -24,6 +24,7 @@ var xrayTemplateConfig string var defaultValueMap = map[string]string{ "xrayTemplateConfig": xrayTemplateConfig, "webListen": "", + "webDomain": "", "webPort": "2053", "webCertFile": "", "webKeyFile": "", @@ -44,7 +45,7 @@ var defaultValueMap = map[string]string{ "subEnable": "false", "subListen": "", "subPort": "2096", - "subPath": "sub/", + "subPath": "/sub/", "subDomain": "", "subCertFile": "", "subKeyFile": "", @@ -225,6 +226,10 @@ func (s *SettingService) GetListen() (string, error) { return s.getString("webListen") } +func (s *SettingService) GetWebDomain() (string, error) { + return s.getString("webDomain") +} + func (s *SettingService) GetTgBotToken() (string, error) { return s.getString("tgBotToken") } diff --git a/web/service/tgbot.go b/web/service/tgbot.go index e0261775..d1a1e3fe 100644 --- a/web/service/tgbot.go +++ b/web/service/tgbot.go @@ -77,13 +77,15 @@ func (t *Tgbot) Start(i18nFS embed.FS) error { return err } - for _, adminId := range strings.Split(tgBotid, ",") { - id, err := strconv.Atoi(adminId) - if err != nil { - logger.Warning("Failed to get IDs from GetTgBotChatId:", err) - return err + if tgBotid != "" { + for _, adminId := range strings.Split(tgBotid, ",") { + id, err := strconv.Atoi(adminId) + if err != nil { + logger.Warning("Failed to get IDs from GetTgBotChatId:", err) + return err + } + adminIds = append(adminIds, int64(id)) } - adminIds = append(adminIds, int64(id)) } bot, err = telego.NewBot(tgBottoken) @@ -188,7 +190,7 @@ func (t *Tgbot) OnReceive() { } func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin bool) { - msg := "" + msg, onlyMessage := "", false command, commandArgs := tu.ParseCommand(message.Text) @@ -204,8 +206,13 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo } msg += "\n\n" + t.I18nBot("tgbot.commands.pleaseChoose") case "status": + onlyMessage = true msg += t.I18nBot("tgbot.commands.status") + case "id": + onlyMessage = true + msg += t.I18nBot("tgbot.commands.getID", "ID=="+strconv.FormatInt(message.From.ID, 10)) case "usage": + onlyMessage = true if len(commandArgs) > 0 { if isAdmin { t.searchClient(chatId, commandArgs[0]) @@ -216,6 +223,7 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo msg += t.I18nBot("tgbot.commands.usage") } case "inbound": + onlyMessage = true if isAdmin && len(commandArgs) > 0 { t.searchInbound(chatId, commandArgs[0]) } else { @@ -224,6 +232,11 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo default: msg += t.I18nBot("tgbot.commands.unknown") } + + if onlyMessage { + t.SendMsgToTgbot(chatId, msg) + return + } t.SendAnswer(chatId, msg, isAdmin) } @@ -498,6 +511,7 @@ func (t *Tgbot) SendMsgToTgbot(chatId int64, msg string, replyMarkup ...telego.R if !isRunning { return } + if msg == "" { logger.Info("[tgbot] message is empty!") return @@ -723,7 +737,7 @@ func (t *Tgbot) getClientUsage(chatId int64, tgUserName string, tgUserID string) output := "" output += t.I18nBot("tgbot.messages.email", "Email=="+traffic.Email) - if (traffic.Enable) { + if traffic.Enable { output += t.I18nBot("tgbot.messages.active") if flag { output += t.I18nBot("tgbot.messages.expireIn", "Time=="+expiryTime) @@ -791,6 +805,7 @@ func (t *Tgbot) clientTelegramUserInfo(chatId int64, email string, messageID ... output := "" output += t.I18nBot("tgbot.messages.email", "Email=="+email) output += t.I18nBot("tgbot.messages.TGUser", "TelegramID=="+tgId) + output += t.I18nBot("tgbot.messages.refreshedOn", "Time=="+time.Now().Format("2006-01-02 15:04:05")) inlineKeyboard := tu.InlineKeyboard( tu.InlineKeyboardRow( @@ -840,7 +855,7 @@ func (t *Tgbot) searchClient(chatId int64, email string, messageID ...int) { flag := false diff := traffic.ExpiryTime/1000 - now if traffic.ExpiryTime == 0 { - expiryTime = t.I18nBot("tgbot.unlimited") + expiryTime = t.I18nBot("tgbot.unlimited") } else if diff > 172800 || !traffic.Enable { expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05") } else if traffic.ExpiryTime < 0 { @@ -860,7 +875,7 @@ func (t *Tgbot) searchClient(chatId int64, email string, messageID ...int) { output := "" output += t.I18nBot("tgbot.messages.email", "Email=="+traffic.Email) - if (traffic.Enable) { + if traffic.Enable { output += t.I18nBot("tgbot.messages.active") if flag { output += t.I18nBot("tgbot.messages.expireIn", "Time=="+expiryTime) @@ -918,7 +933,7 @@ func (t *Tgbot) searchInbound(chatId int64, remark string) { t.SendMsgToTgbot(chatId, msg) return } - + now := time.Now().Unix() for _, inbound := range inbouds { info := "" @@ -958,7 +973,7 @@ func (t *Tgbot) searchInbound(chatId int64, remark string) { output := "" output += t.I18nBot("tgbot.messages.email", "Email=="+traffic.Email) - if (traffic.Enable) { + if traffic.Enable { output += t.I18nBot("tgbot.messages.active") if flag { output += t.I18nBot("tgbot.messages.expireIn", "Time=="+expiryTime) @@ -998,7 +1013,7 @@ func (t *Tgbot) searchForClient(chatId int64, query string) { flag := false diff := traffic.ExpiryTime/1000 - now if traffic.ExpiryTime == 0 { - expiryTime = t.I18nBot("tgbot.unlimited") + expiryTime = t.I18nBot("tgbot.unlimited") } else if diff > 172800 || !traffic.Enable { expiryTime = time.Un
|
