diff --git a/web/assets/js/model/setting.js b/web/assets/js/model/setting.js index 8e010598..72fe77ef 100644 --- a/web/assets/js/model/setting.js +++ b/web/assets/js/model/setting.js @@ -26,6 +26,7 @@ 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 30f1f6a2..28ec95c7 100644 --- a/web/assets/js/util/utils.js +++ b/web/assets/js/util/utils.js @@ -70,6 +70,41 @@ 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(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 { diff --git a/web/controller/inbound.go b/web/controller/inbound.go index c22ce192..a8003484 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,9 +33,13 @@ 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) @@ -190,6 +194,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) { id, err := strconv.Atoi(c.Param("id")) if err != nil { @@ -211,6 +243,38 @@ 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") @@ -234,6 +298,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) { id, err := strconv.Atoi(c.Param("id")) if err != nil { @@ -253,6 +367,44 @@ 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 12206340..f5f375ea 100644 --- a/web/entity/entity.go +++ b/web/entity/entity.go @@ -40,6 +40,7 @@ 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 94e750c7..6558c347 100644 --- a/web/html/common/qrcode_modal.html +++ b/web/html/common/qrcode_modal.html @@ -23,13 +23,15 @@ -