mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-09-11 12:46:19 +00:00
add group user with the same subscription id to all inbounds
This commit is contained in:
parent
783f1a073e
commit
95d03a7ffd
20 changed files with 551 additions and 114 deletions
|
@ -70,6 +70,41 @@ class HttpUtil {
|
||||||
}
|
}
|
||||||
return msg;
|
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 {
|
class PromiseUtil {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"x-ui/database/model"
|
"x-ui/database/model"
|
||||||
"x-ui/web/service"
|
"x-ui/web/service"
|
||||||
"x-ui/web/session"
|
"x-ui/web/session"
|
||||||
|
@ -33,8 +33,10 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
|
||||||
g.POST("/clientIps/:email", a.getClientIps)
|
g.POST("/clientIps/:email", a.getClientIps)
|
||||||
g.POST("/clearClientIps/:email", a.clearClientIps)
|
g.POST("/clearClientIps/:email", a.clearClientIps)
|
||||||
g.POST("/addClient", a.addInboundClient)
|
g.POST("/addClient", a.addInboundClient)
|
||||||
|
g.POST("/addGroupClient", a.addGroupInboundClient)
|
||||||
g.POST("/:id/delClient/:clientId", a.delInboundClient)
|
g.POST("/:id/delClient/:clientId", a.delInboundClient)
|
||||||
g.POST("/updateClient/:clientId", a.updateInboundClient)
|
g.POST("/updateClient/:clientId", a.updateInboundClient)
|
||||||
|
g.POST("/updateClients", a.updateGroupInboundClient)
|
||||||
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
|
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
|
||||||
g.POST("/resetAllTraffics", a.resetAllTraffics)
|
g.POST("/resetAllTraffics", a.resetAllTraffics)
|
||||||
g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
|
g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
|
||||||
|
@ -190,6 +192,34 @@ func (a *InboundController) addInboundClient(c *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *InboundController) addGroupInboundClient(c *gin.Context) {
|
||||||
|
var requestData []model.Inbound
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (a *InboundController) delInboundClient(c *gin.Context) {
|
func (a *InboundController) delInboundClient(c *gin.Context) {
|
||||||
id, err := strconv.Atoi(c.Param("id"))
|
id, err := strconv.Atoi(c.Param("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -234,6 +264,56 @@ func (a *InboundController) updateInboundClient(c *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *InboundController) updateGroupInboundClient(c *gin.Context) {
|
||||||
|
var requestData []map[string]interface{}
|
||||||
|
|
||||||
|
if err := c.ShouldBindJSON(&requestData); err != nil {
|
||||||
|
jsonMsg(c, I18nWeb(c, "pages.inbounds.update"), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
needRestart := false
|
||||||
|
|
||||||
|
for _, item := range requestData {
|
||||||
|
|
||||||
|
inboundMap, ok := item["inbound"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
jsonMsg(c, "Something went wrong!", errors.New("Failed to convert 'inbound' to map"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clientId, ok := item["clientId"].(string)
|
||||||
|
if !ok {
|
||||||
|
jsonMsg(c, "Something went wrong!", errors.New("Failed to convert 'clientId' to string"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
inboundJSON, err := json.Marshal(inboundMap)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "Something went wrong!", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var inboundModel model.Inbound
|
||||||
|
if err := json.Unmarshal(inboundJSON, &inboundModel); err != nil {
|
||||||
|
jsonMsg(c, "Something went wrong!", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if restart, err := a.inboundService.UpdateInboundClient(&inboundModel, clientId); err != nil {
|
||||||
|
jsonMsg(c, "Something went wrong!", err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
needRestart = needRestart || restart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonMsg(c, "Client updated", nil)
|
||||||
|
if needRestart {
|
||||||
|
a.xrayService.SetToNeedRestart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *InboundController) resetClientTraffic(c *gin.Context) {
|
func (a *InboundController) resetClientTraffic(c *gin.Context) {
|
||||||
id, err := strconv.Atoi(c.Param("id"))
|
id, err := strconv.Atoi(c.Param("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
</tr-qr-bg>
|
</tr-qr-bg>
|
||||||
</tr-qr-box>
|
</tr-qr-box>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="!isJustSub">
|
||||||
<template v-for="(row, index) in qrModal.qrcodes">
|
<template v-for="(row, index) in qrModal.qrcodes">
|
||||||
<tr-qr-box class="qr-box">
|
<tr-qr-box class="qr-box">
|
||||||
<a-tag color="green" class="qr-tag"><span>[[ row.remark ]]</span></a-tag>
|
<a-tag color="green" class="qr-tag"><span>[[ row.remark ]]</span></a-tag>
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
</tr-qr-bg>
|
</tr-qr-bg>
|
||||||
</tr-qr-box>
|
</tr-qr-box>
|
||||||
</template>
|
</template>
|
||||||
|
</template>
|
||||||
</tr-qr-modal>
|
</tr-qr-modal>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
|
@ -43,13 +45,13 @@
|
||||||
qrcodes: [],
|
qrcodes: [],
|
||||||
clipboard: null,
|
clipboard: null,
|
||||||
visible: false,
|
visible: false,
|
||||||
subId: '',
|
isJustSub: false,subId: '',
|
||||||
show: function(title = '', dbInbound, client) {
|
show: function(title = '', dbInbound, client, isJustSub = false) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.dbInbound = dbInbound;
|
this.dbInbound = dbInbound;
|
||||||
this.inbound = dbInbound.toInbound();
|
this.inbound = dbInbound.toInbound();
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.subId = '';
|
this.isJustSub = isJustSub;this.subId = '';
|
||||||
this.qrcodes = [];
|
this.qrcodes = [];
|
||||||
if (this.inbound.protocol == Protocols.WIREGUARD) {
|
if (this.inbound.protocol == Protocols.WIREGUARD) {
|
||||||
this.inbound.genInboundLinks(dbInbound.remark).split('\r\n').forEach((l, index) => {
|
this.inbound.genInboundLinks(dbInbound.remark).split('\r\n').forEach((l, index) => {
|
||||||
|
@ -76,7 +78,9 @@
|
||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
el: '#qrcode-modal',
|
el: '#qrcode-modal',
|
||||||
data: {
|
data: {
|
||||||
qrModal: qrModal,
|
qrModal: qrModal,get isJustSub(){
|
||||||
|
return qrModal.isJustSub
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
copyToClipboard(elementId, content) {
|
copyToClipboard(elementId, content) {
|
||||||
|
|
|
@ -4,7 +4,10 @@
|
||||||
:class="themeSwitcher.currentTheme"
|
:class="themeSwitcher.currentTheme"
|
||||||
:ok-text="clientModal.okText" cancel-text='{{ i18n "close" }}'>
|
:ok-text="clientModal.okText" cancel-text='{{ i18n "close" }}'>
|
||||||
<template v-if="isEdit">
|
<template v-if="isEdit">
|
||||||
<a-tag v-if="isExpiry || isTrafficExhausted" color="red" style="margin-bottom: 10px;display: block;text-align: center;">Account is (Expired|Traffic Ended) And Disabled</a-tag>
|
<a-tag v-if="isExpiry || isTrafficExhausted" color="red"
|
||||||
|
style="margin-bottom: 10px;display: block;text-align: center;">Account is (Expired|Traffic Ended) And
|
||||||
|
Disabled
|
||||||
|
</a-tag>
|
||||||
</template>
|
</template>
|
||||||
{{template "form/client"}}
|
{{template "form/client"}}
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
@ -16,7 +19,16 @@
|
||||||
title: '',
|
title: '',
|
||||||
okText: '',
|
okText: '',
|
||||||
isEdit: false,
|
isEdit: false,
|
||||||
|
group: {
|
||||||
|
canGroup: true,
|
||||||
|
isGroup: false,
|
||||||
|
currentClient: null,
|
||||||
|
inbounds: [],
|
||||||
|
clients: [],
|
||||||
|
editIds: []
|
||||||
|
},
|
||||||
dbInbound: new DBInbound(),
|
dbInbound: new DBInbound(),
|
||||||
|
dbInbounds: null,
|
||||||
inbound: new Inbound(),
|
inbound: new Inbound(),
|
||||||
clients: [],
|
clients: [],
|
||||||
clientStats: [],
|
clientStats: [],
|
||||||
|
@ -25,54 +37,150 @@
|
||||||
clientIps: null,
|
clientIps: null,
|
||||||
delayedStart: false,
|
delayedStart: false,
|
||||||
ok() {
|
ok() {
|
||||||
|
if (app.subSettings.enable && clientModal.group.isGroup && clientModal.group.canGroup) {
|
||||||
|
const currentClient = clientModal.group.currentClient;
|
||||||
|
const { limitIp, totalGB, expiryTime, reset, enable, subId, tgId, flow } = currentClient;
|
||||||
|
const uniqueEmails = clientModalApp.makeGroupEmailsUnique(clientModal.dbInbounds, currentClient.email, clientModal.group.clients);
|
||||||
|
|
||||||
|
clientModal.group.clients.forEach((client, index) => {
|
||||||
|
client.email = uniqueEmails[index];
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (clientModal.isEdit) {
|
||||||
|
ObjectUtil.execute(clientModal.confirm, clientModal.group.clients, clientModal.group.inbounds, clientModal.group.editIds);
|
||||||
|
} else {
|
||||||
|
ObjectUtil.execute(clientModal.confirm, clientModal.group.clients, clientModal.group.inbounds);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (clientModal.isEdit) {
|
if (clientModal.isEdit) {
|
||||||
ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.oldClientId);
|
ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.oldClientId);
|
||||||
} else {
|
} else {
|
||||||
ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id);
|
ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
show({ title = '', okText = '{{ i18n "sure" }}', index = null, dbInbound = null, confirm = () => { }, isEdit = false }) {
|
show({
|
||||||
|
title = '',
|
||||||
|
okText = '{{ i18n "sure" }}',
|
||||||
|
index = null,
|
||||||
|
dbInbound = null,
|
||||||
|
dbInbounds = null,
|
||||||
|
confirm = () => {
|
||||||
|
},
|
||||||
|
isEdit = false
|
||||||
|
}) {
|
||||||
|
this.group = {
|
||||||
|
canGroup: true,
|
||||||
|
isGroup: false,
|
||||||
|
currentClient: null,
|
||||||
|
inbounds: [],
|
||||||
|
clients: [],
|
||||||
|
editIds: []
|
||||||
|
}
|
||||||
|
this.dbInbounds = dbInbounds;
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.okText = okText;
|
this.okText = okText;
|
||||||
this.isEdit = isEdit;
|
this.isEdit = isEdit;
|
||||||
|
if (app.subSettings.enable && dbInbounds !== null && Array.isArray(dbInbounds)) {
|
||||||
|
if (isEdit) {
|
||||||
|
this.showProcess(dbInbound, index);
|
||||||
|
let processSingleEdit = true
|
||||||
|
if (this.group.canGroup) {
|
||||||
|
this.group.currentClient = this.clients[this.index]
|
||||||
|
const response = app.getSubGroupClients(dbInbounds, this.group.currentClient)
|
||||||
|
if (response.clients.length > 1) {
|
||||||
|
this.group.isGroup = true;
|
||||||
|
this.group.inbounds = response.inbounds
|
||||||
|
this.group.clients = response.clients
|
||||||
|
this.group.editIds = response.editIds
|
||||||
|
if (this.clients[index].expiryTime < 0) {
|
||||||
|
this.delayedStart = true;
|
||||||
|
}
|
||||||
|
processSingleEdit = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (processSingleEdit) {
|
||||||
|
this.singleEditClientProcess(index)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.group.isGroup = true;
|
||||||
|
dbInbounds.forEach((dbInboundItem) => {
|
||||||
|
this.showProcess(dbInboundItem);
|
||||||
|
this.addClient(this.inbound.protocol, this.clients);
|
||||||
|
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) {
|
||||||
|
this.singleEditClientProcess(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.dbInbound = new DBInbound(dbInbound);
|
||||||
this.inbound = dbInbound.toInbound();
|
this.inbound = dbInbound.toInbound();
|
||||||
this.clients = this.inbound.clients;
|
this.clients = this.inbound.clients;
|
||||||
this.index = index === null ? this.clients.length : index;
|
this.index = index === null ? this.clients.length : index;
|
||||||
this.delayedStart = false;
|
this.delayedStart = false;
|
||||||
if (isEdit) {
|
},
|
||||||
|
singleEditClientProcess(index) {
|
||||||
if (this.clients[index].expiryTime < 0) {
|
if (this.clients[index].expiryTime < 0) {
|
||||||
this.delayedStart = true;
|
this.delayedStart = true;
|
||||||
}
|
}
|
||||||
this.oldClientId = this.getClientId(dbInbound.protocol, clients[index]);
|
this.oldClientId = this.getClientId(this.dbInbound.protocol, this.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;
|
|
||||||
},
|
},
|
||||||
getClientId(protocol, client) {
|
getClientId(protocol, client) {
|
||||||
switch (protocol) {
|
switch (protocol) {
|
||||||
case Protocols.TROJAN: return client.password;
|
case Protocols.TROJAN:
|
||||||
case Protocols.SHADOWSOCKS: return client.email;
|
return client.password;
|
||||||
default: return client.id;
|
case Protocols.SHADOWSOCKS:
|
||||||
|
return client.email;
|
||||||
|
default:
|
||||||
|
return client.id;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addClient(protocol, clients) {
|
addClient(protocol, clients) {
|
||||||
switch (protocol) {
|
switch (protocol) {
|
||||||
case Protocols.VMESS: return clients.push(new Inbound.VmessSettings.VMESS());
|
case Protocols.VMESS:
|
||||||
case Protocols.VLESS: return clients.push(new Inbound.VLESSSettings.VLESS());
|
return clients.push(new Inbound.VmessSettings.VMESS());
|
||||||
case Protocols.TROJAN: return clients.push(new Inbound.TrojanSettings.Trojan());
|
case Protocols.VLESS:
|
||||||
case Protocols.SHADOWSOCKS: return clients.push(new Inbound.ShadowsocksSettings.Shadowsocks(clients[0].method));
|
return clients.push(new Inbound.VLESSSettings.VLESS());
|
||||||
default: return null;
|
case Protocols.TROJAN:
|
||||||
|
return clients.push(new Inbound.TrojanSettings.Trojan());
|
||||||
|
case Protocols.SHADOWSOCKS:
|
||||||
|
return clients.push(new Inbound.ShadowsocksSettings.Shadowsocks(clients[0].method));
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
clientModal.visible = false;
|
clientModal.visible = false;
|
||||||
clientModal.loading(false);
|
clientModal.loading(false);
|
||||||
},
|
},
|
||||||
loading(loading=true) {
|
loading(loading = true) {
|
||||||
clientModal.confirmLoading = loading;
|
clientModal.confirmLoading = loading;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -94,6 +202,18 @@
|
||||||
get isEdit() {
|
get isEdit() {
|
||||||
return this.clientModal.isEdit;
|
return this.clientModal.isEdit;
|
||||||
},
|
},
|
||||||
|
get isGroup() {
|
||||||
|
return this.clientModal.group.isGroup;
|
||||||
|
},
|
||||||
|
get isGroupEdit() {
|
||||||
|
return this.clientModal.group.canGroup;
|
||||||
|
},
|
||||||
|
set isGroupEdit(value) {
|
||||||
|
this.clientModal.group.canGroup = value;
|
||||||
|
if (!value) {
|
||||||
|
this.clientModal.singleEditClientProcess(this.clientModal.index)
|
||||||
|
}
|
||||||
|
},
|
||||||
get datepicker() {
|
get datepicker() {
|
||||||
return app.datepicker;
|
return app.datepicker;
|
||||||
},
|
},
|
||||||
|
@ -120,22 +240,26 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async getDBClientIps(email) {
|
makeGroupEmailsUnique(dbInbounds, baseEmail, groupClients) {
|
||||||
const msg = await HttpUtil.post(`/panel/inbound/clientIps/${email}`);
|
const match = baseEmail.match(/^(.*?)__/);
|
||||||
if (!msg.success) {
|
let base = match ? match[1] : baseEmail;
|
||||||
document.getElementById("clientIPs").value = msg.obj;
|
|
||||||
return;
|
const generatedEmails = groupClients.map((_, index) => `${base}__${index + 1}`);
|
||||||
|
|
||||||
|
const isDuplicate = (emailToCheck) => {
|
||||||
|
return dbInbounds.some((dbInbound) => {
|
||||||
|
const settings = JSON.parse(dbInbound.settings);
|
||||||
|
const clients = settings && settings.clients ? settings.clients : [];
|
||||||
|
return clients.some(client => emailToCheck.includes(client.email));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (generatedEmails.some(email => isDuplicate(email))) {
|
||||||
|
const randomString = `-${RandomUtil.randomLowerAndNum(4)}`;
|
||||||
|
return groupClients.map((_, index) => `${base}${randomString}__${index + 1}`);
|
||||||
}
|
}
|
||||||
let ips = msg.obj;
|
|
||||||
if (typeof ips === 'string' && ips.startsWith('[') && ips.endsWith(']')) {
|
return generatedEmails;
|
||||||
try {
|
|
||||||
ips = JSON.parse(ips);
|
|
||||||
ips = Array.isArray(ips) ? ips.join("\n") : ips;
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error parsing JSON:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.getElementById("clientIPs").value = ips;
|
|
||||||
},
|
},
|
||||||
async clearDBClientIps(email) {
|
async clearDBClientIps(email) {
|
||||||
try {
|
try {
|
||||||
|
@ -147,7 +271,23 @@
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resetClientTraffic(email, dbInboundId, iconElement) {
|
async resetClientTrafficHandler(client, dbInboundId, clients = []) {
|
||||||
|
if (clients.length > 0) {
|
||||||
|
let response;
|
||||||
|
clients.forEach((client) => {
|
||||||
|
const inbound = clientModal.dbInbounds.find(inbound => inbound.id === client.inboundId)
|
||||||
|
if(inbound && app.hasClientStats(inbound, client.email)) {
|
||||||
|
response = HttpUtil.postWithModal('/panel/inbound/' + dbInboundId + '/resetClientTraffic/' + client.email);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return response;
|
||||||
|
} else {
|
||||||
|
return HttpUtil.postWithModal('/panel/inbound/' + dbInboundId + '/resetClientTraffic/' + client.email)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetClientTraffic(client, dbInboundId, iconElement) {
|
||||||
|
const subGroup = app.subSettings.enable && clientModal.group.isGroup && clientModal.group.canGroup && clientModal.dbInbounds && clientModal.dbInbounds.length > 0 ? app.getSubGroupClients(clientModal.dbInbounds, client) : [];
|
||||||
|
const clients = subGroup && subGroup.clients && subGroup.clients.length > 0 ? subGroup.clients : [];
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: '{{ i18n "pages.inbounds.resetTraffic"}}',
|
title: '{{ i18n "pages.inbounds.resetTraffic"}}',
|
||||||
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
|
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
|
||||||
|
@ -156,8 +296,8 @@
|
||||||
cancelText: '{{ i18n "cancel"}}',
|
cancelText: '{{ i18n "cancel"}}',
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
iconElement.disabled = true;
|
iconElement.disabled = true;
|
||||||
const msg = await HttpUtil.postWithModal('/panel/inbound/' + dbInboundId + '/resetClientTraffic/' + email);
|
const msg = await this.resetClientTrafficHandler(client, dbInboundId, clients);
|
||||||
if (msg.success) {
|
if (msg && msg.success) {
|
||||||
this.clientModal.clientStats.up = 0;
|
this.clientModal.clientStats.up = 0;
|
||||||
this.clientModal.clientStats.down = 0;
|
this.clientModal.clientStats.down = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,18 @@
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.enable" }}'>
|
<a-form-item label='{{ i18n "pages.inbounds.enable" }}'>
|
||||||
<a-switch v-model="client.enable"></a-switch>
|
<a-switch v-model="client.enable"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item v-if="isEdit && app.subSettings.enable && isGroup">
|
||||||
|
<template slot="label">
|
||||||
|
<a-tooltip>
|
||||||
|
<template slot="title">
|
||||||
|
<span>{{ i18n "pages.client.isGroupEditDesc" }}</span>
|
||||||
|
</template>
|
||||||
|
{{ i18n "pages.client.isGroupEdit" }}
|
||||||
|
<a-icon type="question-circle"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<a-switch v-model="isGroupEdit"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<template slot="label">
|
<template slot="label">
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
|
@ -134,7 +146,7 @@
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
||||||
<a-icon type="retweet"
|
<a-icon type="retweet"
|
||||||
@click="resetClientTraffic(client.email,clientStats.inboundId,$event.target)"
|
@click="resetClientTraffic(client,clientStats.inboundId,$event.target)"
|
||||||
v-if="client.email.length > 0"></a-icon>
|
v-if="client.email.length > 0"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<template slot="title">{{ i18n "info" }}</template>
|
<template slot="title">{{ i18n "info" }}</template>
|
||||||
<a-icon style="font-size: 24px;" class="normal-icon" type="info-circle" @click="showInfo(record.id,client);"></a-icon>
|
<a-icon style="font-size: 24px;" class="normal-icon" type="info-circle" @click="showInfo(record.id,client);"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip v-if="hasClientStats(record, client.email)">
|
||||||
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
||||||
<a-popconfirm @confirm="resetClientTraffic(client,record.id,false)" title='{{ i18n "pages.inbounds.resetTrafficContent"}}' :overlay-class-name="themeSwitcher.currentTheme" ok-text='{{ i18n "reset"}}' cancel-text='{{ i18n "cancel"}}'>
|
<a-popconfirm @confirm="resetClientTraffic(client,record.id,false)" title='{{ i18n "pages.inbounds.resetTrafficContent"}}' :overlay-class-name="themeSwitcher.currentTheme" ok-text='{{ i18n "reset"}}' cancel-text='{{ i18n "cancel"}}'>
|
||||||
<a-icon slot="icon" type="question-circle-o" :style="themeSwitcher.isDarkTheme ? 'color: var(--color-primary-100)' : 'color: var(--color-primary-100)'"></a-icon>
|
<a-icon slot="icon" type="question-circle-o" :style="themeSwitcher.isDarkTheme ? 'color: var(--color-primary-100)' : 'color: var(--color-primary-100)'"></a-icon>
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
confirmLoading: false,
|
confirmLoading: false,
|
||||||
okText: '{{ i18n "sure" }}',
|
okText: '{{ i18n "sure" }}',
|
||||||
isEdit: false,
|
isEdit: false,
|
||||||
|
isGroup: false,
|
||||||
confirm: null,
|
confirm: null,
|
||||||
inbound: new Inbound(),
|
inbound: new Inbound(),
|
||||||
dbInbound: new DBInbound(),
|
dbInbound: new DBInbound(),
|
||||||
|
@ -61,6 +62,9 @@
|
||||||
get isEdit() {
|
get isEdit() {
|
||||||
return inModal.isEdit;
|
return inModal.isEdit;
|
||||||
},
|
},
|
||||||
|
get isGroup() {
|
||||||
|
return inModal.isGroup;
|
||||||
|
},
|
||||||
get client() {
|
get client() {
|
||||||
return inModal.inbound.clients[0];
|
return inModal.inbound.clients[0];
|
||||||
},
|
},
|
||||||
|
|
|
@ -224,6 +224,10 @@
|
||||||
<a-icon type="rest"></a-icon>
|
<a-icon type="rest"></a-icon>
|
||||||
{{ i18n "pages.inbounds.delDepletedClients" }}
|
{{ i18n "pages.inbounds.delDepletedClients" }}
|
||||||
</a-menu-item>
|
</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-menu>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
@ -859,6 +863,9 @@
|
||||||
case "delDepletedClients":
|
case "delDepletedClients":
|
||||||
this.delDepletedClients(-1)
|
this.delDepletedClients(-1)
|
||||||
break;
|
break;
|
||||||
|
case "addGroupClient":
|
||||||
|
this.openGroupAddClient()
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clickAction(action, dbInbound) {
|
clickAction(action, dbInbound) {
|
||||||
|
@ -1004,6 +1011,18 @@
|
||||||
|
|
||||||
await this.submit(`/panel/inbound/update/${dbInbound.id}`, data, inModal);
|
await this.submit(`/panel/inbound/update/${dbInbound.id}`, data, inModal);
|
||||||
},
|
},
|
||||||
|
openGroupAddClient() {
|
||||||
|
clientModal.show({
|
||||||
|
title: '{{ i18n "pages.client.groupAdd"}}',
|
||||||
|
okText: '{{ i18n "pages.client.submitAdd"}}',
|
||||||
|
dbInbounds: this.dbInbounds,
|
||||||
|
confirm: async (clients, dbInboundIds) => {
|
||||||
|
await this.addGroupClient(clients, dbInboundIds, clientModal);
|
||||||
|
await this.showQrcode(dbInboundIds[0],clients[0], true)
|
||||||
|
},
|
||||||
|
isEdit: false
|
||||||
|
});
|
||||||
|
},
|
||||||
openAddClient(dbInboundId) {
|
openAddClient(dbInboundId) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
clientModal.show({
|
clientModal.show({
|
||||||
|
@ -1012,6 +1031,7 @@
|
||||||
dbInbound: dbInbound,
|
dbInbound: dbInbound,
|
||||||
confirm: async (clients, dbInboundId) => {
|
confirm: async (clients, dbInboundId) => {
|
||||||
await this.addClient(clients, dbInboundId, clientModal);
|
await this.addClient(clients, dbInboundId, clientModal);
|
||||||
|
await this.showQrcode(dbInboundId,clients)
|
||||||
},
|
},
|
||||||
isEdit: false
|
isEdit: false
|
||||||
});
|
});
|
||||||
|
@ -1034,6 +1054,7 @@
|
||||||
clientModal.show({
|
clientModal.show({
|
||||||
title: '{{ i18n "pages.client.edit"}}',
|
title: '{{ i18n "pages.client.edit"}}',
|
||||||
okText: '{{ i18n "pages.client.submitEdit"}}',
|
okText: '{{ i18n "pages.client.submitEdit"}}',
|
||||||
|
dbInbounds: this.dbInbounds,
|
||||||
dbInbound: dbInbound,
|
dbInbound: dbInbound,
|
||||||
index: index,
|
index: index,
|
||||||
confirm: async (client, dbInboundId, clientId) => {
|
confirm: async (client, dbInboundId, clientId) => {
|
||||||
|
@ -1059,12 +1080,36 @@
|
||||||
};
|
};
|
||||||
await this.submit(`/panel/inbound/addClient`, data, modal);
|
await this.submit(`/panel/inbound/addClient`, data, modal);
|
||||||
},
|
},
|
||||||
|
async addGroupClient(clients, dbInboundIds, modal) {
|
||||||
|
const data = []
|
||||||
|
dbInboundIds.forEach((dbInboundId, index) => {
|
||||||
|
data.push({
|
||||||
|
id: dbInboundId,
|
||||||
|
settings: '{"clients": [' + clients[index].toString() + ']}',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
await this.submit(`/panel/inbound/addGroupClient`, data, modal, true)
|
||||||
|
},
|
||||||
async updateClient(client, dbInboundId, clientId) {
|
async updateClient(client, dbInboundId, clientId) {
|
||||||
|
if (Array.isArray(client) && Array.isArray(dbInboundId) && Array.isArray(clientId)){
|
||||||
|
const data = []
|
||||||
|
client.forEach((client, index) => {
|
||||||
|
data.push({
|
||||||
|
clientId: clientId[index],
|
||||||
|
inbound: {
|
||||||
|
id: dbInboundId[index],
|
||||||
|
settings: '{"clients": [' + client.toString() + ']}',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
await this.submit(`/panel/inbound/updateClients`, data, clientModal, true);
|
||||||
|
}else{
|
||||||
const data = {
|
const data = {
|
||||||
id: dbInboundId,
|
id: dbInboundId,
|
||||||
settings: '{"clients": [' + client.toString() + ']}',
|
settings: '{"clients": [' + client.toString() + ']}',
|
||||||
};
|
};
|
||||||
await this.submit(`/panel/inbound/updateClient/${clientId}`, data, clientModal);
|
await this.submit(`/panel/inbound/updateClient/${clientId}`, data, clientModal);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
resetTraffic(dbInboundId) {
|
resetTraffic(dbInboundId) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
|
@ -1092,22 +1137,80 @@
|
||||||
onOk: () => this.submit('/panel/inbound/del/' + dbInboundId),
|
onOk: () => this.submit('/panel/inbound/del/' + dbInboundId),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
delClient(dbInboundId, client,confirmation = true) {
|
async delClientHandler(dbInboundId, currentClient, clients = []) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
if (clients.length > 0) {
|
||||||
clientId = this.getClientId(dbInbound.protocol, client);
|
for (const client of clients) {
|
||||||
|
const dbInbound = this.dbInbounds.find(inbound => inbound.id === client.inboundId);
|
||||||
|
if (dbInbound) {
|
||||||
|
const inbound = dbInbound.toInbound();
|
||||||
|
if (inbound && inbound.clients && inbound.clients.length === 1) {
|
||||||
|
let newClient = Inbound.Settings.getSettings(inbound.protocol).toString();
|
||||||
|
newClient = JSON.parse(newClient);
|
||||||
|
newClient = newClient && newClient.clients && newClient.clients.length > 0 ? JSON.stringify(newClient.clients[0], null, 2) : null;
|
||||||
|
if (newClient) {
|
||||||
|
const data = {
|
||||||
|
id: client.inboundId,
|
||||||
|
settings: '{"clients": [' + newClient + ']}',
|
||||||
|
};
|
||||||
|
await this.submit(`/panel/inbound/addClient`, data, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.submit(`/panel/inbound/${client.inboundId}/delClient/${client.clientId}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
|
const clientId = this.getClientId(dbInbound.protocol, currentClient);
|
||||||
|
await this.submit(`/panel/inbound/${dbInboundId}/delClient/${clientId}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delClient(dbInboundId, currentClient, confirmation = true) {
|
||||||
|
const subGroup = this.subSettings.enable ? this.getSubGroupClients(this.dbInbounds, currentClient) : [];
|
||||||
|
const clients = subGroup && subGroup.clients && subGroup.clients.length > 0 ? subGroup.clients : [];
|
||||||
if (confirmation){
|
if (confirmation){
|
||||||
|
const clientEmails = clients.length > 0 ? clients.map(item => item.email) : currentClient.email
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: '{{ i18n "pages.inbounds.deleteClient"}}' + ' ' + client.email,
|
title: '{{ i18n "pages.inbounds.deleteClient"}}' + ' ' + clientEmails,
|
||||||
content: '{{ i18n "pages.inbounds.deleteClientContent"}}',
|
content: '{{ i18n "pages.inbounds.deleteClientContent"}}',
|
||||||
class: themeSwitcher.currentTheme,
|
class: themeSwitcher.currentTheme,
|
||||||
okText: '{{ i18n "delete"}}',
|
okText: '{{ i18n "delete"}}',
|
||||||
cancelText: '{{ i18n "cancel"}}',
|
cancelText: '{{ i18n "cancel"}}',
|
||||||
onOk: () => this.submit(`/panel/inbound/${dbInboundId}/delClient/${clientId}`),
|
onOk: () => this.delClientHandler(dbInboundId, currentClient, clients),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.submit(`/panel/inbound/${dbInboundId}/delClient/${clientId}`);
|
this.delClientHandler(dbInboundId, currentClient, clients)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getSubGroupClients(dbInbounds, currentClient) {
|
||||||
|
const response = {
|
||||||
|
inbounds: [],
|
||||||
|
clients: [],
|
||||||
|
editIds: []
|
||||||
|
}
|
||||||
|
if (dbInbounds && dbInbounds.length > 0 && currentClient) {
|
||||||
|
dbInbounds.forEach((dbInboundItem) => {
|
||||||
|
const dbInbound = new DBInbound(dbInboundItem);
|
||||||
|
if (dbInbound) {
|
||||||
|
const inbound = dbInbound.toInbound();
|
||||||
|
if (inbound) {
|
||||||
|
const clients = inbound.clients;
|
||||||
|
if (clients.length > 0) {
|
||||||
|
clients.forEach((client) => {
|
||||||
|
if (client['subId'] === currentClient['subId']) {
|
||||||
|
client['inboundId'] = dbInboundItem.id
|
||||||
|
client['clientId'] = this.getClientId(dbInbound.protocol, client)
|
||||||
|
response.inbounds.push(dbInboundItem.id)
|
||||||
|
response.clients.push(client)
|
||||||
|
response.editIds.push(client['clientId'])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
},
|
||||||
getClientId(protocol, client) {
|
getClientId(protocol, client) {
|
||||||
switch (protocol) {
|
switch (protocol) {
|
||||||
case Protocols.TROJAN: return client.password;
|
case Protocols.TROJAN: return client.password;
|
||||||
|
@ -1135,10 +1238,10 @@
|
||||||
}
|
}
|
||||||
return newDbInbound;
|
return newDbInbound;
|
||||||
},
|
},
|
||||||
showQrcode(dbInboundId, client) {
|
showQrcode(dbInboundId, client, isJustSub = false) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
newDbInbound = this.checkFallback(dbInbound);
|
newDbInbound = this.checkFallback(dbInbound);
|
||||||
qrModal.show('{{ i18n "qrCode"}}', newDbInbound, client);
|
qrModal.show('{{ i18n "qrCode"}}', newDbInbound, client, isJustSub);
|
||||||
},
|
},
|
||||||
showInfo(dbInboundId, client) {
|
showInfo(dbInboundId, client) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
|
@ -1158,6 +1261,13 @@
|
||||||
},
|
},
|
||||||
async switchEnableClient(dbInboundId, client) {
|
async switchEnableClient(dbInboundId, client) {
|
||||||
this.loading()
|
this.loading()
|
||||||
|
const subGroup = this.subSettings.enable ? this.getSubGroupClients(this.dbInbounds, client) : [];
|
||||||
|
if (subGroup && subGroup.clients && subGroup.clients.length > 0){
|
||||||
|
await this.updateClient(subGroup.clients.map(item => {
|
||||||
|
item.enable = !item.enable
|
||||||
|
return item
|
||||||
|
}), subGroup.inbounds, subGroup.editIds);
|
||||||
|
}else{
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
inbound = dbInbound.toInbound();
|
inbound = dbInbound.toInbound();
|
||||||
clients = inbound.clients;
|
clients = inbound.clients;
|
||||||
|
@ -1165,10 +1275,11 @@
|
||||||
clients[index].enable = !clients[index].enable;
|
clients[index].enable = !clients[index].enable;
|
||||||
clientId = this.getClientId(dbInbound.protocol, clients[index]);
|
clientId = this.getClientId(dbInbound.protocol, clients[index]);
|
||||||
await this.updateClient(clients[index], dbInboundId, clientId);
|
await this.updateClient(clients[index], dbInboundId, clientId);
|
||||||
|
}
|
||||||
this.loading(false);
|
this.loading(false);
|
||||||
},
|
},
|
||||||
async submit(url, data, modal) {
|
async submit(url, data, model, isJson = false) {
|
||||||
const msg = await HttpUtil.postWithModal(url, data, modal);
|
const msg = isJson ? await HttpUtil.postWithModalJson(url, data, model) : await HttpUtil.postWithModal(url, data, model);
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
await this.getDBInbounds();
|
await this.getDBInbounds();
|
||||||
}
|
}
|
||||||
|
@ -1176,18 +1287,33 @@
|
||||||
getInboundClients(dbInbound) {
|
getInboundClients(dbInbound) {
|
||||||
return dbInbound.toInbound().clients;
|
return dbInbound.toInbound().clients;
|
||||||
},
|
},
|
||||||
|
resetClientTrafficHandler(client, dbInboundId, clients = []) {
|
||||||
|
if (clients.length > 0){
|
||||||
|
clients.forEach((client) => {
|
||||||
|
const inbound = this.dbInbounds.find(inbound => inbound.id === client.inboundId)
|
||||||
|
if(inbound && this.hasClientStats(inbound, client.email)) {
|
||||||
|
this.submit('/panel/inbound/' + client.inboundId + '/resetClientTraffic/' + client.email)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}else {
|
||||||
|
this.submit('/panel/inbound/' + dbInboundId + '/resetClientTraffic/' + client.email);
|
||||||
|
}
|
||||||
|
},
|
||||||
resetClientTraffic(client, dbInboundId, confirmation = true) {
|
resetClientTraffic(client, dbInboundId, confirmation = true) {
|
||||||
|
const subGroup = this.subSettings.enable ? this.getSubGroupClients(this.dbInbounds, client) : [];
|
||||||
|
const clients = subGroup && subGroup.clients && subGroup.clients.length > 0 ? subGroup.clients : [];
|
||||||
if (confirmation){
|
if (confirmation){
|
||||||
|
const clientEmails = clients.length > 0 ? clients.map(item => item.email) : client.email
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: '{{ i18n "pages.inbounds.resetTraffic"}}' + ' ' + client.email,
|
title: '{{ i18n "pages.inbounds.resetTraffic"}}' + ' ' + clientEmails,
|
||||||
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
|
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
|
||||||
class: themeSwitcher.currentTheme,
|
class: themeSwitcher.currentTheme,
|
||||||
okText: '{{ i18n "reset"}}',
|
okText: '{{ i18n "reset"}}',
|
||||||
cancelText: '{{ i18n "cancel"}}',
|
cancelText: '{{ i18n "cancel"}}',
|
||||||
onOk: () => this.submit('/panel/inbound/' + dbInboundId + '/resetClientTraffic/' + client.email),
|
onOk: () => this.resetClientTrafficHandler(client, dbInboundId, clients),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.submit('/panel/inbound/' + dbInboundId + '/resetClientTraffic/' + client.email);
|
this.resetClientTrafficHandler(client, dbInboundId, clients);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resetAllTraffic() {
|
resetAllTraffic() {
|
||||||
|
@ -1223,6 +1349,10 @@
|
||||||
isExpiry(dbInbound, index) {
|
isExpiry(dbInbound, index) {
|
||||||
return dbInbound.toInbound().isExpiry(index);
|
return dbInbound.toInbound().isExpiry(index);
|
||||||
},
|
},
|
||||||
|
hasClientStats(dbInbound, email) {
|
||||||
|
if (email.length == 0) return 0;
|
||||||
|
return !!dbInbound.clientStats.find(stats => stats.email === email);
|
||||||
|
},
|
||||||
getUpStats(dbInbound, email) {
|
getUpStats(dbInbound, email) {
|
||||||
if (email.length == 0) return 0;
|
if (email.length == 0) return 0;
|
||||||
clientStats = dbInbound.clientStats.find(stats => stats.email === email);
|
clientStats = dbInbound.clientStats.find(stats => stats.email === email);
|
||||||
|
|
|
@ -190,6 +190,9 @@
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Add Client"
|
"add" = "Add Client"
|
||||||
|
"groupAdd" = "Agregar usuario a la suscripción"
|
||||||
|
"isGroupEdit" = "Edición de grupo"
|
||||||
|
"isGroupEditDesc" = "Se editan todos los clientes con la misma suscripción"
|
||||||
"edit" = "Edit Client"
|
"edit" = "Edit Client"
|
||||||
"submitAdd" = "Add Client"
|
"submitAdd" = "Add Client"
|
||||||
"submitEdit" = "Save Changes"
|
"submitEdit" = "Save Changes"
|
||||||
|
|
|
@ -190,6 +190,7 @@
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Agregar Cliente"
|
"add" = "Agregar Cliente"
|
||||||
|
"groupAdd" = "Agregar usuario de suscripción"
|
||||||
"edit" = "Editar Cliente"
|
"edit" = "Editar Cliente"
|
||||||
"submitAdd" = "Agregar Cliente"
|
"submitAdd" = "Agregar Cliente"
|
||||||
"submitEdit" = "Guardar Cambios"
|
"submitEdit" = "Guardar Cambios"
|
||||||
|
|
|
@ -190,6 +190,9 @@
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "کاربر جدید"
|
"add" = "کاربر جدید"
|
||||||
|
"groupAdd" = "افزودن کاربر سابسکریپشن"
|
||||||
|
"isGroupEdit" = "ویرایش گروهی"
|
||||||
|
"isGroupEditDesc" = "همه کاربران با سابسکریپشن یکسان ویرایش می شوند"
|
||||||
"edit" = "ویرایش کاربر"
|
"edit" = "ویرایش کاربر"
|
||||||
"submitAdd" = "اضافه کردن"
|
"submitAdd" = "اضافه کردن"
|
||||||
"submitEdit" = "ذخیره تغییرات"
|
"submitEdit" = "ذخیره تغییرات"
|
||||||
|
|
|
@ -190,6 +190,9 @@
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Tambah Klien"
|
"add" = "Tambah Klien"
|
||||||
|
"groupAdd" = "Tambahkan pengguna langganan"
|
||||||
|
"isGroupEdit" = "Pengeditan grup"
|
||||||
|
"isGroupEditDesc" = "Semua klien dengan langganan yang sama akan diedit"
|
||||||
"edit" = "Edit Klien"
|
"edit" = "Edit Klien"
|
||||||
"submitAdd" = "Tambah Klien"
|
"submitAdd" = "Tambah Klien"
|
||||||
"submitEdit" = "Simpan Perubahan"
|
"submitEdit" = "Simpan Perubahan"
|
||||||
|
|
|
@ -190,6 +190,9 @@
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "クライアント追加"
|
"add" = "クライアント追加"
|
||||||
|
"groupAdd" = "サブスクリプション ユーザーの追加"
|
||||||
|
"isGroupEdit" = "グループの編集"
|
||||||
|
"isGroupEditDesc" = "同じサブスクリプションを持つすべてのクライアントが編集されます"
|
||||||
"edit" = "クライアント編集"
|
"edit" = "クライアント編集"
|
||||||
"submitAdd" = "クライアント追加"
|
"submitAdd" = "クライアント追加"
|
||||||
"submitEdit" = "変更を保存"
|
"submitEdit" = "変更を保存"
|
||||||
|
|
|
@ -190,6 +190,9 @@
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Adicionar Cliente"
|
"add" = "Adicionar Cliente"
|
||||||
|
"groupAdd" = "Adicionar usuário de assinatura"
|
||||||
|
"isGroupEdit" = "Edição de grupo"
|
||||||
|
"isGroupEditDesc" = "Todos os clientes com a mesma assinatura são editados"
|
||||||
"edit" = "Editar Cliente"
|
"edit" = "Editar Cliente"
|
||||||
"submitAdd" = "Adicionar Cliente"
|
"submitAdd" = "Adicionar Cliente"
|
||||||
"submitEdit" = "Salvar Alterações"
|
"submitEdit" = "Salvar Alterações"
|
||||||
|
|
|
@ -190,6 +190,9 @@
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Добавить пользователя"
|
"add" = "Добавить пользователя"
|
||||||
|
"groupAdd" = "Добавить пользователя подписки"
|
||||||
|
"isGroupEdit" = "Групповое редактирование"
|
||||||
|
"isGroupEditDesc" = "Все клиенты с одинаковой подпиской редактируются"
|
||||||
"edit" = "Редактировать пользователя"
|
"edit" = "Редактировать пользователя"
|
||||||
"submitAdd" = "Добавить пользователя"
|
"submitAdd" = "Добавить пользователя"
|
||||||
"submitEdit" = "Сохранить изменения"
|
"submitEdit" = "Сохранить изменения"
|
||||||
|
|
|
@ -190,6 +190,9 @@
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Müşteri Ekle"
|
"add" = "Müşteri Ekle"
|
||||||
|
"groupAdd" = "Abonelik kullanıcısı ekle"
|
||||||
|
"isGroupEdit" = "Grup düzenleme"
|
||||||
|
"isGroupEditDesc" = "Aynı aboneliğe sahip tüm istemciler düzenlendi"
|
||||||
"edit" = "Müşteriyi Düzenle"
|
"edit" = "Müşteriyi Düzenle"
|
||||||
"submitAdd" = "Müşteri Ekle"
|
"submitAdd" = "Müşteri Ekle"
|
||||||
"submitEdit" = "Değişiklikleri Kaydet"
|
"submitEdit" = "Değişiklikleri Kaydet"
|
||||||
|
|
|
@ -190,6 +190,9 @@
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Додати клієнта"
|
"add" = "Додати клієнта"
|
||||||
|
"groupAdd" = "Додати підписаного користувача"
|
||||||
|
"isGroupEdit" = "Редагування групи"
|
||||||
|
"isGroupEditDesc" = "Всі клієнти з однаковою підпискою редагуються"
|
||||||
"edit" = "Редагувати клієнта"
|
"edit" = "Редагувати клієнта"
|
||||||
"submitAdd" = "Додати клієнта"
|
"submitAdd" = "Додати клієнта"
|
||||||
"submitEdit" = "Зберегти зміни"
|
"submitEdit" = "Зберегти зміни"
|
||||||
|
|
|
@ -190,6 +190,7 @@
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Thêm người dùng"
|
"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"
|
"edit" = "Chỉnh sửa người dùng"
|
||||||
"submitAdd" = "Thêm"
|
"submitAdd" = "Thêm"
|
||||||
"submitEdit" = "Lưu thay đổi"
|
"submitEdit" = "Lưu thay đổi"
|
||||||
|
|
|
@ -190,6 +190,9 @@
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "添加客户端"
|
"add" = "添加客户端"
|
||||||
|
"groupAdd" = "添加订阅用户"
|
||||||
|
"isGroupEdit" = "群组编辑"
|
||||||
|
"isGroupEditDesc" = "所有具有相同订阅的客户端均被编辑"
|
||||||
"edit" = "编辑客户端"
|
"edit" = "编辑客户端"
|
||||||
"submitAdd" = "添加客户端"
|
"submitAdd" = "添加客户端"
|
||||||
"submitEdit" = "保存修改"
|
"submitEdit" = "保存修改"
|
||||||
|
|
|
@ -190,6 +190,9 @@
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "新增客戶端"
|
"add" = "新增客戶端"
|
||||||
|
"groupAdd" = "新增訂閱使用者"
|
||||||
|
"isGroupEdit" = "群組編輯"
|
||||||
|
"isGroupEditDesc" = "所有具有相同訂閱的用戶端都被編輯"
|
||||||
"edit" = "編輯客戶端"
|
"edit" = "編輯客戶端"
|
||||||
"submitAdd" = "新增客戶端"
|
"submitAdd" = "新增客戶端"
|
||||||
"submitEdit" = "儲存修改"
|
"submitEdit" = "儲存修改"
|
||||||
|
|
Loading…
Reference in a new issue