package service import ( "errors" "strings" "github.com/mhsanaei/3x-ui/v3/database" "github.com/mhsanaei/3x-ui/v3/database/model" "gorm.io/gorm" ) type ClientService struct{} func (s *ClientService) SyncInbound(tx *gorm.DB, inboundId int, clients []model.Client) error { if tx == nil { tx = database.GetDB() } if err := tx.Where("inbound_id = ?", inboundId).Delete(&model.ClientInbound{}).Error; err != nil { return err } for i := range clients { c := clients[i] email := strings.TrimSpace(c.Email) if email == "" { continue } incoming := c.ToRecord() row := &model.ClientRecord{} err := tx.Where("email = ?", email).First(row).Error if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return err } if errors.Is(err, gorm.ErrRecordNotFound) { if err := tx.Create(incoming).Error; err != nil { return err } row = incoming } else { row.UUID = incoming.UUID row.Password = incoming.Password row.Auth = incoming.Auth row.Flow = incoming.Flow row.Security = incoming.Security row.Reverse = incoming.Reverse row.SubID = incoming.SubID row.LimitIP = incoming.LimitIP row.TotalGB = incoming.TotalGB row.ExpiryTime = incoming.ExpiryTime row.Enable = incoming.Enable row.TgID = incoming.TgID row.Comment = incoming.Comment row.Reset = incoming.Reset if incoming.CreatedAt > 0 && (row.CreatedAt == 0 || incoming.CreatedAt < row.CreatedAt) { row.CreatedAt = incoming.CreatedAt } if incoming.UpdatedAt > row.UpdatedAt { row.UpdatedAt = incoming.UpdatedAt } if err := tx.Save(row).Error; err != nil { return err } } link := model.ClientInbound{ ClientId: row.Id, InboundId: inboundId, FlowOverride: c.Flow, } if err := tx.Create(&link).Error; err != nil { return err } } return nil } func (s *ClientService) DetachInbound(tx *gorm.DB, inboundId int) error { if tx == nil { tx = database.GetDB() } return tx.Where("inbound_id = ?", inboundId).Delete(&model.ClientInbound{}).Error } func (s *ClientService) ListForInbound(tx *gorm.DB, inboundId int) ([]model.Client, error) { if tx == nil { tx = database.GetDB() } type joinedRow struct { model.ClientRecord FlowOverride string } var rows []joinedRow err := tx.Table("clients"). Select("clients.*, client_inbounds.flow_override AS flow_override"). Joins("JOIN client_inbounds ON client_inbounds.client_id = clients.id"). Where("client_inbounds.inbound_id = ?", inboundId). Order("clients.id ASC"). Find(&rows).Error if err != nil { return nil, err } out := make([]model.Client, 0, len(rows)) for i := range rows { c := rows[i].ToClient() if rows[i].FlowOverride != "" { c.Flow = rows[i].FlowOverride } out = append(out, *c) } return out, nil } func (s *ClientService) GetRecordByEmail(tx *gorm.DB, email string) (*model.ClientRecord, error) { if tx == nil { tx = database.GetDB() } row := &model.ClientRecord{} err := tx.Where("email = ?", email).First(row).Error if err != nil { return nil, err } return row, nil } func (s *ClientService) GetInboundIdsForEmail(tx *gorm.DB, email string) ([]int, error) { if tx == nil { tx = database.GetDB() } var ids []int err := tx.Table("client_inbounds"). Select("client_inbounds.inbound_id"). Joins("JOIN clients ON clients.id = client_inbounds.client_id"). Where("clients.email = ?", email). Scan(&ids).Error if err != nil { return nil, err } return ids, nil }