fix(clients): backfill missing subId on startup and guard create/update

Legacy clients (and any API consumer that POSTs to AddInboundClient
without a subId) ended up with an empty SubID, which breaks the panel's
sub-link generation. Backfill them once at startup and stop the gap at
the write path so new clients can't reintroduce it.

- util/random: add NumLower(n) — 16-char [0-9a-z] generator that matches
  the frontend's RandomUtil.randomLowerAndNum convention.
- database/db.go: new InboundClientSubIdFix seeder, modeled on
  InboundClientTgIdFix. Loops every inbound, parses settings.clients,
  fills empty/missing subId with random.NumLower(16), persists via the
  same transaction-wrapped Update("settings", …) path, then records in
  HistoryOfSeeders so it runs at most once.
- web/service/client.go: defense-in-depth in AddInboundClient and
  UpdateInboundClient — fill subId on the persisted settings map when
  the payload omits it (Update prefers the previous value before
  generating a fresh one).
- database/db_seed_test: cover empty subId, missing-key subId, and
  preserved-existing subId; assert exactly one HistoryOfSeeders row.
This commit is contained in:
MHSanaei
2026-05-28 18:20:34 +02:00
parent 72b97efa8a
commit 99df5d70a8
4 changed files with 171 additions and 1 deletions
+14
View File
@@ -2880,6 +2880,10 @@ func (s *ClientService) AddInboundClient(inboundSvc *InboundService, data *model
cm["created_at"] = nowTs
}
cm["updated_at"] = nowTs
existingSub, _ := cm["subId"].(string)
if strings.TrimSpace(existingSub) == "" {
cm["subId"] = random.NumLower(16)
}
interfaceClients[i] = cm
}
}
@@ -3118,11 +3122,13 @@ func (s *ClientService) UpdateInboundClient(inboundSvc *InboundService, data *mo
}
settingsClients := oldSettings["clients"].([]any)
var preservedCreated any
var preservedSubID string
if clientIndex >= 0 && clientIndex < len(settingsClients) {
if oldMap, ok := settingsClients[clientIndex].(map[string]any); ok {
if v, ok2 := oldMap["created_at"]; ok2 {
preservedCreated = v
}
preservedSubID, _ = oldMap["subId"].(string)
}
}
if len(interfaceClients) > 0 {
@@ -3132,6 +3138,14 @@ func (s *ClientService) UpdateInboundClient(inboundSvc *InboundService, data *mo
}
newMap["created_at"] = preservedCreated
newMap["updated_at"] = time.Now().Unix() * 1000
newSub, _ := newMap["subId"].(string)
if strings.TrimSpace(newSub) == "" {
if strings.TrimSpace(preservedSubID) != "" {
newMap["subId"] = preservedSubID
} else {
newMap["subId"] = random.NumLower(16)
}
}
interfaceClients[0] = newMap
}
}