fix(clients): reject dot names and recover legacy clients (#7062)

Reject reserved dot-segment client names and recover legacy records through query aliases. Preserve token scopes and path identity precedence, and update the generated API reference.
This commit is contained in:
Benjamin Mitchell
2026-11-05 09:02:39 -08:00
committed by GitHub
parent 2541080c91
commit cd176faf4e
29 changed files with 895 additions and 17 deletions
+28 -3
View File
@@ -53,6 +53,7 @@ func NewClientController(g *gin.RouterGroup) *ClientController {
func (a *ClientController) initRouter(g *gin.RouterGroup) {
g.GET("/list", a.list)
g.GET("/list/paged", a.listPaged)
g.GET("/get", a.get)
g.GET("/get/:email", a.get)
g.GET("/get/subId/:subId", a.getBySubId)
g.GET("/get/tgId/:tgId", a.getByTgId)
@@ -63,7 +64,9 @@ func (a *ClientController) initRouter(g *gin.RouterGroup) {
g.POST("/add", a.create)
g.POST("/renewalPreview", a.renewalPreview)
g.POST("/update", a.update)
g.POST("/update/:email", a.update)
g.POST("/del", a.delete)
g.POST("/del/:email", a.delete)
g.POST("/:email/attach", a.attach)
g.POST("/:email/detach", a.detach)
@@ -159,8 +162,24 @@ func (a *ClientController) buildClientPayload(rec *model.ClientRecord) (gin.H, e
}, nil
}
func (a *ClientController) get(c *gin.Context) {
// Query aliases keep legacy dot-segment names addressable without changing path clients.
func clientEmailParam(c *gin.Context) (string, bool) {
email := c.Param("email")
if email == "" {
email = c.Query("email")
}
if email == "" {
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), errors.New("client email is required"))
return "", false
}
return email, true
}
func (a *ClientController) get(c *gin.Context) {
email, ok := clientEmailParam(c)
if !ok {
return
}
rec, err := a.clientService.GetRecordByEmail(nil, email)
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.obtain"), err)
@@ -244,7 +263,10 @@ func (a *ClientController) create(c *gin.Context) {
}
func (a *ClientController) update(c *gin.Context) {
email := c.Param("email")
email, ok := clientEmailParam(c)
if !ok {
return
}
var req struct {
model.Client
LimitHwid int `json:"limitHwid"`
@@ -273,7 +295,10 @@ func (a *ClientController) update(c *gin.Context) {
}
func (a *ClientController) delete(c *gin.Context) {
email := c.Param("email")
email, ok := clientEmailParam(c)
if !ok {
return
}
keepTraffic := c.Query("keepTraffic") == "1"
needRestart, err := a.clientService.DeleteByEmail(&a.inboundService, email, keepTraffic)
// Flagged before the error check: a partly-applied delete already removed