fix(nodes): propagate single-client deletion to remote nodes (#5352)

Deleting a client attached to a remote-node inbound could silently fail
to reach the node, so the node's next traffic snapshot resurrected the
client once the 90s delete tombstone expired.

Two paths in the single-client delete (Delete -> DelInboundClientByEmail):

- A disabled client was skipped entirely: the node-propagation and
  mark-dirty block sat behind the client's enable flag (needApiDel), so a
  disabled client on a node never detached and never marked the node
  dirty. The bulk and multi-client delete paths already handle the node
  case independently of enable state; mirror that structure here.

- Remote.DeleteUser returned nil when resolveRemoteID failed, hiding the
  failure from the caller so the node was never marked dirty. Surface the
  error like AddClient/UpdateUser do, so the caller marks the node dirty
  and the next reconcile converges.

Add a regression test asserting a disabled node client's deletion marks
the node dirty.
This commit is contained in:
MHSanaei
2026-06-15 17:56:12 +02:00
parent cf5f37e409
commit cbb21b7575
3 changed files with 70 additions and 11 deletions
+4 -1
View File
@@ -339,7 +339,10 @@ func (r *Remote) DeleteUser(ctx context.Context, ib *model.Inbound, email string
}
id, err := r.resolveRemoteID(ctx, ib.Tag)
if err != nil {
return nil
// Can't confirm the delete reached the node — surface it so the caller
// marks the node dirty and a reconcile converges, instead of silently
// dropping the delete and letting the next snapshot resurrect the client.
return fmt.Errorf("remote DeleteUser: resolve tag %q: %w", ib.Tag, err)
}
body := map[string]any{"inboundIds": []int{id}}
_, err = r.do(ctx, http.MethodPost,