mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-05-31 10:14:15 +00:00
fix(nodes): clean up orphaned client_inbounds on node inbound removal
When a remote node disconnects or one of its inbounds vanishes from the traffic snapshot, setRemoteTrafficLocked deleted the central inbound row but left the client_inbounds join rows behind. Affected clients ended up linked to hundreds of phantom inbounds, and editing one then failed with "record not found" / "Load Old Data Error" because Update aborted on the first GetInbound miss. - Detach client_inbounds rows when deleting a vanished node inbound - Prune stale links during client Update instead of aborting the save - Drop orphaned client_inbounds rows on startup to heal existing DBs Closes #4636
This commit is contained in:
parent
b395a1b951
commit
169068d8fb
3 changed files with 26 additions and 0 deletions
|
|
@ -83,6 +83,21 @@ func initModels() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := pruneOrphanedClientInbounds(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pruneOrphanedClientInbounds() error {
|
||||||
|
res := db.Exec("DELETE FROM client_inbounds WHERE inbound_id NOT IN (SELECT id FROM inbounds)")
|
||||||
|
if res.Error != nil {
|
||||||
|
log.Printf("Error pruning orphaned client_inbounds rows: %v", res.Error)
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
if res.RowsAffected > 0 {
|
||||||
|
log.Printf("Pruned %d orphaned client_inbounds row(s)", res.RowsAffected)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -629,6 +629,14 @@ func (s *ClientService) Update(inboundSvc *InboundService, id int, updated model
|
||||||
for _, ibId := range inboundIds {
|
for _, ibId := range inboundIds {
|
||||||
inbound, getErr := inboundSvc.GetInbound(ibId)
|
inbound, getErr := inboundSvc.GetInbound(ibId)
|
||||||
if getErr != nil {
|
if getErr != nil {
|
||||||
|
if errors.Is(getErr, gorm.ErrRecordNotFound) {
|
||||||
|
if err := database.GetDB().
|
||||||
|
Where("client_id = ? AND inbound_id = ?", id, ibId).
|
||||||
|
Delete(&model.ClientInbound{}).Error; err != nil {
|
||||||
|
return needRestart, err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
return needRestart, getErr
|
return needRestart, getErr
|
||||||
}
|
}
|
||||||
oldKey := clientKeyForProtocol(inbound.Protocol, existing)
|
oldKey := clientKeyForProtocol(inbound.Protocol, existing)
|
||||||
|
|
|
||||||
|
|
@ -1432,6 +1432,9 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
|
||||||
Delete(&xray.ClientTraffic{}).Error; err != nil {
|
Delete(&xray.ClientTraffic{}).Error; err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
if err := s.clientService.DetachInbound(tx, c.Id); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
if err := tx.Where("id = ?", c.Id).
|
if err := tx.Where("id = ?", c.Id).
|
||||||
Delete(&model.Inbound{}).Error; err != nil {
|
Delete(&model.Inbound{}).Error; err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue