mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-05-28 23:15:59 +03:00
Add new admin panel, failover, dns fallback, providers, limiters. Update XHTTP
This commit is contained in:
137
service/manager_api/grpc/server/converter.go
Normal file
137
service/manager_api/grpc/server/converter.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
pb "github.com/sagernet/sing-box/service/manager_api/grpc/manager"
|
||||
)
|
||||
|
||||
func toIntSlice(values []int32) []int {
|
||||
out := make([]int, len(values))
|
||||
for i, v := range values {
|
||||
out[i] = int(v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func toInt32Slice(values []int) []int32 {
|
||||
out := make([]int32, len(values))
|
||||
for i, v := range values {
|
||||
out[i] = int32(v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func convertSquad(v CM.Squad) *pb.Squad {
|
||||
return &pb.Squad{
|
||||
Id: int32(v.ID),
|
||||
Name: v.Name,
|
||||
CreatedAt: v.CreatedAt.UnixNano(),
|
||||
UpdatedAt: v.UpdatedAt.UnixNano(),
|
||||
}
|
||||
}
|
||||
|
||||
func convertNode(v CM.Node) *pb.Node {
|
||||
return &pb.Node{
|
||||
Uuid: v.UUID,
|
||||
Name: v.Name,
|
||||
SquadIds: toInt32Slice(v.SquadIDs),
|
||||
CreatedAt: v.CreatedAt.UnixNano(),
|
||||
UpdatedAt: v.UpdatedAt.UnixNano(),
|
||||
}
|
||||
}
|
||||
|
||||
func convertUser(v CM.User) *pb.User {
|
||||
return &pb.User{
|
||||
Id: int32(v.ID),
|
||||
SquadIds: toInt32Slice(v.SquadIDs),
|
||||
Username: v.Username,
|
||||
Inbound: v.Inbound,
|
||||
Type: v.Type,
|
||||
Uuid: v.UUID,
|
||||
Password: v.Password,
|
||||
Secret: v.Secret,
|
||||
Flow: v.Flow,
|
||||
AlterId: int32(v.AlterID),
|
||||
CreatedAt: v.CreatedAt.UnixNano(),
|
||||
UpdatedAt: v.UpdatedAt.UnixNano(),
|
||||
}
|
||||
}
|
||||
|
||||
func convertBandwidthLimiter(v CM.BandwidthLimiter) *pb.BandwidthLimiter {
|
||||
return &pb.BandwidthLimiter{
|
||||
Id: int32(v.ID),
|
||||
SquadIds: toInt32Slice(v.SquadIDs),
|
||||
Username: v.Username,
|
||||
Outbound: v.Outbound,
|
||||
Strategy: v.Strategy,
|
||||
ConnectionType: v.ConnectionType,
|
||||
Mode: v.Mode,
|
||||
FlowKeys: v.FlowKeys,
|
||||
Speed: v.Speed,
|
||||
RawSpeed: v.RawSpeed,
|
||||
CreatedAt: v.CreatedAt.UnixNano(),
|
||||
UpdatedAt: v.UpdatedAt.UnixNano(),
|
||||
}
|
||||
}
|
||||
|
||||
func convertTrafficLimiter(v CM.TrafficLimiter) *pb.TrafficLimiter {
|
||||
return &pb.TrafficLimiter{
|
||||
Id: int32(v.ID),
|
||||
SquadIds: toInt32Slice(v.SquadIDs),
|
||||
Username: v.Username,
|
||||
Outbound: v.Outbound,
|
||||
Strategy: v.Strategy,
|
||||
Mode: v.Mode,
|
||||
RawUsed: v.RawUsed,
|
||||
Quota: v.Quota,
|
||||
RawQuota: v.RawQuota,
|
||||
CreatedAt: v.CreatedAt.UnixNano(),
|
||||
UpdatedAt: v.UpdatedAt.UnixNano(),
|
||||
}
|
||||
}
|
||||
|
||||
func convertConnectionLimiter(v CM.ConnectionLimiter) *pb.ConnectionLimiter {
|
||||
return &pb.ConnectionLimiter{
|
||||
Id: int32(v.ID),
|
||||
SquadIds: toInt32Slice(v.SquadIDs),
|
||||
Username: v.Username,
|
||||
Outbound: v.Outbound,
|
||||
Strategy: v.Strategy,
|
||||
ConnectionType: v.ConnectionType,
|
||||
LockType: v.LockType,
|
||||
Count: v.Count,
|
||||
CreatedAt: v.CreatedAt.UnixNano(),
|
||||
UpdatedAt: v.UpdatedAt.UnixNano(),
|
||||
}
|
||||
}
|
||||
|
||||
func convertRateLimiter(v CM.RateLimiter) *pb.RateLimiter {
|
||||
return &pb.RateLimiter{
|
||||
Id: int32(v.ID),
|
||||
SquadIds: toInt32Slice(v.SquadIDs),
|
||||
Username: v.Username,
|
||||
Outbound: v.Outbound,
|
||||
Strategy: v.Strategy,
|
||||
ConnectionType: v.ConnectionType,
|
||||
Count: v.Count,
|
||||
Interval: v.Interval,
|
||||
CreatedAt: v.CreatedAt.UnixNano(),
|
||||
UpdatedAt: v.UpdatedAt.UnixNano(),
|
||||
}
|
||||
}
|
||||
|
||||
func convertFilters(req *pb.Filters) map[string][]string {
|
||||
filters := map[string][]string{}
|
||||
for k, v := range req.GetValues() {
|
||||
filters[k] = append([]string(nil), v.GetValues()...)
|
||||
}
|
||||
return filters
|
||||
}
|
||||
|
||||
func convertListFilters(req *pb.Filters) map[string][]string {
|
||||
filters := convertFilters(req)
|
||||
if _, ok := filters["limit"]; !ok {
|
||||
filters["limit"] = []string{"100"}
|
||||
}
|
||||
return filters
|
||||
}
|
||||
465
service/manager_api/grpc/server/rpc.go
Normal file
465
service/manager_api/grpc/server/rpc.go
Normal file
@@ -0,0 +1,465 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
pb "github.com/sagernet/sing-box/service/manager_api/grpc/manager"
|
||||
)
|
||||
|
||||
func (s *Server) CreateSquad(_ context.Context, req *pb.SquadCreate) (*pb.Squad, error) {
|
||||
v, err := s.manager.CreateSquad(CM.SquadCreate{Name: req.GetName()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertSquad(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) GetSquads(_ context.Context, req *pb.Filters) (*pb.SquadList, error) {
|
||||
items, err := s.manager.GetSquads(convertListFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*pb.Squad, len(items))
|
||||
for i, v := range items {
|
||||
out[i] = convertSquad(v)
|
||||
}
|
||||
return &pb.SquadList{Values: out}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetSquadsCount(_ context.Context, req *pb.Filters) (*pb.CountReply, error) {
|
||||
n, err := s.manager.GetSquadsCount(convertFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.CountReply{Count: int64(n)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetSquad(_ context.Context, req *pb.IdRequest) (*pb.Squad, error) {
|
||||
v, err := s.manager.GetSquad(int(req.GetId()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertSquad(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateSquad(_ context.Context, req *pb.SquadUpdateRequest) (*pb.Squad, error) {
|
||||
v, err := s.manager.UpdateSquad(int(req.GetId()), CM.SquadUpdate{Name: req.GetUpdate().GetName()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertSquad(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteSquad(_ context.Context, req *pb.IdRequest) (*pb.Squad, error) {
|
||||
v, err := s.manager.DeleteSquad(int(req.GetId()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertSquad(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) CreateNode(_ context.Context, req *pb.NodeCreate) (*pb.Node, error) {
|
||||
v, err := s.manager.CreateNode(CM.NodeCreate{
|
||||
UUID: req.GetUuid(),
|
||||
Name: req.GetName(),
|
||||
SquadIDs: toIntSlice(req.GetSquadIds()),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertNode(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) GetNodes(_ context.Context, req *pb.Filters) (*pb.NodeList, error) {
|
||||
items, err := s.manager.GetNodes(convertListFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*pb.Node, len(items))
|
||||
for i, v := range items {
|
||||
out[i] = convertNode(v)
|
||||
}
|
||||
return &pb.NodeList{Values: out}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetNodesCount(_ context.Context, req *pb.Filters) (*pb.CountReply, error) {
|
||||
n, err := s.manager.GetNodesCount(convertFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.CountReply{Count: int64(n)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetNode(_ context.Context, req *pb.UuidRequest) (*pb.Node, error) {
|
||||
v, err := s.manager.GetNode(req.GetUuid())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertNode(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) GetNodeStatus(_ context.Context, req *pb.UuidRequest) (*pb.NodeStatusReply, error) {
|
||||
status, err := s.manager.GetNodeStatus(req.GetUuid())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.NodeStatusReply{Status: status}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateNode(_ context.Context, req *pb.NodeUpdateRequest) (*pb.Node, error) {
|
||||
v, err := s.manager.UpdateNode(req.GetUuid(), CM.NodeUpdate{Name: req.GetUpdate().GetName()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertNode(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteNode(_ context.Context, req *pb.UuidRequest) (*pb.Node, error) {
|
||||
v, err := s.manager.DeleteNode(req.GetUuid())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertNode(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) CreateUser(_ context.Context, req *pb.UserCreate) (*pb.User, error) {
|
||||
v, err := s.manager.CreateUser(CM.UserCreate{
|
||||
SquadIDs: toIntSlice(req.GetSquadIds()),
|
||||
Username: req.GetUsername(),
|
||||
Inbound: req.GetInbound(),
|
||||
Type: req.GetType(),
|
||||
UUID: req.GetUuid(),
|
||||
Password: req.GetPassword(),
|
||||
Secret: req.GetSecret(),
|
||||
Flow: req.GetFlow(),
|
||||
AlterID: int(req.GetAlterId()),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertUser(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) GetUsers(_ context.Context, req *pb.Filters) (*pb.UserList, error) {
|
||||
items, err := s.manager.GetUsers(convertListFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*pb.User, len(items))
|
||||
for i, v := range items {
|
||||
out[i] = convertUser(v)
|
||||
}
|
||||
return &pb.UserList{Values: out}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetUsersCount(_ context.Context, req *pb.Filters) (*pb.CountReply, error) {
|
||||
n, err := s.manager.GetUsersCount(convertFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.CountReply{Count: int64(n)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetUser(_ context.Context, req *pb.IdRequest) (*pb.User, error) {
|
||||
v, err := s.manager.GetUser(int(req.GetId()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertUser(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateUser(_ context.Context, req *pb.UserUpdateRequest) (*pb.User, error) {
|
||||
u := req.GetUpdate()
|
||||
v, err := s.manager.UpdateUser(int(req.GetId()), CM.UserUpdate{
|
||||
UUID: u.GetUuid(),
|
||||
Password: u.GetPassword(),
|
||||
Secret: u.GetSecret(),
|
||||
Flow: u.GetFlow(),
|
||||
AlterID: int(u.GetAlterId()),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertUser(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteUser(_ context.Context, req *pb.IdRequest) (*pb.User, error) {
|
||||
v, err := s.manager.DeleteUser(int(req.GetId()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertUser(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) CreateBandwidthLimiter(_ context.Context, req *pb.BandwidthLimiterCreate) (*pb.BandwidthLimiter, error) {
|
||||
v, err := s.manager.CreateBandwidthLimiter(CM.BandwidthLimiterCreate{
|
||||
SquadIDs: toIntSlice(req.GetSquadIds()),
|
||||
Username: req.GetUsername(),
|
||||
Outbound: req.GetOutbound(),
|
||||
Strategy: req.GetStrategy(),
|
||||
ConnectionType: req.GetConnectionType(),
|
||||
Mode: req.GetMode(),
|
||||
FlowKeys: req.GetFlowKeys(),
|
||||
Speed: req.GetSpeed(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertBandwidthLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) GetBandwidthLimiters(_ context.Context, req *pb.Filters) (*pb.BandwidthLimiterList, error) {
|
||||
items, err := s.manager.GetBandwidthLimiters(convertListFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*pb.BandwidthLimiter, len(items))
|
||||
for i, v := range items {
|
||||
out[i] = convertBandwidthLimiter(v)
|
||||
}
|
||||
return &pb.BandwidthLimiterList{Values: out}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetBandwidthLimitersCount(_ context.Context, req *pb.Filters) (*pb.CountReply, error) {
|
||||
n, err := s.manager.GetBandwidthLimitersCount(convertFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.CountReply{Count: int64(n)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetBandwidthLimiter(_ context.Context, req *pb.IdRequest) (*pb.BandwidthLimiter, error) {
|
||||
v, err := s.manager.GetBandwidthLimiter(int(req.GetId()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertBandwidthLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateBandwidthLimiter(_ context.Context, req *pb.BandwidthLimiterUpdateRequest) (*pb.BandwidthLimiter, error) {
|
||||
u := req.GetUpdate()
|
||||
v, err := s.manager.UpdateBandwidthLimiter(int(req.GetId()), CM.BandwidthLimiterUpdate{
|
||||
Username: u.GetUsername(),
|
||||
Outbound: u.GetOutbound(),
|
||||
Strategy: u.GetStrategy(),
|
||||
ConnectionType: u.GetConnectionType(),
|
||||
Mode: u.GetMode(),
|
||||
FlowKeys: u.GetFlowKeys(),
|
||||
Speed: u.GetSpeed(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertBandwidthLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteBandwidthLimiter(_ context.Context, req *pb.IdRequest) (*pb.BandwidthLimiter, error) {
|
||||
v, err := s.manager.DeleteBandwidthLimiter(int(req.GetId()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertBandwidthLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) CreateTrafficLimiter(_ context.Context, req *pb.TrafficLimiterCreate) (*pb.TrafficLimiter, error) {
|
||||
v, err := s.manager.CreateTrafficLimiter(CM.TrafficLimiterCreate{
|
||||
SquadIDs: toIntSlice(req.GetSquadIds()),
|
||||
Username: req.GetUsername(),
|
||||
Outbound: req.GetOutbound(),
|
||||
Strategy: req.GetStrategy(),
|
||||
Mode: req.GetMode(),
|
||||
Quota: req.GetQuota(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTrafficLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) GetTrafficLimiters(_ context.Context, req *pb.Filters) (*pb.TrafficLimiterList, error) {
|
||||
items, err := s.manager.GetTrafficLimiters(convertListFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*pb.TrafficLimiter, len(items))
|
||||
for i, v := range items {
|
||||
out[i] = convertTrafficLimiter(v)
|
||||
}
|
||||
return &pb.TrafficLimiterList{Values: out}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetTrafficLimitersCount(_ context.Context, req *pb.Filters) (*pb.CountReply, error) {
|
||||
n, err := s.manager.GetTrafficLimitersCount(convertFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.CountReply{Count: int64(n)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetTrafficLimiter(_ context.Context, req *pb.IdRequest) (*pb.TrafficLimiter, error) {
|
||||
v, err := s.manager.GetTrafficLimiter(int(req.GetId()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTrafficLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateTrafficLimiter(_ context.Context, req *pb.TrafficLimiterUpdateRequest) (*pb.TrafficLimiter, error) {
|
||||
u := req.GetUpdate()
|
||||
v, err := s.manager.UpdateTrafficLimiter(int(req.GetId()), CM.TrafficLimiterUpdate{
|
||||
Username: u.GetUsername(),
|
||||
Outbound: u.GetOutbound(),
|
||||
Strategy: u.GetStrategy(),
|
||||
Mode: u.GetMode(),
|
||||
Quota: u.GetQuota(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTrafficLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteTrafficLimiter(_ context.Context, req *pb.IdRequest) (*pb.TrafficLimiter, error) {
|
||||
v, err := s.manager.DeleteTrafficLimiter(int(req.GetId()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTrafficLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) CreateConnectionLimiter(_ context.Context, req *pb.ConnectionLimiterCreate) (*pb.ConnectionLimiter, error) {
|
||||
v, err := s.manager.CreateConnectionLimiter(CM.ConnectionLimiterCreate{
|
||||
SquadIDs: toIntSlice(req.GetSquadIds()),
|
||||
Username: req.GetUsername(),
|
||||
Outbound: req.GetOutbound(),
|
||||
Strategy: req.GetStrategy(),
|
||||
ConnectionType: req.GetConnectionType(),
|
||||
LockType: req.GetLockType(),
|
||||
Count: req.GetCount(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertConnectionLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) GetConnectionLimiters(_ context.Context, req *pb.Filters) (*pb.ConnectionLimiterList, error) {
|
||||
items, err := s.manager.GetConnectionLimiters(convertListFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*pb.ConnectionLimiter, len(items))
|
||||
for i, v := range items {
|
||||
out[i] = convertConnectionLimiter(v)
|
||||
}
|
||||
return &pb.ConnectionLimiterList{Values: out}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetConnectionLimitersCount(_ context.Context, req *pb.Filters) (*pb.CountReply, error) {
|
||||
n, err := s.manager.GetConnectionLimitersCount(convertFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.CountReply{Count: int64(n)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetConnectionLimiter(_ context.Context, req *pb.IdRequest) (*pb.ConnectionLimiter, error) {
|
||||
v, err := s.manager.GetConnectionLimiter(int(req.GetId()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertConnectionLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateConnectionLimiter(_ context.Context, req *pb.ConnectionLimiterUpdateRequest) (*pb.ConnectionLimiter, error) {
|
||||
u := req.GetUpdate()
|
||||
v, err := s.manager.UpdateConnectionLimiter(int(req.GetId()), CM.ConnectionLimiterUpdate{
|
||||
Username: u.GetUsername(),
|
||||
Outbound: u.GetOutbound(),
|
||||
Strategy: u.GetStrategy(),
|
||||
ConnectionType: u.GetConnectionType(),
|
||||
LockType: u.GetLockType(),
|
||||
Count: u.GetCount(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertConnectionLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteConnectionLimiter(_ context.Context, req *pb.IdRequest) (*pb.ConnectionLimiter, error) {
|
||||
v, err := s.manager.DeleteConnectionLimiter(int(req.GetId()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertConnectionLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) CreateRateLimiter(_ context.Context, req *pb.RateLimiterCreate) (*pb.RateLimiter, error) {
|
||||
v, err := s.manager.CreateRateLimiter(CM.RateLimiterCreate{
|
||||
SquadIDs: toIntSlice(req.GetSquadIds()),
|
||||
Username: req.GetUsername(),
|
||||
Outbound: req.GetOutbound(),
|
||||
Strategy: req.GetStrategy(),
|
||||
ConnectionType: req.GetConnectionType(),
|
||||
Count: req.GetCount(),
|
||||
Interval: req.GetInterval(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertRateLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) GetRateLimiters(_ context.Context, req *pb.Filters) (*pb.RateLimiterList, error) {
|
||||
items, err := s.manager.GetRateLimiters(convertListFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*pb.RateLimiter, len(items))
|
||||
for i, v := range items {
|
||||
out[i] = convertRateLimiter(v)
|
||||
}
|
||||
return &pb.RateLimiterList{Values: out}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetRateLimitersCount(_ context.Context, req *pb.Filters) (*pb.CountReply, error) {
|
||||
n, err := s.manager.GetRateLimitersCount(convertFilters(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.CountReply{Count: int64(n)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetRateLimiter(_ context.Context, req *pb.IdRequest) (*pb.RateLimiter, error) {
|
||||
v, err := s.manager.GetRateLimiter(int(req.GetId()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertRateLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateRateLimiter(_ context.Context, req *pb.RateLimiterUpdateRequest) (*pb.RateLimiter, error) {
|
||||
u := req.GetUpdate()
|
||||
v, err := s.manager.UpdateRateLimiter(int(req.GetId()), CM.RateLimiterUpdate{
|
||||
Username: u.GetUsername(),
|
||||
Outbound: u.GetOutbound(),
|
||||
Strategy: u.GetStrategy(),
|
||||
ConnectionType: u.GetConnectionType(),
|
||||
Count: u.GetCount(),
|
||||
Interval: u.GetInterval(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertRateLimiter(v), nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteRateLimiter(_ context.Context, req *pb.IdRequest) (*pb.RateLimiter, error) {
|
||||
v, err := s.manager.DeleteRateLimiter(int(req.GetId()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertRateLimiter(v), nil
|
||||
}
|
||||
157
service/manager_api/grpc/server/server.go
Normal file
157
service/manager_api/grpc/server/server.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
boxService "github.com/sagernet/sing-box/adapter/service"
|
||||
"github.com/sagernet/sing-box/common/listener"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
pb "github.com/sagernet/sing-box/service/manager_api/grpc/manager"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
aTLS "github.com/sagernet/sing/common/tls"
|
||||
"github.com/sagernet/sing/service"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
pb.UnimplementedManagerServer
|
||||
boxService.Adapter
|
||||
|
||||
ctx context.Context
|
||||
logger log.ContextLogger
|
||||
listener *listener.Listener
|
||||
tlsConfig tls.ServerConfig
|
||||
grpcServer *grpc.Server
|
||||
manager CM.Manager
|
||||
options option.ManagerAPIServerOptions
|
||||
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewServer(ctx context.Context, logger log.ContextLogger, tag string, options option.ManagerAPIServerOptions) (*Server, error) {
|
||||
if options.APIKey == "" {
|
||||
return nil, E.New("missing api key")
|
||||
}
|
||||
return &Server{
|
||||
Adapter: boxService.NewAdapter(C.TypeManagerAPI, tag),
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
listener: listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: []string{N.NetworkTCP},
|
||||
Listen: options.ListenOptions,
|
||||
}),
|
||||
options: options,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
boxManager := service.FromContext[adapter.ServiceManager](s.ctx)
|
||||
managerService, ok := boxManager.Get(s.options.Manager)
|
||||
if !ok {
|
||||
return E.New("manager ", s.options.Manager, " not found")
|
||||
}
|
||||
s.manager, ok = managerService.(CM.Manager)
|
||||
if !ok {
|
||||
return E.New("invalid ", s.options.Manager, " manager")
|
||||
}
|
||||
if s.options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(s.ctx, s.logger, common.PtrValueOrDefault(s.options.TLS))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.tlsConfig = tlsConfig
|
||||
}
|
||||
if s.tlsConfig != nil {
|
||||
if err := s.tlsConfig.Start(); err != nil {
|
||||
return E.Cause(err, "create TLS config")
|
||||
}
|
||||
}
|
||||
tcpListener, err := s.listener.ListenTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.tlsConfig != nil {
|
||||
if !common.Contains(s.tlsConfig.NextProtos(), http2.NextProtoTLS) {
|
||||
s.tlsConfig.SetNextProtos(append([]string{"h2"}, s.tlsConfig.NextProtos()...))
|
||||
}
|
||||
tcpListener = aTLS.NewListener(tcpListener, s.tlsConfig)
|
||||
}
|
||||
s.grpcServer = grpc.NewServer(
|
||||
grpc.ChainUnaryInterceptor(s.unaryAuthInterceptor, unaryErrorInterceptor),
|
||||
grpc.StreamInterceptor(s.streamAuthInterceptor),
|
||||
)
|
||||
pb.RegisterManagerServer(s.grpcServer, s)
|
||||
go func() {
|
||||
if err := s.grpcServer.Serve(tcpListener); err != nil && !errors.Is(err, grpc.ErrServerStopped) {
|
||||
s.logger.Error("serve error: ", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Close() error {
|
||||
if s.grpcServer != nil {
|
||||
s.grpcServer.GracefulStop()
|
||||
}
|
||||
return common.Close(
|
||||
common.PtrOrNil(s.listener),
|
||||
s.tlsConfig,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Server) unaryAuthInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||
if err := s.authorize(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
func (s *Server) streamAuthInterceptor(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
if err := s.authorize(ss.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
return handler(srv, ss)
|
||||
}
|
||||
|
||||
func unaryErrorInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||
resp, err := handler(ctx, req)
|
||||
if err == CM.ErrNotFound {
|
||||
return resp, status.Error(codes.NotFound, err.Error())
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (s *Server) authorize(ctx context.Context) error {
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return status.Error(codes.Unauthenticated, "missing api key")
|
||||
}
|
||||
values := md.Get("authorization")
|
||||
if len(values) == 0 {
|
||||
return status.Error(codes.Unauthenticated, "missing api key")
|
||||
}
|
||||
if subtle.ConstantTimeCompare([]byte(values[0]), []byte(s.options.APIKey)) == 0 {
|
||||
return status.Error(codes.Unauthenticated, "invalid api key")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user