mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-06-09 04:48:16 +03:00
Add new admin panel, failover, dns fallback, providers, limiters. Update XHTTP
This commit is contained in:
@@ -9,6 +9,14 @@ type Squad struct {
|
||||
UpdatedAt time.Time `json:"updated_at" validate:"required"`
|
||||
}
|
||||
|
||||
type DeletedSquad struct {
|
||||
Squad Squad
|
||||
OrphanedNodeUUIDs []string
|
||||
OrphanedConnectionLimiterIDs []int
|
||||
OrphanedTrafficLimiterIDs []int
|
||||
SurvivingNodeUUIDs []string
|
||||
}
|
||||
|
||||
type SquadCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
@@ -20,7 +28,7 @@ type SquadUpdate struct {
|
||||
type Node struct {
|
||||
UUID string `json:"uuid" validate:"required,uuid4"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required,min=1"`
|
||||
CreatedAt time.Time `json:"created_at" validate:"required"`
|
||||
UpdatedAt time.Time `json:"updated_at" validate:"required"`
|
||||
}
|
||||
@@ -28,7 +36,7 @@ type Node struct {
|
||||
type NodeCreate struct {
|
||||
UUID string `json:"uuid" validate:"required,uuid4"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type NodeUpdate struct {
|
||||
@@ -42,10 +50,10 @@ type BaseNode struct {
|
||||
|
||||
type User struct {
|
||||
ID int `json:"id" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required,min=1"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
Inbound string `json:"inbound" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
UUID string `json:"uuid" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Secret string `json:"secret" validate:"required"`
|
||||
@@ -56,10 +64,10 @@ type User struct {
|
||||
}
|
||||
|
||||
type UserCreate struct {
|
||||
SquadIDs []int `json:"squad_ids" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required,min=1"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=hysteria hysteria2 mtproxy trojan tuic vless vmess"`
|
||||
Inbound string `json:"inbound" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=hysteria hysteria2 mtproxy trojan tuic vless vmess"`
|
||||
UUID string `json:"uuid" validate:"omitempty,uuid4"`
|
||||
Password string `json:"password" validate:"omitempty"`
|
||||
Secret string `json:"secret" validate:"omitempty"`
|
||||
@@ -85,53 +93,54 @@ type BaseUser struct {
|
||||
|
||||
type ConnectionLimiter struct {
|
||||
ID int `json:"id" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required,min=1"`
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=connection"`
|
||||
ConnectionType string `json:"connection_type" validate:"omitempty,oneof=hwid mux ip"`
|
||||
LockType string `json:"lock_type" validate:"omitempty,oneof=manager"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=connection bypass"`
|
||||
ConnectionType string `json:"connection_type" validate:"omitempty,oneof=default hwid mux ip"`
|
||||
LockType string `json:"lock_type" validate:"required,oneof=manager default"`
|
||||
Count uint32 `json:"count" validate:"required"`
|
||||
CreatedAt time.Time `json:"created_at" validate:"required"`
|
||||
UpdatedAt time.Time `json:"updated_at" validate:"required"`
|
||||
}
|
||||
|
||||
type ConnectionLimiterCreate struct {
|
||||
SquadIDs []int `json:"squad_ids" validate:"required"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required,min=1"`
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=connection"`
|
||||
ConnectionType string `json:"type" validate:"omitempty,oneof=hwid mux ip"`
|
||||
LockType string `json:"lock_type" validate:"omitempty,oneof=manager"`
|
||||
Count uint32 `json:"count" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=connection bypass"`
|
||||
ConnectionType string `json:"connection_type" validate:"excluded_if=Strategy bypass,omitempty,oneof=default hwid mux ip"`
|
||||
LockType string `json:"lock_type" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass,omitempty,oneof=manager default"`
|
||||
Count uint32 `json:"count" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
|
||||
}
|
||||
|
||||
type ConnectionLimiterUpdate struct {
|
||||
Username string `json:"username" validate:"required"`
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=connection"`
|
||||
ConnectionType string `json:"type" validate:"omitempty,oneof=hwid mux ip"`
|
||||
LockType string `json:"lock_type" validate:"omitempty,oneof=manager"`
|
||||
Count uint32 `json:"count" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=connection bypass"`
|
||||
ConnectionType string `json:"connection_type" validate:"excluded_if=Strategy bypass,omitempty,oneof=default hwid mux ip"`
|
||||
LockType string `json:"lock_type" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass,omitempty,oneof=manager default"`
|
||||
Count uint32 `json:"count" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
|
||||
}
|
||||
|
||||
type BaseConnectionLimiter struct {
|
||||
Username string `json:"username" validate:"required"`
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=connection"`
|
||||
ConnectionType string `json:"type" validate:"omitempty,oneof=hwid mux ip"`
|
||||
LockType string `json:"lock_type" validate:"omitempty,oneof=manager"`
|
||||
Count uint32 `json:"count" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=connection bypass"`
|
||||
ConnectionType string `json:"connection_type" validate:"excluded_if=Strategy bypass,omitempty,oneof=default hwid mux ip"`
|
||||
LockType string `json:"lock_type" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass,omitempty,oneof=manager default"`
|
||||
Count uint32 `json:"count" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
|
||||
}
|
||||
|
||||
type BandwidthLimiter struct {
|
||||
ID int `json:"id" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required,min=1"`
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required"`
|
||||
Mode string `json:"mode" 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"`
|
||||
Speed string `json:"speed" validate:"required"`
|
||||
RawSpeed uint64 `json:"raw_speed" validate:"required"`
|
||||
CreatedAt time.Time `json:"created_at" validate:"required"`
|
||||
@@ -139,30 +148,116 @@ type BandwidthLimiter struct {
|
||||
}
|
||||
|
||||
type BandwidthLimiterCreate struct {
|
||||
SquadIDs []int `json:"squad_ids" validate:"required"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=global connection"`
|
||||
Mode string `json:"mode" validate:"required"`
|
||||
ConnectionType string `json:"connection_type" validate:"omitempty"`
|
||||
Speed string `json:"speed" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required,min=1"`
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
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"`
|
||||
Speed string `json:"speed" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
|
||||
}
|
||||
|
||||
type BandwidthLimiterUpdate struct {
|
||||
Username string `json:"username" validate:"required"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=global connection"`
|
||||
Mode string `json:"mode" validate:"required"`
|
||||
ConnectionType string `json:"connection_type" validate:"omitempty"`
|
||||
Speed string `json:"speed" validate:"required"`
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
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"`
|
||||
Speed string `json:"speed" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
|
||||
}
|
||||
|
||||
type BaseBandwidthLimiter struct {
|
||||
Username string `json:"username" validate:"required"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=global connection"`
|
||||
Mode string `json:"mode" validate:"required"`
|
||||
ConnectionType string `json:"connection_type" validate:"omitempty"`
|
||||
Speed string `json:"speed" validate:"required"`
|
||||
RawSpeed uint64 `json:"raw_speed" validate:"required"`
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type TrafficLimiter struct {
|
||||
ID int `json:"id" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required,min=1"`
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=global bypass"`
|
||||
Mode string `json:"mode" validate:"required"`
|
||||
RawUsed uint64 `json:"raw_used" validate:"required"`
|
||||
Quota string `json:"quota" validate:"required"`
|
||||
RawQuota uint64 `json:"raw_quota" validate:"required"`
|
||||
Usage uint8 `json:"usage"`
|
||||
CreatedAt time.Time `json:"created_at" validate:"required"`
|
||||
UpdatedAt time.Time `json:"updated_at" validate:"required"`
|
||||
}
|
||||
|
||||
type TrafficLimiterCreate struct {
|
||||
SquadIDs []int `json:"squad_ids" validate:"required,min=1"`
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=global bypass"`
|
||||
Mode string `json:"mode" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
|
||||
Quota string `json:"quota" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
|
||||
}
|
||||
|
||||
type TrafficLimiterUpdate struct {
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=global bypass"`
|
||||
Mode string `json:"mode" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
|
||||
Quota string `json:"quota" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
|
||||
}
|
||||
|
||||
type BaseTrafficLimiter struct {
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=global bypass"`
|
||||
Mode string `json:"mode" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
|
||||
RawUsed uint64 `json:"raw_used" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
|
||||
Quota string `json:"quota" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
|
||||
RawQuota uint64 `json:"raw_quota" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass"`
|
||||
}
|
||||
|
||||
type RateLimiter struct {
|
||||
ID int `json:"id" validate:"required"`
|
||||
SquadIDs []int `json:"squad_ids" validate:"required,min=1"`
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=fixed_window sliding_window token_bucket leaky_bucket bypass"`
|
||||
ConnectionType string `json:"connection_type" validate:"required,oneof=hwid mux ip default"`
|
||||
Count uint32 `json:"count" validate:"required"`
|
||||
Interval string `json:"interval" validate:"required"`
|
||||
CreatedAt time.Time `json:"created_at" validate:"required"`
|
||||
UpdatedAt time.Time `json:"updated_at" validate:"required"`
|
||||
}
|
||||
|
||||
type RateLimiterCreate struct {
|
||||
SquadIDs []int `json:"squad_ids" validate:"required,min=1"`
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=fixed_window sliding_window token_bucket leaky_bucket bypass"`
|
||||
ConnectionType string `json:"connection_type" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass,omitempty,oneof=hwid mux ip default"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type RateLimiterUpdate struct {
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=fixed_window sliding_window token_bucket leaky_bucket bypass"`
|
||||
ConnectionType string `json:"connection_type" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass,omitempty,oneof=hwid mux ip default"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type BaseRateLimiter struct {
|
||||
Username string `json:"username" validate:"omitempty"`
|
||||
Outbound string `json:"outbound" validate:"required"`
|
||||
Strategy string `json:"strategy" validate:"required,oneof=fixed_window sliding_window token_bucket leaky_bucket bypass"`
|
||||
ConnectionType string `json:"connection_type" validate:"excluded_if=Strategy bypass,required_unless=Strategy bypass,omitempty,oneof=hwid mux ip default"`
|
||||
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"`
|
||||
}
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
package constant
|
||||
|
||||
type NodeManager interface {
|
||||
AddNode(id string, node ConnectedNode) error
|
||||
AcquireLock(limiterId int, id string) (string, error)
|
||||
RefreshLock(limiterId int, id string, handleId string) error
|
||||
ReleaseLock(limiterId int, id string, handleId string) error
|
||||
}
|
||||
|
||||
type Manager interface {
|
||||
NodeManager
|
||||
|
||||
CreateSquad(user SquadCreate) (Squad, error)
|
||||
GetSquads(filters map[string][]string) ([]Squad, error)
|
||||
GetSquadsCount(filters map[string][]string) (int, error)
|
||||
@@ -21,7 +12,7 @@ type Manager interface {
|
||||
GetNodes(filters map[string][]string) ([]Node, error)
|
||||
GetNodesCount(filters map[string][]string) (int, error)
|
||||
GetNode(uuid string) (Node, error)
|
||||
GetNodeStatus(uuid string) string
|
||||
GetNodeStatus(uuid string) (string, error)
|
||||
UpdateNode(uuid string, node NodeUpdate) (Node, error)
|
||||
DeleteNode(uuid string) (Node, error)
|
||||
|
||||
@@ -39,10 +30,35 @@ type Manager interface {
|
||||
UpdateBandwidthLimiter(id int, limiter BandwidthLimiterUpdate) (BandwidthLimiter, error)
|
||||
DeleteBandwidthLimiter(id int) (BandwidthLimiter, error)
|
||||
|
||||
CreateTrafficLimiter(limiter TrafficLimiterCreate) (TrafficLimiter, error)
|
||||
GetTrafficLimiters(filters map[string][]string) ([]TrafficLimiter, error)
|
||||
GetTrafficLimitersCount(filters map[string][]string) (int, error)
|
||||
GetTrafficLimiter(id int) (TrafficLimiter, error)
|
||||
UpdateTrafficLimiter(id int, limiter TrafficLimiterUpdate) (TrafficLimiter, error)
|
||||
UpdateTrafficLimiterUsed(id int, used uint64) (TrafficLimiter, error)
|
||||
DeleteTrafficLimiter(id int) (TrafficLimiter, error)
|
||||
|
||||
CreateConnectionLimiter(limiter ConnectionLimiterCreate) (ConnectionLimiter, error)
|
||||
GetConnectionLimiters(filters map[string][]string) ([]ConnectionLimiter, error)
|
||||
GetConnectionLimitersCount(filters map[string][]string) (int, error)
|
||||
GetConnectionLimiter(id int) (ConnectionLimiter, error)
|
||||
UpdateConnectionLimiter(id int, limiter ConnectionLimiterUpdate) (ConnectionLimiter, error)
|
||||
DeleteConnectionLimiter(id int) (ConnectionLimiter, error)
|
||||
|
||||
CreateRateLimiter(limiter RateLimiterCreate) (RateLimiter, error)
|
||||
GetRateLimiters(filters map[string][]string) ([]RateLimiter, error)
|
||||
GetRateLimitersCount(filters map[string][]string) (int, error)
|
||||
GetRateLimiter(id int) (RateLimiter, error)
|
||||
UpdateRateLimiter(id int, limiter RateLimiterUpdate) (RateLimiter, error)
|
||||
DeleteRateLimiter(id int) (RateLimiter, error)
|
||||
}
|
||||
|
||||
type NodeManager interface {
|
||||
AddNode(id string, node ConnectedNode) error
|
||||
|
||||
AcquireLock(limiterId int, id string) (string, error)
|
||||
RefreshLock(limiterId int, id string, handleId string) error
|
||||
ReleaseLock(limiterId int, id string, handleId string) error
|
||||
|
||||
AddTrafficUsage(limiterId int, n uint64) (uint64, error)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,14 @@ type ConnectedNode interface {
|
||||
UpdateBandwidthLimiters(limiter []BandwidthLimiter)
|
||||
DeleteBandwidthLimiter(limiter BandwidthLimiter)
|
||||
|
||||
UpdateTrafficLimiter(limiter TrafficLimiter)
|
||||
UpdateTrafficLimiters(limiter []TrafficLimiter)
|
||||
DeleteTrafficLimiter(limiter TrafficLimiter)
|
||||
|
||||
UpdateRateLimiter(limiter RateLimiter)
|
||||
UpdateRateLimiters(limiter []RateLimiter)
|
||||
DeleteRateLimiter(limiter RateLimiter)
|
||||
|
||||
IsLocal() bool
|
||||
IsOnline() bool
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ type Repository interface {
|
||||
GetSquadsCount(filters map[string][]string) (int, error)
|
||||
GetSquad(id int) (Squad, error)
|
||||
UpdateSquad(id int, user SquadUpdate) (Squad, error)
|
||||
DeleteSquad(id int) (Squad, error)
|
||||
DeleteSquad(id int) (DeletedSquad, error)
|
||||
|
||||
CreateNode(node NodeCreate) (Node, error)
|
||||
GetNodes(filters map[string][]string) ([]Node, error)
|
||||
@@ -35,4 +35,19 @@ type Repository interface {
|
||||
GetBandwidthLimiter(id int) (BandwidthLimiter, error)
|
||||
UpdateBandwidthLimiter(id int, limiter BandwidthLimiterUpdate) (BandwidthLimiter, error)
|
||||
DeleteBandwidthLimiter(id int) (BandwidthLimiter, error)
|
||||
|
||||
CreateTrafficLimiter(limiter TrafficLimiterCreate) (TrafficLimiter, error)
|
||||
GetTrafficLimiters(filters map[string][]string) ([]TrafficLimiter, error)
|
||||
GetTrafficLimitersCount(filters map[string][]string) (int, error)
|
||||
GetTrafficLimiter(id int) (TrafficLimiter, error)
|
||||
UpdateTrafficLimiter(id int, limiter TrafficLimiterUpdate) (TrafficLimiter, error)
|
||||
UpdateTrafficLimiterUsed(id int, used uint64) (TrafficLimiter, error)
|
||||
DeleteTrafficLimiter(id int) (TrafficLimiter, error)
|
||||
|
||||
CreateRateLimiter(limiter RateLimiterCreate) (RateLimiter, error)
|
||||
GetRateLimiters(filters map[string][]string) ([]RateLimiter, error)
|
||||
GetRateLimitersCount(filters map[string][]string) (int, error)
|
||||
GetRateLimiter(id int) (RateLimiter, error)
|
||||
UpdateRateLimiter(id int, limiter RateLimiterUpdate) (RateLimiter, error)
|
||||
DeleteRateLimiter(id int) (RateLimiter, error)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
"github.com/sagernet/sing/common/byteformats"
|
||||
"github.com/sagernet/sing-box/common/byteformats"
|
||||
)
|
||||
|
||||
type Filter func(sb *sqlbuilder.SelectBuilder, value []string) error
|
||||
@@ -96,6 +96,20 @@ func ExistsAndWhereInFilter(subquery *sqlbuilder.SelectBuilder, field string) Fi
|
||||
}
|
||||
}
|
||||
|
||||
func InFilter(field string) Filter {
|
||||
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
|
||||
if len(value) == 0 {
|
||||
return nil
|
||||
}
|
||||
values := make([]interface{}, len(value))
|
||||
for i, v := range value {
|
||||
values[i] = v
|
||||
}
|
||||
sb.Where(sb.In(field, values...))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func SortAscFilter() Filter {
|
||||
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
|
||||
sb.OrderByAsc(value[0])
|
||||
|
||||
@@ -36,8 +36,8 @@ var migrations = map[string]string{
|
||||
id SERIAL PRIMARY KEY,
|
||||
node_uuid VARCHAR(36),
|
||||
username TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
inbound TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
uuid TEXT NOT NULL,
|
||||
password TEXT NOT NULL,
|
||||
secret TEXT NOT NULL,
|
||||
@@ -100,11 +100,236 @@ var migrations = map[string]string{
|
||||
);
|
||||
`,
|
||||
"1_initialize_schema.down.sql": `
|
||||
DROP TABLE IF EXISTS squas;
|
||||
DROP TABLE IF EXISTS nodes;
|
||||
DROP TABLE IF EXISTS users;
|
||||
DROP TABLE IF EXISTS bandwidth_limiter_to_squad;
|
||||
DROP TABLE IF EXISTS bandwidth_limiters;
|
||||
DROP TABLE IF EXISTS connection_limiter_to_squad;
|
||||
DROP TABLE IF EXISTS connection_limiters;
|
||||
DROP TABLE IF EXISTS user_to_squad;
|
||||
DROP TABLE IF EXISTS users;
|
||||
DROP TABLE IF EXISTS node_to_squad;
|
||||
DROP TABLE IF EXISTS nodes;
|
||||
DROP TABLE IF EXISTS squads;
|
||||
`,
|
||||
"2_init_limiters_and_indexes.up.sql": `
|
||||
CREATE TABLE traffic_limiters (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
outbound TEXT NOT NULL,
|
||||
strategy TEXT NOT NULL,
|
||||
mode TEXT NOT NULL,
|
||||
raw_used BIGINT NOT NULL DEFAULT 0,
|
||||
quota TEXT NOT NULL,
|
||||
raw_quota BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL,
|
||||
UNIQUE (username, outbound)
|
||||
);
|
||||
|
||||
CREATE TABLE traffic_limiter_to_squad (
|
||||
traffic_limiter_id INTEGER NOT NULL,
|
||||
squad_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (traffic_limiter_id, squad_id),
|
||||
FOREIGN KEY (traffic_limiter_id) REFERENCES traffic_limiters(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE rate_limiters (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
outbound TEXT NOT NULL,
|
||||
strategy TEXT NOT NULL,
|
||||
connection_type TEXT NOT NULL,
|
||||
count INTEGER NOT NULL,
|
||||
interval TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL,
|
||||
UNIQUE (username, outbound)
|
||||
);
|
||||
|
||||
CREATE TABLE rate_limiter_to_squad (
|
||||
rate_limiter_id INTEGER NOT NULL,
|
||||
squad_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (rate_limiter_id, squad_id),
|
||||
FOREIGN KEY (rate_limiter_id) REFERENCES rate_limiters(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE bandwidth_limiters ADD COLUMN flow_keys JSONB NOT NULL DEFAULT '[]'::jsonb;
|
||||
|
||||
UPDATE connection_limiters SET lock_type = 'default' WHERE lock_type = '';
|
||||
|
||||
ALTER TABLE node_to_squad
|
||||
DROP CONSTRAINT node_to_squad_squad_id_fkey,
|
||||
ADD CONSTRAINT node_to_squad_squad_id_fkey
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE user_to_squad
|
||||
DROP CONSTRAINT user_to_squad_squad_id_fkey,
|
||||
ADD CONSTRAINT user_to_squad_squad_id_fkey
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE connection_limiter_to_squad
|
||||
DROP CONSTRAINT connection_limiter_to_squad_squad_id_fkey,
|
||||
ADD CONSTRAINT connection_limiter_to_squad_squad_id_fkey
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE bandwidth_limiter_to_squad
|
||||
DROP CONSTRAINT bandwidth_limiter_to_squad_squad_id_fkey,
|
||||
ADD CONSTRAINT bandwidth_limiter_to_squad_squad_id_fkey
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE CASCADE;
|
||||
|
||||
CREATE INDEX idx_squads_created_at ON squads(created_at);
|
||||
CREATE INDEX idx_squads_updated_at ON squads(updated_at);
|
||||
|
||||
CREATE INDEX idx_nodes_created_at ON nodes(created_at);
|
||||
CREATE INDEX idx_nodes_updated_at ON nodes(updated_at);
|
||||
|
||||
CREATE INDEX idx_node_to_squad_squad_id ON node_to_squad(squad_id);
|
||||
|
||||
CREATE INDEX idx_users_node_uuid ON users(node_uuid);
|
||||
CREATE INDEX idx_users_inbound ON users(inbound);
|
||||
CREATE INDEX idx_users_type ON users(type);
|
||||
CREATE INDEX idx_users_created_at ON users(created_at);
|
||||
CREATE INDEX idx_users_updated_at ON users(updated_at);
|
||||
|
||||
CREATE INDEX idx_user_to_squad_squad_id ON user_to_squad(squad_id);
|
||||
|
||||
CREATE INDEX idx_connection_limiters_outbound ON connection_limiters(outbound);
|
||||
CREATE INDEX idx_connection_limiters_strategy ON connection_limiters(strategy);
|
||||
CREATE INDEX idx_connection_limiters_connection_type ON connection_limiters(connection_type);
|
||||
CREATE INDEX idx_connection_limiters_lock_type ON connection_limiters(lock_type);
|
||||
CREATE INDEX idx_connection_limiters_created_at ON connection_limiters(created_at);
|
||||
CREATE INDEX idx_connection_limiters_updated_at ON connection_limiters(updated_at);
|
||||
|
||||
CREATE INDEX idx_connection_limiter_to_squad_squad_id ON connection_limiter_to_squad(squad_id);
|
||||
|
||||
CREATE INDEX idx_bandwidth_limiters_outbound ON bandwidth_limiters(outbound);
|
||||
CREATE INDEX idx_bandwidth_limiters_strategy ON bandwidth_limiters(strategy);
|
||||
CREATE INDEX idx_bandwidth_limiters_mode ON bandwidth_limiters(mode);
|
||||
CREATE INDEX idx_bandwidth_limiters_connection_type ON bandwidth_limiters(connection_type);
|
||||
CREATE INDEX idx_bandwidth_limiters_raw_speed ON bandwidth_limiters(raw_speed);
|
||||
CREATE INDEX idx_bandwidth_limiters_created_at ON bandwidth_limiters(created_at);
|
||||
CREATE INDEX idx_bandwidth_limiters_updated_at ON bandwidth_limiters(updated_at);
|
||||
|
||||
CREATE INDEX idx_bandwidth_limiter_to_squad_squad_id ON bandwidth_limiter_to_squad(squad_id);
|
||||
|
||||
CREATE INDEX idx_traffic_limiters_outbound ON traffic_limiters(outbound);
|
||||
CREATE INDEX idx_traffic_limiters_strategy ON traffic_limiters(strategy);
|
||||
CREATE INDEX idx_traffic_limiters_mode ON traffic_limiters(mode);
|
||||
CREATE INDEX idx_traffic_limiters_raw_used ON traffic_limiters(raw_used);
|
||||
CREATE INDEX idx_traffic_limiters_raw_quota ON traffic_limiters(raw_quota);
|
||||
CREATE INDEX idx_traffic_limiters_created_at ON traffic_limiters(created_at);
|
||||
CREATE INDEX idx_traffic_limiters_updated_at ON traffic_limiters(updated_at);
|
||||
|
||||
CREATE INDEX idx_traffic_limiter_to_squad_squad_id ON traffic_limiter_to_squad(squad_id);
|
||||
|
||||
CREATE INDEX idx_rate_limiters_outbound ON rate_limiters(outbound);
|
||||
CREATE INDEX idx_rate_limiters_strategy ON rate_limiters(strategy);
|
||||
CREATE INDEX idx_rate_limiters_connection_type ON rate_limiters(connection_type);
|
||||
CREATE INDEX idx_rate_limiters_interval ON rate_limiters("interval");
|
||||
CREATE INDEX idx_rate_limiters_count ON rate_limiters(count);
|
||||
CREATE INDEX idx_rate_limiters_created_at ON rate_limiters(created_at);
|
||||
CREATE INDEX idx_rate_limiters_updated_at ON rate_limiters(updated_at);
|
||||
|
||||
CREATE INDEX idx_rate_limiter_to_squad_squad_id ON rate_limiter_to_squad(squad_id);
|
||||
|
||||
UPDATE connection_limiters SET connection_type = 'source_ip' WHERE connection_type = 'ip';
|
||||
UPDATE bandwidth_limiters SET connection_type = 'source_ip' WHERE connection_type = 'ip';
|
||||
UPDATE rate_limiters SET connection_type = 'source_ip' WHERE connection_type = 'ip';
|
||||
UPDATE bandwidth_limiters
|
||||
SET flow_keys = REPLACE(flow_keys::text, '"ip"', '"source_ip"')::jsonb
|
||||
WHERE flow_keys @> '["ip"]'::jsonb;
|
||||
|
||||
UPDATE bandwidth_limiters SET mode = 'bidirectional' WHERE mode = 'duplex';
|
||||
UPDATE traffic_limiters SET mode = 'bidirectional' WHERE mode = 'duplex';
|
||||
`,
|
||||
"2_init_limiters_and_indexes.down.sql": `
|
||||
UPDATE traffic_limiters SET mode = 'duplex' WHERE mode = 'bidirectional';
|
||||
UPDATE bandwidth_limiters SET mode = 'duplex' WHERE mode = 'bidirectional';
|
||||
|
||||
UPDATE bandwidth_limiters
|
||||
SET flow_keys = REPLACE(flow_keys::text, '"source_ip"', '"ip"')::jsonb
|
||||
WHERE flow_keys @> '["source_ip"]'::jsonb;
|
||||
UPDATE rate_limiters SET connection_type = 'ip' WHERE connection_type = 'source_ip';
|
||||
UPDATE bandwidth_limiters SET connection_type = 'ip' WHERE connection_type = 'source_ip';
|
||||
UPDATE connection_limiters SET connection_type = 'ip' WHERE connection_type = 'source_ip';
|
||||
|
||||
DROP INDEX IF EXISTS idx_rate_limiter_to_squad_squad_id;
|
||||
DROP INDEX IF EXISTS idx_rate_limiters_updated_at;
|
||||
DROP INDEX IF EXISTS idx_rate_limiters_created_at;
|
||||
DROP INDEX IF EXISTS idx_rate_limiters_count;
|
||||
DROP INDEX IF EXISTS idx_rate_limiters_interval;
|
||||
DROP INDEX IF EXISTS idx_rate_limiters_connection_type;
|
||||
DROP INDEX IF EXISTS idx_rate_limiters_strategy;
|
||||
DROP INDEX IF EXISTS idx_rate_limiters_outbound;
|
||||
|
||||
DROP INDEX IF EXISTS idx_traffic_limiter_to_squad_squad_id;
|
||||
DROP INDEX IF EXISTS idx_traffic_limiters_updated_at;
|
||||
DROP INDEX IF EXISTS idx_traffic_limiters_created_at;
|
||||
DROP INDEX IF EXISTS idx_traffic_limiters_raw_quota;
|
||||
DROP INDEX IF EXISTS idx_traffic_limiters_raw_used;
|
||||
DROP INDEX IF EXISTS idx_traffic_limiters_mode;
|
||||
DROP INDEX IF EXISTS idx_traffic_limiters_strategy;
|
||||
DROP INDEX IF EXISTS idx_traffic_limiters_outbound;
|
||||
|
||||
DROP INDEX IF EXISTS idx_bandwidth_limiter_to_squad_squad_id;
|
||||
DROP INDEX IF EXISTS idx_bandwidth_limiters_updated_at;
|
||||
DROP INDEX IF EXISTS idx_bandwidth_limiters_created_at;
|
||||
DROP INDEX IF EXISTS idx_bandwidth_limiters_raw_speed;
|
||||
DROP INDEX IF EXISTS idx_bandwidth_limiters_connection_type;
|
||||
DROP INDEX IF EXISTS idx_bandwidth_limiters_mode;
|
||||
DROP INDEX IF EXISTS idx_bandwidth_limiters_strategy;
|
||||
DROP INDEX IF EXISTS idx_bandwidth_limiters_outbound;
|
||||
|
||||
DROP INDEX IF EXISTS idx_connection_limiter_to_squad_squad_id;
|
||||
DROP INDEX IF EXISTS idx_connection_limiters_updated_at;
|
||||
DROP INDEX IF EXISTS idx_connection_limiters_created_at;
|
||||
DROP INDEX IF EXISTS idx_connection_limiters_lock_type;
|
||||
DROP INDEX IF EXISTS idx_connection_limiters_connection_type;
|
||||
DROP INDEX IF EXISTS idx_connection_limiters_strategy;
|
||||
DROP INDEX IF EXISTS idx_connection_limiters_outbound;
|
||||
|
||||
DROP INDEX IF EXISTS idx_user_to_squad_squad_id;
|
||||
DROP INDEX IF EXISTS idx_users_updated_at;
|
||||
DROP INDEX IF EXISTS idx_users_created_at;
|
||||
DROP INDEX IF EXISTS idx_users_type;
|
||||
DROP INDEX IF EXISTS idx_users_inbound;
|
||||
DROP INDEX IF EXISTS idx_users_node_uuid;
|
||||
|
||||
DROP INDEX IF EXISTS idx_node_to_squad_squad_id;
|
||||
|
||||
DROP INDEX IF EXISTS idx_nodes_updated_at;
|
||||
DROP INDEX IF EXISTS idx_nodes_created_at;
|
||||
|
||||
DROP INDEX IF EXISTS idx_squads_updated_at;
|
||||
DROP INDEX IF EXISTS idx_squads_created_at;
|
||||
|
||||
ALTER TABLE bandwidth_limiter_to_squad
|
||||
DROP CONSTRAINT bandwidth_limiter_to_squad_squad_id_fkey,
|
||||
ADD CONSTRAINT bandwidth_limiter_to_squad_squad_id_fkey
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE RESTRICT;
|
||||
|
||||
ALTER TABLE connection_limiter_to_squad
|
||||
DROP CONSTRAINT connection_limiter_to_squad_squad_id_fkey,
|
||||
ADD CONSTRAINT connection_limiter_to_squad_squad_id_fkey
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE RESTRICT;
|
||||
|
||||
ALTER TABLE user_to_squad
|
||||
DROP CONSTRAINT user_to_squad_squad_id_fkey,
|
||||
ADD CONSTRAINT user_to_squad_squad_id_fkey
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE RESTRICT;
|
||||
|
||||
ALTER TABLE node_to_squad
|
||||
DROP CONSTRAINT node_to_squad_squad_id_fkey,
|
||||
ADD CONSTRAINT node_to_squad_squad_id_fkey
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE RESTRICT;
|
||||
|
||||
UPDATE connection_limiters SET lock_type = '' WHERE lock_type = 'default';
|
||||
ALTER TABLE bandwidth_limiters DROP COLUMN flow_keys;
|
||||
DROP TABLE IF EXISTS rate_limiter_to_squad;
|
||||
DROP TABLE IF EXISTS rate_limiters;
|
||||
DROP TABLE IF EXISTS traffic_limiter_to_squad;
|
||||
DROP TABLE IF EXISTS traffic_limiters;
|
||||
`,
|
||||
}
|
||||
|
||||
@@ -113,12 +338,10 @@ func Migrate(db *sql.DB) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sourceDriver := source.NewRawDriver(migrations)
|
||||
if err := sourceDriver.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := migrate.NewWithInstance(
|
||||
"raw",
|
||||
sourceDriver,
|
||||
@@ -128,6 +351,5 @@ func Migrate(db *sql.DB) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.Up()
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
36
service/manager/repository/postgresql/utils.go
Normal file
36
service/manager/repository/postgresql/utils.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package postgresql
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type stringSliceJSON []string
|
||||
|
||||
func (s *stringSliceJSON) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*s = nil
|
||||
return nil
|
||||
}
|
||||
var data []byte
|
||||
switch v := src.(type) {
|
||||
case []byte:
|
||||
data = v
|
||||
case string:
|
||||
data = []byte(v)
|
||||
default:
|
||||
return fmt.Errorf("stringSliceJSON.Scan: unsupported type %T", src)
|
||||
}
|
||||
if len(data) == 0 {
|
||||
*s = nil
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(data, (*[]string)(s))
|
||||
}
|
||||
|
||||
func marshalStringSlice(values []string) ([]byte, error) {
|
||||
if values == nil {
|
||||
values = []string{}
|
||||
}
|
||||
return json.Marshal(values)
|
||||
}
|
||||
169
service/manager/repository/sqlite/filter.go
Normal file
169
service/manager/repository/sqlite/filter.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
"github.com/sagernet/sing-box/common/byteformats"
|
||||
)
|
||||
|
||||
type Filter func(sb *sqlbuilder.SelectBuilder, value []string) error
|
||||
|
||||
func EqualFilter(field string) Filter {
|
||||
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
|
||||
sb.Where(sb.Equal(field, value[0]))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func EqualOrNullFilter(field string) Filter {
|
||||
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
|
||||
sb.Where(sb.Or(sb.Equal(field, value[0]), sb.IsNull(field)))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func GreaterThanFilter(field string) Filter {
|
||||
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
|
||||
sb.Where(sb.GreaterThan(field, value[0]))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func LessThanFilter(field string) Filter {
|
||||
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
|
||||
sb.Where(sb.LessThan(field, value[0]))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func GreaterEqualThanFilter(field string) Filter {
|
||||
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
|
||||
sb.Where(sb.GreaterEqualThan(field, value[0]))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func LessEqualThanFilter(field string) Filter {
|
||||
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
|
||||
sb.Where(sb.LessEqualThan(field, value[0]))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func SpeedGreaterEqualThanFilter(field string) Filter {
|
||||
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
|
||||
bytesSpeed, err := json.Marshal(value[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
speed := &byteformats.NetworkBytesCompat{}
|
||||
err = speed.UnmarshalJSON(bytesSpeed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb.Where(sb.GreaterEqualThan(field, speed.Value()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func SpeedLessEqualThanFilter(field string) Filter {
|
||||
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
|
||||
bytesSpeed, err := json.Marshal(value[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
speed := &byteformats.NetworkBytesCompat{}
|
||||
err = speed.UnmarshalJSON(bytesSpeed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb.Where(sb.LessEqualThan(field, speed.Value()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ExistsAndWhereInFilter(subquery *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.Where(subquery.In(field, values...))
|
||||
sb.Where(sb.Exists(subquery))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func InFilter(field string) Filter {
|
||||
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
|
||||
if len(value) == 0 {
|
||||
return nil
|
||||
}
|
||||
values := make([]interface{}, len(value))
|
||||
for i, v := range value {
|
||||
values[i] = v
|
||||
}
|
||||
sb.Where(sb.In(field, values...))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func SortAscFilter() 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])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ReplacedSortDescFilter(replace map[string]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])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func LimitFilter() Filter {
|
||||
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
|
||||
limit, err := strconv.Atoi(value[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb.Limit(limit)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func OffsetFilter() Filter {
|
||||
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
|
||||
offset, err := strconv.Atoi(value[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb.Offset(offset)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
237
service/manager/repository/sqlite/migration.go
Normal file
237
service/manager/repository/sqlite/migration.go
Normal file
@@ -0,0 +1,237 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/golang-migrate/migrate/v4/database/sqlite"
|
||||
"github.com/sagernet/sing-box/common/migrate/source"
|
||||
)
|
||||
|
||||
var migrations = map[string]string{
|
||||
"1_initialize_schema.up.sql": `
|
||||
CREATE TABLE squads (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE nodes (
|
||||
uuid VARCHAR(36) PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE node_to_squad (
|
||||
node_uuid VARCHAR(36) NOT NULL,
|
||||
squad_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (node_uuid, squad_id),
|
||||
FOREIGN KEY (node_uuid) REFERENCES nodes(uuid) ON DELETE CASCADE,
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_uuid VARCHAR(36),
|
||||
username TEXT NOT NULL,
|
||||
inbound TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
uuid TEXT NOT NULL,
|
||||
password TEXT NOT NULL,
|
||||
secret TEXT NOT NULL,
|
||||
flow TEXT NOT NULL,
|
||||
alter_id INTEGER NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL,
|
||||
UNIQUE (username, inbound)
|
||||
);
|
||||
|
||||
CREATE TABLE user_to_squad (
|
||||
user_id INTEGER NOT NULL,
|
||||
squad_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (user_id, squad_id),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE connection_limiters (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL,
|
||||
outbound TEXT NOT NULL,
|
||||
strategy TEXT NOT NULL,
|
||||
connection_type TEXT NOT NULL,
|
||||
lock_type TEXT NOT NULL,
|
||||
count INTEGER NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL,
|
||||
UNIQUE (username, outbound)
|
||||
);
|
||||
|
||||
CREATE TABLE connection_limiter_to_squad (
|
||||
connection_limiter_id INTEGER NOT NULL,
|
||||
squad_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (connection_limiter_id, squad_id),
|
||||
FOREIGN KEY (connection_limiter_id) REFERENCES connection_limiters(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE bandwidth_limiters (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL,
|
||||
outbound TEXT NOT NULL,
|
||||
strategy TEXT NOT NULL,
|
||||
mode TEXT NOT NULL,
|
||||
connection_type TEXT NOT NULL,
|
||||
speed TEXT NOT NULL,
|
||||
raw_speed BIGINT NOT NULL DEFAULT 0,
|
||||
flow_keys TEXT NOT NULL DEFAULT '[]',
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL,
|
||||
UNIQUE (username, outbound)
|
||||
);
|
||||
|
||||
CREATE TABLE bandwidth_limiter_to_squad (
|
||||
bandwidth_limiter_id INTEGER NOT NULL,
|
||||
squad_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (bandwidth_limiter_id, squad_id),
|
||||
FOREIGN KEY (bandwidth_limiter_id) REFERENCES bandwidth_limiters(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE traffic_limiters (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL,
|
||||
outbound TEXT NOT NULL,
|
||||
strategy TEXT NOT NULL,
|
||||
mode TEXT NOT NULL,
|
||||
raw_used BIGINT NOT NULL DEFAULT 0,
|
||||
quota TEXT NOT NULL,
|
||||
raw_quota BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL,
|
||||
UNIQUE (username, outbound)
|
||||
);
|
||||
|
||||
CREATE TABLE traffic_limiter_to_squad (
|
||||
traffic_limiter_id INTEGER NOT NULL,
|
||||
squad_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (traffic_limiter_id, squad_id),
|
||||
FOREIGN KEY (traffic_limiter_id) REFERENCES traffic_limiters(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE rate_limiters (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL,
|
||||
outbound TEXT NOT NULL,
|
||||
strategy TEXT NOT NULL,
|
||||
connection_type TEXT NOT NULL,
|
||||
count INTEGER NOT NULL,
|
||||
interval TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL,
|
||||
UNIQUE (username, outbound)
|
||||
);
|
||||
|
||||
CREATE TABLE rate_limiter_to_squad (
|
||||
rate_limiter_id INTEGER NOT NULL,
|
||||
squad_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (rate_limiter_id, squad_id),
|
||||
FOREIGN KEY (rate_limiter_id) REFERENCES rate_limiters(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (squad_id) REFERENCES squads(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_squads_created_at ON squads(created_at);
|
||||
CREATE INDEX idx_squads_updated_at ON squads(updated_at);
|
||||
|
||||
CREATE INDEX idx_nodes_created_at ON nodes(created_at);
|
||||
CREATE INDEX idx_nodes_updated_at ON nodes(updated_at);
|
||||
|
||||
CREATE INDEX idx_node_to_squad_squad_id ON node_to_squad(squad_id);
|
||||
|
||||
CREATE INDEX idx_users_node_uuid ON users(node_uuid);
|
||||
CREATE INDEX idx_users_inbound ON users(inbound);
|
||||
CREATE INDEX idx_users_type ON users(type);
|
||||
CREATE INDEX idx_users_created_at ON users(created_at);
|
||||
CREATE INDEX idx_users_updated_at ON users(updated_at);
|
||||
|
||||
CREATE INDEX idx_user_to_squad_squad_id ON user_to_squad(squad_id);
|
||||
|
||||
CREATE INDEX idx_connection_limiters_outbound ON connection_limiters(outbound);
|
||||
CREATE INDEX idx_connection_limiters_strategy ON connection_limiters(strategy);
|
||||
CREATE INDEX idx_connection_limiters_connection_type ON connection_limiters(connection_type);
|
||||
CREATE INDEX idx_connection_limiters_lock_type ON connection_limiters(lock_type);
|
||||
CREATE INDEX idx_connection_limiters_created_at ON connection_limiters(created_at);
|
||||
CREATE INDEX idx_connection_limiters_updated_at ON connection_limiters(updated_at);
|
||||
|
||||
CREATE INDEX idx_connection_limiter_to_squad_squad_id ON connection_limiter_to_squad(squad_id);
|
||||
|
||||
CREATE INDEX idx_bandwidth_limiters_outbound ON bandwidth_limiters(outbound);
|
||||
CREATE INDEX idx_bandwidth_limiters_strategy ON bandwidth_limiters(strategy);
|
||||
CREATE INDEX idx_bandwidth_limiters_mode ON bandwidth_limiters(mode);
|
||||
CREATE INDEX idx_bandwidth_limiters_connection_type ON bandwidth_limiters(connection_type);
|
||||
CREATE INDEX idx_bandwidth_limiters_raw_speed ON bandwidth_limiters(raw_speed);
|
||||
CREATE INDEX idx_bandwidth_limiters_created_at ON bandwidth_limiters(created_at);
|
||||
CREATE INDEX idx_bandwidth_limiters_updated_at ON bandwidth_limiters(updated_at);
|
||||
|
||||
CREATE INDEX idx_bandwidth_limiter_to_squad_squad_id ON bandwidth_limiter_to_squad(squad_id);
|
||||
|
||||
CREATE INDEX idx_traffic_limiters_outbound ON traffic_limiters(outbound);
|
||||
CREATE INDEX idx_traffic_limiters_strategy ON traffic_limiters(strategy);
|
||||
CREATE INDEX idx_traffic_limiters_mode ON traffic_limiters(mode);
|
||||
CREATE INDEX idx_traffic_limiters_raw_used ON traffic_limiters(raw_used);
|
||||
CREATE INDEX idx_traffic_limiters_raw_quota ON traffic_limiters(raw_quota);
|
||||
CREATE INDEX idx_traffic_limiters_created_at ON traffic_limiters(created_at);
|
||||
CREATE INDEX idx_traffic_limiters_updated_at ON traffic_limiters(updated_at);
|
||||
|
||||
CREATE INDEX idx_traffic_limiter_to_squad_squad_id ON traffic_limiter_to_squad(squad_id);
|
||||
|
||||
CREATE INDEX idx_rate_limiters_outbound ON rate_limiters(outbound);
|
||||
CREATE INDEX idx_rate_limiters_strategy ON rate_limiters(strategy);
|
||||
CREATE INDEX idx_rate_limiters_connection_type ON rate_limiters(connection_type);
|
||||
CREATE INDEX idx_rate_limiters_interval ON rate_limiters("interval");
|
||||
CREATE INDEX idx_rate_limiters_count ON rate_limiters(count);
|
||||
CREATE INDEX idx_rate_limiters_created_at ON rate_limiters(created_at);
|
||||
CREATE INDEX idx_rate_limiters_updated_at ON rate_limiters(updated_at);
|
||||
|
||||
CREATE INDEX idx_rate_limiter_to_squad_squad_id ON rate_limiter_to_squad(squad_id);
|
||||
`,
|
||||
"1_initialize_schema.down.sql": `
|
||||
DROP TABLE IF EXISTS rate_limiter_to_squad;
|
||||
DROP TABLE IF EXISTS rate_limiters;
|
||||
DROP TABLE IF EXISTS traffic_limiter_to_squad;
|
||||
DROP TABLE IF EXISTS traffic_limiters;
|
||||
DROP TABLE IF EXISTS bandwidth_limiter_to_squad;
|
||||
DROP TABLE IF EXISTS bandwidth_limiters;
|
||||
DROP TABLE IF EXISTS connection_limiter_to_squad;
|
||||
DROP TABLE IF EXISTS connection_limiters;
|
||||
DROP TABLE IF EXISTS user_to_squad;
|
||||
DROP TABLE IF EXISTS users;
|
||||
DROP TABLE IF EXISTS node_to_squad;
|
||||
DROP TABLE IF EXISTS nodes;
|
||||
DROP TABLE IF EXISTS squads;
|
||||
`,
|
||||
}
|
||||
|
||||
func Migrate(db *sql.DB) error {
|
||||
driver, err := sqlite.WithInstance(db, &sqlite.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sourceDriver := source.NewRawDriver(migrations)
|
||||
if err := sourceDriver.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := migrate.NewWithInstance(
|
||||
"raw",
|
||||
sourceDriver,
|
||||
"sqlite",
|
||||
driver,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.Up()
|
||||
}
|
||||
2238
service/manager/repository/sqlite/repository.go
Normal file
2238
service/manager/repository/sqlite/repository.go
Normal file
File diff suppressed because it is too large
Load Diff
63
service/manager/repository/sqlite/utils.go
Normal file
63
service/manager/repository/sqlite/utils.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type intSliceJSON []int
|
||||
|
||||
func (s *intSliceJSON) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*s = nil
|
||||
return nil
|
||||
}
|
||||
var data []byte
|
||||
switch v := src.(type) {
|
||||
case []byte:
|
||||
data = v
|
||||
case string:
|
||||
data = []byte(v)
|
||||
default:
|
||||
return fmt.Errorf("intSliceJSON.Scan: unsupported type %T", src)
|
||||
}
|
||||
if len(data) == 0 {
|
||||
*s = nil
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(data, (*[]int)(s))
|
||||
}
|
||||
|
||||
type stringSliceJSON []string
|
||||
|
||||
func (s *stringSliceJSON) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*s = nil
|
||||
return nil
|
||||
}
|
||||
var data []byte
|
||||
switch v := src.(type) {
|
||||
case []byte:
|
||||
data = v
|
||||
case string:
|
||||
data = []byte(v)
|
||||
default:
|
||||
return fmt.Errorf("stringSliceJSON.Scan: unsupported type %T", src)
|
||||
}
|
||||
if len(data) == 0 {
|
||||
*s = nil
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(data, (*[]string)(s))
|
||||
}
|
||||
|
||||
func marshalStringSlice(values []string) (string, error) {
|
||||
if values == nil {
|
||||
values = []string{}
|
||||
}
|
||||
data, err := json.Marshal(values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/patrickmn/go-cache/v2"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
boxService "github.com/sagernet/sing-box/adapter/service"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
@@ -18,7 +17,9 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/service/manager/constant"
|
||||
"github.com/sagernet/sing-box/service/manager/repository/postgresql"
|
||||
"github.com/sagernet/sing-box/service/manager/repository/sqlite"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/shtorm-7/go-cache/v2"
|
||||
)
|
||||
|
||||
func RegisterService(registry *boxService.Registry) {
|
||||
@@ -33,12 +34,13 @@ type Service struct {
|
||||
nodes map[string]constant.ConnectedNode
|
||||
|
||||
limiterLocks map[int]map[string]*cache.Cache[string, struct{}]
|
||||
trafficUsage map[int]*TrafficUsage
|
||||
|
||||
userValidator *validator.Validate
|
||||
defaultValidator *validator.Validate
|
||||
|
||||
mtx sync.RWMutex
|
||||
connLockMtx sync.Mutex
|
||||
trafficMtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.ManagerServiceOptions) (adapter.Service, error) {
|
||||
@@ -50,11 +52,16 @@ func NewService(ctx context.Context, logger log.ContextLogger, tag string, optio
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "sqlite":
|
||||
repository, err = sqlite.NewSQLiteRepository(ctx, options.Database.DSN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, E.New("unknown driver \"", options.Database.Driver, "\"")
|
||||
}
|
||||
userValidator := validator.New()
|
||||
userValidator.RegisterStructValidation(func(sl validator.StructLevel) {
|
||||
defaultValidator := validator.New()
|
||||
defaultValidator.RegisterStructValidation(func(sl validator.StructLevel) {
|
||||
user := sl.Current().Interface().(constant.UserCreate)
|
||||
switch user.Type {
|
||||
case "vless":
|
||||
@@ -85,16 +92,41 @@ func NewService(ctx context.Context, logger log.ContextLogger, tag string, optio
|
||||
}
|
||||
}
|
||||
}, constant.UserCreate{})
|
||||
return &Service{
|
||||
validateRateLimiterInterval := func(sl validator.StructLevel, interval string) {
|
||||
if interval == "" {
|
||||
return
|
||||
}
|
||||
if _, err := time.ParseDuration(interval); err != nil {
|
||||
sl.ReportError(interval, "interval", "Interval", "duration", "")
|
||||
}
|
||||
}
|
||||
defaultValidator.RegisterStructValidation(func(sl validator.StructLevel) {
|
||||
validateRateLimiterInterval(sl, sl.Current().Interface().(constant.RateLimiterCreate).Interval)
|
||||
}, constant.RateLimiterCreate{})
|
||||
defaultValidator.RegisterStructValidation(func(sl validator.StructLevel) {
|
||||
validateRateLimiterInterval(sl, sl.Current().Interface().(constant.RateLimiterUpdate).Interval)
|
||||
}, constant.RateLimiterUpdate{})
|
||||
service := &Service{
|
||||
Adapter: boxService.NewAdapter(C.TypeManager, tag),
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
repository: repository,
|
||||
nodes: make(map[string]constant.ConnectedNode, 0),
|
||||
limiterLocks: make(map[int]map[string]*cache.Cache[string, struct{}]),
|
||||
userValidator: userValidator,
|
||||
defaultValidator: validator.New(),
|
||||
}, nil
|
||||
trafficUsage: make(map[int]*TrafficUsage),
|
||||
defaultValidator: defaultValidator,
|
||||
}
|
||||
limiters, err := service.repository.GetTrafficLimiters(map[string][]string{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, limiter := range limiters {
|
||||
service.trafficUsage[limiter.ID] = &TrafficUsage{
|
||||
used: limiter.RawUsed,
|
||||
quota: limiter.RawQuota,
|
||||
}
|
||||
}
|
||||
return service, nil
|
||||
}
|
||||
|
||||
func (s *Service) CreateSquad(node constant.SquadCreate) (constant.Squad, error) {
|
||||
@@ -139,12 +171,70 @@ func (s *Service) UpdateSquad(id int, squad constant.SquadUpdate) (constant.Squa
|
||||
|
||||
func (s *Service) DeleteSquad(id int) (constant.Squad, error) {
|
||||
s.mtx.Lock()
|
||||
s.trafficMtx.Lock()
|
||||
s.connLockMtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
deletedSquad, err := s.repository.DeleteSquad(id)
|
||||
defer s.trafficMtx.Unlock()
|
||||
defer s.connLockMtx.Unlock()
|
||||
deleted, err := s.repository.DeleteSquad(id)
|
||||
if err != nil {
|
||||
return deletedSquad, err
|
||||
return deleted.Squad, err
|
||||
}
|
||||
return deletedSquad, nil
|
||||
for _, uuid := range deleted.OrphanedNodeUUIDs {
|
||||
if connectedNode, ok := s.nodes[uuid]; ok {
|
||||
connectedNode.Close()
|
||||
delete(s.nodes, uuid)
|
||||
}
|
||||
}
|
||||
for _, lid := range deleted.OrphanedTrafficLimiterIDs {
|
||||
delete(s.trafficUsage, lid)
|
||||
}
|
||||
for _, lid := range deleted.OrphanedConnectionLimiterIDs {
|
||||
delete(s.limiterLocks, lid)
|
||||
}
|
||||
for _, uuid := range deleted.SurvivingNodeUUIDs {
|
||||
connectedNode, ok := s.nodes[uuid]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
node, err := s.repository.GetNode(uuid)
|
||||
if err != nil {
|
||||
s.closeAllNodes()
|
||||
return deleted.Squad, err
|
||||
}
|
||||
squadIDs := convertIntSliceToStringSlice(node.SquadIDs)
|
||||
users, err := s.repository.GetUsers(map[string][]string{"squad_id_in": squadIDs})
|
||||
if err != nil {
|
||||
s.closeAllNodes()
|
||||
return deleted.Squad, err
|
||||
}
|
||||
connectedNode.UpdateUsers(users)
|
||||
bandwidthLimiters, err := s.repository.GetBandwidthLimiters(map[string][]string{"squad_id_in": squadIDs})
|
||||
if err != nil {
|
||||
s.closeAllNodes()
|
||||
return deleted.Squad, err
|
||||
}
|
||||
connectedNode.UpdateBandwidthLimiters(bandwidthLimiters)
|
||||
trafficLimiters, err := s.repository.GetTrafficLimiters(map[string][]string{"squad_id_in": squadIDs})
|
||||
if err != nil {
|
||||
s.closeAllNodes()
|
||||
return deleted.Squad, err
|
||||
}
|
||||
connectedNode.UpdateTrafficLimiters(trafficLimiters)
|
||||
connectionLimiters, err := s.repository.GetConnectionLimiters(map[string][]string{"squad_id_in": squadIDs})
|
||||
if err != nil {
|
||||
s.closeAllNodes()
|
||||
return deleted.Squad, err
|
||||
}
|
||||
connectedNode.UpdateConnectionLimiters(connectionLimiters)
|
||||
rateLimiters, err := s.repository.GetRateLimiters(map[string][]string{"squad_id_in": squadIDs})
|
||||
if err != nil {
|
||||
s.closeAllNodes()
|
||||
return deleted.Squad, err
|
||||
}
|
||||
connectedNode.UpdateRateLimiters(rateLimiters)
|
||||
}
|
||||
return deleted.Squad, nil
|
||||
}
|
||||
|
||||
func (s *Service) CreateNode(node constant.NodeCreate) (constant.Node, error) {
|
||||
@@ -173,14 +263,14 @@ func (s *Service) GetNode(uuid string) (constant.Node, error) {
|
||||
return s.repository.GetNode(uuid)
|
||||
}
|
||||
|
||||
func (s *Service) GetNodeStatus(uuid string) string {
|
||||
func (s *Service) GetNodeStatus(uuid string) (string, error) {
|
||||
s.mtx.RLock()
|
||||
defer s.mtx.RUnlock()
|
||||
node, ok := s.nodes[uuid]
|
||||
if !ok || !node.IsOnline() {
|
||||
return "offline"
|
||||
return "offline", nil
|
||||
}
|
||||
return "online"
|
||||
return "online", nil
|
||||
}
|
||||
|
||||
func (s *Service) UpdateNode(uuid string, node constant.NodeUpdate) (constant.Node, error) {
|
||||
@@ -215,7 +305,7 @@ func (s *Service) DeleteNode(uuid string) (constant.Node, error) {
|
||||
func (s *Service) CreateUser(user constant.UserCreate) (constant.User, error) {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
err := s.userValidator.Struct(user)
|
||||
err := s.defaultValidator.Struct(user)
|
||||
if err != nil {
|
||||
return constant.User{}, err
|
||||
}
|
||||
@@ -294,6 +384,221 @@ func (s *Service) DeleteUser(id int) (constant.User, error) {
|
||||
return deletedUser, nil
|
||||
}
|
||||
|
||||
func (s *Service) CreateBandwidthLimiter(limiter constant.BandwidthLimiterCreate) (constant.BandwidthLimiter, error) {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
err := s.defaultValidator.Struct(limiter)
|
||||
if err != nil {
|
||||
return constant.BandwidthLimiter{}, err
|
||||
}
|
||||
createdLimiter, err := s.repository.CreateBandwidthLimiter(limiter)
|
||||
if err != nil {
|
||||
return createdLimiter, err
|
||||
}
|
||||
nodes, err := s.repository.GetNodes(map[string][]string{
|
||||
"squad_id_in": convertIntSliceToStringSlice(createdLimiter.SquadIDs),
|
||||
})
|
||||
if err != nil {
|
||||
s.closeAllNodes()
|
||||
return createdLimiter, err
|
||||
}
|
||||
for _, node := range nodes {
|
||||
if node, ok := s.nodes[node.UUID]; ok {
|
||||
node.UpdateBandwidthLimiter(createdLimiter)
|
||||
}
|
||||
}
|
||||
return createdLimiter, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetBandwidthLimiters(filters map[string][]string) ([]constant.BandwidthLimiter, error) {
|
||||
return s.repository.GetBandwidthLimiters(filters)
|
||||
}
|
||||
|
||||
func (s *Service) GetBandwidthLimitersCount(filters map[string][]string) (int, error) {
|
||||
return s.repository.GetBandwidthLimitersCount(filters)
|
||||
}
|
||||
|
||||
func (s *Service) GetBandwidthLimiter(id int) (constant.BandwidthLimiter, error) {
|
||||
return s.repository.GetBandwidthLimiter(id)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateBandwidthLimiter(id int, limiter constant.BandwidthLimiterUpdate) (constant.BandwidthLimiter, error) {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
err := s.defaultValidator.Struct(limiter)
|
||||
if err != nil {
|
||||
return constant.BandwidthLimiter{}, err
|
||||
}
|
||||
updatedLimiter, err := s.repository.UpdateBandwidthLimiter(id, limiter)
|
||||
if err != nil {
|
||||
return updatedLimiter, err
|
||||
}
|
||||
nodes, err := s.repository.GetNodes(map[string][]string{
|
||||
"squad_id_in": convertIntSliceToStringSlice(updatedLimiter.SquadIDs),
|
||||
})
|
||||
if err != nil {
|
||||
s.closeAllNodes()
|
||||
return updatedLimiter, err
|
||||
}
|
||||
for _, node := range nodes {
|
||||
if node, ok := s.nodes[node.UUID]; ok {
|
||||
node.UpdateBandwidthLimiter(updatedLimiter)
|
||||
}
|
||||
}
|
||||
return updatedLimiter, nil
|
||||
}
|
||||
|
||||
func (s *Service) DeleteBandwidthLimiter(id int) (constant.BandwidthLimiter, error) {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
deletedLimiter, err := s.repository.DeleteBandwidthLimiter(id)
|
||||
if err != nil {
|
||||
return deletedLimiter, err
|
||||
}
|
||||
nodes, err := s.repository.GetNodes(map[string][]string{
|
||||
"squad_id_in": convertIntSliceToStringSlice(deletedLimiter.SquadIDs),
|
||||
})
|
||||
if err != nil {
|
||||
s.closeAllNodes()
|
||||
return deletedLimiter, err
|
||||
}
|
||||
for _, node := range nodes {
|
||||
if node, ok := s.nodes[node.UUID]; ok {
|
||||
node.DeleteBandwidthLimiter(deletedLimiter)
|
||||
}
|
||||
}
|
||||
return deletedLimiter, nil
|
||||
}
|
||||
|
||||
func (s *Service) CreateTrafficLimiter(limiter constant.TrafficLimiterCreate) (constant.TrafficLimiter, error) {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
err := s.defaultValidator.Struct(limiter)
|
||||
if err != nil {
|
||||
return constant.TrafficLimiter{}, err
|
||||
}
|
||||
createdLimiter, err := s.repository.CreateTrafficLimiter(limiter)
|
||||
if err != nil {
|
||||
return createdLimiter, err
|
||||
}
|
||||
s.trafficMtx.Lock()
|
||||
s.trafficUsage[createdLimiter.ID] = &TrafficUsage{
|
||||
used: createdLimiter.RawUsed,
|
||||
quota: createdLimiter.RawQuota,
|
||||
}
|
||||
s.trafficMtx.Unlock()
|
||||
nodes, err := s.repository.GetNodes(map[string][]string{
|
||||
"squad_id_in": convertIntSliceToStringSlice(createdLimiter.SquadIDs),
|
||||
})
|
||||
if err != nil {
|
||||
s.closeAllNodes()
|
||||
return createdLimiter, err
|
||||
}
|
||||
for _, node := range nodes {
|
||||
if node, ok := s.nodes[node.UUID]; ok {
|
||||
node.UpdateTrafficLimiter(createdLimiter)
|
||||
}
|
||||
}
|
||||
return createdLimiter, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetTrafficLimiters(filters map[string][]string) ([]constant.TrafficLimiter, error) {
|
||||
return s.repository.GetTrafficLimiters(filters)
|
||||
}
|
||||
|
||||
func (s *Service) GetTrafficLimitersCount(filters map[string][]string) (int, error) {
|
||||
return s.repository.GetTrafficLimitersCount(filters)
|
||||
}
|
||||
|
||||
func (s *Service) GetTrafficLimiter(id int) (constant.TrafficLimiter, error) {
|
||||
return s.repository.GetTrafficLimiter(id)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateTrafficLimiter(id int, limiter constant.TrafficLimiterUpdate) (constant.TrafficLimiter, error) {
|
||||
s.mtx.Lock()
|
||||
s.trafficMtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
defer s.trafficMtx.Unlock()
|
||||
err := s.defaultValidator.Struct(limiter)
|
||||
if err != nil {
|
||||
return constant.TrafficLimiter{}, err
|
||||
}
|
||||
updatedLimiter, err := s.repository.UpdateTrafficLimiter(id, limiter)
|
||||
if err != nil {
|
||||
return updatedLimiter, err
|
||||
}
|
||||
s.trafficUsage[updatedLimiter.ID] = &TrafficUsage{
|
||||
used: updatedLimiter.RawUsed,
|
||||
quota: updatedLimiter.RawQuota,
|
||||
}
|
||||
nodes, err := s.repository.GetNodes(map[string][]string{
|
||||
"squad_id_in": convertIntSliceToStringSlice(updatedLimiter.SquadIDs),
|
||||
})
|
||||
if err != nil {
|
||||
s.closeAllNodes()
|
||||
return updatedLimiter, err
|
||||
}
|
||||
for _, node := range nodes {
|
||||
if node, ok := s.nodes[node.UUID]; ok {
|
||||
node.UpdateTrafficLimiter(updatedLimiter)
|
||||
}
|
||||
}
|
||||
return updatedLimiter, nil
|
||||
}
|
||||
|
||||
func (s *Service) UpdateTrafficLimiterUsed(id int, used uint64) (constant.TrafficLimiter, error) {
|
||||
s.mtx.Lock()
|
||||
s.trafficMtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
defer s.trafficMtx.Unlock()
|
||||
updatedLimiter, err := s.repository.UpdateTrafficLimiterUsed(id, used)
|
||||
if err != nil {
|
||||
return updatedLimiter, err
|
||||
}
|
||||
s.trafficUsage[updatedLimiter.ID] = &TrafficUsage{
|
||||
used: updatedLimiter.RawUsed,
|
||||
quota: updatedLimiter.RawQuota,
|
||||
}
|
||||
nodes, err := s.repository.GetNodes(map[string][]string{
|
||||
"squad_id_in": convertIntSliceToStringSlice(updatedLimiter.SquadIDs),
|
||||
})
|
||||
if err != nil {
|
||||
s.closeAllNodes()
|
||||
return updatedLimiter, err
|
||||
}
|
||||
for _, node := range nodes {
|
||||
if node, ok := s.nodes[node.UUID]; ok {
|
||||
node.UpdateTrafficLimiter(updatedLimiter)
|
||||
}
|
||||
}
|
||||
return updatedLimiter, nil
|
||||
}
|
||||
|
||||
func (s *Service) DeleteTrafficLimiter(id int) (constant.TrafficLimiter, error) {
|
||||
s.mtx.Lock()
|
||||
s.trafficMtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
defer s.trafficMtx.Unlock()
|
||||
deletedLimiter, err := s.repository.DeleteTrafficLimiter(id)
|
||||
if err != nil {
|
||||
return deletedLimiter, err
|
||||
}
|
||||
delete(s.trafficUsage, deletedLimiter.ID)
|
||||
nodes, err := s.repository.GetNodes(map[string][]string{
|
||||
"squad_id_in": convertIntSliceToStringSlice(deletedLimiter.SquadIDs),
|
||||
})
|
||||
if err != nil {
|
||||
s.closeAllNodes()
|
||||
return deletedLimiter, err
|
||||
}
|
||||
for _, node := range nodes {
|
||||
if node, ok := s.nodes[node.UUID]; ok {
|
||||
node.DeleteTrafficLimiter(deletedLimiter)
|
||||
}
|
||||
}
|
||||
return deletedLimiter, nil
|
||||
}
|
||||
|
||||
func (s *Service) CreateConnectionLimiter(limiter constant.ConnectionLimiterCreate) (constant.ConnectionLimiter, error) {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
@@ -390,14 +695,14 @@ func (s *Service) DeleteConnectionLimiter(id int) (constant.ConnectionLimiter, e
|
||||
return deletedLimiter, nil
|
||||
}
|
||||
|
||||
func (s *Service) CreateBandwidthLimiter(limiter constant.BandwidthLimiterCreate) (constant.BandwidthLimiter, error) {
|
||||
func (s *Service) CreateRateLimiter(limiter constant.RateLimiterCreate) (constant.RateLimiter, error) {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
err := s.defaultValidator.Struct(limiter)
|
||||
if err != nil {
|
||||
return constant.BandwidthLimiter{}, err
|
||||
return constant.RateLimiter{}, err
|
||||
}
|
||||
createdLimiter, err := s.repository.CreateBandwidthLimiter(limiter)
|
||||
createdLimiter, err := s.repository.CreateRateLimiter(limiter)
|
||||
if err != nil {
|
||||
return createdLimiter, err
|
||||
}
|
||||
@@ -410,32 +715,32 @@ func (s *Service) CreateBandwidthLimiter(limiter constant.BandwidthLimiterCreate
|
||||
}
|
||||
for _, node := range nodes {
|
||||
if node, ok := s.nodes[node.UUID]; ok {
|
||||
node.UpdateBandwidthLimiter(createdLimiter)
|
||||
node.UpdateRateLimiter(createdLimiter)
|
||||
}
|
||||
}
|
||||
return createdLimiter, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetBandwidthLimiters(filters map[string][]string) ([]constant.BandwidthLimiter, error) {
|
||||
return s.repository.GetBandwidthLimiters(filters)
|
||||
func (s *Service) GetRateLimiters(filters map[string][]string) ([]constant.RateLimiter, error) {
|
||||
return s.repository.GetRateLimiters(filters)
|
||||
}
|
||||
|
||||
func (s *Service) GetBandwidthLimitersCount(filters map[string][]string) (int, error) {
|
||||
return s.repository.GetBandwidthLimitersCount(filters)
|
||||
func (s *Service) GetRateLimitersCount(filters map[string][]string) (int, error) {
|
||||
return s.repository.GetRateLimitersCount(filters)
|
||||
}
|
||||
|
||||
func (s *Service) GetBandwidthLimiter(id int) (constant.BandwidthLimiter, error) {
|
||||
return s.repository.GetBandwidthLimiter(id)
|
||||
func (s *Service) GetRateLimiter(id int) (constant.RateLimiter, error) {
|
||||
return s.repository.GetRateLimiter(id)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateBandwidthLimiter(id int, limiter constant.BandwidthLimiterUpdate) (constant.BandwidthLimiter, error) {
|
||||
func (s *Service) UpdateRateLimiter(id int, limiter constant.RateLimiterUpdate) (constant.RateLimiter, error) {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
err := s.defaultValidator.Struct(limiter)
|
||||
if err != nil {
|
||||
return constant.BandwidthLimiter{}, err
|
||||
return constant.RateLimiter{}, err
|
||||
}
|
||||
updatedLimiter, err := s.repository.UpdateBandwidthLimiter(id, limiter)
|
||||
updatedLimiter, err := s.repository.UpdateRateLimiter(id, limiter)
|
||||
if err != nil {
|
||||
return updatedLimiter, err
|
||||
}
|
||||
@@ -448,16 +753,16 @@ func (s *Service) UpdateBandwidthLimiter(id int, limiter constant.BandwidthLimit
|
||||
}
|
||||
for _, node := range nodes {
|
||||
if node, ok := s.nodes[node.UUID]; ok {
|
||||
node.UpdateBandwidthLimiter(updatedLimiter)
|
||||
node.UpdateRateLimiter(updatedLimiter)
|
||||
}
|
||||
}
|
||||
return updatedLimiter, nil
|
||||
}
|
||||
|
||||
func (s *Service) DeleteBandwidthLimiter(id int) (constant.BandwidthLimiter, error) {
|
||||
func (s *Service) DeleteRateLimiter(id int) (constant.RateLimiter, error) {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
deletedLimiter, err := s.repository.DeleteBandwidthLimiter(id)
|
||||
deletedLimiter, err := s.repository.DeleteRateLimiter(id)
|
||||
if err != nil {
|
||||
return deletedLimiter, err
|
||||
}
|
||||
@@ -470,7 +775,7 @@ func (s *Service) DeleteBandwidthLimiter(id int) (constant.BandwidthLimiter, err
|
||||
}
|
||||
for _, node := range nodes {
|
||||
if node, ok := s.nodes[node.UUID]; ok {
|
||||
node.DeleteBandwidthLimiter(deletedLimiter)
|
||||
node.DeleteRateLimiter(deletedLimiter)
|
||||
}
|
||||
}
|
||||
return deletedLimiter, nil
|
||||
@@ -500,6 +805,13 @@ func (s *Service) AddNode(uuid string, node constant.ConnectedNode) error {
|
||||
return err
|
||||
}
|
||||
node.UpdateBandwidthLimiters(bandwidthLimiters)
|
||||
trafficLimiters, err := s.repository.GetTrafficLimiters(map[string][]string{
|
||||
"squad_id_in": squadIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
node.UpdateTrafficLimiters(trafficLimiters)
|
||||
connectionLimiters, err := s.repository.GetConnectionLimiters(map[string][]string{
|
||||
"squad_id_in": squadIDs,
|
||||
})
|
||||
@@ -507,6 +819,13 @@ func (s *Service) AddNode(uuid string, node constant.ConnectedNode) error {
|
||||
return err
|
||||
}
|
||||
node.UpdateConnectionLimiters(connectionLimiters)
|
||||
rateLimiters, err := s.repository.GetRateLimiters(map[string][]string{
|
||||
"squad_id_in": squadIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
node.UpdateRateLimiters(rateLimiters)
|
||||
s.nodes[uuid] = node
|
||||
return nil
|
||||
}
|
||||
@@ -579,6 +898,25 @@ func (s *Service) ReleaseLock(limiterId int, id string, handleId string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) AddTrafficUsage(limiterId int, n uint64) (uint64, error) {
|
||||
s.trafficMtx.Lock()
|
||||
defer s.trafficMtx.Unlock()
|
||||
trafficStat, ok := s.trafficUsage[limiterId]
|
||||
if !ok {
|
||||
return 0, E.New("limiter not found")
|
||||
}
|
||||
trafficStat.used = trafficStat.used + n
|
||||
if trafficStat.used > trafficStat.quota {
|
||||
trafficStat.used = trafficStat.quota
|
||||
}
|
||||
updatedLimiter, err := s.repository.UpdateTrafficLimiterUsed(limiterId, trafficStat.used)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
trafficStat.used = updatedLimiter.RawUsed
|
||||
return trafficStat.used, nil
|
||||
}
|
||||
|
||||
func (s *Service) Start(stage adapter.StartStage) error {
|
||||
return nil
|
||||
}
|
||||
@@ -587,6 +925,11 @@ func (s *Service) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type TrafficUsage struct {
|
||||
used uint64
|
||||
quota uint64
|
||||
}
|
||||
|
||||
func (s *Service) closeAllNodes() {
|
||||
for _, node := range s.nodes {
|
||||
node.Close()
|
||||
|
||||
Reference in New Issue
Block a user