mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-05-13 13:58:22 +00:00
fix(inbound): prevent shared client_traffic row deletion when sibling inbound is deleted
This commit is contained in:
parent
686ca1f637
commit
821e50da51
1 changed files with 37 additions and 10 deletions
|
|
@ -5,6 +5,7 @@ package service
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
|
@ -422,11 +423,10 @@ func (s *InboundService) DelInbound(id int) (bool, error) {
|
|||
logger.Debug("No enabled inbound found to remove by api, id:", id)
|
||||
}
|
||||
|
||||
// Delete client traffics of inbounds
|
||||
err := db.Where("inbound_id = ?", id).Delete(xray.ClientTraffic{}).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// Load the inbound and its clients before touching client_traffics,
|
||||
// so we can check for sibling-inbound sharing before deleting rows
|
||||
// (AddClientStat uses OnConflict{DoNothing: true} on email — two
|
||||
// inbounds with clients sharing the same email share one row).
|
||||
inbound, err := s.GetInbound(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
@ -435,11 +435,31 @@ func (s *InboundService) DelInbound(id int) (bool, error) {
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// Bulk-delete client IPs for every email in this inbound. The previous
|
||||
// per-client loop fired one DELETE per row — at 7k+ clients that meant
|
||||
// thousands of synchronous SQL roundtrips and a multi-second freeze.
|
||||
// Chunked to stay under SQLite's bind-variable limit on huge inbounds.
|
||||
|
||||
// Delete client_traffic rows, but only for emails that are NOT
|
||||
// referenced by another inbound. A shared row is "owned" by whichever
|
||||
// inbound was added first — when that owner is deleted the shared row
|
||||
// disappears and the sibling hits "record not found" later.
|
||||
if len(clients) > 0 {
|
||||
for i := range clients {
|
||||
if clients[i].Email == "" {
|
||||
continue
|
||||
}
|
||||
stillUsed, checkErr := s.emailUsedByOtherInbounds(clients[i].Email, id)
|
||||
if checkErr != nil {
|
||||
return false, checkErr
|
||||
}
|
||||
if stillUsed {
|
||||
continue
|
||||
}
|
||||
if err := s.DelClientStat(db, clients[i].Email); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bulk-delete client IPs for every email in this inbound.
|
||||
{
|
||||
emails := make([]string, 0, len(clients))
|
||||
for i := range clients {
|
||||
if clients[i].Email != "" {
|
||||
|
|
@ -1222,8 +1242,15 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
|
|||
|
||||
if len(email) > 0 {
|
||||
notDepleted := true
|
||||
// The client_traffic row may have been deleted already when the
|
||||
// sibling inbound that "owned" it (the one whose AddClientStat
|
||||
// actually created the row) was deleted — AddClientStat uses
|
||||
// OnConflict{DoNothing:true}, so two inbounds with clients
|
||||
// sharing the same email share one row.
|
||||
err = db.Model(xray.ClientTraffic{}).Select("enable").Where("email = ?", email).First(¬Depleted).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
notDepleted = false
|
||||
} else if err != nil {
|
||||
logger.Error("Get stats error")
|
||||
return false, err
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue