package controller import ( "encoding/json" "fmt" "strconv" "time" "github.com/mhsanaei/3x-ui/v3/database/model" "github.com/mhsanaei/3x-ui/v3/web/service" "github.com/gin-gonic/gin" ) type ClientController struct { clientService service.ClientService inboundService service.InboundService xrayService service.XrayService } func NewClientController(g *gin.RouterGroup) *ClientController { a := &ClientController{} a.initRouter(g) return a } func (a *ClientController) initRouter(g *gin.RouterGroup) { g.GET("/list", a.list) g.GET("/get/:id", a.get) g.POST("/add", a.create) g.POST("/update/:id", a.update) g.POST("/del/:id", a.delete) g.POST("/:id/attach", a.attach) g.POST("/:id/detach", a.detach) g.POST("/resetAllTraffics", a.resetAllTraffics) g.POST("/delDepleted", a.delDepleted) g.POST("/resetTraffic/:email", a.resetTrafficByEmail) g.POST("/updateTraffic/:email", a.updateTrafficByEmail) g.POST("/clientIps/:email", a.getClientIps) g.POST("/clearClientIps/:email", a.clearClientIps) g.POST("/onlines", a.onlines) g.POST("/lastOnline", a.lastOnline) g.GET("/traffic/:email", a.getTrafficByEmail) g.GET("/traffic/byId/:id", a.getTrafficsByClientID) g.GET("/subLinks/:subId", a.getSubLinks) g.GET("/links/:id/:email", a.getClientLinks) } func (a *ClientController) list(c *gin.Context) { rows, err := a.clientService.List() if err != nil { jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.obtain"), err) return } jsonObj(c, rows, nil) } func (a *ClientController) get(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { jsonMsg(c, I18nWeb(c, "get"), err) return } rec, err := a.clientService.GetByID(id) if err != nil { jsonMsg(c, I18nWeb(c, "get"), err) return } inboundIds, err := a.clientService.GetInboundIdsForRecord(id) if err != nil { jsonMsg(c, I18nWeb(c, "get"), err) return } jsonObj(c, gin.H{"client": rec, "inboundIds": inboundIds}, nil) } func (a *ClientController) create(c *gin.Context) { var payload service.ClientCreatePayload if err := c.ShouldBindJSON(&payload); err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } needRestart, err := a.clientService.Create(&a.inboundService, &payload) if err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientAddSuccess"), nil) if needRestart { a.xrayService.SetToNeedRestart() } } func (a *ClientController) update(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } var updated model.Client if err := c.ShouldBindJSON(&updated); err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } needRestart, err := a.clientService.Update(&a.inboundService, id, updated) if err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientUpdateSuccess"), nil) if needRestart { a.xrayService.SetToNeedRestart() } } func (a *ClientController) delete(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } keepTraffic := c.Query("keepTraffic") == "1" needRestart, err := a.clientService.Delete(&a.inboundService, id, keepTraffic) if err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientDeleteSuccess"), nil) if needRestart { a.xrayService.SetToNeedRestart() } } type attachDetachBody struct { InboundIds []int `json:"inboundIds"` } func (a *ClientController) attach(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } var body attachDetachBody if err := c.ShouldBindJSON(&body); err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } needRestart, err := a.clientService.Attach(&a.inboundService, id, body.InboundIds) if err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientAddSuccess"), nil) if needRestart { a.xrayService.SetToNeedRestart() } } func (a *ClientController) resetAllTraffics(c *gin.Context) { needRestart, err := a.clientService.ResetAllTraffics() if err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.resetAllClientTrafficSuccess"), nil) if needRestart { a.xrayService.SetToNeedRestart() } } func (a *ClientController) delDepleted(c *gin.Context) { deleted, needRestart, err := a.clientService.DelDepleted(&a.inboundService) if err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } jsonObj(c, gin.H{"deleted": deleted}, nil) if needRestart { a.xrayService.SetToNeedRestart() } } func (a *ClientController) resetTrafficByEmail(c *gin.Context) { email := c.Param("email") needRestart, err := a.clientService.ResetTrafficByEmail(&a.inboundService, email) if err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.resetInboundClientTrafficSuccess"), nil) if needRestart { a.xrayService.SetToNeedRestart() } } type trafficUpdateRequest struct { Upload int64 `json:"upload"` Download int64 `json:"download"` } func (a *ClientController) updateTrafficByEmail(c *gin.Context) { email := c.Param("email") var req trafficUpdateRequest if err := c.ShouldBindJSON(&req); err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } if err := a.inboundService.UpdateClientTrafficByEmail(email, req.Upload, req.Download); err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientUpdateSuccess"), nil) } func (a *ClientController) getClientIps(c *gin.Context) { email := c.Param("email") ips, err := a.inboundService.GetInboundClientIps(email) if err != nil || ips == "" { jsonObj(c, "No IP Record", nil) return } type ipWithTimestamp struct { IP string `json:"ip"` Timestamp int64 `json:"timestamp"` } var ipsWithTime []ipWithTimestamp if err := json.Unmarshal([]byte(ips), &ipsWithTime); err == nil && len(ipsWithTime) > 0 { formatted := make([]string, 0, len(ipsWithTime)) for _, item := range ipsWithTime { if item.IP == "" { continue } if item.Timestamp > 0 { ts := time.Unix(item.Timestamp, 0).Local().Format("2006-01-02 15:04:05") formatted = append(formatted, fmt.Sprintf("%s (%s)", item.IP, ts)) continue } formatted = append(formatted, item.IP) } jsonObj(c, formatted, nil) return } var oldIps []string if err := json.Unmarshal([]byte(ips), &oldIps); err == nil && len(oldIps) > 0 { jsonObj(c, oldIps, nil) return } jsonObj(c, ips, nil) } func (a *ClientController) clearClientIps(c *gin.Context) { email := c.Param("email") if err := a.inboundService.ClearClientIps(email); err != nil { jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.updateSuccess"), err) return } jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.logCleanSuccess"), nil) } func (a *ClientController) onlines(c *gin.Context) { jsonObj(c, a.inboundService.GetOnlineClients(), nil) } func (a *ClientController) lastOnline(c *gin.Context) { data, err := a.inboundService.GetClientsLastOnline() jsonObj(c, data, err) } func (a *ClientController) getTrafficByEmail(c *gin.Context) { email := c.Param("email") traffic, err := a.inboundService.GetClientTrafficByEmail(email) if err != nil { jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.trafficGetError"), err) return } jsonObj(c, traffic, nil) } func (a *ClientController) getTrafficsByClientID(c *gin.Context) { id := c.Param("id") traffics, err := a.inboundService.GetClientTrafficByID(id) if err != nil { jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.trafficGetError"), err) return } jsonObj(c, traffics, nil) } func (a *ClientController) getSubLinks(c *gin.Context) { links, err := a.inboundService.GetSubLinks(resolveHost(c), c.Param("subId")) if err != nil { jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.obtain"), err) return } jsonObj(c, links, nil) } func (a *ClientController) getClientLinks(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { jsonMsg(c, I18nWeb(c, "get"), err) return } links, err := a.inboundService.GetClientLinks(resolveHost(c), id, c.Param("email")) if err != nil { jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.obtain"), err) return } jsonObj(c, links, nil) } func (a *ClientController) detach(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } var body attachDetachBody if err := c.ShouldBindJSON(&body); err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } needRestart, err := a.clientService.Detach(&a.inboundService, id, body.InboundIds) if err != nil { jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) return } jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientDeleteSuccess"), nil) if needRestart { a.xrayService.SetToNeedRestart() } }