From 2f36a4047c7c32ca17aaea8875bb5c087430b40d Mon Sep 17 00:00:00 2001 From: mhsanaei Date: Wed, 10 Sep 2025 16:36:12 +0200 Subject: [PATCH] API: delClientByEmail --- web/controller/inbound.go | 21 +++++++++ web/service/inbound.go | 92 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/web/controller/inbound.go b/web/controller/inbound.go index 10a58daa..8d610e7d 100644 --- a/web/controller/inbound.go +++ b/web/controller/inbound.go @@ -46,6 +46,7 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) { g.POST("/onlines", a.onlines) g.POST("/lastOnline", a.lastOnline) g.POST("/updateClientTraffic/:email", a.updateClientTraffic) + g.POST("/:id/delClientByEmail/:email", a.delInboundClientByEmail) } func (a *InboundController) getInbounds(c *gin.Context) { @@ -374,3 +375,23 @@ func (a *InboundController) updateClientTraffic(c *gin.Context) { jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientUpdateSuccess"), nil) } + +func (a *InboundController) delInboundClientByEmail(c *gin.Context) { + inboundId, err := strconv.Atoi(c.Param("id")) + if err != nil { + jsonMsg(c, "Invalid inbound ID", err) + return + } + + email := c.Param("email") + needRestart, err := a.inboundService.DelInboundClientByEmail(inboundId, email) + if err != nil { + jsonMsg(c, "Failed to delete client by email", err) + return + } + + jsonMsg(c, "Client deleted successfully", nil) + if needRestart { + a.xrayService.SetToNeedRestart() + } +} diff --git a/web/service/inbound.go b/web/service/inbound.go index a4592160..2646b1e7 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -2247,3 +2247,95 @@ func (s *InboundService) FilterAndSortClientEmails(emails []string) ([]string, [ return validEmails, extraEmails, nil } +func (s *InboundService) DelInboundClientByEmail(inboundId int, email string) (bool, error) { + oldInbound, err := s.GetInbound(inboundId) + if err != nil { + logger.Error("Load Old Data Error") + return false, err + } + + var settings map[string]any + if err := json.Unmarshal([]byte(oldInbound.Settings), &settings); err != nil { + return false, err + } + + interfaceClients, ok := settings["clients"].([]any) + if !ok { + return false, common.NewError("invalid clients format in inbound settings") + } + + var newClients []any + needApiDel := false + found := false + + for _, client := range interfaceClients { + c, ok := client.(map[string]any) + if !ok { + continue + } + if cEmail, ok := c["email"].(string); ok && cEmail == email { + // matched client, drop it + found = true + needApiDel, _ = c["enable"].(bool) + } else { + newClients = append(newClients, client) + } + } + + if !found { + return false, common.NewError(fmt.Sprintf("client with email %s not found", email)) + } + if len(newClients) == 0 { + return false, common.NewError("no client remained in Inbound") + } + + settings["clients"] = newClients + newSettings, err := json.MarshalIndent(settings, "", " ") + if err != nil { + return false, err + } + + oldInbound.Settings = string(newSettings) + + db := database.GetDB() + + // remove IP bindings + if err := s.DelClientIPs(db, email); err != nil { + logger.Error("Error in delete client IPs") + return false, err + } + + needRestart := false + + // remove stats too + if len(email) > 0 { + traffic, err := s.GetClientTrafficByEmail(email) + if err != nil { + return false, err + } + if traffic != nil { + if err := s.DelClientStat(db, email); err != nil { + logger.Error("Delete stats Data Error") + return false, err + } + } + + if needApiDel { + s.xrayApi.Init(p.GetAPIPort()) + if err1 := s.xrayApi.RemoveUser(oldInbound.Tag, email); err1 == nil { + logger.Debug("Client deleted by api:", email) + needRestart = false + } else { + if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", email)) { + logger.Debug("User is already deleted. Nothing to do more...") + } else { + logger.Debug("Error in deleting client by api:", err1) + needRestart = true + } + } + s.xrayApi.Close() + } + } + + return needRestart, db.Save(oldInbound).Error +}