fix(inbounds): keep the stored client list and enable on inbound save

Invariant: saving an inbound's configuration never changes which clients it
holds nor whether it is enabled; both have their own endpoints. The edit
modal posts back the clients and the enable flag it loaded when it opened.
A client added meanwhile (another admin, the bot, the API, LDAP) was
detached and its stats deleted; a client deleted meanwhile came back with
its credentials, restoring access that had been revoked; an inbound
switched off meanwhile was switched back on.

For every save but a master's node-sync push, UpdateInbound now takes the
client list and enable from the row it re-reads inside the writer; this
replaces the lifecycle-only carry from the previous commit. Client
validation (renewal schedule, Hysteria auth, TUIC credentials) moves after
that swap so it judges the clients actually saved: a protocol switch keeps
the stored clients, and #6268's refusal must apply to them.

The edit form no longer loads or sends clients, so neither the JSON editor
nor validation sees a copy the server ignores, and the enable switch shows
only when adding; the list toggle (/setEnable) covers existing inbounds.

Tests that added or re-keyed clients through a panel inbound save pinned
the old rule; they now drive the master-push path, where payload clients
still apply.
This commit is contained in:
MHSanaei
2026-09-28 13:23:48 +02:00
parent fb7418f7bd
commit 823db05966
13 changed files with 207 additions and 96 deletions
@@ -48,7 +48,8 @@ func TestClientRenewalWriteValidation(t *testing.T) {
}
_, _, err = inboundSvc.AddInbound(&update)
case "update inbound":
_, _, err = inboundSvc.UpdateInbound(&update)
// A panel save keeps the stored clients; only a master's push writes them.
_, _, err = (&InboundService{FromNodeSync: true}).UpdateInbound(&update)
case "add inbound client":
client.Email = "invalid-new-client"
update.Settings = clientsSettings(t, []model.Client{client})