diff options
| author | MHSanaei <ho3ein.sanaei@gmail.com> | 2023-05-22 17:01:41 +0300 |
|---|---|---|
| committer | MHSanaei <ho3ein.sanaei@gmail.com> | 2023-05-22 17:01:41 +0300 |
| commit | 1fa9101b405ad1ba0127317ea4f8a151048b97ee (patch) | |
| tree | e03a93684953b3473a717a283953464bf368008a /web | |
| parent | 3f2e1aede90984d3bafab377509f712e5ce51ec0 (diff) | |
[feature] add multi domain tls (CDN ready)
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
Diffstat (limited to 'web')
| -rw-r--r-- | web/assets/js/model/models.js | 4 | ||||
| -rw-r--r-- | web/assets/js/model/xray.js | 37 | ||||
| -rw-r--r-- | web/controller/inbound.go | 7 | ||||
| -rw-r--r-- | web/html/common/qrcode_modal.html | 86 | ||||
| -rw-r--r-- | web/html/xui/form/tls_settings.html | 16 | ||||
| -rw-r--r-- | web/html/xui/inbound_client_table.html | 2 | ||||
| -rw-r--r-- | web/html/xui/inbound_info_modal.html | 79 | ||||
| -rw-r--r-- | web/html/xui/inbound_modal.html | 12 | ||||
| -rw-r--r-- | web/html/xui/inbounds.html | 4 |
9 files changed, 167 insertions, 80 deletions
diff --git a/web/assets/js/model/models.js b/web/assets/js/model/models.js index dc4c85d0..d8395b56 100644 --- a/web/assets/js/model/models.js +++ b/web/assets/js/model/models.js @@ -153,9 +153,9 @@ class DBInbound { } } - genLink(clientIndex) { + genLink(address=this.address, remark=this.remark, clientIndex=0) { const inbound = this.toInbound(); - return inbound.genLink(this.address, this.remark, clientIndex); + return inbound.genLink(address, remark, clientIndex); } get genInboundLinks() { diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js index 9a8fae80..bc8d4d4a 100644 --- a/web/assets/js/model/xray.js +++ b/web/assets/js/model/xray.js @@ -498,8 +498,7 @@ class TlsStreamSettings extends XrayCommonClass { } if (!ObjectUtil.isEmpty(json.settings)) { - settings = new TlsStreamSettings.Settings(json.settings.allowInsecure , json.settings.fingerprint, json.settings.serverName); - } + settings = new TlsStreamSettings.Settings(json.settings.allowInsecure , json.settings.fingerprint, json.settings.serverName, json.settings.domains); } return new TlsStreamSettings( json.serverName, json.minVersion, @@ -566,17 +565,19 @@ TlsStreamSettings.Cert = class extends XrayCommonClass { }; TlsStreamSettings.Settings = class extends XrayCommonClass { - constructor(allowInsecure = false, fingerprint = '', serverName = '') { + constructor(allowInsecure = false, fingerprint = '', serverName = '', domains = []) { super(); this.allowInsecure = allowInsecure; this.fingerprint = fingerprint; this.serverName = serverName; + this.domains = domains; } static fromJson(json = {}) { return new TlsStreamSettings.Settings( json.allowInsecure, json.fingerprint, - json.servername, + json.serverName, + json.domains, ); } toJson() { @@ -584,6 +585,7 @@ TlsStreamSettings.Settings = class extends XrayCommonClass { allowInsecure: this.allowInsecure, fingerprint: this.fingerprint, serverName: this.serverName, + domains: this.domains, }; } }; @@ -1507,25 +1509,13 @@ class Inbound extends XrayCommonClass { genLink(address='', remark='', clientIndex=0) { switch (this.protocol) { - case Protocols.VMESS: - if (this.settings.vmesses[clientIndex].email != ""){ - remark += '-' + this.settings.vmesses[clientIndex].email - } + case Protocols.VMESS: return this.genVmessLink(address, remark, clientIndex); case Protocols.VLESS: - if (this.settings.vlesses[clientIndex].email != ""){ - remark += '-' + this.settings.vlesses[clientIndex].email - } return this.genVLESSLink(address, remark, clientIndex); case Protocols.SHADOWSOCKS: - if (this.settings.shadowsockses[clientIndex].email != ""){ - remark = this.settings.shadowsockses[clientIndex].email - } return this.genSSLink(address, remark, clientIndex); case Protocols.TROJAN: - if (this.settings.trojans[clientIndex].email != ""){ - remark += '-' + this.settings.trojans[clientIndex].email - } return this.genTrojanLink(address, remark, clientIndex); default: return ''; } @@ -1537,12 +1527,17 @@ class Inbound extends XrayCommonClass { case Protocols.VMESS: case Protocols.VLESS: case Protocols.TROJAN: - JSON.parse(this.settings).clients.forEach((_,index) => { - link += this.genLink(address, remark, index) + '\r\n'; + case Protocols.SHADOWSOCKS: + JSON.parse(this.settings).clients.forEach((client,index) => { + if(this.tls && !ObjectUtil.isArrEmpty(this.stream.tls.settings.domains)){ + this.stream.tls.settings.domains.forEach((domain) => { + link += this.genLink(domain.domain, remark + '-' + client.email + '-' + domain.remark, index) + '\r\n'; + }); + } else { + link += this.genLink(address, remark + '-' + client.email, index) + '\r\n'; + } }); return link; - case Protocols.SHADOWSOCKS: - return (this.genSSLink(address, remark) + '\r\n'); default: return ''; } } diff --git a/web/controller/inbound.go b/web/controller/inbound.go index d13e40bc..5ce58d53 100644 --- a/web/controller/inbound.go +++ b/web/controller/inbound.go @@ -146,11 +146,18 @@ func (a *InboundController) getClientIps(c *gin.Context) { ips, err := a.inboundService.GetInboundClientIps(email) if err != nil { + jsonObj(c, "Failed to get client IPs", nil) + return + } + + if ips == "" { jsonObj(c, "No IP Record", nil) return } + jsonObj(c, ips, nil) } + func (a *InboundController) clearClientIps(c *gin.Context) { email := c.Param("email") diff --git a/web/html/common/qrcode_modal.html b/web/html/common/qrcode_modal.html index 855c349a..3271f3ad 100644 --- a/web/html/common/qrcode_modal.html +++ b/web/html/common/qrcode_modal.html @@ -7,47 +7,53 @@ <a-tag color="green" style="margin-bottom: 10px;display: block;text-align: center;"> {{ i18n "pages.inbounds.clickOnQRcode" }} </a-tag> - <a-tag v-if="qrModal.clientName" color="orange" style="margin-bottom: 10px;display: block;text-align: center;"> - {{ i18n "pages.inbounds.email" }}: "[[ qrModal.clientName ]]" - </a-tag> - <canvas @click="copyToClipboard()" id="qrCode" style="width: 100%; height: 100%; margin-top: 10px;"></canvas> + <template v-if="app.subSettings.enable && qrModal.subId"> + <a-divider>Subscription</a-divider> + <canvas @click="copyToClipboard('qrCode-sub',genSubLink(qrModal.client.subId))" id="qrCode-sub" style="width: 100%; height: 100%;"></canvas> + </template> + <a-divider>{{ i18n "pages.inbounds.client" }}</a-divider> + <template v-for="(row, index) in qrModal.qrcodes"> + <a-tag color="orange" style="margin-top: 10px;display: block;text-align: center;">[[ row.remark ]]</a-tag> + <canvas @click="copyToClipboard('qrCode-'+index, row.link)" :id="'qrCode-'+index" style="width: 100%; height: 100%;"></canvas> + </template> </a-modal> <script> const qrModal = { title: '', - content: '', + clientIndex: 0, inbound: new Inbound(), dbInbound: new DBInbound(), - copyText: '', - clientName: null, - qrcode: null, + client: null, + qrcodes: [], clipboard: null, visible: false, - show: function (title = '', content = '', dbInbound = new DBInbound(), copyText = '', clientName = null) { + subId: '', + show: function (title = '', dbInbound = new DBInbound(), clientIndex = 0) { this.title = title; - this.content = content; + this.clientIndex = clientIndex; this.dbInbound = dbInbound; this.inbound = dbInbound.toInbound(); - this.clientName = clientName; - if (ObjectUtil.isEmpty(copyText)) { - this.copyText = content; + settings = JSON.parse(this.inbound.settings); + this.client = settings.clients[clientIndex]; + remark = this.dbInbound.remark + "-" + this.client.email; + address = this.dbInbound.address; + this.qrcodes = []; + if (this.inbound.tls && !ObjectUtil.isArrEmpty(this.inbound.stream.tls.settings.domains)) { + this.inbound.stream.tls.settings.domains.forEach((domain) => { + this.qrcodes.push({ + remark: remark + "-" + domain.remark, + link: this.inbound.genLink(domain.domain, remark + "-" + domain.remark, clientIndex) + }); + }); } else { - this.copyText = copyText; + this.qrcodes.push({ + remark: remark, + link: this.inbound.genLink(address, remark, clientIndex) + }); } this.visible = true; - qrModalApp.$nextTick(() => { - if (this.qrcode === null) { - this.qrcode = new QRious({ - element: document.querySelector('#qrCode'), - size: 260, - value: content, - }); - } else { - this.qrcode.value = content; - } - }); }, close: function () { this.visible = false; @@ -61,16 +67,40 @@ qrModal: qrModal, }, methods: { - copyToClipboard() { - this.qrModal.clipboard = new ClipboardJS('#qrCode', { - text: () => this.qrModal.copyText, + copyToClipboard(elmentId,content) { + this.qrModal.clipboard = new ClipboardJS('#'+elmentId, { + text: () => content, }); this.qrModal.clipboard.on('success', () => { app.$message.success('{{ i18n "copied" }}') this.qrModal.clipboard.destroy(); }); + }, + setQrCode(elmentId,content) { + new QRious({ + 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; } }, + updated() { + if (qrModal.client.subId){ + qrModal.subId = qrModal.client.subId; + this.setQrCode("qrCode-sub",this.genSubLink(this.subId)); + } + qrModal.qrcodes.forEach((element,index) => { + this.setQrCode("qrCode-"+index, element.link); + }); + } }); </script> diff --git a/web/html/xui/form/tls_settings.html b/web/html/xui/form/tls_settings.html index d80e820e..31e559da 100644 --- a/web/html/xui/form/tls_settings.html +++ b/web/html/xui/form/tls_settings.html @@ -33,7 +33,21 @@ <!-- tls settings --> <a-form v-if="inbound.tls" layout="inline"> - <a-form-item label='{{ i18n "domainName" }}'> + <a-form-item label='Multi Domain'> + <a-switch v-model="multiDomain"></a-switch> + <a-button v-if="multiDomain" size="small" @click="inbound.stream.tls.settings.domains.push({remark: '', domain: ''})">+</a-button> + </a-form-item> + <a-form-item v-if="multiDomain" label='Domains'> + <a-input-group v-for="(row, index) in inbound.stream.tls.settings.domains"> + <a-input style="width: 40%" v-model.trim="row.remark" addon-before='{{ i18n "remark" }}'></a-input> + <a-input style="width: 60%" v-model.trim="row.domain" addon-before='{{ i18n "host" }}'> + <template slot="addonAfter"> + <a-button size="small" style="margin: 0px" @click="inbound.stream.tls.settings.domains.splice(index, 1)">-</a-button> + </template> + </a-input> + </a-input-group> + </a-form-item> + <a-form-item v-else label='{{ i18n "domainName" }}'> <a-input v-model.trim="inbound.stream.tls.server" style="width: 250px"></a-input> </a-form-item> <a-form-item label="CipherSuites"> diff --git a/web/html/xui/inbound_client_table.html b/web/html/xui/inbound_client_table.html index 4b09b43a..bb825437 100644 --- a/web/html/xui/inbound_client_table.html +++ b/web/html/xui/inbound_client_table.html @@ -29,7 +29,7 @@ <a-tag v-if="!isClientEnabled(record, client.email)" color="red">{{ i18n "depleted" }}</a-tag> </template> <template slot="traffic" slot-scope="text, client"> - <a-tag :color="statsColor(record, client.email)" @click="alert(usageColor(0,1024,512))">[[ sizeFormat(getUpStats(record, client.email)) ]] / [[ sizeFormat(getDownStats(record, client.email)) ]]</a-tag> + <a-tag :color="statsColor(record, client.email)">[[ sizeFormat(getUpStats(record, client.email)) ]] / [[ sizeFormat(getDownStats(record, client.email)) ]]</a-tag> <template v-if="client._totalGB > 0"> <a-tag :color="statsColor(record, client.email)">[[client._totalGB]]GB</a-tag> </template> diff --git a/web/html/xui/inbound_info_modal.html b/web/html/xui/inbound_info_modal.html index be2bcf4a..b7b3436b 100644 --- a/web/html/xui/inbound_info_modal.html +++ b/web/html/xui/inbound_info_modal.html @@ -59,10 +59,9 @@ </td> <td v-else-if="inbound.reality"> reality: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br /> - reality {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag> + reality Destination: <a-tag :color="inbound.stream.reality.dest ? 'green' : 'orange'">[[ inbound.stream.reality.dest ]]</a-tag> </td> - <td v-else> - tls: <a-tag color="red">{{ i18n "disabled" }}</a-tag> + <td v-else>tls: <a-tag color="red">{{ i18n "disabled" }}</a-tag> </td> </tr> </table> @@ -110,17 +109,16 @@ </td> </tr> </table> - <table v-if="infoModal.clientSettings.subId + infoModal.clientSettings.tgId" style="margin-bottom: 10px;"> - <tr v-if="infoModal.clientSettings.subId"> - <td>Subscription link</td> - <td><a :href="[[ subBase + infoModal.clientSettings.subId ]]" target="_blank">[[ subBase + infoModal.clientSettings.subId ]]</a></td> - <td><a-icon id="copy-sub-link" type="snippets" @click="copyToClipboard('copy-sub-link', subBase + infoModal.clientSettings.subId)"></a-icon></td> - </tr> - <tr v-if="infoModal.clientSettings.tgId"> - <td>Telegram ID</td> - <td><a :href="[[ tgBase + infoModal.clientSettings.tgId ]]" target="_blank">@[[ infoModal.clientSettings.tgId ]]</a></td> - </tr> - </table> + <template v-if="app.subSettings.enable && infoModal.clientSettings.subId"> + <a-divider>Subscription link</a-divider> + <a :href="[[ infoModal.subLink ]]" target="_blank">[[ infoModal.subLink ]]</a> + <a-icon id="copy-sub-link" type="snippets" @click="copyToClipboard('copy-sub-link', infoModal.subLink)"></a-icon> + </template> + <template v-if="app.tgBotEnable && infoModal.clientSettings.tgId"> + <a-divider>Telegram Username</a-divider> + <a :href="[[ infoModal.tgLink ]]" target="_blank">@[[ infoModal.clientSettings.tgId ]]</a> + <a-icon id="copy-tg-link" type="snippets" @click="copyToClipboard('copy-tg-link', '@' + infoModal.clientSettings.tgId)"></a-icon> + </template> </template> <template v-else> <a-divider></a-divider> @@ -190,8 +188,14 @@ </template> <div v-if="dbInbound.hasLink()"> <a-divider>URL</a-divider> - <p>[[ infoModal.link ]]</p> - <button class="ant-btn ant-btn-primary" id="copy-url-link" @click="copyToClipboard('copy-url-link', infoModal.link)"><a-icon type="snippets"></a-icon>{{ i18n "copy" }}</button> + <a-row v-for="(link,index) in infoModal.links"> + <a-col :span="21"><a-tag color="cyan">[[ link.remark ]]</a-tag><br />[[ link.link ]]</a-col> + <a-col :span="3" style="text-align: right;"> + <button class="ant-btn ant-btn-primary" :id="'copy-url-link-'+index" @click="copyToClipboard('copy-url-link-'+index, link.link)"> + <a-icon type="snippets"></a-icon>{{ i18n "copy" }} + </button> + </a-col> + </a-row> </div> </a-modal> <script> @@ -206,23 +210,56 @@ upStats: 0, downStats: 0, clipboard: null, - link: null, + links: [], index: null, isExpired: false, + subLink: '', + tgLink: '', show(dbInbound, index) { this.index = index; this.inbound = dbInbound.toInbound(); this.dbInbound = new DBInbound(dbInbound); - this.link = dbInbound.genLink(index); this.settings = JSON.parse(this.inbound.settings); this.clientSettings = this.settings.clients ? Object.values(this.settings.clients)[index] : null; this.isExpired = this.inbound.isExpiry(index); this.clientStats = this.settings.clients ? this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email) : []; + remark = this.dbInbound.remark + "-" + this.clientSettings.email; + address = this.dbInbound.address; + this.links = []; + if (this.inbound.tls && !ObjectUtil.isArrEmpty(this.inbound.stream.tls.settings.domains)) { + this.inbound.stream.tls.settings.domains.forEach((domain) => { + this.links.push({ + remark: remark + "-" + domain.remark, + link: this.inbound.genLink(domain.domain, remark + "-" + domain.remark, index) + }); + }); + } else { + this.links.push({ + remark: remark, + link: this.inbound.genLink(address, remark, index) + }); + } + if (this.clientSettings) { + if (this.clientSettings.subId) { + this.subLink = this.genSubLink(this.clientSettings.subId); + } + if (this.clientSettings.tgId) { + this.tgLink = "https://t.me/" + this.clientSettings.tgId; + } + } this.visible = true; }, close() { 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 infoModalApp = new Vue({ @@ -248,12 +285,6 @@ } return infoModal.dbInbound.isEnable; }, - get subBase() { - return window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : "") + basePath + "sub/"; - }, - get tgBase() { - return "https://t.me/" - }, }, methods: { copyToClipboard(elmentId, content) { diff --git a/web/html/xui/inbound_modal.html b/web/html/xui/inbound_modal.html index 73ed6234..25e19473 100644 --- a/web/html/xui/inbound_modal.html +++ b/web/html/xui/inbound_modal.html @@ -90,6 +90,18 @@ set delayedExpireDays(days) { this.client.expiryTime = -86400000 * days; }, + get multiDomain() { + return this.inbound.stream.tls.settings.domains.length > 0; + }, + set multiDomain(value) { + if (value) { + inModal.inbound.stream.tls.server = ""; + inModal.inbound.stream.tls.settings.domains = [{remark: "", domain: window.location.host.split(":")[0]}]; + } else { + inModal.inbound.stream.tls.server = ""; + inModal.inbound.stream.tls.settings.domains = []; + } + } }, methods: { streamNetworkChange() { diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html index a66e84a9..b15798d4 100644 --- a/web/html/xui/inbounds.html +++ b/web/html/xui/inbounds.html @@ -746,9 +746,7 @@ } }, showQrcode(dbInbound, clientIndex) { - const clientName = JSON.parse(dbInbound.settings).clients[clientIndex].email; - const link = dbInbound.genLink(clientIndex); - qrModal.show('{{ i18n "qrCode"}}', link, dbInbound, '', clientName); + qrModal.show('{{ i18n "qrCode"}}', dbInbound, clientIndex); }, showInfo(dbInbound, index) { infoModal.show(dbInbound, index); |
