From b3e1052c6189f67cb78fc82fa29ea2cc0c9ef912 Mon Sep 17 00:00:00 2001 From: Javad Tinatpak Date: Tue, 6 Aug 2024 16:37:12 +0330 Subject: [PATCH] API - Update Client to inbounds --- README.md | 3 +- web/controller/api.go | 1 + web/controller/inbound.go | 42 +++++++++- web/service/inbound.go | 156 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 199 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3e261b7d..7ae5f374 100644 --- a/README.md +++ b/README.md @@ -459,7 +459,7 @@ Enter the user ID in input field number 4. The Telegram accounts with this id wi | `GET` | `"/list"` | Get all inbounds | | `GET` | `"/get/:id"` | Get inbound with inbound.id | | `GET` | `"/getClientTraffics/:email"` | Get Client Traffics with email | -| `GET` | `"/getClientTrafficsById/:id"` | Get client's traffic By ID | +| `GET` | `"/getClientTrafficsById/:id"` | Get client's traffic By ID | | `GET` | `"/createbackup"` | Telegram bot sends backup to admins | | `POST` | `"/add"` | Add inbound | | `POST` | `"/del/:id"` | Delete Inbound | @@ -470,6 +470,7 @@ Enter the user ID in input field number 4. The Telegram accounts with this id wi | `POST` | `"/addClientInbounds"` | Add Client to inbounds | | `POST` | `"/:id/delClient/:clientId"` | Delete Client by clientId\* | | `POST` | `"/updateClient/:clientId"` | Update Client by clientId\* | +| `POST` | `"/updateClientInbounds/:subId"` | Update Client by subId\* | | `POST` | `"/:id/resetClientTraffic/:email"` | Reset Client's Traffic | | `POST` | `"/resetAllTraffics"` | Reset traffics of all inbounds | | `POST` | `"/resetAllClientTraffics/:id"` | Reset traffics of all clients in an inbound | diff --git a/web/controller/api.go b/web/controller/api.go index 8e98bc9d..0ddb879b 100644 --- a/web/controller/api.go +++ b/web/controller/api.go @@ -43,6 +43,7 @@ func (a *APIController) initRouter(g *gin.RouterGroup) { {"POST", "/addClientInbounds", a.inboundController.addClientToMultipleInbounds}, {"POST", "/:id/delClient/:clientId", a.inboundController.delInboundClient}, {"POST", "/updateClient/:clientId", a.inboundController.updateInboundClient}, + {"POST", "/updateClientInbounds/:subId", a.inboundController.updateClientInMultipleInbounds}, {"POST", "/:id/resetClientTraffic/:email", a.inboundController.resetClientTraffic}, {"POST", "/resetAllTraffics", a.inboundController.resetAllTraffics}, {"POST", "/resetAllClientTraffics/:id", a.inboundController.resetAllClientTraffics}, diff --git a/web/controller/inbound.go b/web/controller/inbound.go index 7db6feec..93b409e2 100644 --- a/web/controller/inbound.go +++ b/web/controller/inbound.go @@ -18,7 +18,6 @@ type InboundController struct { } type AddClientPayload struct { - Id int `json:"id"` Settings string `json:"settings"` InboundIds []int `json:"inboundIds"` } @@ -42,6 +41,7 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) { g.POST("/addClientInbounds", a.addClientToMultipleInbounds) g.POST("/:id/delClient/:clientId", a.delInboundClient) g.POST("/updateClient/:clientId", a.updateInboundClient) + g.POST("/updateClientInbounds/:subId", a.updateClientInMultipleInbounds) g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic) g.POST("/resetAllTraffics", a.resetAllTraffics) g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics) @@ -206,7 +206,6 @@ func (a *InboundController) addClientToMultipleInbounds(c *gin.Context) { } data := &model.Inbound{ - Id: payload.Id, Settings: payload.Settings, } @@ -236,6 +235,45 @@ func (a *InboundController) addClientToMultipleInbounds(c *gin.Context) { } +func (a *InboundController) updateClientInMultipleInbounds(c *gin.Context) { + var payload AddClientPayload + subId := c.Param("subId") + + if err := c.ShouldBindJSON(&payload); err != nil { + jsonMsg(c, "Invalid request payload", err) + return + } + + data := &model.Inbound{ + Settings: payload.Settings, + } + + // If no specific inbound IDs are provided, add to all inbounds + if len(payload.InboundIds) == 0 { + allInbounds, err := a.inboundService.GetAllInbounds() + if err != nil { + jsonMsg(c, "Could not retrieve inbounds", err) + return + } + + for _, inbound := range allInbounds { + payload.InboundIds = append(payload.InboundIds, inbound.Id) + } + } + + needRestart := true + + needRestart, err = a.inboundService.UpdateClientInMultipleInbounds(data, subId, payload.InboundIds) + if err != nil { + jsonMsg(c, "Something went wrong!", err) + return + } + jsonMsg(c, "Client updated in multiple inbounds", nil) + if needRestart { + a.xrayService.SetToNeedRestart() + } +} + func (a *InboundController) delInboundClient(c *gin.Context) { diff --git a/web/service/inbound.go b/web/service/inbound.go index 8c8ef58e..bc1a902a 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -838,6 +838,162 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin return needRestart, tx.Save(oldInbound).Error } +func (s *InboundService) UpdateClientInMultipleInbounds(data *model.Inbound, subId string, inboundIds []int) (bool, error) { + var needRestart bool + + for _, inboundId := range inboundIds { + inbound, err := s.GetInbound(inboundId) + if err != nil { + return false, err + } + + clients, err := s.GetClients(data) + if err != nil { + return false, err + } + + var settings map[string]interface{} + err = json.Unmarshal([]byte(inbound.Settings), &settings) + if err != nil { + return false, err + } + + interfaceClients := settings["clients"].([]interface{}) + + oldClients, err := s.GetClients(inbound) + if err != nil { + return false, err + } + + oldEmail := "" + newClientsubId := "" + clientIndex := -1 + for index, oldClient := range oldClients { + oldClientsubId := "" + if inbound.Protocol == "trojan" { + oldClientsubId = oldClient.SubID + newClientsubId = clients[0].SubID + } else if inbound.Protocol == "shadowsocks" { + oldClientsubId = oldClient.SubID + newClientsubId = clients[0].SubID + } else { + oldClientsubId = oldClient.SubID + newClientsubId = clients[0].SubID + } + if subId == oldClientsubId { + oldEmail = oldClient.Email + clientIndex = index + break + } + } + + if newClientsubId == "" || clientIndex == -1 { + return false, common.NewError("empty client SubID") + } + + if len(clients[0].Email) > 0 && clients[0].Email != oldEmail { + existEmail, err := s.checkEmailsExistForClients(clients) + if err != nil { + return false, err + } + if existEmail != "" { + return false, common.NewError("Duplicate email:", existEmail) + } + } + + var oldSettings map[string]interface{} + err = json.Unmarshal([]byte(inbound.Settings), &oldSettings) + if err != nil { + return false, err + } + settingsClients := oldSettings["clients"].([]interface{}) + settingsClients[clientIndex] = interfaceClients[0] + oldSettings["clients"] = settingsClients + + newSettings, err := json.MarshalIndent(oldSettings, "", " ") + if err != nil { + return false, err + } + + inbound.Settings = string(newSettings) + db := database.GetDB() + tx := db.Begin() + + defer func() { + if err != nil { + tx.Rollback() + } else { + tx.Commit() + } + }() + + if len(clients[0].Email) > 0 { + if len(oldEmail) > 0 { + err = s.UpdateClientStat(tx, oldEmail, &clients[0]) + if err != nil { + return false, err + } + err = s.UpdateClientIPs(tx, oldEmail, clients[0].Email) + if err != nil { + return false, err + } + } else { + s.AddClientStat(tx, inbound.Id, &clients[0]) + } + } else { + err = s.DelClientStat(tx, oldEmail) + if err != nil { + return false, err + } + err = s.DelClientIPs(tx, oldEmail) + if err != nil { + return false, err + } + } + + if len(oldEmail) > 0 { + s.xrayApi.Init(p.GetAPIPort()) + err1 := s.xrayApi.RemoveUser(inbound.Tag, oldEmail) + if err1 == nil { + logger.Debug("Old client deleted by api:", clients[0].Email) + } else { + logger.Debug("Error in deleting client by api:", err1) + needRestart = true + } + if clients[0].Enable { + cipher := "" + if inbound.Protocol == "shadowsocks" { + cipher = oldSettings["method"].(string) + } + err1 := s.xrayApi.AddUser(string(inbound.Protocol), inbound.Tag, map[string]interface{}{ + "email": clients[0].Email, + "id": clients[0].ID, + "flow": clients[0].Flow, + "password": clients[0].Password, + "cipher": cipher, + }) + if err1 == nil { + logger.Debug("Client edited by api:", clients[0].Email) + } else { + logger.Debug("Error in adding client by api:", err1) + needRestart = true + } + } + s.xrayApi.Close() + } else { + logger.Debug("Client old email not found") + needRestart = true + } + + if err := tx.Save(inbound).Error; err != nil { + return false, err + } + } + + return needRestart, nil +} + + func (s *InboundService) AddTraffic(inboundTraffics []*xray.Traffic, clientTraffics []*xray.ClientTraffic) (error, bool) { var err error db := database.GetDB()