mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-07-17 13:21:06 +03:00
Add OpenVPN, TrustTunnel, Sudoku, inbound managers. Fixes
This commit is contained in:
@@ -34,10 +34,16 @@ export interface NodeUpdate {
|
||||
export type NodeStatus = "online" | "offline";
|
||||
|
||||
export type UserType =
|
||||
| "anytls"
|
||||
| "http"
|
||||
| "hysteria"
|
||||
| "hysteria2"
|
||||
| "mixed"
|
||||
| "mtproxy"
|
||||
| "naive"
|
||||
| "socks"
|
||||
| "trojan"
|
||||
| "trusttunnel"
|
||||
| "tuic"
|
||||
| "vless"
|
||||
| "vmess";
|
||||
|
||||
@@ -44,10 +44,11 @@ const CONN_TYPES: { value: ConnectionType; label: string }[] = [
|
||||
];
|
||||
const FLOW_KEYS: { value: string; label: string }[] = [
|
||||
{ value: "user", label: "User" },
|
||||
{ value: "destination", label: "Destination" },
|
||||
{ value: "source_ip", label: "Source IP" },
|
||||
{ value: "hwid", label: "HWID" },
|
||||
{ value: "mux", label: "Mux" },
|
||||
{ value: "source_ip", label: "Source IP" },
|
||||
{ value: "protocol", label: "Protocol" },
|
||||
{ value: "destination", label: "Destination" },
|
||||
];
|
||||
|
||||
export function BandwidthLimitersPage() {
|
||||
|
||||
@@ -17,10 +17,16 @@ import {
|
||||
|
||||
// Display labels mirror service/admin_panel/tables/user.go.
|
||||
const USER_TYPES: { value: UserType; label: string }[] = [
|
||||
{ value: "anytls", label: "AnyTLS" },
|
||||
{ value: "http", label: "HTTP" },
|
||||
{ value: "hysteria", label: "Hysteria" },
|
||||
{ value: "hysteria2", label: "Hysteria2" },
|
||||
{ value: "mixed", label: "Mixed" },
|
||||
{ value: "mtproxy", label: "MTProxy" },
|
||||
{ value: "naive", label: "Naive" },
|
||||
{ value: "socks", label: "SOCKS" },
|
||||
{ value: "trojan", label: "Trojan" },
|
||||
{ value: "trusttunnel", label: "TrustTunnel" },
|
||||
{ value: "tuic", label: "TUIC" },
|
||||
{ value: "vless", label: "VLESS" },
|
||||
{ value: "vmess", label: "VMess" },
|
||||
@@ -38,7 +44,7 @@ const FLOW_OPTIONS: { value: string; label: string }[] = [
|
||||
// same rule up-front (required fields invisible for the current type
|
||||
// are filtered out before validateRequired runs).
|
||||
const SHOW_UUID = new Set<UserType>(["vless", "vmess", "tuic"]);
|
||||
const SHOW_PASSWORD = new Set<UserType>(["hysteria", "hysteria2", "trojan", "tuic"]);
|
||||
const SHOW_PASSWORD = new Set<UserType>(["anytls", "http", "hysteria", "hysteria2", "mixed", "naive", "socks", "trojan", "trusttunnel", "tuic"]);
|
||||
const SHOW_SECRET = new Set<UserType>(["mtproxy"]);
|
||||
const SHOW_FLOW = new Set<UserType>(["vless"]);
|
||||
const SHOW_ALTER_ID = new Set<UserType>(["vmess"]);
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -51,3 +51,5 @@ type Repository interface {
|
||||
UpdateRateLimiter(id int, limiter RateLimiterUpdate) (RateLimiter, error)
|
||||
DeleteRateLimiter(id int) (RateLimiter, error)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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, "\"")
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
@@ -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, "\"")
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
89
service/node/inbound/anytls.go
Normal file
89
service/node/inbound/anytls.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/protocol/anytls"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
"github.com/sagernet/sing-box/service/node/constant"
|
||||
)
|
||||
|
||||
type AnyTLSManager struct {
|
||||
inbounds map[string]*AnyTLSUserManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewAnyTLSManager() *AnyTLSManager {
|
||||
return &AnyTLSManager{
|
||||
inbounds: make(map[string]*AnyTLSUserManager),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AnyTLSManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
m.inbounds[inbound.Tag()] = &AnyTLSUserManager{
|
||||
inbound: inbound.(*anytls.Inbound),
|
||||
usersMap: make(map[string]option.AnyTLSUser),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AnyTLSManager) GetUserManager(tag string) (constant.UserManager, bool) {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
inbound, ok := m.inbounds[tag]
|
||||
return inbound, ok
|
||||
}
|
||||
|
||||
func (m *AnyTLSManager) GetUserManagerTags() []string {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.inbounds))
|
||||
for tag := range m.inbounds {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
type AnyTLSUserManager struct {
|
||||
inbound *anytls.Inbound
|
||||
usersMap map[string]option.AnyTLSUser
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func (i *AnyTLSUserManager) postUpdate() {
|
||||
users := make([]option.AnyTLSUser, 0, len(i.usersMap))
|
||||
for _, user := range i.usersMap {
|
||||
users = append(users, user)
|
||||
}
|
||||
i.inbound.UpdateUsers(users)
|
||||
}
|
||||
|
||||
func (i *AnyTLSUserManager) UpdateUser(user CM.User) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
i.usersMap[user.Username] = option.AnyTLSUser{Name: user.Username, Password: user.Password}
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *AnyTLSUserManager) UpdateUsers(users []CM.User) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
clear(i.usersMap)
|
||||
for _, user := range users {
|
||||
i.usersMap[user.Username] = option.AnyTLSUser{Name: user.Username, Password: user.Password}
|
||||
}
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *AnyTLSUserManager) DeleteUser(username string) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
delete(i.usersMap, username)
|
||||
i.postUpdate()
|
||||
}
|
||||
89
service/node/inbound/http.go
Normal file
89
service/node/inbound/http.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/protocol/http"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
"github.com/sagernet/sing-box/service/node/constant"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
)
|
||||
|
||||
type HTTPManager struct {
|
||||
inbounds map[string]*HTTPUserManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewHTTPManager() *HTTPManager {
|
||||
return &HTTPManager{
|
||||
inbounds: make(map[string]*HTTPUserManager),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *HTTPManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
m.inbounds[inbound.Tag()] = &HTTPUserManager{
|
||||
inbound: inbound.(*http.Inbound),
|
||||
usersMap: make(map[string]auth.User),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *HTTPManager) GetUserManager(tag string) (constant.UserManager, bool) {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
inbound, ok := m.inbounds[tag]
|
||||
return inbound, ok
|
||||
}
|
||||
|
||||
func (m *HTTPManager) GetUserManagerTags() []string {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.inbounds))
|
||||
for tag := range m.inbounds {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
type HTTPUserManager struct {
|
||||
inbound *http.Inbound
|
||||
usersMap map[string]auth.User
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func (i *HTTPUserManager) postUpdate() {
|
||||
users := make([]auth.User, 0, len(i.usersMap))
|
||||
for _, user := range i.usersMap {
|
||||
users = append(users, user)
|
||||
}
|
||||
i.inbound.UpdateUsers(users)
|
||||
}
|
||||
|
||||
func (i *HTTPUserManager) UpdateUser(user CM.User) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
i.usersMap[user.Username] = auth.User{Username: user.Username, Password: user.Password}
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *HTTPUserManager) UpdateUsers(users []CM.User) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
clear(i.usersMap)
|
||||
for _, user := range users {
|
||||
i.usersMap[user.Username] = auth.User{Username: user.Username, Password: user.Password}
|
||||
}
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *HTTPUserManager) DeleteUser(username string) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
delete(i.usersMap, username)
|
||||
i.postUpdate()
|
||||
}
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
)
|
||||
|
||||
type HysteriaManager struct {
|
||||
access sync.Mutex
|
||||
inbounds map[string]*HysteriaUserManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewHysteriaManager() *HysteriaManager {
|
||||
@@ -22,8 +23,8 @@ func NewHysteriaManager() *HysteriaManager {
|
||||
}
|
||||
|
||||
func (m *HysteriaManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
m.inbounds[inbound.Tag()] = &HysteriaUserManager{
|
||||
inbound: inbound.(*hysteria.Inbound),
|
||||
usersMap: make(map[string]option.HysteriaUser),
|
||||
@@ -32,15 +33,15 @@ func (m *HysteriaManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
}
|
||||
|
||||
func (m *HysteriaManager) GetUserManager(tag string) (constant.UserManager, bool) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
inbound, ok := m.inbounds[tag]
|
||||
return inbound, ok
|
||||
}
|
||||
|
||||
func (m *HysteriaManager) GetUserManagerTags() []string {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.inbounds))
|
||||
for tag := range m.inbounds {
|
||||
tags = append(tags, tag)
|
||||
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
)
|
||||
|
||||
type Hysteria2Manager struct {
|
||||
access sync.Mutex
|
||||
inbounds map[string]*Hysteria2UserManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewHysteria2Manager() *Hysteria2Manager {
|
||||
@@ -22,8 +23,8 @@ func NewHysteria2Manager() *Hysteria2Manager {
|
||||
}
|
||||
|
||||
func (m *Hysteria2Manager) AddUserManager(inbound adapter.Inbound) error {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
m.inbounds[inbound.Tag()] = &Hysteria2UserManager{
|
||||
inbound: inbound.(*hysteria2.Inbound),
|
||||
usersMap: make(map[string]option.Hysteria2User),
|
||||
@@ -32,15 +33,15 @@ func (m *Hysteria2Manager) AddUserManager(inbound adapter.Inbound) error {
|
||||
}
|
||||
|
||||
func (m *Hysteria2Manager) GetUserManager(tag string) (constant.UserManager, bool) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
inbound, ok := m.inbounds[tag]
|
||||
return inbound, ok
|
||||
}
|
||||
|
||||
func (m *Hysteria2Manager) GetUserManagerTags() []string {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.inbounds))
|
||||
for tag := range m.inbounds {
|
||||
tags = append(tags, tag)
|
||||
|
||||
89
service/node/inbound/mixed.go
Normal file
89
service/node/inbound/mixed.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/protocol/mixed"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
"github.com/sagernet/sing-box/service/node/constant"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
)
|
||||
|
||||
type MixedManager struct {
|
||||
inbounds map[string]*MixedUserManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewMixedManager() *MixedManager {
|
||||
return &MixedManager{
|
||||
inbounds: make(map[string]*MixedUserManager),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MixedManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
m.inbounds[inbound.Tag()] = &MixedUserManager{
|
||||
inbound: inbound.(*mixed.Inbound),
|
||||
usersMap: make(map[string]auth.User),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MixedManager) GetUserManager(tag string) (constant.UserManager, bool) {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
inbound, ok := m.inbounds[tag]
|
||||
return inbound, ok
|
||||
}
|
||||
|
||||
func (m *MixedManager) GetUserManagerTags() []string {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.inbounds))
|
||||
for tag := range m.inbounds {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
type MixedUserManager struct {
|
||||
inbound *mixed.Inbound
|
||||
usersMap map[string]auth.User
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func (i *MixedUserManager) postUpdate() {
|
||||
users := make([]auth.User, 0, len(i.usersMap))
|
||||
for _, user := range i.usersMap {
|
||||
users = append(users, user)
|
||||
}
|
||||
i.inbound.UpdateUsers(users)
|
||||
}
|
||||
|
||||
func (i *MixedUserManager) UpdateUser(user CM.User) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
i.usersMap[user.Username] = auth.User{Username: user.Username, Password: user.Password}
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *MixedUserManager) UpdateUsers(users []CM.User) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
clear(i.usersMap)
|
||||
for _, user := range users {
|
||||
i.usersMap[user.Username] = auth.User{Username: user.Username, Password: user.Password}
|
||||
}
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *MixedUserManager) DeleteUser(username string) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
delete(i.usersMap, username)
|
||||
i.postUpdate()
|
||||
}
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
)
|
||||
|
||||
type MTProxyManager struct {
|
||||
access sync.Mutex
|
||||
inbounds map[string]*MTProxyUserManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewMTProxyManager() *MTProxyManager {
|
||||
@@ -22,8 +23,8 @@ func NewMTProxyManager() *MTProxyManager {
|
||||
}
|
||||
|
||||
func (m *MTProxyManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
m.inbounds[inbound.Tag()] = &MTProxyUserManager{
|
||||
inbound: inbound.(*mtproxy.Inbound),
|
||||
usersMap: make(map[string]option.MTProxyUser),
|
||||
@@ -32,15 +33,15 @@ func (m *MTProxyManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
}
|
||||
|
||||
func (m *MTProxyManager) GetUserManager(tag string) (constant.UserManager, bool) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
inbound, ok := m.inbounds[tag]
|
||||
return inbound, ok
|
||||
}
|
||||
|
||||
func (m *MTProxyManager) GetUserManagerTags() []string {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.inbounds))
|
||||
for tag := range m.inbounds {
|
||||
tags = append(tags, tag)
|
||||
|
||||
89
service/node/inbound/naive.go
Normal file
89
service/node/inbound/naive.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/protocol/naive"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
"github.com/sagernet/sing-box/service/node/constant"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
)
|
||||
|
||||
type NaiveManager struct {
|
||||
inbounds map[string]*NaiveUserManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewNaiveManager() *NaiveManager {
|
||||
return &NaiveManager{
|
||||
inbounds: make(map[string]*NaiveUserManager),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *NaiveManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
m.inbounds[inbound.Tag()] = &NaiveUserManager{
|
||||
inbound: inbound.(*naive.Inbound),
|
||||
usersMap: make(map[string]auth.User),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *NaiveManager) GetUserManager(tag string) (constant.UserManager, bool) {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
inbound, ok := m.inbounds[tag]
|
||||
return inbound, ok
|
||||
}
|
||||
|
||||
func (m *NaiveManager) GetUserManagerTags() []string {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.inbounds))
|
||||
for tag := range m.inbounds {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
type NaiveUserManager struct {
|
||||
inbound *naive.Inbound
|
||||
usersMap map[string]auth.User
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func (i *NaiveUserManager) postUpdate() {
|
||||
users := make([]auth.User, 0, len(i.usersMap))
|
||||
for _, user := range i.usersMap {
|
||||
users = append(users, user)
|
||||
}
|
||||
i.inbound.UpdateUsers(users)
|
||||
}
|
||||
|
||||
func (i *NaiveUserManager) UpdateUser(user CM.User) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
i.usersMap[user.Username] = auth.User{Username: user.Username, Password: user.Password}
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *NaiveUserManager) UpdateUsers(users []CM.User) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
clear(i.usersMap)
|
||||
for _, user := range users {
|
||||
i.usersMap[user.Username] = auth.User{Username: user.Username, Password: user.Password}
|
||||
}
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *NaiveUserManager) DeleteUser(username string) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
delete(i.usersMap, username)
|
||||
i.postUpdate()
|
||||
}
|
||||
89
service/node/inbound/socks.go
Normal file
89
service/node/inbound/socks.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/protocol/socks"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
"github.com/sagernet/sing-box/service/node/constant"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
)
|
||||
|
||||
type SocksManager struct {
|
||||
inbounds map[string]*SocksUserManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewSocksManager() *SocksManager {
|
||||
return &SocksManager{
|
||||
inbounds: make(map[string]*SocksUserManager),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SocksManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
m.inbounds[inbound.Tag()] = &SocksUserManager{
|
||||
inbound: inbound.(*socks.Inbound),
|
||||
usersMap: make(map[string]auth.User),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SocksManager) GetUserManager(tag string) (constant.UserManager, bool) {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
inbound, ok := m.inbounds[tag]
|
||||
return inbound, ok
|
||||
}
|
||||
|
||||
func (m *SocksManager) GetUserManagerTags() []string {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.inbounds))
|
||||
for tag := range m.inbounds {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
type SocksUserManager struct {
|
||||
inbound *socks.Inbound
|
||||
usersMap map[string]auth.User
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func (i *SocksUserManager) postUpdate() {
|
||||
users := make([]auth.User, 0, len(i.usersMap))
|
||||
for _, user := range i.usersMap {
|
||||
users = append(users, user)
|
||||
}
|
||||
i.inbound.UpdateUsers(users)
|
||||
}
|
||||
|
||||
func (i *SocksUserManager) UpdateUser(user CM.User) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
i.usersMap[user.Username] = auth.User{Username: user.Username, Password: user.Password}
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *SocksUserManager) UpdateUsers(users []CM.User) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
clear(i.usersMap)
|
||||
for _, user := range users {
|
||||
i.usersMap[user.Username] = auth.User{Username: user.Username, Password: user.Password}
|
||||
}
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *SocksUserManager) DeleteUser(username string) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
delete(i.usersMap, username)
|
||||
i.postUpdate()
|
||||
}
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
)
|
||||
|
||||
type TrojanManager struct {
|
||||
access sync.Mutex
|
||||
inbounds map[string]*TrojanUserManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewTrojanManager() *TrojanManager {
|
||||
@@ -22,8 +23,8 @@ func NewTrojanManager() *TrojanManager {
|
||||
}
|
||||
|
||||
func (m *TrojanManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
m.inbounds[inbound.Tag()] = &TrojanUserManager{
|
||||
inbound: inbound.(*trojan.Inbound),
|
||||
usersMap: make(map[string]option.TrojanUser),
|
||||
@@ -32,15 +33,15 @@ func (m *TrojanManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
}
|
||||
|
||||
func (m *TrojanManager) GetUserManager(tag string) (constant.UserManager, bool) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
inbound, ok := m.inbounds[tag]
|
||||
return inbound, ok
|
||||
}
|
||||
|
||||
func (m *TrojanManager) GetUserManagerTags() []string {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.inbounds))
|
||||
for tag := range m.inbounds {
|
||||
tags = append(tags, tag)
|
||||
|
||||
89
service/node/inbound/trusttunnel.go
Normal file
89
service/node/inbound/trusttunnel.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/protocol/trusttunnel"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
"github.com/sagernet/sing-box/service/node/constant"
|
||||
)
|
||||
|
||||
type TrustTunnelManager struct {
|
||||
inbounds map[string]*TrustTunnelUserManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewTrustTunnelManager() *TrustTunnelManager {
|
||||
return &TrustTunnelManager{
|
||||
inbounds: make(map[string]*TrustTunnelUserManager),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TrustTunnelManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
m.inbounds[inbound.Tag()] = &TrustTunnelUserManager{
|
||||
inbound: inbound.(*trusttunnel.Inbound),
|
||||
usersMap: make(map[string]option.TrustTunnelUser),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *TrustTunnelManager) GetUserManager(tag string) (constant.UserManager, bool) {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
inbound, ok := m.inbounds[tag]
|
||||
return inbound, ok
|
||||
}
|
||||
|
||||
func (m *TrustTunnelManager) GetUserManagerTags() []string {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.inbounds))
|
||||
for tag := range m.inbounds {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
type TrustTunnelUserManager struct {
|
||||
inbound *trusttunnel.Inbound
|
||||
usersMap map[string]option.TrustTunnelUser
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func (i *TrustTunnelUserManager) postUpdate() {
|
||||
users := make([]option.TrustTunnelUser, 0, len(i.usersMap))
|
||||
for _, user := range i.usersMap {
|
||||
users = append(users, user)
|
||||
}
|
||||
i.inbound.UpdateUsers(users)
|
||||
}
|
||||
|
||||
func (i *TrustTunnelUserManager) UpdateUser(user CM.User) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
i.usersMap[user.Username] = option.TrustTunnelUser{Name: user.Username, Password: user.Password}
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *TrustTunnelUserManager) UpdateUsers(users []CM.User) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
clear(i.usersMap)
|
||||
for _, user := range users {
|
||||
i.usersMap[user.Username] = option.TrustTunnelUser{Name: user.Username, Password: user.Password}
|
||||
}
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func (i *TrustTunnelUserManager) DeleteUser(username string) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
delete(i.usersMap, username)
|
||||
i.postUpdate()
|
||||
}
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
)
|
||||
|
||||
type TUICManager struct {
|
||||
access sync.Mutex
|
||||
inbounds map[string]*TUICUserManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewTUICManager() *TUICManager {
|
||||
@@ -22,8 +23,8 @@ func NewTUICManager() *TUICManager {
|
||||
}
|
||||
|
||||
func (m *TUICManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
m.inbounds[inbound.Tag()] = &TUICUserManager{
|
||||
inbound: inbound.(*tuic.Inbound),
|
||||
usersMap: make(map[string]option.TUICUser),
|
||||
@@ -32,15 +33,15 @@ func (m *TUICManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
}
|
||||
|
||||
func (m *TUICManager) GetUserManager(tag string) (constant.UserManager, bool) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
inbound, ok := m.inbounds[tag]
|
||||
return inbound, ok
|
||||
}
|
||||
|
||||
func (m *TUICManager) GetUserManagerTags() []string {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.inbounds))
|
||||
for tag := range m.inbounds {
|
||||
tags = append(tags, tag)
|
||||
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
)
|
||||
|
||||
type VLESSManager struct {
|
||||
access sync.Mutex
|
||||
inbounds map[string]*VLESSUserManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewVLESSManager() *VLESSManager {
|
||||
@@ -22,8 +23,8 @@ func NewVLESSManager() *VLESSManager {
|
||||
}
|
||||
|
||||
func (m *VLESSManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
m.inbounds[inbound.Tag()] = &VLESSUserManager{
|
||||
inbound: inbound.(*vless.Inbound),
|
||||
usersMap: make(map[string]option.VLESSUser),
|
||||
@@ -32,15 +33,15 @@ func (m *VLESSManager) AddUserManager(inbound adapter.Inbound) error {
|
||||
}
|
||||
|
||||
func (m *VLESSManager) GetUserManager(tag string) (constant.UserManager, bool) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
inbound, ok := m.inbounds[tag]
|
||||
return inbound, ok
|
||||
}
|
||||
|
||||
func (m *VLESSManager) GetUserManagerTags() []string {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
tags := make([]string, 0, len(m.inbounds))
|
||||
for tag := range m.inbounds {
|
||||
tags = append(tags, tag)
|
||||
|
||||
@@ -12,7 +12,8 @@ import (
|
||||
|
||||
type VMessManager struct {
|
||||
inbounds map[string]*VMessUserManager
|
||||
mtx sync.Mutex
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewVMessManager() *VMessManager {
|
||||
|
||||
@@ -2,6 +2,7 @@ package limiter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -50,6 +51,7 @@ func (m *BandwidthLimiterManager) AddBandwidthLimiterStrategyManager(outbound ad
|
||||
manager: m,
|
||||
strategy: strategy,
|
||||
strategiesMap: make(map[string]bandwidth.BandwidthStrategy),
|
||||
limitersMap: make(map[string]CM.BandwidthLimiter),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -75,6 +77,7 @@ type BandwidthLimiterStrategyManager struct {
|
||||
manager *BandwidthLimiterManager
|
||||
strategy ManagedBandwidthStrategy
|
||||
strategiesMap map[string]bandwidth.BandwidthStrategy
|
||||
limitersMap map[string]CM.BandwidthLimiter
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
@@ -86,12 +89,25 @@ func (i *BandwidthLimiterStrategyManager) postUpdate() {
|
||||
func (i *BandwidthLimiterStrategyManager) UpdateBandwidthLimiter(limiter CM.BandwidthLimiter) {
|
||||
i.mtx.Lock()
|
||||
defer i.mtx.Unlock()
|
||||
if existing, ok := i.strategiesMap[limiter.Username]; ok {
|
||||
oldLimiter := i.limitersMap[limiter.Username]
|
||||
if isSameStrategy(oldLimiter, limiter) {
|
||||
if oldLimiter.RawSpeed != limiter.RawSpeed {
|
||||
if s, ok := existing.(bandwidth.SpeedUpdater); ok {
|
||||
s.SetSpeed(limiter.RawSpeed)
|
||||
}
|
||||
}
|
||||
i.limitersMap[limiter.Username] = limiter
|
||||
return
|
||||
}
|
||||
}
|
||||
strategy, err := bandwidth.CreateStrategy(limiter.Strategy, limiter.Mode, limiter.ConnectionType, limiter.RawSpeed, limiter.FlowKeys)
|
||||
if err != nil {
|
||||
i.manager.logger.ErrorContext(i.manager.ctx, err)
|
||||
return
|
||||
}
|
||||
i.strategiesMap[limiter.Username] = strategy
|
||||
i.limitersMap[limiter.Username] = limiter
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
@@ -118,3 +134,10 @@ func (i *BandwidthLimiterStrategyManager) DeleteBandwidthLimiter(username string
|
||||
delete(i.strategiesMap, username)
|
||||
i.postUpdate()
|
||||
}
|
||||
|
||||
func isSameStrategy(a, b CM.BandwidthLimiter) bool {
|
||||
return a.Strategy == b.Strategy &&
|
||||
a.Mode == b.Mode &&
|
||||
a.ConnectionType == b.ConnectionType &&
|
||||
slices.Equal(a.FlowKeys, b.FlowKeys)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/onclose"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/protocol/limiter/connection"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
@@ -45,7 +46,7 @@ func (m *ConnectionLimiterManager) AddConnectionLimiterStrategyManager(outbound
|
||||
}
|
||||
strategy, ok := limiter.GetStrategy().(ManagedConnectionStrategy)
|
||||
if !ok {
|
||||
return E.New("strategy ", strategy, " is not manager")
|
||||
return E.New("strategy for outbound ", outbound.Tag(), " is not manager")
|
||||
}
|
||||
m.managers[outbound.Tag()] = &ConnectionLimiterStrategyManager{
|
||||
manager: m,
|
||||
@@ -152,7 +153,7 @@ type ManagerLock struct {
|
||||
func (i *ConnectionLimiterStrategyManager) newManagerLock(limiterId int) connection.LockIDGetter {
|
||||
conns := make(map[string]*ManagerLock)
|
||||
mtx := sync.Mutex{}
|
||||
return func(id string) (connection.CloseHandlerFunc, context.Context, error) {
|
||||
return func(id string) (onclose.CloseHandlerFunc, context.Context, error) {
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
conn, ok := conns[id]
|
||||
@@ -171,6 +172,7 @@ func (i *ConnectionLimiterStrategyManager) newManagerLock(limiterId int) connect
|
||||
case <-time.After(time.Second * 5):
|
||||
err := nodeManager.RefreshLock(limiterId, id, handleId)
|
||||
if err != nil {
|
||||
i.manager.logger.ErrorContext(ctx, "failed to refresh lock: ", err)
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
@@ -185,18 +187,17 @@ func (i *ConnectionLimiterStrategyManager) newManagerLock(limiterId int) connect
|
||||
conns[id] = conn
|
||||
}
|
||||
conn.handles++
|
||||
var once sync.Once
|
||||
return func() {
|
||||
once.Do(func() {
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
conn.handles--
|
||||
if conn.handles == 0 {
|
||||
conn.cancel()
|
||||
i.manager.nodeManager.ReleaseLock(limiterId, id, conn.handleId)
|
||||
delete(conns, id)
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
conn.handles--
|
||||
if conn.handles == 0 {
|
||||
conn.cancel()
|
||||
if err := i.manager.nodeManager.ReleaseLock(limiterId, id, conn.handleId); err != nil {
|
||||
i.manager.logger.ErrorContext(i.manager.ctx, "failed to release lock: ", err)
|
||||
}
|
||||
})
|
||||
delete(conns, id)
|
||||
}
|
||||
}, conn.ctx, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,5 +212,5 @@ func (l *TrafficLimiter) UpdateRemainingTraffic() error {
|
||||
} else {
|
||||
l.new += new
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ func RegisterService(registry *boxService.Registry) {
|
||||
type Service struct {
|
||||
boxService.Adapter
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
logger log.ContextLogger
|
||||
inboundManagers map[string]constant.InboundManager
|
||||
bandwidthManager constant.BandwidthLimiterManager
|
||||
@@ -32,13 +33,17 @@ type Service struct {
|
||||
rateManager constant.RateLimiterManager
|
||||
options option.NodeServiceOptions
|
||||
|
||||
nodeManager CM.NodeManager
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.NodeServiceOptions) (adapter.Service, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return &Service{
|
||||
Adapter: boxService.NewAdapter(C.TypeManager, tag),
|
||||
Adapter: boxService.NewAdapter(C.TypeNode, tag),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
logger: logger,
|
||||
options: options,
|
||||
}, nil
|
||||
@@ -57,16 +62,23 @@ func (s *Service) Start(stage adapter.StartStage) error {
|
||||
if !ok {
|
||||
return E.New("invalid ", s.options.Manager, " manager")
|
||||
}
|
||||
s.nodeManager = nodeManager
|
||||
inboundManager := service.FromContext[adapter.InboundManager](s.ctx)
|
||||
outboundManager := service.FromContext[adapter.OutboundManager](s.ctx)
|
||||
s.inboundManagers = map[string]constant.InboundManager{
|
||||
"hysteria": inbound.NewHysteriaManager(),
|
||||
"hysteria2": inbound.NewHysteria2Manager(),
|
||||
"mtproxy": inbound.NewMTProxyManager(),
|
||||
"trojan": inbound.NewTrojanManager(),
|
||||
"tuic": inbound.NewTUICManager(),
|
||||
"vless": inbound.NewVLESSManager(),
|
||||
"vmess": inbound.NewVMessManager(),
|
||||
"anytls": inbound.NewAnyTLSManager(),
|
||||
"http": inbound.NewHTTPManager(),
|
||||
"hysteria": inbound.NewHysteriaManager(),
|
||||
"hysteria2": inbound.NewHysteria2Manager(),
|
||||
"mixed": inbound.NewMixedManager(),
|
||||
"mtproxy": inbound.NewMTProxyManager(),
|
||||
"naive": inbound.NewNaiveManager(),
|
||||
"socks": inbound.NewSocksManager(),
|
||||
"trojan": inbound.NewTrojanManager(),
|
||||
"trusttunnel": inbound.NewTrustTunnelManager(),
|
||||
"tuic": inbound.NewTUICManager(),
|
||||
"vless": inbound.NewVLESSManager(),
|
||||
"vmess": inbound.NewVMessManager(),
|
||||
}
|
||||
s.connectionManager = limiter.NewConnectionLimiterManager(s.ctx, nodeManager, s.logger)
|
||||
s.bandwidthManager = limiter.NewBandwidthLimiterManager(s.ctx, nodeManager, s.logger)
|
||||
@@ -320,5 +332,6 @@ func (s *Service) IsOnline() bool {
|
||||
}
|
||||
|
||||
func (s *Service) Close() error {
|
||||
s.cancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.31.1
|
||||
// protoc v7.34.1
|
||||
// source: service/node_manager_api/manager/manager.proto
|
||||
|
||||
package manager
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc v6.31.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc v7.34.1
|
||||
// source: service/node_manager_api/manager/manager.proto
|
||||
|
||||
package manager
|
||||
|
||||
@@ -229,7 +229,7 @@ func (s *RemoteNode) send(data *pb.NodeData) {
|
||||
}
|
||||
|
||||
func (s *RemoteNode) close(err error) {
|
||||
if err != nil {
|
||||
if err == nil || s.err != nil {
|
||||
return
|
||||
}
|
||||
s.err = err
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
boxService "github.com/sagernet/sing-box/adapter/service"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"golang.org/x/net/http2"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
@@ -134,9 +136,21 @@ func (s *APIServer) Start(stage adapter.StartStage) error {
|
||||
}
|
||||
tcpListener = aTLS.NewListener(tcpListener, s.tlsConfig)
|
||||
}
|
||||
keepAliveTime := time.Duration(s.options.KeepAlive)
|
||||
if keepAliveTime <= 0 {
|
||||
keepAliveTime = 10 * time.Second
|
||||
}
|
||||
keepAliveTimeout := time.Duration(s.options.KeepAliveTimeout)
|
||||
if keepAliveTimeout <= 0 {
|
||||
keepAliveTimeout = 5 * time.Second
|
||||
}
|
||||
s.grpcServer = grpc.NewServer(
|
||||
grpc.ChainUnaryInterceptor(s.unaryAuthInterceptor),
|
||||
grpc.StreamInterceptor(s.streamAuthInterceptor),
|
||||
grpc.KeepaliveParams(keepalive.ServerParameters{
|
||||
Time: keepAliveTime,
|
||||
Timeout: keepAliveTimeout,
|
||||
}),
|
||||
)
|
||||
pb.RegisterManagerServer(s.grpcServer, s)
|
||||
go func() {
|
||||
|
||||
Reference in New Issue
Block a user