Files
3x-ui-agent/internal/web/service/sub_balancer.go
T
Sanaei f727d04f65
Release 3X-UI for Docker / build (push) Canceled after 0s
Release 3X-UI / build (386) (push) Canceled after 0s
Release 3X-UI / build (amd64) (push) Canceled after 0s
Release 3X-UI / build (arm64) (push) Canceled after 0s
Release 3X-UI / build (armv5) (push) Canceled after 0s
Release 3X-UI / build (armv6) (push) Canceled after 0s
Release 3X-UI / build (armv7) (push) Canceled after 0s
Release 3X-UI / build (s390x) (push) Canceled after 0s
Release 3X-UI / Build for Windows (amd64) (push) Canceled after 0s
Release 3X-UI / Publish rolling dev release (push) Canceled after 0s
v3.7.0
2026-08-24 15:07:15 +02:00

102 lines
2.7 KiB
Go

package service
import (
"strings"
"github.com/mhsanaei/3x-ui/v3/internal/database"
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
"github.com/mhsanaei/3x-ui/v3/internal/util/common"
)
var subBalancerStrategies = map[string]struct{}{
"leastLoad": {},
"leastPing": {},
"random": {},
"roundRobin": {},
}
// SubBalancerService manages client-side JSON-subscription balancers; rows
// are read per request by internal/sub, so mutations need no xray restart.
type SubBalancerService struct{}
func (s *SubBalancerService) validate(b *model.SubBalancer) error {
b.Remark = strings.TrimSpace(b.Remark)
if b.Remark == "" {
return common.NewError("balancer remark is required")
}
if len(b.Remark) > 256 {
return common.NewError("balancer remark too long (max 256)")
}
if b.Strategy == "" {
b.Strategy = "random"
}
if _, ok := subBalancerStrategies[b.Strategy]; !ok {
return common.NewError("invalid balancer strategy:", b.Strategy)
}
if len(b.InboundIds) == 0 {
return common.NewError("balancer must select at least one inbound")
}
if b.SortOrder < 1 {
b.SortOrder = 1
}
return nil
}
// List returns all balancers in subscription order.
func (s *SubBalancerService) List() ([]*model.SubBalancer, error) {
var balancers []*model.SubBalancer
err := database.GetDB().Model(&model.SubBalancer{}).
Order("sort_order asc, id asc").Find(&balancers).Error
return balancers, err
}
func (s *SubBalancerService) Get(id int) (*model.SubBalancer, error) {
var balancer model.SubBalancer
if err := database.GetDB().First(&balancer, id).Error; err != nil {
return nil, err
}
return &balancer, nil
}
func (s *SubBalancerService) Create(balancer *model.SubBalancer) (*model.SubBalancer, error) {
if err := s.validate(balancer); err != nil {
return nil, err
}
if err := database.GetDB().Create(balancer).Error; err != nil {
return nil, err
}
return balancer, nil
}
func (s *SubBalancerService) Update(id int, balancer *model.SubBalancer, enabled *bool) (*model.SubBalancer, error) {
if err := s.validate(balancer); err != nil {
return nil, err
}
current, err := s.Get(id)
if err != nil {
return nil, err
}
current.Remark = balancer.Remark
current.Strategy = balancer.Strategy
current.InboundIds = balancer.InboundIds
current.SortOrder = balancer.SortOrder
if enabled != nil {
current.Enabled = *enabled
}
if err := database.GetDB().Save(current).Error; err != nil {
return nil, err
}
return current, nil
}
func (s *SubBalancerService) Delete(id int) error {
res := database.GetDB().Delete(&model.SubBalancer{}, id)
if res.Error != nil {
return res.Error
}
if res.RowsAffected == 0 {
return common.NewError("sub balancer not found")
}
return nil
}