fix(clients): store client group names trimmed

The groups page lists clients.group_name as stored, while every group
action trims the name it is given. A name with surrounding white space,
saved by a node snapshot or by a client edit, was listed as a separate
group, and deleting, renaming or resetting that row acted on the real
group of the trimmed name instead.

Group names are trimmed wherever a client record is built or updated,
and a one-time seeder folds stored names onto their trimmed form.
This commit is contained in:
MHSanaei
2026-11-01 00:03:48 +01:00
parent 376ab34c0f
commit d114845e44
5 changed files with 79 additions and 3 deletions
+1 -1
View File
@@ -923,7 +923,7 @@ func (s *ClientService) Update(inboundSvc *InboundService, id int, updated model
// including the empty string that removes the client from its group.
if err := database.GetDB().Model(&model.ClientRecord{}).
Where("id = ?", id).
UpdateColumn("group_name", updated.Group).Error; err != nil {
UpdateColumn("group_name", strings.TrimSpace(updated.Group)).Error; err != nil {
return needRestart, err
}
@@ -181,3 +181,22 @@ func TestClientUpdate_ClearsGroup(t *testing.T) {
t.Errorf("inbound settings still carry a group key after removal: %s", ibAfter.Settings)
}
}
// The groups page lists stored names but addresses a group by its trimmed name, so a
// padded name from a node made actions on its row hit the real group.
func TestSetRemoteTraffic_StoresTrimmedGroupName(t *testing.T) {
db := initTrafficTestDB(t)
seedNodeRow(t, db, &model.Node{Id: 1, Name: "n1", Address: "127.0.0.1", Port: 2096, ApiToken: "tok", Enable: true})
createNodeInbound(t, db, 1, "n1-in", 41001)
syncNodeWithSettings(t, &InboundService{}, 1, "n1-in",
`{"clients":[{"email":"who@x","id":"66666666-6666-4666-8666-666666666666","group":"vip\t","enable":true}]}`)
var row model.ClientRecord
if err := db.Where("email = ?", "who@x").First(&row).Error; err != nil {
t.Fatalf("lookup client: %v", err)
}
if row.Group != "vip" {
t.Fatalf("group stored as %q, want %q", row.Group, "vip")
}
}