From 5eb80eca8e6b64fdc4c2834ce2e0a4c413374896 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Wed, 27 May 2026 11:23:33 +0200 Subject: [PATCH] fix(clients): avoid duplicate ClientRecord when email is changed on edit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SyncInbound (invoked from UpdateInboundClient during a client edit) looks up the ClientRecord row by the email present in the inbound's settings. After an email change the lookup misses the original row, hits the gorm.ErrRecordNotFound branch, and inserts a fresh ClientRecord with the new email. The original row stays in place with its inbound link cleared, so the clients list shows both — the original as an orphan and the new one as if it had just been created. Rename the existing ClientRecord row to the new email up front, before the inbound loop runs. SyncInbound then finds and updates the same row instead of creating a duplicate. A pre-check rejects renames that would collide with another client's email so the unique index keeps its meaning. --- web/service/client.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/web/service/client.go b/web/service/client.go index 34831ae1..05b39b54 100644 --- a/web/service/client.go +++ b/web/service/client.go @@ -595,6 +595,27 @@ func (s *ClientService) Update(inboundSvc *InboundService, id int, updated model updated.CreatedAt = existing.CreatedAt } + // Rename the ClientRecord row up front when the email changes. SyncInbound + // (invoked from UpdateInboundClient below) looks up by email — without + // renaming first it would treat the new email as a brand-new client, + // insert a duplicate ClientRecord, and leave the original orphaned. + if updated.Email != existing.Email { + var collisionCount int64 + if err := database.GetDB().Model(&model.ClientRecord{}). + Where("email = ? AND id <> ?", updated.Email, id). + Count(&collisionCount).Error; err != nil { + return false, err + } + if collisionCount > 0 { + return false, common.NewError("Duplicate email:", updated.Email) + } + if err := database.GetDB().Model(&model.ClientRecord{}). + Where("id = ?", id). + Update("email", updated.Email).Error; err != nil { + return false, err + } + } + needRestart := false for _, ibId := range inboundIds { inbound, getErr := inboundSvc.GetInbound(ibId)