New feature: Bulk add #28 (plus adjust methods)

This commit is contained in:
Alireza Ahmadi
2023-03-02 12:50:59 +01:00
parent 340b54d45f
commit f87dad254b
6 changed files with 165 additions and 22 deletions

View File

@@ -0,0 +1,107 @@
{{define "clientsBulkModal"}}
<a-modal id="client-bulk-modal" v-model="clientsBulkModal.visible" :title="clientsBulkModal.title" @ok="clientsBulkModal.ok"
:closable="true" :mask-closable="false"
:ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}'>
<a-form layout="inline">
<a-form-item>
<span slot="label">{{ i18n "pages.client.clientCount" }}</span>
<a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number>
</a-form-item>
<a-form-item>
<span slot="label">
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
<a-tooltip>
<template slot="title">
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</span>
<a-input-number v-model="clientsBulkModal.totalGB" :min="0"></a-input-number>
</a-form-item>
<a-form-item>
<span slot="label">
<span >{{ i18n "pages.inbounds.expireDate" }}</span>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</span>
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
v-model="clientsBulkModal.expiryTime" style="width: 300px;"></a-date-picker>
</a-form-item>
</a-form>
</a-modal>
<script>
const clientsBulkModal = {
visible: false,
title: '',
okText: '',
confirm: null,
dbInbound: new DBInbound(),
inbound: new Inbound(),
clients: [],
quantity: 1,
totalGB: 0,
expiryTime: '',
ok() {
for (let i = 0; i < clientsBulkModal.quantity; i++) {
newClient = clientsBulkModal.newClient(clientsBulkModal.dbInbound.protocol);
newClient._totalGB = clientsBulkModal.totalGB;
newClient._expiryTime = clientsBulkModal.expiryTime;
clientsBulkModal.clients.push(newClient);
}
ObjectUtil.execute(clientsBulkModal.confirm, clientsBulkModal.inbound, clientsBulkModal.dbInbound);
},
show({ title='', okText='{{ i18n "sure" }}', dbInbound=null, confirm=(inbound, dbInbound)=>{} }) {
this.visible = true;
this.title = title;
this.okText = okText;
this.confirm = confirm;
this.quantity = 1;
this.totalGB = 0;
this.expiryTime = '';
this.dbInbound = new DBInbound(dbInbound);
this.inbound = dbInbound.toInbound();
this.clients = this.getClients(this.inbound.protocol, this.inbound.settings);
},
getClients(protocol, clientSettings) {
switch(protocol){
case Protocols.VMESS: return clientSettings.vmesses;
case Protocols.VLESS: return clientSettings.vlesses;
case Protocols.TROJAN: return clientSettings.trojans;
default: return null;
}
},
newClient(protocol) {
switch (protocol) {
case Protocols.VMESS: return new Inbound.VmessSettings.Vmess();
case Protocols.VLESS: return new Inbound.VLESSSettings.VLESS();
case Protocols.TROJAN: return new Inbound.TrojanSettings.Trojan();
default: return null;
}
},
close() {
clientsBulkModal.visible = false;
clientsBulkModal.loading(false);
},
loading(loading) {
clientsBulkModal.confirmLoading = loading;
},
};
const clientsBulkModalApp = new Vue({
delimiters: ['[[', ']]'],
el: '#client-bulk-modal',
data: {
clientsBulkModal,
get inbound() {
return this.clientsBulkModal.inbound;
},
},
});
</script>
{{end}}

View File

@@ -77,6 +77,10 @@
<a-icon type="user"></a-icon>
{{ i18n "pages.client.add"}}
</a-menu-item>
<a-menu-item key="addBulkClient">
<a-icon type="team"></a-icon>
{{ i18n "pages.client.bulk"}}
</a-menu-item>
<a-menu-item key="export">
<a-icon type="export"></a-icon>
{{ i18n "pages.inbounds.export"}}
@@ -113,7 +117,7 @@
<template v-else>{{ i18n "none" }}</template>
</template>
<template slot="enable" slot-scope="text, dbInbound">
<a-switch v-model="dbInbound.enable" @change="switchEnable(dbInbound)"></a-switch>
<a-switch v-model="dbInbound.enable" @change="switchEnable(dbInbound.id)"></a-switch>
</template>
<template slot="expiryTime" slot-scope="text, dbInbound">
<template v-if="dbInbound.expiryTime > 0">
@@ -283,20 +287,23 @@
case "qrcode":
this.showQrcode(dbInbound);
break;
case "export":
this.inboundLinks(dbInbound.id);
break;
case "edit":
this.openEditInbound(dbInbound.id);
break;
case "addClient":
this.openAddClient(dbInbound)
this.openAddClient(dbInbound.id)
break;
case "addBulkClient":
this.openAddBulkClient(dbInbound.id)
break;
case "export":
this.inboundLinks(dbInbound.id);
break;
case "resetTraffic":
this.resetTraffic(dbInbound);
this.resetTraffic(dbInbound.id);
break;
case "delete":
this.delInbound(dbInbound);
this.delInbound(dbInbound.id);
break;
}
},
@@ -313,8 +320,8 @@
isEdit: false
});
},
openEditInbound(dbInbound_id) {
dbInbound = this.dbInbounds.find(row => row.id === dbInbound_id);
openEditInbound(dbInboundId) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
const inbound = dbInbound.toInbound();
inModal.show({
title: '{{ i18n "pages.inbounds.modifyInbound"}}',
@@ -366,8 +373,8 @@
};
await this.submit(`/xui/inbound/update/${dbInbound.id}`, data, inModal);
},
openAddClient(inbound) {
dbInbound = this.dbInbounds.find(row => row.id === inbound.id);
openAddClient(dbInboundId) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
clientModal.show({
title: '{{ i18n "pages.client.add"}}',
okText: '{{ i18n "pages.client.submitAdd"}}',
@@ -380,6 +387,19 @@
isEdit: false
});
},
openAddBulkClient(dbInboundId) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
clientsBulkModal.show({
title: '{{ i18n "pages.client.bulk"}} ' + dbInbound.remark,
okText: '{{ i18n "pages.client.bulk"}}',
dbInbound: dbInbound,
confirm: async (inbound, dbInbound) => {
clientsBulkModal.loading();
await this.addClient(inbound, dbInbound);
clientsBulkModal.close();
},
});
},
openEditClient(dbInboundId, client) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
clients = this.getInboundClients(dbInbound);
@@ -406,16 +426,17 @@
id: dbInbound.id,
settings: inbound.settings.toString(),
};
await this.submit('/xui/inbound/addClient', data, clientModal);
await this.submit('/xui/inbound/addClient', data);
},
async updateClient(inbound, dbInbound, index) {
const data = {
id: dbInbound.id,
settings: inbound.settings.toString(),
};
await this.submit(`/xui/inbound/updateClient/${index}`, data, clientModal);
await this.submit(`/xui/inbound/updateClient/${index}`, data);
},
resetTraffic(dbInbound) {
resetTraffic(dbInboundId) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
this.$confirm({
title: '{{ i18n "pages.inbounds.resetTraffic"}}',
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
@@ -429,13 +450,13 @@
},
});
},
delInbound(dbInbound) {
delInbound(dbInboundId) {
this.$confirm({
title: '{{ i18n "pages.inbounds.deleteInbound"}}',
content: '{{ i18n "pages.inbounds.deleteInboundContent"}}',
okText: '{{ i18n "delete"}}',
cancelText: '{{ i18n "cancel"}}',
onOk: () => this.submit('/xui/inbound/del/' + dbInbound.id),
onOk: () => this.submit('/xui/inbound/del/' + dbInboundId),
});
},
delClient(dbInboundId,client) {
@@ -472,11 +493,12 @@
showInfo(dbInbound, index) {
infoModal.show(dbInbound, index);
},
switchEnable(dbInbound) {
this.submit(`/xui/inbound/update/${dbInbound.id}`, dbInbound);
switchEnable(dbInboundId) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
this.submit(`/xui/inbound/update/${dbInboundId}`, dbInbound);
},
async submit(url, data, modal) {
const msg = await HttpUtil.postWithModal(url, data, modal);
async submit(url, data) {
const msg = await HttpUtil.postWithModal(url, data);
if (msg.success) {
await this.getDBInbounds();
}
@@ -490,13 +512,13 @@
return dbInbound.toInbound().settings.trojans
}
},
resetClientTraffic(client,dbInbound_Id) {
resetClientTraffic(client,dbInboundId) {
this.$confirm({
title: '{{ i18n "pages.inbounds.resetTraffic"}}',
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
okText: '{{ i18n "reset"}}',
cancelText: '{{ i18n "cancel"}}',
onOk: () => this.submit('/xui/inbound/' + dbInbound_Id + '/resetClientTraffic/'+ client.email),
onOk: () => this.submit('/xui/inbound/' + dbInboundId + '/resetClientTraffic/'+ client.email),
})
},
isExpiry(dbInbound, index) {
@@ -586,5 +608,6 @@
{{template "textModal"}}
{{template "inboundInfoModal"}}
{{template "clientsModal"}}
{{template "clientsBulkModal"}}
</body>
</html>

View File

@@ -251,11 +251,21 @@ func (s *InboundService) AddInboundClient(inbound *model.Inbound) error {
return err
}
oldClients, err := s.getClients(oldInbound)
if err != nil {
return err
}
oldInbound.Settings = inbound.Settings
if len(clients[len(clients)-1].Email) > 0 {
s.AddClientStat(inbound.Id, &clients[len(clients)-1])
}
for i := len(oldClients); i < len(clients); i++ {
if len(clients[i].Email) > 0 {
s.AddClientStat(inbound.Id, &clients[i])
}
}
db := database.GetDB()
return db.Save(oldInbound).Error
}

View File

@@ -134,6 +134,7 @@
"submitAdd" = "Add client"
"submitEdit" = "Save changes"
"clientCount" = "Number of clients"
"bulk" = "Add bulk"
[pages.inbounds.toasts]
"obtain" = "Obtain"

View File

@@ -134,6 +134,7 @@
"submitAdd" = "اضافه کردن"
"submitEdit" = "ذخیره تغییرات"
"clientCount" = "تعداد کاربران"
"bulk" = "انبوه سازی"
[pages.inbounds.toasts]
"obtain" = "Obtain"

View File

@@ -134,6 +134,7 @@
"submitAdd" = "添加客户端"
"submitEdit" = "保存修改"
"clientCount" = "客户数量"
"bulk" = "批量创建"
[pages.inbounds.toasts]
"obtain" = "获取"