CI / go-test (push) Canceled after 0s
CI / postgres-durable-first (push) Canceled after 0s
CI / codegen (push) Canceled after 0s
CI / govulncheck (push) Canceled after 0s
CI / race (push) Canceled after 0s
CI / fuzz-smoke (push) Canceled after 0s
CI / golangci (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CodeQL Advanced / Analyze (go) (push) Canceled after 0s
CodeQL Advanced / Analyze (actions) (push) Canceled after 0s
CodeQL Advanced / Analyze (javascript-typescript) (push) Canceled after 0s
Docs CI / build (push) Canceled after 0s
Docs Deploy (GitHub Pages) / 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
Deploy Smoke Tests / noninteractive-install (ubuntu-24.04-arm) (push) Canceled after 0s
Deploy Smoke Tests / noninteractive-install (ubuntu-latest) (push) Canceled after 0s
Deploy Smoke Tests / release-tag-install (ubuntu-24.04-arm) (push) Canceled after 0s
Deploy Smoke Tests / release-tag-install (ubuntu-latest) (push) Canceled after 0s
Docs Deploy (GitHub Pages) / deploy (push) Canceled after 0s
Release 3X-UI / Publish rolling dev release (push) Canceled after 0s
102 lines
2.7 KiB
Go
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
|
|
}
|