| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 | {{define "clientsModal"}}<a-modal id="client-modal" v-model="clientModal.visible" :title="clientModal.title" @ok="clientModal.ok"         :confirm-loading="clientModal.confirmLoading" :closable="true" :mask-closable="false"         :class="themeSwitcher.currentTheme"         :ok-text="clientModal.okText" cancel-text='{{ i18n "close" }}'>    <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>    </template>    {{template "form/client"}}</a-modal><script>    const clientModal = {        visible: false,        confirmLoading: false,        title: '',        okText: '',        isEdit: false,        group: {            canGroup: true,            isGroup: false,            currentClient: null,            inbounds: [],            clients: [],            editIds: []        },        dbInbound: new DBInbound(),        dbInbounds: null,        inbound: new Inbound(),        clients: [],        clientStats: [],        oldClientId: "",        index: null,        clientIps: null,        delayedStart: false,        ok() {            if (app.subSettings.enable && clientModal.group.isGroup && clientModal.group.canGroup) {                const currentClient = clientModal.group.currentClient;                const { limitIp, comment, 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.comment = comment;                    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) {                    ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.oldClientId);                } else {                    ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id);                }            }        },        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.title = title;            this.okText = okText;            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);                        if (this.dbInbound.isMultiUser()) {                            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.inbound = dbInbound.toInbound();            if (this.dbInbound.isMultiUser()) {                this.clients = this.inbound.clients;                this.index = index === null ? this.clients.length : index;                this.delayedStart = false;            }        },        singleEditClientProcess(index) {            if (this.clients[index].expiryTime < 0) {                this.delayedStart = true;            }            this.oldClientId = this.getClientId(this.dbInbound.protocol, this.clients[index]);        },        getClientId(protocol, client) {            switch (protocol) {                case Protocols.TROJAN: return client.password;                case Protocols.SHADOWSOCKS: return client.email;                default: return client.id;            }        },        addClient(protocol, clients) {            switch (protocol) {                case Protocols.VMESS: return clients.push(new Inbound.VmessSettings.VMESS());                case Protocols.VLESS: return clients.push(new Inbound.VLESSSettings.VLESS());                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() {            clientModal.visible = false;            clientModal.loading(false);        },        loading(loading = true) {            clientModal.confirmLoading = loading;        },    };    const clientModalApp = new Vue({        delimiters: ['[[', ']]'],        el: '#client-modal',        data: {            clientModal,            get inbound() {                return this.clientModal.inbound;            },            get client() {                return this.clientModal.clients[this.clientModal.index];            },            get clientStats() {                return this.clientModal.clientStats;            },            get 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() {                return app.datepicker;            },            get isTrafficExhausted() {                if (!clientStats) return false                if (clientStats.total <= 0) return false                if (clientStats.up + clientStats.down < clientStats.total) return false                return true            },            get isExpiry() {                return this.clientModal.isEdit && this.client.expiryTime >0 ? (this.client.expiryTime < new Date().getTime()) : false;            },            get delayedStart() {                return this.clientModal.delayedStart;            },            set delayedStart(value) {                this.clientModal.delayedStart = value;            },            get delayedExpireDays() {                return this.client && this.client.expiryTime < 0 ? this.client.expiryTime / -86400000 : 0;            },            set delayedExpireDays(days) {                this.client.expiryTime = -86400000 * days;            },        },        methods: {            makeGroupEmailsUnique(dbInbounds, baseEmail, groupClients) {                // Extract the base part of the email (before the "__" if present)                const match = baseEmail.match(/^(.*?)__/);                const base = match ? match[1] : baseEmail;                // Generate initial emails for each client in the group                const generatedEmails = groupClients.map((_, index) => `${base}__${index + 1}`);                // Function to check if an email already exists in dbInbounds but belongs to a different subId                const isDuplicate = (emailToCheck, clientSubId) => {                    return dbInbounds.some((dbInbound) => {                        const settings = JSON.parse(dbInbound.settings);                        const clients = settings && settings.clients ? settings.clients : [];                        return clients.some(client => client.email === emailToCheck && client.subId !== clientSubId);                    });                };                // Check if any of the generated emails are duplicates                const hasDuplicates = generatedEmails.some((email, index) => {                    return isDuplicate(email, groupClients[index].subId);                });                // If duplicates exist, add a random string to the base email to ensure uniqueness                if (hasDuplicates) {                    const randomString = `-${RandomUtil.randomLowerAndNum(4)}`;                    return groupClients.map((_, index) => `${base}${randomString}__${index + 1}`);                }                return generatedEmails;            },            async getDBClientIps(email) {                const msg = await HttpUtil.post(`/panel/inbound/clientIps/${email}`);                if (!msg.success) {                    document.getElementById("clientIPs").value = msg.obj;                    return;                }                let ips = msg.obj;                if (typeof ips === 'string' && ips.startsWith('[') && ips.endsWith(']')) {                    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) {                try {                    const msg = await HttpUtil.post(`/panel/inbound/clearClientIps/${email}`);                    if (!msg.success) {                        return;                    }                    document.getElementById("clientIPs").value = "";                } catch (error) {                }            },            async resetClientTrafficHandler(client, dbInboundId, clients = []) {                if (clients.length > 0) {                    const resetRequests = clients                        .filter(client => {                            const inbound = clientModal.dbInbounds.find(inbound => inbound.id === client.inboundId);                            return inbound && app.hasClientStats(inbound, client.email);                        }).map(client => ({ inboundId: client.inboundId, email: client.email}));                    return HttpUtil.postWithModalJson('/panel/inbound/resetGroupClientTraffic', resetRequests, null)                } 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 > 1 ? subGroup.clients : [];                this.$confirm({                    title: '{{ i18n "pages.inbounds.resetTraffic"}}',                    content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',                    class: themeSwitcher.currentTheme,                    okText: '{{ i18n "reset"}}',                    cancelText: '{{ i18n "cancel"}}',                    onOk: async () => {                        iconElement.disabled = true;                        const msg = await this.resetClientTrafficHandler(client, dbInboundId, clients);                        if (msg && msg.success) {                            this.clientModal.clientStats.up = 0;                            this.clientModal.clientStats.down = 0;                        }                        iconElement.disabled = false;                    },                })            },        },    });</script>{{end}}
 |