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
127 lines
3.4 KiB
Go
127 lines
3.4 KiB
Go
package controller
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/service"
|
|
)
|
|
|
|
// SubBalancerController manages client-side JSON-subscription balancers.
|
|
type SubBalancerController struct {
|
|
SubBalancerService service.SubBalancerService
|
|
}
|
|
|
|
func NewSubBalancerController(g *gin.RouterGroup) *SubBalancerController {
|
|
a := &SubBalancerController{}
|
|
g = g.Group("/sub-balancers")
|
|
g.GET("", a.list)
|
|
g.POST("", a.create)
|
|
g.POST("/:id", a.update)
|
|
g.DELETE("/:id", a.del)
|
|
g.POST("/:id/del", a.del)
|
|
return a
|
|
}
|
|
|
|
// parseSubBalancerForm reads the urlencoded form (HttpUtil default): scalars
|
|
// via ShouldBind, inboundIds as repeated keys. enabled is returned as *bool so
|
|
// Update can keep the stored value when the key is absent; a bad value is a 400.
|
|
func parseSubBalancerForm(c *gin.Context) (*model.SubBalancer, *bool, error) {
|
|
form := struct {
|
|
Remark string `form:"remark"`
|
|
Strategy string `form:"strategy"`
|
|
SortOrder int `form:"sortOrder"`
|
|
}{}
|
|
if err := c.ShouldBind(&form); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
var enabled *bool
|
|
if raw, ok := c.GetPostForm("enabled"); ok {
|
|
v, err := strconv.ParseBool(raw)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("invalid enabled %q: %w", raw, err)
|
|
}
|
|
enabled = &v
|
|
}
|
|
balancer := &model.SubBalancer{
|
|
Remark: form.Remark,
|
|
Strategy: form.Strategy,
|
|
SortOrder: form.SortOrder,
|
|
}
|
|
for _, raw := range c.PostFormArray("inboundIds") {
|
|
id, err := strconv.Atoi(raw)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("invalid inbound id %q: %w", raw, err)
|
|
}
|
|
balancer.InboundIds = append(balancer.InboundIds, id)
|
|
}
|
|
return balancer, enabled, nil
|
|
}
|
|
|
|
func (a *SubBalancerController) parseID(c *gin.Context) (int, error) {
|
|
id, err := strconv.Atoi(c.Param("id"))
|
|
if err != nil || id < 1 {
|
|
return 0, fmt.Errorf("invalid id %q", c.Param("id"))
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
func (a *SubBalancerController) list(c *gin.Context) {
|
|
balancers, err := a.SubBalancerService.List()
|
|
if err != nil {
|
|
jsonMsg(c, I18nWeb(c, "pages.settings.subBalancers.toasts.list"), err)
|
|
return
|
|
}
|
|
jsonObj(c, balancers, nil)
|
|
}
|
|
|
|
func (a *SubBalancerController) create(c *gin.Context) {
|
|
balancer, enabled, err := parseSubBalancerForm(c)
|
|
if err != nil {
|
|
jsonMsg(c, I18nWeb(c, "pages.settings.subBalancers.toasts.create"), err)
|
|
return
|
|
}
|
|
balancer.Enabled = enabled == nil || *enabled
|
|
created, err := a.SubBalancerService.Create(balancer)
|
|
if err != nil {
|
|
jsonMsg(c, I18nWeb(c, "pages.settings.subBalancers.toasts.create"), err)
|
|
return
|
|
}
|
|
jsonObj(c, created, nil)
|
|
}
|
|
|
|
func (a *SubBalancerController) update(c *gin.Context) {
|
|
id, err := a.parseID(c)
|
|
if err != nil {
|
|
jsonMsg(c, I18nWeb(c, "pages.settings.subBalancers.toasts.invalidId"), err)
|
|
return
|
|
}
|
|
balancer, enabled, err := parseSubBalancerForm(c)
|
|
if err != nil {
|
|
jsonMsg(c, I18nWeb(c, "pages.settings.subBalancers.toasts.update"), err)
|
|
return
|
|
}
|
|
updated, err := a.SubBalancerService.Update(id, balancer, enabled)
|
|
if err != nil {
|
|
jsonMsg(c, I18nWeb(c, "pages.settings.subBalancers.toasts.update"), err)
|
|
return
|
|
}
|
|
jsonObj(c, updated, nil)
|
|
}
|
|
|
|
func (a *SubBalancerController) del(c *gin.Context) {
|
|
id, err := a.parseID(c)
|
|
if err != nil {
|
|
jsonMsg(c, I18nWeb(c, "pages.settings.subBalancers.toasts.invalidId"), err)
|
|
return
|
|
}
|
|
if err := a.SubBalancerService.Delete(id); err != nil {
|
|
jsonMsg(c, I18nWeb(c, "pages.settings.subBalancers.toasts.delete"), err)
|
|
return
|
|
}
|
|
jsonObj(c, "", nil)
|
|
}
|