fix(listener): reclaim soft-deleted names so UNIQUE(name) can be reused
This commit is contained in:
1 parent
69df7e5165
commit
2c8b7fc887
1 file changed
+22
-3
@@ -123,6 +123,9 @@ func (s *Service) Delete(id uint) error {
|
||||
if err := s.db.Delete(&models.Listener{}, id).Error; err != nil {
|
||||
return fmt.Errorf("failed to delete listener: %w", err)
|
||||
}
|
||||
// Soft-delete keeps the row; rename so UNIQUE(name) can be reused immediately.
|
||||
freed := fmt.Sprintf("%s__deleted_%d", previous.Name, id)
|
||||
_ = s.db.Unscoped().Model(&models.Listener{}).Where("id = ?", id).Update("name", freed).Error
|
||||
if err := s.regenerateConfigLocked(); err != nil {
|
||||
if rollbackErr := s.db.Unscoped().Save(&previous).Error; rollbackErr != nil {
|
||||
return fmt.Errorf("%v; rollback deleted listener failed: %w", err, rollbackErr)
|
||||
@@ -145,17 +148,33 @@ func (s *Service) ensureUniqueName(candidate *models.Listener) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("listener name is required")
|
||||
}
|
||||
var count int64
|
||||
// Only live rows should block create/update. Soft-deleted rows still occupy
|
||||
// the SQLite UNIQUE index, so reclaim those names first.
|
||||
var active int64
|
||||
q := s.db.Model(&models.Listener{}).Where("name = ?", name)
|
||||
if candidate.ID != 0 {
|
||||
q = q.Where("id <> ?", candidate.ID)
|
||||
}
|
||||
if err := q.Count(&count).Error; err != nil {
|
||||
if err := q.Count(&active).Error; err != nil {
|
||||
return fmt.Errorf("check listener name uniqueness: %w", err)
|
||||
}
|
||||
if count > 0 {
|
||||
if active > 0 {
|
||||
return fmt.Errorf("listener name %q already exists", name)
|
||||
}
|
||||
var soft []models.Listener
|
||||
sq := s.db.Unscoped().Where("name = ? AND deleted_at IS NOT NULL", name)
|
||||
if candidate.ID != 0 {
|
||||
sq = sq.Where("id <> ?", candidate.ID)
|
||||
}
|
||||
if err := sq.Find(&soft).Error; err != nil {
|
||||
return fmt.Errorf("check soft-deleted listener names: %w", err)
|
||||
}
|
||||
for _, row := range soft {
|
||||
newName := fmt.Sprintf("%s__deleted_%d", name, row.ID)
|
||||
if err := s.db.Unscoped().Model(&models.Listener{}).Where("id = ?", row.ID).Update("name", newName).Error; err != nil {
|
||||
return fmt.Errorf("reclaim soft-deleted name %q: %w", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user