Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--database/db.go54
-rw-r--r--database/model/model.go4
-rw-r--r--install.sh2
-rw-r--r--web/assets/js/model/setting.js1
-rw-r--r--web/assets/js/util/utils.js35
-rw-r--r--web/controller/inbound.go154
-rw-r--r--web/entity/entity.go1
-rw-r--r--web/html/common/qrcode_modal.html24
-rw-r--r--web/html/xui/client_modal.html197
-rw-r--r--web/html/xui/form/client.html14
-rw-r--r--web/html/xui/inbound_client_table.html2
-rw-r--r--web/html/xui/inbound_modal.html4
-rw-r--r--web/html/xui/inbounds.html234
-rw-r--r--web/html/xui/settings.html1
-rw-r--r--web/job/client_traffic_sync_job.go178
-rw-r--r--web/service/setting.go10
-rw-r--r--web/translation/translate.en_US.toml5
-rw-r--r--web/translation/translate.es_ES.toml3
-rw-r--r--web/translation/translate.fa_IR.toml5
-rw-r--r--web/translation/translate.id_ID.toml5
-rw-r--r--web/translation/translate.ja_JP.toml5
-rw-r--r--web/translation/translate.pt_BR.toml5
-rw-r--r--web/translation/translate.ru_RU.toml5
-rw-r--r--web/translation/translate.tr_TR.toml5
-rw-r--r--web/translation/translate.uk_UA.toml5
-rw-r--r--web/translation/translate.vi_VN.toml5
-rw-r--r--web/translation/translate.zh_CN.toml5
-rw-r--r--web/translation/translate.zh_TW.toml5
-rw-r--r--web/web.go7
-rw-r--r--x-ui.sh2
-rw-r--r--xray/client_traffic.go4
31 files changed, 96 insertions, 890 deletions
diff --git a/database/db.go b/database/db.go
index d252cfe0..300a73c0 100644
--- a/database/db.go
+++ b/database/db.go
@@ -26,35 +26,20 @@ const (
)
func initModels() error {
- // Order matters: first create tables without dependencies
- baseModels := []interface{}{
+ models := []interface{}{
&model.User{},
- &model.Setting{},
- }
-
- // Migrate base models
- for _, model := range baseModels {
- if err := db.AutoMigrate(model); err != nil {
- log.Printf("Error auto migrating base model: %v", err)
- return err
- }
- }
-
- // Then migrate models with dependencies
- dependentModels := []interface{}{
&model.Inbound{},
&model.OutboundTraffics{},
+ &model.Setting{},
&model.InboundClientIps{},
&xray.ClientTraffic{},
}
-
- for _, model := range dependentModels {
+ for _, model := range models {
if err := db.AutoMigrate(model); err != nil {
- log.Printf("Error auto migrating dependent model: %v", err)
+ log.Printf("Error auto migrating model: %v", err)
return err
}
}
-
return nil
}
@@ -97,31 +82,9 @@ func InitDB(dbPath string) error {
}
c := &gorm.Config{
- Logger: gormLogger,
- SkipDefaultTransaction: true,
- PrepareStmt: true,
+ Logger: gormLogger,
}
-
- dsn := dbPath + "?cache=shared&_journal_mode=WAL&_synchronous=NORMAL"
- db, err = gorm.Open(sqlite.Open(dsn), c)
- if err != nil {
- return err
- }
-
- sqlDB, err := db.DB()
- if err != nil {
- return err
- }
-
- _, err = sqlDB.Exec("PRAGMA cache_size = -64000;")
- if err != nil {
- return err
- }
- _, err = sqlDB.Exec("PRAGMA temp_store = MEMORY;")
- if err != nil {
- return err
- }
- _, err = sqlDB.Exec("PRAGMA foreign_keys = ON;")
+ db, err = gorm.Open(sqlite.Open(dbPath), c)
if err != nil {
return err
}
@@ -138,11 +101,6 @@ func InitDB(dbPath string) error {
func CloseDB() error {
if db != nil {
-
- if err := Checkpoint(); err != nil {
- log.Printf("error executing checkpoint: %v", err)
- }
-
sqlDB, err := db.DB()
if err != nil {
return err
diff --git a/database/model/model.go b/database/model/model.go
index 915cae0b..e9d1836f 100644
--- a/database/model/model.go
+++ b/database/model/model.go
@@ -29,14 +29,14 @@ type User struct {
type Inbound struct {
Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
- UserId int `json:"-" gorm:"index"`
+ UserId int `json:"-"`
Up int64 `json:"up" form:"up"`
Down int64 `json:"down" form:"down"`
Total int64 `json:"total" form:"total"`
Remark string `json:"remark" form:"remark"`
Enable bool `json:"enable" form:"enable"`
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"`
- ClientStats []xray.ClientTraffic `gorm:"foreignKey:InboundId;references:Id;constraint:OnDelete:CASCADE" json:"clientStats"`
+ ClientStats []xray.ClientTraffic `gorm:"foreignKey:InboundId;references:Id" json:"clientStats" form:"clientStats"`
// config part
Listen string `json:"listen" form:"listen"`
diff --git a/install.sh b/install.sh
index f5ce9b96..150ae86c 100644
--- a/install.sh
+++ b/install.sh
@@ -283,4 +283,4 @@ install_x-ui() {
echo -e "${green}Running...${plain}"
install_base
-install_x-ui $1 \ No newline at end of file
+install_x-ui $1
diff --git a/web/assets/js/model/setting.js b/web/assets/js/model/setting.js
index 72fe77ef..8e010598 100644
--- a/web/assets/js/model/setting.js
+++ b/web/assets/js/model/setting.js
@@ -26,7 +26,6 @@ class AllSetting {
this.xrayTemplateConfig = "";
this.secretEnable = false;
this.subEnable = false;
- this.subSyncEnable = true;
this.subListen = "";
this.subPort = 2096;
this.subPath = "/sub/";
diff --git a/web/assets/js/util/utils.js b/web/assets/js/util/utils.js
index 10825490..30f1f6a2 100644
--- a/web/assets/js/util/utils.js
+++ b/web/assets/js/util/utils.js
@@ -70,41 +70,6 @@ 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(basePath + url.replace(/^\/+|\/+$/g, ''), 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 a8003484..c22ce192 100644
--- a/web/controller/inbound.go
+++ b/web/controller/inbound.go
@@ -1,10 +1,10 @@
package controller
import (
- "errors"
"encoding/json"
"fmt"
"strconv"
+
"x-ui/database/model"
"x-ui/web/service"
"x-ui/web/session"
@@ -33,13 +33,9 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
g.POST("/clientIps/:email", a.getClientIps)
g.POST("/clearClientIps/:email", a.clearClientIps)
g.POST("/addClient", a.addInboundClient)
- g.POST("/addGroupClient", a.addGroupInboundClient)
g.POST("/:id/delClient/:clientId", a.delInboundClient)
- g.POST("/delGroupClients", a.delGroupClients)
g.POST("/updateClient/:clientId", a.updateInboundClient)
- g.POST("/updateClients", a.updateGroupInboundClient)
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
- g.POST("/resetGroupClientTraffic", a.resetGroupClientTraffic)
g.POST("/resetAllTraffics", a.resetAllTraffics)
g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
g.POST("/delDepletedClients/:id", a.delDepletedClients)
@@ -194,34 +190,6 @@ 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) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
@@ -243,38 +211,6 @@ func (a *InboundController) delInboundClient(c *gin.Context) {
}
}
-func (a *InboundController) delGroupClients(c *gin.Context) {
- var requestData []struct {
- InboundID int `json:"inboundId"`
- ClientID string `json:"clientId"`
- }
-
- if err := c.ShouldBindJSON(&requestData); err != nil {
- jsonMsg(c, "Invalid request data", err)
- return
- }
-
- needRestart := false
-
- for _, req := range requestData {
- needRestartTmp, err := a.inboundService.DelInboundClient(req.InboundID, req.ClientID)
- if err != nil {
- jsonMsg(c, "Failed to delete client", err)
- return
- }
-
- if needRestartTmp {
- needRestart = true
- }
- }
-
- jsonMsg(c, "Clients deleted successfully", nil)
-
- if needRestart {
- a.xrayService.SetToNeedRestart()
- }
-}
-
func (a *InboundController) updateInboundClient(c *gin.Context) {
clientId := c.Param("clientId")
@@ -298,56 +234,6 @@ 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) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
@@ -367,44 +253,6 @@ func (a *InboundController) resetClientTraffic(c *gin.Context) {
}
}
-func (a *InboundController) resetGroupClientTraffic(c *gin.Context) {
- var requestData []struct {
- InboundID int `json:"inboundId"` // Map JSON "inboundId" to struct field "InboundID"
- Email string `json:"email"` // Map JSON "email" to struct field "Email"
- }
-
- // Parse JSON body directly using ShouldBindJSON
- if err := c.ShouldBindJSON(&requestData); err != nil {
- jsonMsg(c, "Invalid request data", err)
- return
- }
-
- needRestart := false
-
- // Process each request data
- for _, req := range requestData {
- needRestartTmp, err := a.inboundService.ResetClientTraffic(req.InboundID, req.Email)
- if err != nil {
- jsonMsg(c, "Failed to reset client traffic", err)
- return
- }
-
- // If any request requires a restart, set needRestart to true
- if needRestartTmp {
- needRestart = true
- }
- }
-
- // Send response back to the client
- jsonMsg(c, "Traffic reset for all clients", nil)
-
- // Restart the service if required
- if needRestart {
- a.xrayService.SetToNeedRestart()
- }
-}
-
-
func (a *InboundController) resetAllTraffics(c *gin.Context) {
err := a.inboundService.ResetAllTraffics()
if err != nil {
diff --git a/web/entity/entity.go b/web/entity/entity.go
index f5f375ea..12206340 100644
--- a/web/entity/entity.go
+++ b/web/entity/entity.go
@@ -40,7 +40,6 @@ type AllSetting struct {
TimeLocation string `json:"timeLocation" form:"timeLocation"`
SecretEnable bool `json:"secretEnable" form:"secretEnable"`
SubEnable bool `json:"subEnable" form:"subEnable"`
- SubSyncEnable bool `json:"subSyncEnable" form:"subSyncEnable"`
SubListen string `json:"subListen" form:"subListen"`
SubPort int `json:"subPort" form:"subPort"`
SubPath string `json:"subPath" form:"subPath"`
diff --git a/web/html/common/qrcode_modal.html b/web/html/common/qrcode_modal.html
index 6558c347..94e750c7 100644
--- a/web/html/common/qrcode_modal.html
+++ b/web/html/common/qrcode_modal.html
@@ -23,15 +23,13 @@
</tr-qr-bg>
</tr-qr-box>
</template>
- <template v-if="!isJustSub">
- <template v-for="(row, index) in qrModal.qrcodes">
- <tr-qr-box class="qr-box">
- <a-tag color="green" class="qr-tag"><span>[[ row.remark ]]</span></a-tag>
- <tr-qr-bg class="qr-bg">
- <canvas @click="copyToClipboard('qrCode-'+index, row.link)" :id="'qrCode-'+index" class="qr-cv"></canvas>
- </tr-qr-bg>
- </tr-qr-box>
- </template>
+ <template v-for="(row, index) in qrModal.qrcodes">
+ <tr-qr-box class="qr-box">
+ <a-tag color="green" class="qr-tag"><span>[[ row.remark ]]</span></a-tag>
+ <tr-qr-bg class="qr-bg">
+ <canvas @click="copyToClipboard('qrCode-'+index, row.link)" :id="'qrCode-'+index" class="qr-cv"></canvas>
+ </tr-qr-bg>
+ </tr-qr-box>
</template>
</tr-qr-modal>
</a-modal>
@@ -45,14 +43,12 @@
qrcodes: [],
clipboard: null,
visible: false,
- isJustSub: false,
subId: '',
- show: function(title = '', dbInbound, client, isJustSub = false) {
+ show: function(title = '', dbInbound, client) {
this.title = title;
this.dbInbound = dbInbound;
this.inbound = dbInbound.toInbound();
this.client = client;
- this.isJustSub = isJustSub;
this.subId = '';
this.qrcodes = [];
if (this.inbound.protocol == Protocols.WIREGUARD) {
@@ -80,9 +76,7 @@
delimiters: ['[[', ']]'],
el: '#qrcode-modal',
data: {
- qrModal: qrModal,get isJustSub(){
- return qrModal.isJustSub
- }
+ qrModal: qrModal,
},
methods: {
copyToClipboard(elementId, content) {
diff --git a/web/html/xui/client_modal.html b/web/html/xui/client_modal.html
index f2ea30d0..aa62e02a 100644
--- a/web/html/xui/client_modal.html
+++ b/web/html/xui/client_modal.html
@@ -16,16 +16,7 @@
title: '',
okText: '',
isEdit: false,
- group: {
- canGroup: true,
- isGroup: false,
- currentClient: null,
- inbounds: [],
- clients: [],
- editIds: []
- },
dbInbound: new DBInbound(),
- dbInbounds: null,
inbound: new Inbound(),
clients: [],
clientStats: [],
@@ -34,126 +25,33 @@
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);
- }
+ if (clientModal.isEdit) {
+ ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.oldClientId);
} 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);
- }
+ 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;
+ show({ title = '', okText = '{{ i18n "sure" }}', index = null, dbInbound = null, confirm = () => { }, isEdit = false }) {
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]
+ 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.showProcess(dbInbound, index);
- if (isEdit) {
- this.singleEditClientProcess(index)
- } else {
- this.addClient(this.inbound.protocol, this.clients);
- }
+ 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;
@@ -174,7 +72,7 @@
clientModal.visible = false;
clientModal.loading(false);
},
- loading(loading = true) {
+ loading(loading=true) {
clientModal.confirmLoading = loading;
},
};
@@ -196,18 +94,6 @@
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;
},
@@ -234,36 +120,6 @@
},
},
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) {
@@ -291,22 +147,7 @@
} 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 : [];
+ resetClientTraffic(email, dbInboundId, iconElement) {
this.$confirm({
title: '{{ i18n "pages.inbounds.resetTraffic"}}',
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
@@ -315,8 +156,8 @@
cancelText: '{{ i18n "cancel"}}',
onOk: async () => {
iconElement.disabled = true;
- const msg = await this.resetClientTrafficHandler(client, dbInboundId, clients);
- if (msg && msg.success) {
+ const msg = await HttpUtil.postWithModal('/panel/inbound/' + dbInboundId + '/resetClientTraffic/' + email);
+ if (msg.success) {
this.clientModal.clientStats.up = 0;
this.clientModal.clientStats.down = 0;
}
diff --git a/web/html/xui/form/client.html b/web/html/xui/form/client.html
index a0a1ced8..0b894f01 100644
--- a/web/html/xui/form/client.html
+++ b/web/html/xui/form/client.html
@@ -3,18 +3,6 @@
<a-form-item label='{{ i18n "pages.inbounds.enable" }}'>
<a-switch v-model="client.enable"></a-switch>
</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>
<template slot="label">
<a-tooltip>
@@ -146,7 +134,7 @@
<a-tooltip>
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
<a-icon type="retweet"
- @click="resetClientTraffic(client,clientStats.inboundId,$event.target)"
+ @click="resetClientTraffic(client.email,clientStats.inboundId,$event.target)"
v-if="client.email.length > 0"></a-icon>
</a-tooltip>
</a-form-item>
diff --git a/web/html/xui/inbound_client_table.html b/web/html/xui/inbound_client_table.html
index 74d77eab..13593cea 100644
--- a/web/html/xui/inbound_client_table.html
+++ b/web/html/xui/inbound_client_table.html
@@ -12,7 +12,7 @@
<template slot="title">{{ i18n "info" }}</template>
<a-icon style="fo