Feature: Copy clients between inbounds (#4087)

* feat: copy clients between inbounds

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* fix: copy clients modal not opening

* revert: undo install.sh/deploy.sh changes; i18n: add copy-clients translations for all languages

---------

Co-authored-by: Нестеров Руслан <r.nesterov@comagic.dev>
This commit is contained in:
Rs.Nest
2026-04-23 16:19:07 +03:00
committed by GitHub
parent ff25072690
commit 6bcaf61c44
16 changed files with 641 additions and 0 deletions

View File

@@ -41,6 +41,7 @@ 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("/:id/copyClients", a.copyInboundClients)
g.POST("/:id/delClient/:clientId", a.delInboundClient)
g.POST("/updateClient/:clientId", a.updateInboundClient)
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
@@ -54,6 +55,12 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
g.POST("/:id/delClientByEmail/:email", a.delInboundClientByEmail)
}
type CopyInboundClientsRequest struct {
SourceInboundID int `form:"sourceInboundId" json:"sourceInboundId"`
ClientEmails []string `form:"clientEmails" json:"clientEmails"`
Flow string `form:"flow" json:"flow"`
}
// getInbounds retrieves the list of inbounds for the logged-in user.
func (a *InboundController) getInbounds(c *gin.Context) {
user := session.GetLoginUser(c)
@@ -260,6 +267,36 @@ func (a *InboundController) addInboundClient(c *gin.Context) {
}
}
// copyInboundClients copies clients from source inbound to target inbound.
func (a *InboundController) copyInboundClients(c *gin.Context) {
targetID, err := strconv.Atoi(c.Param("id"))
if err != nil {
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
return
}
req := &CopyInboundClientsRequest{}
err = c.ShouldBind(req)
if err != nil {
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
return
}
if req.SourceInboundID <= 0 {
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), fmt.Errorf("invalid source inbound id"))
return
}
result, needRestart, err := a.inboundService.CopyInboundClients(targetID, req.SourceInboundID, req.ClientEmails, req.Flow)
if err != nil {
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
return
}
jsonObj(c, result, nil)
if needRestart {
a.xrayService.SetToNeedRestart()
}
}
// delInboundClient deletes a client from an inbound by inbound ID and client ID.
func (a *InboundController) delInboundClient(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))