mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-09 14:16:07 +00:00
cb5b3a803a
Adding a WireGuard client on the master broke every WireGuard connection on
the sub-node until Xray was manually restarted on the node. Adding the same
client directly on the node worked.
Root cause: the panel stores WireGuard clients under the settings key
`clients` (the shape every other protocol uses), but xray-core's wireguard
inbound is configured with `peers`. The `clients`->`peers` conversion lived
only in the full-config generation path (XrayService.GetXrayConfig), which
runs on a full Xray restart. The live gRPC AddInbound path goes through
(*Inbound).GenXrayInboundConfig, which passed the WireGuard settings verbatim
- with `clients` and no `peers`.
Why the master path broke it and the node path did not:
- Adding on the node is a single safe operation: AddInboundClient -> AddUser
-> AlterInbound{AddUser} -> wireguard.Server.AddUser, which appends one peer
via IPC without touching the others. The inbound is local (NodeID == nil),
so nothing is marked dirty and no reconcile runs.
- Adding on the master does two things: it pushes the client to the node
(the same safe hot-add, which succeeds), and it marks the node dirty. The
reconcile then pushes panel/api/inbounds/update/:id to the node, whose
InboundService.UpdateInbound applies it live via DelInbound + AddInbound
(buildRuntimeInboundForAPI -> Local.AddInbound -> GenXrayInboundConfig).
That re-adds the wireguard inbound with zero peers, wiping the device and
dropping every connected client. A manual restart regenerated the full
config, converted clients to peers, and restored them - hence "only a
restart fixes it".
Fix: convert WireGuard `clients` to `peers` in GenXrayInboundConfig itself,
the single chokepoint for every live AddInbound (create, edit, node
reconcile). WireguardClientsToPeers always rebuilds `peers` from `clients`
(matching GetXrayConfig field for field) and drops the `clients` key. It does
not gate on `peers` being absent: the panel seeds every WireGuard inbound with
an empty `peers: []` placeholder (frontend inbound-defaults), so a
"skip if peers present" guard would match that placeholder and make the
conversion never run, leaving the live path emitting zero peers. The
conversion stays idempotent by removing `clients`, so a second call - or an
inbound with no `clients` - is a no-op, leaving the full-config path
unaffected. This also fixes plain WireGuard inbound edits on a standalone
panel, which went through the same peerless rebuild.