diff options
| author | Ali Rahimi <alirahimi818@gmail.com> | 2024-01-21 17:26:19 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-01-21 17:26:19 +0300 |
| commit | 5c695ca6520c9cd9c44b18119a862f8f480969af (patch) | |
| tree | 56e739defaf1f1d0326cb30235b1cb6dcf2699e2 | |
| parent | e7ce8c8ddb8472695b296eac305c5ac9b8c1d3d8 (diff) | |
add group user with the same subscription id to all inbounds (#1650)
| -rw-r--r-- | web/assets/js/util/utils.js | 35 | ||||
| -rw-r--r-- | web/controller/inbound.go | 39 | ||||
| -rw-r--r-- | web/html/common/qrcode_modal.html | 17 | ||||
| -rw-r--r-- | web/html/xui/client_modal.html | 80 | ||||
| -rw-r--r-- | web/html/xui/form/client.html | 4 | ||||
| -rw-r--r-- | web/html/xui/inbound_modal.html | 4 | ||||
| -rw-r--r-- | web/html/xui/inbounds.html | 59 | ||||
| -rw-r--r-- | web/translation/translate.en_US.toml | 1 | ||||
| -rw-r--r-- | web/translation/translate.es_ES.toml | 1 | ||||
| -rw-r--r-- | web/translation/translate.fa_IR.toml | 1 | ||||
| -rw-r--r-- | web/translation/translate.ru_RU.toml | 1 | ||||
| -rw-r--r-- | web/translation/translate.vi_VN.toml | 1 | ||||
| -rw-r--r-- | web/translation/translate.zh_Hans.toml | 1 |
13 files changed, 196 insertions, 48 deletions
diff --git a/web/assets/js/util/utils.js b/web/assets/js/util/utils.js index 61b322bd..48ff237d 100644 --- a/web/assets/js/util/utils.js +++ b/web/assets/js/util/utils.js @@ -83,6 +83,41 @@ class HttpUtil { } return msg; } + + static async jsonPost(url, data) { + let msg; + try { + const requestOptions = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }; + const resp = await fetch(url, requestOptions); + const response = await resp.json(); + + msg = this._respToMsg({data : response}); + } catch (e) { + msg = new Msg(false, e.toString()); + } + this._handleMsg(msg); + return msg; + } + + static async postWithModalJson(url, data, modal) { + if (modal) { + modal.loading(true); + } + const msg = await this.jsonPost(url, data); + if (modal) { + modal.loading(false); + if (msg instanceof Msg && msg.success) { + modal.close(); + } + } + return msg; + } } class PromiseUtil { diff --git a/web/controller/inbound.go b/web/controller/inbound.go index 86da9813..b274be64 100644 --- a/web/controller/inbound.go +++ b/web/controller/inbound.go @@ -159,24 +159,31 @@ func (a *InboundController) clearClientIps(c *gin.Context) { } func (a *InboundController) addInboundClient(c *gin.Context) { - data := &model.Inbound{} - err := c.ShouldBind(data) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.update"), err) - return - } + var requestData []model.Inbound - needRestart := true + err := c.ShouldBindJSON(&requestData) + + if err != nil { + jsonMsg(c, I18nWeb(c, "pages.inbounds.update"), err) + return + } + + needRestart := true + + for _, data := range requestData { + + needRestart, err = a.inboundService.AddInboundClient(&data) + if err != nil { + jsonMsg(c, "Something went wrong!", err) + return + } + } + + jsonMsg(c, "Client(s) added", nil) + if err == nil && needRestart { + a.xrayService.SetToNeedRestart() + } - needRestart, err = a.inboundService.AddInboundClient(data) - if err != nil { - jsonMsg(c, "Something went wrong!", err) - return - } - jsonMsg(c, "Client(s) added", nil) - if err == nil && needRestart { - a.xrayService.SetToNeedRestart() - } } func (a *InboundController) delInboundClient(c *gin.Context) { diff --git a/web/html/common/qrcode_modal.html b/web/html/common/qrcode_modal.html index 3c4fd929..31b3450c 100644 --- a/web/html/common/qrcode_modal.html +++ b/web/html/common/qrcode_modal.html @@ -11,10 +11,12 @@ <a-divider>Subscription</a-divider> <div class="qr-bg"><canvas @click="copyToClipboard('qrCode-sub',genSubLink(qrModal.client.subId))" id="qrCode-sub" style="width: 100%; height: 100%;"></canvas></div> </template> - <a-divider>{{ i18n "pages.inbounds.client" }}</a-divider> - <template v-for="(row, index) in qrModal.qrcodes"> - <a-tag color="green" style="margin: 10px 0; display: block; text-align: center;">[[ row.remark ]]</a-tag> - <div class="qr-bg"><canvas @click="copyToClipboard('qrCode-'+index, row.link)" :id="'qrCode-'+index" style="width: 100%; height: 100%;"></canvas></div> + <a-divider v-if="!isJustSub">{{ i18n "pages.inbounds.client" }}</a-divider> + <template v-if="!isJustSub"> + <template v-for="(row, index) in qrModal.qrcodes"> + <a-tag color="green" style="margin: 10px 0; display: block; text-align: center;">[[ row.remark ]]</a-tag> + <div class="qr-bg"><canvas @click="copyToClipboard('qrCode-'+index, row.link)" :id="'qrCode-'+index" style="width: 100%; height: 100%;"></canvas></div> + </template> </template> </a-modal> @@ -27,12 +29,14 @@ qrcodes: [], clipboard: null, visible: false, + isJustSub: false, subId: '', - show: function (title = '', dbInbound, client) { + show: function (title = '', dbInbound, client, isJustSub = false) { this.title = title; this.dbInbound = dbInbound; this.inbound = dbInbound.toInbound(); this.client = client; + this.isJustSub = isJustSub; this.subId = ''; this.qrcodes = []; this.inbound.genAllLinks(this.dbInbound.remark, app.remarkModel, client).forEach(l => { @@ -53,6 +57,9 @@ el: '#qrcode-modal', data: { qrModal: qrModal, + get isJustSub(){ + return qrModal.isJustSub + } }, methods: { copyToClipboard(elmentId, content) { diff --git a/web/html/xui/client_modal.html b/web/html/xui/client_modal.html index 4b270607..02c548e3 100644 --- a/web/html/xui/client_modal.html +++ b/web/html/xui/client_modal.html @@ -15,7 +15,12 @@ confirmLoading: false, title: '', okText: '', - isEdit: false, + group: { + isGroup: false, + currentClient: null, + inbounds: [], + clients: [], + }, dbInbound: new DBInbound(), inbound: new Inbound(), clients: [], @@ -28,30 +33,76 @@ if (clientModal.isEdit) { ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.oldClientId); } else { - ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id); + if (clientModal.group.isGroup) { + const currentClient = clientModal.group.currentClient; + + clientModal.group.clients.forEach((client, index) => { + const { email, limitIp, totalGB, expiryTime, reset, enable, subId, tgId, flow } = currentClient; + + client.email = `${email}-${index + 1}`; + client.limitIp = limitIp; + client.totalGB = totalGB; + client.expiryTime = expiryTime; + client.reset = reset; + client.enable = enable; + + if (subId) { + client.subId = subId; + } + if (tgId) { + client.tgId = tgId; + } + if (flow) { + client.flow = flow; + } + }); + ObjectUtil.execute(clientModal.confirm, clientModal.group.clients, clientModal.group.inbounds); + } else { + ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id); + } } }, show({ title = '', okText = '{{ i18n "sure" }}', index = null, dbInbound = null, confirm = () => { }, isEdit = false }) { + this.group = { + isGroup: false, + currentClient: null, + inbounds: [], + clients: [], + } this.visible = true; this.title = title; this.okText = okText; this.isEdit = isEdit; + if (Array.isArray(dbInbound)) { + this.group.isGroup = true; + dbInbound.forEach((dbInboundItem) => { + this.showProcess(dbInboundItem); + this.group.inbounds.push(dbInboundItem.id) + this.group.clients.push(this.clients[this.index]) + }) + this.group.currentClient = this.clients[this.index] + } else { + this.showProcess(dbInbound, index); + if (isEdit) { + if (this.clients[index].expiryTime < 0) { + this.delayedStart = true; + } + this.oldClientId = this.getClientId(dbInbound.protocol, clients[index]); + } else { + this.addClient(this.inbound.protocol, this.clients); + } + } + this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clients[this.index].email); + this.confirm = confirm; + }, + showProcess(dbInbound, index = null) { this.dbInbound = new DBInbound(dbInbound); this.inbound = dbInbound.toInbound(); this.clients = this.inbound.clients; this.index = index === null ? this.clients.length : index; this.delayedStart = false; - if (isEdit) { - if (this.clients[index].expiryTime < 0) { - this.delayedStart = true; - } - this.oldClientId = this.getClientId(dbInbound.protocol, clients[index]); - } else { - this.addClient(this.inbound.protocol, this.clients); - } - this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clients[this.index].email); - this.confirm = confirm; - }, + this.addClient(this.inbound.protocol, this.clients); + }, getClientId(protocol, client) { switch (protocol) { case Protocols.TROJAN: return client.password; @@ -94,6 +145,9 @@ get isEdit() { return this.clientModal.isEdit; }, + get isGroup() { + return this.clientModal.group.isGroup; + }, get datepicker() { return app.datepicker; }, diff --git a/web/html/xui/form/client.html b/web/html/xui/form/client.html index 204aca72..f4ac25f3 100644 --- a/web/html/xui/form/client.html +++ b/web/html/xui/form/client.html @@ -15,7 +15,7 @@ </template> <a-input v-model.trim="client.email"></a-input> </a-form-item> - <a-form-item v-if="inbound.protocol === Protocols.TROJAN || inbound.protocol === Protocols.SHADOWSOCKS"> + <a-form-item v-if="(inbound.protocol === Protocols.TROJAN || inbound.protocol === Protocols.SHADOWSOCKS) && !isGroup"> <template slot="label"> <a-tooltip> <template slot="title"> @@ -28,7 +28,7 @@ </template> <a-input v-model.trim="client.password"></a-input> </a-form-item> - <a-form-item v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS"> + <a-form-item v-if="(inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS) && !isGroup"> <template slot="label"> <a-tooltip> <template slot="title"> diff --git a/web/html/xui/inbound_modal.html b/web/html/xui/inbound_modal.html index ab42e09c..7f9ed740 100644 --- a/web/html/xui/inbound_modal.html +++ b/web/html/xui/inbound_modal.html @@ -13,6 +13,7 @@ confirmLoading: false, okText: '{{ i18n "sure" }}', isEdit: false, + isGroup: false, confirm: null, inbound: new Inbound(), dbInbound: new DBInbound(), @@ -60,6 +61,9 @@ get isEdit() { return inModal.isEdit; }, + get isGroup() { + return inModal.isGroup; + }, get client() { return inModal.inbound.clients[0]; }, diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html index c986e3fd..53693ab3 100644 --- a/web/html/xui/inbounds.html +++ b/web/html/xui/inbounds.html @@ -145,6 +145,10 @@ <a-icon type="rest"></a-icon> {{ i18n "pages.inbounds.delDepletedClients" }} </a-menu-item> + <a-menu-item v-if="subSettings.enable && dbInbounds.length > 0" key="addGroupClient"> + <a-icon type="usergroup-add"></a-icon> + {{ i18n "pages.client.groupAdd"}} + </a-menu-item> </a-menu> </a-dropdown> </a-col> @@ -285,7 +289,7 @@ <p v-for="clientEmail in clientCount[dbInbound.id].online">[[ clientEmail ]]</p> </template> <a-tag style="margin:0; padding: 0 2px;" color="blue" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag> - </a-popover> + </a-popover> </template> </template> <template slot="traffic" slot-scope="text, dbInbound"> @@ -339,7 +343,7 @@ <a-tag style="margin:0;" color="blue">[[ dbInbound.toInbound().stream.network ]]</a-tag> <a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="green">tls</a-tag> <a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="green">reality</a-tag> - </template> + </template> </td> </tr> <tr> @@ -373,7 +377,7 @@ <p v-for="clientEmail in clientCount[dbInbound.id].online">[[ clientEmail ]]</p> </template> <a-tag style="margin:0; padding: 0 2px;" color="green" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag> - </a-popover> + </a-popover> </td> </tr> <tr> @@ -740,6 +744,9 @@ case "delDepletedClients": this.delDepletedClients(-1) break; + case "addGroupClient": + this.openGroupAddClient() + break; } }, clickAction(action, dbInbound) { @@ -883,6 +890,20 @@ await this.submit(`/panel/inbound/update/${dbInbound.id}`, data, inModal); }, + openGroupAddClient() { + clientModal.show({ + title: '{{ i18n "pages.client.groupAdd"}}', + okText: '{{ i18n "pages.client.submitAdd"}}', + dbInbound: this.dbInbounds, + confirm: async (clients, dbInboundIds) => { + clientModal.loading(); + await this.addGroupClient(clients, dbInboundIds); + clientModal.close(); + await this.showQrcode(dbInboundIds[0],clients[0], true) + }, + isEdit: false + }); + }, openAddClient(dbInboundId) { dbInbound = this.dbInbounds.find(row => row.id === dbInboundId); clientModal.show({ @@ -893,6 +914,7 @@ clientModal.loading(); await this.addClient(clients, dbInboundId); clientModal.close(); + await this.showQrcode(dbInboundId,clients) }, isEdit: false }); @@ -936,11 +958,24 @@ } }, async addClient(clients, dbInboundId) { - const data = { + const data = [{ id: dbInboundId, settings: '{"clients": [' + clients.toString() + ']}', - }; - await this.submit(`/panel/inbound/addClient`, data); + }]; + + await this.submit(`/panel/inbound/addClient`, data, true) + }, + + async addGroupClient(clients, dbInboundIds) { + const data = [] + dbInboundIds.forEach((dbInboundId, index) => { + data.push({ + id: dbInboundId, + settings: '{"clients": [' + clients[index].toString() + ']}', + }) + }) + + await this.submit(`/panel/inbound/addClient`, data, true) }, async updateClient(client, dbInboundId, clientId) { const data = { @@ -1001,8 +1036,8 @@ checkFallback(dbInbound) { newDbInbound = new DBInbound(dbInbound); if (dbInbound.listen.startsWith("@")){ - rootInbound = this.inbounds.find((i) => - i.isTcp && + rootInbound = this.inbounds.find((i) => + i.isTcp && ['trojan','vless'].includes(i.protocol) && i.settings.fallbacks.find(f => f.dest === dbInbound.listen) ); @@ -1018,10 +1053,10 @@ } return newDbInbound; }, - showQrcode(dbInboundId, client) { + showQrcode(dbInboundId, client, isJustSub = false) { dbInbound = this.dbInbounds.find(row => row.id === dbInboundId); newDbInbound = this.checkFallback(dbInbound); - qrModal.show('{{ i18n "qrCode"}}', newDbInbound, client); + qrModal.show('{{ i18n "qrCode"}}', newDbInbound, client, isJustSub); }, showInfo(dbInboundId, client) { dbInbound = this.dbInbounds.find(row => row.id === dbInboundId); @@ -1050,8 +1085,8 @@ await this.updateClient(clients[index], dbInboundId, clientId); this.loading(false); }, - async submit(url, data) { - const msg = await HttpUtil.postWithModal(url, data); + async submit(url, data, isJson = false) { + const msg = isJson ? await HttpUtil.postWithModalJson(url, data) : await HttpUtil.postWithModal(url, data); if (msg.success) { await this.getDBInbounds(); } diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index 006378ab..b2d74c3b 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -184,6 +184,7 @@ [pages.client] "add" = "Add Client" +"groupAdd" = "Add subscription user" "edit" = "Edit Client" "submitAdd" = "Add Client" "submitEdit" = "Save Changes" diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index bbf50b6c..83c9460b 100644 --- a/web/translation/translate.es_ES.toml +++ b/web/translation/translate.es_ES.toml @@ -184,6 +184,7 @@ [pages.client] "add" = "Agregar Cliente" +"groupAdd" = "Agregar usuario de suscripción" "edit" = "Editar Cliente" "submitAdd" = "Agregar Cliente" "submitEdit" = "Guardar Cambios" diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index 1de8538c..9613c12e 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -184,6 +184,7 @@ [pages.client] "add" = "کاربر جدید" +"groupAdd" = "کاربر جدید سابسکریپشن" "edit" = "ویرایش کاربر" "submitAdd" = "اضافه کردن" "submitEdit" = "ذخیره تغییرات" diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index 2f33311e..7cf2be0a 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -184,6 +184,7 @@ [pages.client] "add" = "Добавить пользователя" +"groupAdd" = "Добавить пользователя подписки" "edit" = "Редактировать пользователя" "submitAdd" = "Добавить пользователя" "submitEdit" = "Сохранить изменения" diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml index 1a2cfaa0..9cd7bc46 100644 --- a/web/translation/translate.vi_VN.toml +++ b/web/translation/translate.vi_VN.toml @@ -184,6 +184,7 @@ [pages.client]
"add" = "Thêm người dùng"
+"groupAdd" = "Thêm người dùng đăng ký"
"edit" = "Chỉnh sửa người dùng"
"submitAdd" = "Thêm"
"submitEdit" = "Lưu thay đổi"
diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml index 966ef3f7..c229dc29 100644 --- a/web/translation/translate.zh_Hans.toml +++ b/web/translation/translate.zh_Hans.toml @@ -184,6 +184,7 @@ [pages.client] "add" = "添加客户端" +"groupAdd" = "添加订阅用户" "edit" = "编辑客户端" "submitAdd" = "添加客户端" "submitEdit" = "保存修改" |
