Add OpenVPN, TrustTunnel, Sudoku, inbound managers. Fixes

This commit is contained in:
Shtorm
2026-06-04 01:47:50 +03:00
parent 9b3da79c32
commit 195a33379d
164 changed files with 16665 additions and 1332 deletions

View File

@@ -67,7 +67,7 @@ type UserCreate struct {
SquadIDs []int `json:"squad_ids" validate:"required,min=1"`
Username string `json:"username" validate:"required"`
Inbound string `json:"inbound" validate:"required"`
Type string `json:"type" validate:"required,oneof=hysteria hysteria2 mtproxy trojan tuic vless vmess"`
Type string `json:"type" validate:"required,oneof=anytls http hysteria hysteria2 mixed mtproxy naive socks trojan trusttunnel tuic vless vmess"`
UUID string `json:"uuid" validate:"omitempty,uuid4"`
Password string `json:"password" validate:"omitempty"`
Secret string `json:"secret" validate:"omitempty"`
@@ -140,7 +140,7 @@ type BandwidthLimiter struct {
Strategy string `json:"strategy" validate:"required"`
ConnectionType string `json:"connection_type" validate:"omitempty"`
Mode string `json:"mode" validate:"required"`
FlowKeys []string `json:"flow_keys" validate:"omitempty,dive,oneof=user destination ip hwid mux"`
FlowKeys []string `json:"flow_keys" validate:"omitempty,dive,oneof=user source_ip hwid mux protocol destination"`
Speed string `json:"speed" validate:"required"`
RawSpeed uint64 `json:"raw_speed" validate:"required"`
CreatedAt time.Time `json:"created_at" validate:"required"`
@@ -154,7 +154,7 @@ type BandwidthLimiterCreate struct {
Strategy string `json:"strategy" validate:"required,oneof=global connection bypass"`
ConnectionType string `json:"connection_type" validate:"excluded_if=Strategy bypass,omitempty"`
Mode string `json:"mode" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
FlowKeys []string `json:"flow_keys" validate:"excluded_if=Strategy bypass,omitempty,dive,oneof=user destination ip hwid mux"`
FlowKeys []string `json:"flow_keys" validate:"excluded_if=Strategy bypass,omitempty,dive,oneof=user source_ip hwid mux protocol destination"`
Speed string `json:"speed" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
}
@@ -164,7 +164,7 @@ type BandwidthLimiterUpdate struct {
Strategy string `json:"strategy" validate:"required,oneof=global connection bypass"`
ConnectionType string `json:"connection_type" validate:"excluded_if=Strategy bypass,omitempty"`
Mode string `json:"mode" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
FlowKeys []string `json:"flow_keys" validate:"excluded_if=Strategy bypass,omitempty,dive,oneof=user destination ip hwid mux"`
FlowKeys []string `json:"flow_keys" validate:"excluded_if=Strategy bypass,omitempty,dive,oneof=user source_ip hwid mux protocol destination"`
Speed string `json:"speed" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
}
@@ -174,7 +174,7 @@ type BaseBandwidthLimiter struct {
Strategy string `json:"strategy" validate:"required,oneof=global connection bypass"`
ConnectionType string `json:"connection_type" validate:"excluded_if=Strategy bypass,omitempty"`
Mode string `json:"mode" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
FlowKeys []string `json:"flow_keys" validate:"excluded_if=Strategy bypass,omitempty,dive,oneof=user destination ip hwid mux"`
FlowKeys []string `json:"flow_keys" validate:"excluded_if=Strategy bypass,omitempty,dive,oneof=user source_ip hwid mux protocol destination"`
Speed string `json:"speed" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
RawSpeed uint64 `json:"raw_speed" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
}
@@ -261,3 +261,5 @@ type BaseRateLimiter struct {
Count uint32 `json:"count" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
Interval string `json:"interval" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
}

View File

@@ -51,3 +51,5 @@ type Repository interface {
UpdateRateLimiter(id int, limiter RateLimiterUpdate) (RateLimiter, error)
DeleteRateLimiter(id int) (RateLimiter, error)
}

View File

@@ -2,10 +2,12 @@ package postgresql
import (
"encoding/json"
"slices"
"strconv"
"github.com/huandu/go-sqlbuilder"
"github.com/sagernet/sing-box/common/byteformats"
"github.com/sagernet/sing/common/byteformats"
E "github.com/sagernet/sing/common/exceptions"
)
type Filter func(sb *sqlbuilder.SelectBuilder, value []string) error
@@ -84,12 +86,13 @@ func SpeedLessEqualThanFilter(field string) Filter {
}
}
func ExistsAndWhereInFilter(subquery *sqlbuilder.SelectBuilder, field string) Filter {
func ExistsAndWhereInFilter(subqueryFactory func() *sqlbuilder.SelectBuilder, field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
values := make([]interface{}, len(value))
for i, v := range value {
values[i] = v
}
subquery := subqueryFactory()
subquery.Where(subquery.In(field, values...))
sb.Where(sb.Exists(subquery))
return nil
@@ -110,38 +113,54 @@ func InFilter(field string) Filter {
}
}
func SortAscFilter() Filter {
func SortAscFilter(columns []string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
sb.OrderByAsc(value[0])
return nil
}
}
func SortDescFilter() Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
sb.OrderByDesc(value[0])
return nil
}
}
func ReplacedSortAscFilter(replace map[string]string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
if replacedValue, ok := replace[value[0]]; ok {
sb.OrderByAsc(replacedValue)
} else {
sb.OrderByAsc(value[0])
column, err := isValidSortColumn(value[0], columns)
if err != nil {
return err
}
sb.OrderByAsc(column)
return nil
}
}
func ReplacedSortDescFilter(replace map[string]string) Filter {
func SortDescFilter(columns []string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
if replacedValue, ok := replace[value[0]]; ok {
sb.OrderByDesc(replacedValue)
} else {
sb.OrderByDesc(value[0])
column, err := isValidSortColumn(value[0], columns)
if err != nil {
return err
}
sb.OrderByDesc(column)
return nil
}
}
func ReplacedSortAscFilter(replace map[string]string, columns []string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
column, ok := replace[value[0]]
if !ok {
column = value[0]
}
column, err := isValidSortColumn(column, columns)
if err != nil {
return err
}
sb.OrderByAsc(column)
return nil
}
}
func ReplacedSortDescFilter(replace map[string]string, columns []string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
column, ok := replace[value[0]]
if !ok {
column = value[0]
}
column, err := isValidSortColumn(column, columns)
if err != nil {
return err
}
sb.OrderByDesc(column)
return nil
}
}
@@ -167,3 +186,10 @@ func OffsetFilter() Filter {
return nil
}
}
func isValidSortColumn(column string, columns []string) (string, error) {
if slices.Contains(columns, column) {
return column, nil
}
return "", E.New("invalid sort column \"", column, "\"")
}

View File

@@ -4,14 +4,15 @@ import (
"context"
"database/sql"
"encoding/json"
"errors"
"time"
"github.com/golang-migrate/migrate/v4"
"github.com/huandu/go-sqlbuilder"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/sagernet/sing-box/common/byteformats"
"github.com/sagernet/sing-box/service/manager/constant"
"github.com/sagernet/sing/common/byteformats"
)
var squadFilters, nodeFilters, userFilters, bandwidthLimiterFilters, connectionLimiterFilters, trafficLimiterFilters, rateLimiterFilters map[string]Filter
@@ -37,6 +38,13 @@ func NewPostgreSQLRepository(ctx context.Context, dsn string) (*PostgreSQLReposi
return &PostgreSQLRepository{db: pool, ctx: ctx}, nil
}
func notFoundErr(err error) error {
if errors.Is(err, pgx.ErrNoRows) {
return constant.ErrNotFound
}
return err
}
func (r *PostgreSQLRepository) CreateSquad(squad constant.SquadCreate) (constant.Squad, error) {
var s constant.Squad
now := time.Now()
@@ -138,7 +146,7 @@ func (r *PostgreSQLRepository) GetSquad(id int) (constant.Squad, error) {
&s.CreatedAt,
&s.UpdatedAt,
)
return s, err
return s, notFoundErr(err)
}
func (r *PostgreSQLRepository) UpdateSquad(id int, squad constant.SquadUpdate) (constant.Squad, error) {
@@ -450,10 +458,7 @@ func (r *PostgreSQLRepository) GetNode(uuid string) (constant.Node, error) {
&n.CreatedAt,
&n.UpdatedAt,
)
if err != nil && err.Error() == "no rows in result set" {
return n, constant.ErrNotFound
}
return n, err
return n, notFoundErr(err)
}
func (r *PostgreSQLRepository) UpdateNode(uuid string, node constant.NodeUpdate) (constant.Node, error) {
@@ -696,7 +701,7 @@ func (r *PostgreSQLRepository) GetUser(id int) (constant.User, error) {
&u.CreatedAt,
&u.UpdatedAt,
)
return u, err
return u, notFoundErr(err)
}
func (r *PostgreSQLRepository) UpdateUser(id int, user constant.UserUpdate) (constant.User, error) {
@@ -974,7 +979,7 @@ func (r *PostgreSQLRepository) GetConnectionLimiter(id int) (constant.Connection
&cl.CreatedAt,
&cl.UpdatedAt,
)
return cl, err
return cl, notFoundErr(err)
}
func (r *PostgreSQLRepository) UpdateConnectionLimiter(id int, limiter constant.ConnectionLimiterUpdate) (constant.ConnectionLimiter, error) {
@@ -1275,7 +1280,7 @@ func (r *PostgreSQLRepository) GetBandwidthLimiter(id int) (constant.BandwidthLi
&bl.UpdatedAt,
)
bl.FlowKeys = []string(flowKeys)
return bl, err
return bl, notFoundErr(err)
}
func (r *PostgreSQLRepository) UpdateBandwidthLimiter(id int, limiter constant.BandwidthLimiterUpdate) (constant.BandwidthLimiter, error) {
@@ -1594,7 +1599,7 @@ func (r *PostgreSQLRepository) GetTrafficLimiter(id int) (constant.TrafficLimite
&tl.CreatedAt,
&tl.UpdatedAt,
)
return tl, err
return tl, notFoundErr(err)
}
func (r *PostgreSQLRepository) UpdateTrafficLimiter(id int, limiter constant.TrafficLimiterUpdate) (constant.TrafficLimiter, error) {
@@ -1928,7 +1933,7 @@ func (r *PostgreSQLRepository) GetRateLimiter(id int) (constant.RateLimiter, err
&rl.CreatedAt,
&rl.UpdatedAt,
)
return rl, err
return rl, notFoundErr(err)
}
func (r *PostgreSQLRepository) UpdateRateLimiter(id int, limiter constant.RateLimiterUpdate) (constant.RateLimiter, error) {
@@ -2029,8 +2034,8 @@ func init() {
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": SortAscFilter(),
"sort_desc": SortDescFilter(),
"sort_asc": SortAscFilter([]string{"id", "name", "created_at", "updated_at"}),
"sort_desc": SortDescFilter([]string{"id", "name", "created_at", "updated_at"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}
@@ -2038,8 +2043,8 @@ func init() {
"uuid": EqualFilter("uuid"),
"pk": EqualFilter("uuid"),
"name": EqualFilter("name"),
"squad_id_in": ExistsAndWhereInFilter(
sqlbuilder.PostgreSQL.NewSelectBuilder().
"squad_id_in": ExistsAndWhereInFilter(func() *sqlbuilder.SelectBuilder {
return sqlbuilder.PostgreSQL.NewSelectBuilder().
Select(
"squad_id",
).
@@ -2048,23 +2053,22 @@ func init() {
).
From(
"node_to_squad",
),
"node_to_squad.squad_id",
),
)
}, "node_to_squad.squad_id"),
"created_at_start": GreaterThanFilter("created_at"),
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": SortAscFilter(),
"sort_desc": SortDescFilter(),
"sort_asc": SortAscFilter([]string{"uuid", "name", "created_at", "updated_at"}),
"sort_desc": SortDescFilter([]string{"uuid", "name", "created_at", "updated_at"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}
userFilters = map[string]Filter{
"id": EqualFilter("id"),
"pk": EqualFilter("id"),
"squad_id_in": ExistsAndWhereInFilter(
sqlbuilder.PostgreSQL.NewSelectBuilder().
"squad_id_in": ExistsAndWhereInFilter(func() *sqlbuilder.SelectBuilder {
return sqlbuilder.PostgreSQL.NewSelectBuilder().
Select(
"squad_id",
).
@@ -2073,26 +2077,24 @@ func init() {
).
From(
"user_to_squad",
),
"user_to_squad.squad_id",
),
)
}, "user_to_squad.squad_id"),
"username": EqualFilter("username"),
"inbound": EqualFilter("inbound"),
"type": EqualFilter("type"),
"created_at_start": GreaterThanFilter("created_at"),
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": SortAscFilter(),
"sort_desc": SortDescFilter(),
"sort_asc": SortAscFilter([]string{"id", "username", "inbound", "type", "created_at", "updated_at"}),
"sort_desc": SortDescFilter([]string{"id", "username", "inbound", "type", "created_at", "updated_at"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}
connectionLimiterFilters = map[string]Filter{
"id": EqualFilter("id"),
"pk": EqualFilter("id"),
"squad_id_in": ExistsAndWhereInFilter(
sqlbuilder.PostgreSQL.NewSelectBuilder().
"squad_id_in": ExistsAndWhereInFilter(func() *sqlbuilder.SelectBuilder {
return sqlbuilder.PostgreSQL.NewSelectBuilder().
Select(
"squad_id",
).
@@ -2101,9 +2103,8 @@ func init() {
).
From(
"connection_limiter_to_squad",
),
"connection_limiter_to_squad.squad_id",
),
)
}, "connection_limiter_to_squad.squad_id"),
"strategy": EqualFilter("strategy"),
"username": EqualFilter("username"),
"outbound": EqualFilter("outbound"),
@@ -2113,16 +2114,16 @@ func init() {
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": SortAscFilter(),
"sort_desc": SortDescFilter(),
"sort_asc": SortAscFilter([]string{"id", "username", "outbound", "strategy", "connection_type", "lock_type", "count", "created_at", "updated_at"}),
"sort_desc": SortDescFilter([]string{"id", "username", "outbound", "strategy", "connection_type", "lock_type", "count", "created_at", "updated_at"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}
bandwidthLimiterFilters = map[string]Filter{
"id": EqualFilter("id"),
"pk": EqualFilter("id"),
"squad_id_in": ExistsAndWhereInFilter(
sqlbuilder.PostgreSQL.NewSelectBuilder().
"squad_id_in": ExistsAndWhereInFilter(func() *sqlbuilder.SelectBuilder {
return sqlbuilder.PostgreSQL.NewSelectBuilder().
Select(
"squad_id",
).
@@ -2131,31 +2132,31 @@ func init() {
).
From(
"bandwidth_limiter_to_squad",
),
"bandwidth_limiter_to_squad.squad_id",
),
)
}, "bandwidth_limiter_to_squad.squad_id"),
"strategy": EqualFilter("strategy"),
"mode": EqualFilter("mode"),
"type": EqualFilter("type"),
"username": EqualFilter("username"),
"down_start": SpeedGreaterEqualThanFilter("raw_down"),
"down_end": SpeedLessEqualThanFilter("raw_down"),
"up_start": SpeedGreaterEqualThanFilter("raw_up"),
"up_end": SpeedLessEqualThanFilter("raw_up"),
"created_at_start": GreaterThanFilter("created_at"),
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": ReplacedSortAscFilter(map[string]string{"down": "raw_down", "up": "raw_up"}),
"sort_desc": ReplacedSortDescFilter(map[string]string{"down": "raw_down", "up": "raw_up"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
"sort_asc": ReplacedSortAscFilter(
map[string]string{"speed": "raw_speed"},
[]string{"id", "username", "outbound", "strategy", "mode", "raw_speed", "created_at", "updated_at"},
),
"sort_desc": ReplacedSortDescFilter(
map[string]string{"speed": "raw_speed"},
[]string{"id", "username", "outbound", "strategy", "mode", "raw_speed", "created_at", "updated_at"},
),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}
trafficLimiterFilters = map[string]Filter{
"id": EqualFilter("id"),
"pk": EqualFilter("id"),
"squad_id_in": ExistsAndWhereInFilter(
sqlbuilder.PostgreSQL.NewSelectBuilder().
"squad_id_in": ExistsAndWhereInFilter(func() *sqlbuilder.SelectBuilder {
return sqlbuilder.PostgreSQL.NewSelectBuilder().
Select(
"squad_id",
).
@@ -2164,31 +2165,36 @@ func init() {
).
From(
"traffic_limiter_to_squad",
),
"traffic_limiter_to_squad.squad_id",
),
)
}, "traffic_limiter_to_squad.squad_id"),
"username": EqualFilter("username"),
"outbound": EqualFilter("outbound"),
"strategy": EqualFilter("strategy"),
"mode": EqualFilter("mode"),
"used_start": SpeedGreaterEqualThanFilter("raw_used"),
"used_end": SpeedLessEqualThanFilter("raw_used"),
"used_start": SpeedGreaterEqualThanFilter("raw_used"),
"used_end": SpeedLessEqualThanFilter("raw_used"),
"quota_start": SpeedGreaterEqualThanFilter("raw_quota"),
"quota_end": SpeedLessEqualThanFilter("raw_quota"),
"created_at_start": GreaterThanFilter("created_at"),
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": ReplacedSortAscFilter(map[string]string{"used": "raw_used", "quota": "raw_quota"}),
"sort_desc": ReplacedSortDescFilter(map[string]string{"used": "raw_used", "quota": "raw_quota"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
"sort_asc": ReplacedSortAscFilter(
map[string]string{"used": "raw_used", "quota": "raw_quota"},
[]string{"id", "username", "outbound", "strategy", "mode", "raw_used", "raw_quota", "created_at", "updated_at"},
),
"sort_desc": ReplacedSortDescFilter(
map[string]string{"used": "raw_used", "quota": "raw_quota"},
[]string{"id", "username", "outbound", "strategy", "mode", "raw_used", "raw_quota", "created_at", "updated_at"},
),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}
rateLimiterFilters = map[string]Filter{
"id": EqualFilter("id"),
"pk": EqualFilter("id"),
"squad_id_in": ExistsAndWhereInFilter(
sqlbuilder.PostgreSQL.NewSelectBuilder().
"squad_id_in": ExistsAndWhereInFilter(func() *sqlbuilder.SelectBuilder {
return sqlbuilder.PostgreSQL.NewSelectBuilder().
Select(
"squad_id",
).
@@ -2197,9 +2203,8 @@ func init() {
).
From(
"rate_limiter_to_squad",
),
"rate_limiter_to_squad.squad_id",
),
)
}, "rate_limiter_to_squad.squad_id"),
"strategy": EqualFilter("strategy"),
"username": EqualFilter("username"),
"outbound": EqualFilter("outbound"),
@@ -2211,8 +2216,8 @@ func init() {
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": SortAscFilter(),
"sort_desc": SortDescFilter(),
"sort_asc": SortAscFilter([]string{"id", "username", "outbound", "strategy", "connection_type", "count", "interval", "created_at", "updated_at"}),
"sort_desc": SortDescFilter([]string{"id", "username", "outbound", "strategy", "connection_type", "count", "interval", "created_at", "updated_at"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}

View File

@@ -2,10 +2,12 @@ package sqlite
import (
"encoding/json"
"slices"
"strconv"
"github.com/huandu/go-sqlbuilder"
"github.com/sagernet/sing-box/common/byteformats"
"github.com/sagernet/sing/common/byteformats"
E "github.com/sagernet/sing/common/exceptions"
)
type Filter func(sb *sqlbuilder.SelectBuilder, value []string) error
@@ -84,12 +86,13 @@ func SpeedLessEqualThanFilter(field string) Filter {
}
}
func ExistsAndWhereInFilter(subquery *sqlbuilder.SelectBuilder, field string) Filter {
func ExistsAndWhereInFilter(subqueryFactory func() *sqlbuilder.SelectBuilder, field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
values := make([]interface{}, len(value))
for i, v := range value {
values[i] = v
}
subquery := subqueryFactory()
subquery.Where(subquery.In(field, values...))
sb.Where(sb.Exists(subquery))
return nil
@@ -110,38 +113,54 @@ func InFilter(field string) Filter {
}
}
func SortAscFilter() Filter {
func SortAscFilter(columns []string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
sb.OrderByAsc(value[0])
return nil
}
}
func SortDescFilter() Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
sb.OrderByDesc(value[0])
return nil
}
}
func ReplacedSortAscFilter(replace map[string]string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
if replacedValue, ok := replace[value[0]]; ok {
sb.OrderByAsc(replacedValue)
} else {
sb.OrderByAsc(value[0])
column, err := isValidSortColumn(value[0], columns)
if err != nil {
return err
}
sb.OrderByAsc(column)
return nil
}
}
func ReplacedSortDescFilter(replace map[string]string) Filter {
func SortDescFilter(columns []string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
if replacedValue, ok := replace[value[0]]; ok {
sb.OrderByDesc(replacedValue)
} else {
sb.OrderByDesc(value[0])
column, err := isValidSortColumn(value[0], columns)
if err != nil {
return err
}
sb.OrderByDesc(column)
return nil
}
}
func ReplacedSortAscFilter(replace map[string]string, columns []string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
column, ok := replace[value[0]]
if !ok {
column = value[0]
}
column, err := isValidSortColumn(column, columns)
if err != nil {
return err
}
sb.OrderByAsc(column)
return nil
}
}
func ReplacedSortDescFilter(replace map[string]string, columns []string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
column, ok := replace[value[0]]
if !ok {
column = value[0]
}
column, err := isValidSortColumn(column, columns)
if err != nil {
return err
}
sb.OrderByDesc(column)
return nil
}
}
@@ -167,3 +186,10 @@ func OffsetFilter() Filter {
return nil
}
}
func isValidSortColumn(column string, columns []string) (string, error) {
if slices.Contains(columns, column) {
return column, nil
}
return "", E.New("invalid sort column \"", column, "\"")
}

View File

@@ -9,8 +9,8 @@ import (
"github.com/golang-migrate/migrate/v4"
"github.com/huandu/go-sqlbuilder"
"github.com/sagernet/sing-box/common/byteformats"
"github.com/sagernet/sing-box/service/manager/constant"
"github.com/sagernet/sing/common/byteformats"
_ "modernc.org/sqlite"
)
@@ -33,6 +33,13 @@ func NewSQLiteRepository(ctx context.Context, dsn string) (*SQLiteRepository, er
return &SQLiteRepository{db: db, ctx: ctx}, nil
}
func notFoundErr(err error) error {
if errors.Is(err, sql.ErrNoRows) {
return constant.ErrNotFound
}
return err
}
func (r *SQLiteRepository) CreateSquad(squad constant.SquadCreate) (constant.Squad, error) {
var s constant.Squad
now := time.Now()
@@ -134,7 +141,7 @@ func (r *SQLiteRepository) GetSquad(id int) (constant.Squad, error) {
&s.CreatedAt,
&s.UpdatedAt,
)
return s, err
return s, notFoundErr(err)
}
func (r *SQLiteRepository) UpdateSquad(id int, squad constant.SquadUpdate) (constant.Squad, error) {
@@ -446,13 +453,9 @@ func (r *SQLiteRepository) GetNode(uuid string) (constant.Node, error) {
&n.CreatedAt,
&n.UpdatedAt,
)
if errors.Is(err, sql.ErrNoRows) {
return n, constant.ErrNotFound
}
n.SquadIDs = []int(squadIDs)
return n, err
return n, notFoundErr(err)
}
func (r *SQLiteRepository) UpdateNode(uuid string, node constant.NodeUpdate) (constant.Node, error) {
var n constant.Node
err := r.db.QueryRowContext(
@@ -693,7 +696,7 @@ func (r *SQLiteRepository) GetUser(id int) (constant.User, error) {
&u.UpdatedAt,
)
u.SquadIDs = []int(squadIDs)
return u, err
return u, notFoundErr(err)
}
func (r *SQLiteRepository) UpdateUser(id int, user constant.UserUpdate) (constant.User, error) {
@@ -975,7 +978,7 @@ func (r *SQLiteRepository) GetConnectionLimiter(id int) (constant.ConnectionLimi
&cl.UpdatedAt,
)
cl.SquadIDs = []int(squadIDs)
return cl, err
return cl, notFoundErr(err)
}
func (r *SQLiteRepository) UpdateConnectionLimiter(id int, limiter constant.ConnectionLimiterUpdate) (constant.ConnectionLimiter, error) {
@@ -1280,7 +1283,7 @@ func (r *SQLiteRepository) GetBandwidthLimiter(id int) (constant.BandwidthLimite
)
bl.SquadIDs = []int(squadIDs)
bl.FlowKeys = []string(flowKeys)
return bl, err
return bl, notFoundErr(err)
}
func (r *SQLiteRepository) UpdateBandwidthLimiter(id int, limiter constant.BandwidthLimiterUpdate) (constant.BandwidthLimiter, error) {
@@ -1603,7 +1606,7 @@ func (r *SQLiteRepository) GetTrafficLimiter(id int) (constant.TrafficLimiter, e
&tl.UpdatedAt,
)
tl.SquadIDs = []int(squadIDs)
return tl, err
return tl, notFoundErr(err)
}
func (r *SQLiteRepository) UpdateTrafficLimiter(id int, limiter constant.TrafficLimiterUpdate) (constant.TrafficLimiter, error) {
@@ -1943,7 +1946,7 @@ func (r *SQLiteRepository) GetRateLimiter(id int) (constant.RateLimiter, error)
&rl.UpdatedAt,
)
rl.SquadIDs = []int(squadIDs)
return rl, err
return rl, notFoundErr(err)
}
func (r *SQLiteRepository) UpdateRateLimiter(id int, limiter constant.RateLimiterUpdate) (constant.RateLimiter, error) {
@@ -2048,8 +2051,8 @@ func init() {
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": SortAscFilter(),
"sort_desc": SortDescFilter(),
"sort_asc": SortAscFilter([]string{"id", "name", "created_at", "updated_at"}),
"sort_desc": SortDescFilter([]string{"id", "name", "created_at", "updated_at"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}
@@ -2057,8 +2060,8 @@ func init() {
"uuid": EqualFilter("uuid"),
"pk": EqualFilter("uuid"),
"name": EqualFilter("name"),
"squad_id_in": ExistsAndWhereInFilter(
sqlbuilder.SQLite.NewSelectBuilder().
"squad_id_in": ExistsAndWhereInFilter(func() *sqlbuilder.SelectBuilder {
return sqlbuilder.SQLite.NewSelectBuilder().
Select(
"squad_id",
).
@@ -2067,23 +2070,22 @@ func init() {
).
From(
"node_to_squad",
),
"node_to_squad.squad_id",
),
)
}, "node_to_squad.squad_id"),
"created_at_start": GreaterThanFilter("created_at"),
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": SortAscFilter(),
"sort_desc": SortDescFilter(),
"sort_asc": SortAscFilter([]string{"uuid", "name", "created_at", "updated_at"}),
"sort_desc": SortDescFilter([]string{"uuid", "name", "created_at", "updated_at"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}
userFilters = map[string]Filter{
"id": EqualFilter("id"),
"pk": EqualFilter("id"),
"squad_id_in": ExistsAndWhereInFilter(
sqlbuilder.SQLite.NewSelectBuilder().
"squad_id_in": ExistsAndWhereInFilter(func() *sqlbuilder.SelectBuilder {
return sqlbuilder.SQLite.NewSelectBuilder().
Select(
"squad_id",
).
@@ -2092,26 +2094,24 @@ func init() {
).
From(
"user_to_squad",
),
"user_to_squad.squad_id",
),
)
}, "user_to_squad.squad_id"),
"username": EqualFilter("username"),
"inbound": EqualFilter("inbound"),
"type": EqualFilter("type"),
"created_at_start": GreaterThanFilter("created_at"),
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": SortAscFilter(),
"sort_desc": SortDescFilter(),
"sort_asc": SortAscFilter([]string{"id", "username", "inbound", "type", "created_at", "updated_at"}),
"sort_desc": SortDescFilter([]string{"id", "username", "inbound", "type", "created_at", "updated_at"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}
connectionLimiterFilters = map[string]Filter{
"id": EqualFilter("id"),
"pk": EqualFilter("id"),
"squad_id_in": ExistsAndWhereInFilter(
sqlbuilder.SQLite.NewSelectBuilder().
"squad_id_in": ExistsAndWhereInFilter(func() *sqlbuilder.SelectBuilder {
return sqlbuilder.SQLite.NewSelectBuilder().
Select(
"squad_id",
).
@@ -2120,9 +2120,8 @@ func init() {
).
From(
"connection_limiter_to_squad",
),
"connection_limiter_to_squad.squad_id",
),
)
}, "connection_limiter_to_squad.squad_id"),
"strategy": EqualFilter("strategy"),
"username": EqualFilter("username"),
"outbound": EqualFilter("outbound"),
@@ -2132,16 +2131,16 @@ func init() {
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": SortAscFilter(),
"sort_desc": SortDescFilter(),
"sort_asc": SortAscFilter([]string{"id", "username", "outbound", "strategy", "connection_type", "lock_type", "count", "created_at", "updated_at"}),
"sort_desc": SortDescFilter([]string{"id", "username", "outbound", "strategy", "connection_type", "lock_type", "count", "created_at", "updated_at"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}
bandwidthLimiterFilters = map[string]Filter{
"id": EqualFilter("id"),
"pk": EqualFilter("id"),
"squad_id_in": ExistsAndWhereInFilter(
sqlbuilder.SQLite.NewSelectBuilder().
"squad_id_in": ExistsAndWhereInFilter(func() *sqlbuilder.SelectBuilder {
return sqlbuilder.SQLite.NewSelectBuilder().
Select(
"squad_id",
).
@@ -2150,31 +2149,31 @@ func init() {
).
From(
"bandwidth_limiter_to_squad",
),
"bandwidth_limiter_to_squad.squad_id",
),
)
}, "bandwidth_limiter_to_squad.squad_id"),
"strategy": EqualFilter("strategy"),
"mode": EqualFilter("mode"),
"type": EqualFilter("type"),
"username": EqualFilter("username"),
"down_start": SpeedGreaterEqualThanFilter("raw_down"),
"down_end": SpeedLessEqualThanFilter("raw_down"),
"up_start": SpeedGreaterEqualThanFilter("raw_up"),
"up_end": SpeedLessEqualThanFilter("raw_up"),
"created_at_start": GreaterThanFilter("created_at"),
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": ReplacedSortAscFilter(map[string]string{"down": "raw_down", "up": "raw_up"}),
"sort_desc": ReplacedSortDescFilter(map[string]string{"down": "raw_down", "up": "raw_up"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
"sort_asc": ReplacedSortAscFilter(
map[string]string{"speed": "raw_speed"},
[]string{"id", "username", "outbound", "strategy", "mode", "raw_speed", "created_at", "updated_at"},
),
"sort_desc": ReplacedSortDescFilter(
map[string]string{"speed": "raw_speed"},
[]string{"id", "username", "outbound", "strategy", "mode", "raw_speed", "created_at", "updated_at"},
),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}
trafficLimiterFilters = map[string]Filter{
"id": EqualFilter("id"),
"pk": EqualFilter("id"),
"squad_id_in": ExistsAndWhereInFilter(
sqlbuilder.SQLite.NewSelectBuilder().
"squad_id_in": ExistsAndWhereInFilter(func() *sqlbuilder.SelectBuilder {
return sqlbuilder.SQLite.NewSelectBuilder().
Select(
"squad_id",
).
@@ -2183,31 +2182,36 @@ func init() {
).
From(
"traffic_limiter_to_squad",
),
"traffic_limiter_to_squad.squad_id",
),
)
}, "traffic_limiter_to_squad.squad_id"),
"username": EqualFilter("username"),
"outbound": EqualFilter("outbound"),
"strategy": EqualFilter("strategy"),
"mode": EqualFilter("mode"),
"used_start": SpeedGreaterEqualThanFilter("raw_used"),
"used_end": SpeedLessEqualThanFilter("raw_used"),
"used_start": SpeedGreaterEqualThanFilter("raw_used"),
"used_end": SpeedLessEqualThanFilter("raw_used"),
"quota_start": SpeedGreaterEqualThanFilter("raw_quota"),
"quota_end": SpeedLessEqualThanFilter("raw_quota"),
"created_at_start": GreaterThanFilter("created_at"),
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": ReplacedSortAscFilter(map[string]string{"used": "raw_used", "quota": "raw_quota"}),
"sort_desc": ReplacedSortDescFilter(map[string]string{"used": "raw_used", "quota": "raw_quota"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
"sort_asc": ReplacedSortAscFilter(
map[string]string{"used": "raw_used", "quota": "raw_quota"},
[]string{"id", "username", "outbound", "strategy", "mode", "raw_used", "raw_quota", "created_at", "updated_at"},
),
"sort_desc": ReplacedSortDescFilter(
map[string]string{"used": "raw_used", "quota": "raw_quota"},
[]string{"id", "username", "outbound", "strategy", "mode", "raw_used", "raw_quota", "created_at", "updated_at"},
),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}
rateLimiterFilters = map[string]Filter{
"id": EqualFilter("id"),
"pk": EqualFilter("id"),
"squad_id_in": ExistsAndWhereInFilter(
sqlbuilder.SQLite.NewSelectBuilder().
"squad_id_in": ExistsAndWhereInFilter(func() *sqlbuilder.SelectBuilder {
return sqlbuilder.SQLite.NewSelectBuilder().
Select(
"squad_id",
).
@@ -2216,9 +2220,8 @@ func init() {
).
From(
"rate_limiter_to_squad",
),
"rate_limiter_to_squad.squad_id",
),
)
}, "rate_limiter_to_squad.squad_id"),
"strategy": EqualFilter("strategy"),
"username": EqualFilter("username"),
"outbound": EqualFilter("outbound"),
@@ -2230,8 +2233,8 @@ func init() {
"created_at_end": LessThanFilter("created_at"),
"updated_at_start": GreaterThanFilter("updated_at"),
"updated_at_end": LessThanFilter("updated_at"),
"sort_asc": SortAscFilter(),
"sort_desc": SortDescFilter(),
"sort_asc": SortAscFilter([]string{"id", "username", "outbound", "strategy", "connection_type", "count", "interval", "created_at", "updated_at"}),
"sort_desc": SortDescFilter([]string{"id", "username", "outbound", "strategy", "connection_type", "count", "interval", "created_at", "updated_at"}),
"offset": OffsetFilter(),
"limit": LimitFilter(),
}

View File

@@ -20,6 +20,10 @@ import (
"github.com/sagernet/sing-box/service/manager/repository/sqlite"
E "github.com/sagernet/sing/common/exceptions"
"github.com/shtorm-7/go-cache/v2"
wpconstant "github.com/shtorm-7/workerpool/constant"
"github.com/shtorm-7/workerpool/pool"
"github.com/shtorm-7/workerpool/tools"
"github.com/shtorm-7/workerpool/worker"
)
func RegisterService(registry *boxService.Registry) {
@@ -28,16 +32,19 @@ func RegisterService(registry *boxService.Registry) {
type Service struct {
boxService.Adapter
ctx context.Context
logger log.ContextLogger
repository constant.Repository
nodes map[string]constant.ConnectedNode
ctx context.Context
logger log.ContextLogger
repository constant.Repository
nodes map[string]constant.ConnectedNode
limiterLocks map[int]map[string]*cache.Cache[string, struct{}]
trafficUsage map[int]*TrafficUsage
defaultValidator *validator.Validate
broadcastQueue wpconstant.Queue
broadcastPool wpconstant.Pool
mtx sync.RWMutex
connLockMtx sync.Mutex
trafficMtx sync.Mutex
@@ -106,6 +113,13 @@ func NewService(ctx context.Context, logger log.ContextLogger, tag string, optio
defaultValidator.RegisterStructValidation(func(sl validator.StructLevel) {
validateRateLimiterInterval(sl, sl.Current().Interface().(constant.RateLimiterUpdate).Interval)
}, constant.RateLimiterUpdate{})
broadcastQueue := make(wpconstant.Queue)
broadcastWorkers := make([]wpconstant.WorkerFactory, 16)
for i := range broadcastWorkers {
broadcastWorkers[i] = worker.NewWorkerFactory(broadcastQueue)
}
broadcastPool := pool.NewPool(broadcastWorkers)
broadcastPool.Start()
service := &Service{
Adapter: boxService.NewAdapter(C.TypeManager, tag),
ctx: ctx,
@@ -115,6 +129,8 @@ func NewService(ctx context.Context, logger log.ContextLogger, tag string, optio
limiterLocks: make(map[int]map[string]*cache.Cache[string, struct{}]),
trafficUsage: make(map[int]*TrafficUsage),
defaultValidator: defaultValidator,
broadcastQueue: broadcastQueue,
broadcastPool: broadcastPool,
}
limiters, err := service.repository.GetTrafficLimiters(map[string][]string{})
if err != nil {
@@ -320,11 +336,9 @@ func (s *Service) CreateUser(user constant.UserCreate) (constant.User, error) {
s.closeAllNodes()
return createdUser, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.UpdateUser(createdUser)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.UpdateUser(createdUser)
})
return createdUser, nil
}
@@ -343,6 +357,10 @@ func (s *Service) GetUser(id int) (constant.User, error) {
func (s *Service) UpdateUser(id int, user constant.UserUpdate) (constant.User, error) {
s.mtx.Lock()
defer s.mtx.Unlock()
err := s.defaultValidator.Struct(user)
if err != nil {
return constant.User{}, err
}
updatedUser, err := s.repository.UpdateUser(id, user)
if err != nil {
return updatedUser, err
@@ -354,11 +372,9 @@ func (s *Service) UpdateUser(id int, user constant.UserUpdate) (constant.User, e
s.closeAllNodes()
return updatedUser, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.UpdateUser(updatedUser)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.UpdateUser(updatedUser)
})
return updatedUser, nil
}
@@ -376,11 +392,9 @@ func (s *Service) DeleteUser(id int) (constant.User, error) {
s.closeAllNodes()
return deletedUser, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.DeleteUser(deletedUser)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.DeleteUser(deletedUser)
})
return deletedUser, nil
}
@@ -402,11 +416,9 @@ func (s *Service) CreateBandwidthLimiter(limiter constant.BandwidthLimiterCreate
s.closeAllNodes()
return createdLimiter, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.UpdateBandwidthLimiter(createdLimiter)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.UpdateBandwidthLimiter(createdLimiter)
})
return createdLimiter, nil
}
@@ -440,11 +452,9 @@ func (s *Service) UpdateBandwidthLimiter(id int, limiter constant.BandwidthLimit
s.closeAllNodes()
return updatedLimiter, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.UpdateBandwidthLimiter(updatedLimiter)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.UpdateBandwidthLimiter(updatedLimiter)
})
return updatedLimiter, nil
}
@@ -462,11 +472,9 @@ func (s *Service) DeleteBandwidthLimiter(id int) (constant.BandwidthLimiter, err
s.closeAllNodes()
return deletedLimiter, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.DeleteBandwidthLimiter(deletedLimiter)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.DeleteBandwidthLimiter(deletedLimiter)
})
return deletedLimiter, nil
}
@@ -494,11 +502,9 @@ func (s *Service) CreateTrafficLimiter(limiter constant.TrafficLimiterCreate) (c
s.closeAllNodes()
return createdLimiter, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.UpdateTrafficLimiter(createdLimiter)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.UpdateTrafficLimiter(createdLimiter)
})
return createdLimiter, nil
}
@@ -538,11 +544,9 @@ func (s *Service) UpdateTrafficLimiter(id int, limiter constant.TrafficLimiterUp
s.closeAllNodes()
return updatedLimiter, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.UpdateTrafficLimiter(updatedLimiter)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.UpdateTrafficLimiter(updatedLimiter)
})
return updatedLimiter, nil
}
@@ -566,11 +570,9 @@ func (s *Service) UpdateTrafficLimiterUsed(id int, used uint64) (constant.Traffi
s.closeAllNodes()
return updatedLimiter, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.UpdateTrafficLimiter(updatedLimiter)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.UpdateTrafficLimiter(updatedLimiter)
})
return updatedLimiter, nil
}
@@ -591,11 +593,9 @@ func (s *Service) DeleteTrafficLimiter(id int) (constant.TrafficLimiter, error)
s.closeAllNodes()
return deletedLimiter, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.DeleteTrafficLimiter(deletedLimiter)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.DeleteTrafficLimiter(deletedLimiter)
})
return deletedLimiter, nil
}
@@ -617,11 +617,9 @@ func (s *Service) CreateConnectionLimiter(limiter constant.ConnectionLimiterCrea
s.closeAllNodes()
return createdLimiter, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.UpdateConnectionLimiter(createdLimiter)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.UpdateConnectionLimiter(createdLimiter)
})
return createdLimiter, nil
}
@@ -655,11 +653,9 @@ func (s *Service) UpdateConnectionLimiter(id int, limiter constant.ConnectionLim
s.closeAllNodes()
return updatedLimiter, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.UpdateConnectionLimiter(updatedLimiter)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.UpdateConnectionLimiter(updatedLimiter)
})
if limiter.LockType != "manager" {
s.connLockMtx.Lock()
defer s.connLockMtx.Unlock()
@@ -682,11 +678,9 @@ func (s *Service) DeleteConnectionLimiter(id int) (constant.ConnectionLimiter, e
s.closeAllNodes()
return deletedLimiter, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.DeleteConnectionLimiter(deletedLimiter)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.DeleteConnectionLimiter(deletedLimiter)
})
if deletedLimiter.LockType == "manager" {
s.connLockMtx.Lock()
defer s.connLockMtx.Unlock()
@@ -713,11 +707,9 @@ func (s *Service) CreateRateLimiter(limiter constant.RateLimiterCreate) (constan
s.closeAllNodes()
return createdLimiter, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.UpdateRateLimiter(createdLimiter)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.UpdateRateLimiter(createdLimiter)
})
return createdLimiter, nil
}
@@ -751,11 +743,9 @@ func (s *Service) UpdateRateLimiter(id int, limiter constant.RateLimiterUpdate)
s.closeAllNodes()
return updatedLimiter, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.UpdateRateLimiter(updatedLimiter)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.UpdateRateLimiter(updatedLimiter)
})
return updatedLimiter, nil
}
@@ -773,11 +763,9 @@ func (s *Service) DeleteRateLimiter(id int) (constant.RateLimiter, error) {
s.closeAllNodes()
return deletedLimiter, err
}
for _, node := range nodes {
if node, ok := s.nodes[node.UUID]; ok {
node.DeleteRateLimiter(deletedLimiter)
}
}
s.dispatchToNodes(nodes, func(node constant.ConnectedNode) {
node.DeleteRateLimiter(deletedLimiter)
})
return deletedLimiter, nil
}
@@ -922,6 +910,7 @@ func (s *Service) Start(stage adapter.StartStage) error {
}
func (s *Service) Close() error {
s.broadcastPool.Stop()
return nil
}
@@ -936,6 +925,22 @@ func (s *Service) closeAllNodes() {
}
}
func (s *Service) dispatchToNodes(nodes []constant.Node, fn func(node constant.ConnectedNode)) {
awaits := make([]<-chan struct{}, 0, len(nodes))
for _, node := range nodes {
connectedNode, ok := s.nodes[node.UUID]
if !ok {
continue
}
awaits = append(awaits, tools.Await(s.broadcastQueue, func() {
fn(connectedNode)
}))
}
for _, await := range awaits {
<-await
}
}
func convertIntSliceToStringSlice(values []int) []string {
result := make([]string, len(values))
for i, v := range values {