diff options
| author | Sanaei <ho3ein.sanaei@gmail.com> | 2025-02-04 13:27:58 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-04 13:27:58 +0300 |
| commit | d18a1a37ceb7c005a5a829ba8b9ef2d103c181b5 (patch) | |
| tree | aa9af2e8beb4ce502684f1af300a8902db9f7b2c | |
| parent | 04c6b2722b7b945ed12e743de70c05e5f8663815 (diff) | |
revert group management (#2656)
* Revert "json post base path bug fixed (#2647)"
This reverts commit 04cf250a547bb64265d256e7d15af7cea5ecfa67.
* Revert "Group Management of Subscription Clients"
* Revert "fix getSubGroupClients for enable/disable and edit clients."
* Revert "Enhance database initialization in db.go (#2645)"
This reverts commit 66fe84181b9c4e2f6c6be943a7f486b4308c32ff.
* Revert "Add checkpoint handling in CloseDB function (#2646)"
This reverts commit 4dd40f6f192e3f94f2ea4fe9e942e6663b5a1527.
* Revert "Improved database model migration and added indexing (#2655)"
This reverts commit b922d986d6783ce28d00ca948024dee44a11f29e.
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"` @@ -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/ |
