mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-24 21:46:07 +00:00
fix(hosts): assign group ids to imported hosts and repair empty ones
Host rows created from a legacy streamSettings.externalProxy during inbound import got an empty group_id, and the one-time HostGroupIds seeder had already been gated off, so the UI rendered them under a synthetic fallback_<id> group the update/delete API could not resolve, failing every edit with "host group not found". Assign a real group id in externalProxyEntryToHost at creation, and replace the seeder with backfillEmptyHostGroupIds, an idempotent startup repair that runs on every boot so rows from older builds and restored backups are healed too. Also rename the leaked internal error "host group not found" to "host not found" since groups are not a user-facing concept.
This commit is contained in:
@@ -67,6 +67,47 @@ func TestMigrate_ExternalProxyToHosts(t *testing.T) {
|
||||
b.Port != 80 || b.Remark != "B" {
|
||||
t.Fatalf("host B mapping wrong: %+v", b)
|
||||
}
|
||||
if a.GroupId == "" || b.GroupId == "" {
|
||||
t.Fatalf("group ids must be assigned at creation: a=%q b=%q", a.GroupId, b.GroupId)
|
||||
}
|
||||
if a.GroupId == b.GroupId {
|
||||
t.Fatalf("each entry must get its own group id, both = %q", a.GroupId)
|
||||
}
|
||||
}
|
||||
|
||||
// #1b — a hosts row that entered the DB without a group_id (older-build import
|
||||
// or restored backup) is repaired on every start, so it stays addressable by
|
||||
// the group-scoped update/delete API instead of surfacing as fallback_<id>.
|
||||
func TestBackfillEmptyHostGroupIds_RepairsLegacyRows(t *testing.T) {
|
||||
initMigrateDB(t)
|
||||
ib := seedInboundWithStream(t, "m1b", 5556, `{"network":"tcp","security":"none"}`)
|
||||
legacy := &model.Host{InboundId: ib.Id, Remark: "legacy", Address: "c.cdn.com", Port: 443, Security: "tls"}
|
||||
if err := GetDB().Create(legacy).Error; err != nil {
|
||||
t.Fatalf("create legacy host: %v", err)
|
||||
}
|
||||
|
||||
if err := backfillEmptyHostGroupIds(); err != nil {
|
||||
t.Fatalf("backfill: %v", err)
|
||||
}
|
||||
|
||||
var got model.Host
|
||||
if err := GetDB().First(&got, legacy.Id).Error; err != nil {
|
||||
t.Fatalf("reload host: %v", err)
|
||||
}
|
||||
if got.GroupId == "" {
|
||||
t.Fatal("group_id still empty after backfill")
|
||||
}
|
||||
|
||||
if err := backfillEmptyHostGroupIds(); err != nil {
|
||||
t.Fatalf("second backfill: %v", err)
|
||||
}
|
||||
var again model.Host
|
||||
if err := GetDB().First(&again, legacy.Id).Error; err != nil {
|
||||
t.Fatalf("reload host after second run: %v", err)
|
||||
}
|
||||
if again.GroupId != got.GroupId {
|
||||
t.Fatalf("second run must not touch repaired rows: %q -> %q", got.GroupId, again.GroupId)
|
||||
}
|
||||
}
|
||||
|
||||
// #2 — a second run is a no-op (the HistoryOfSeeders gate).
|
||||
|
||||
Reference in New Issue
Block a user