big refactor. make compatible v2 and v1 interface

This commit is contained in:
Hiddify
2024-03-16 01:39:33 +01:00
parent c51f73faed
commit 361419b95e
27 changed files with 1455 additions and 1085 deletions

View File

@@ -6,12 +6,11 @@ import (
pb "github.com/hiddify/libcore/hiddifyrpc"
"github.com/sagernet/sing-box/experimental/libbox"
"github.com/sagernet/sing/common/observable"
)
var systemInfoObserver = observable.Observer[pb.SystemInfo]{}
var outboundsInfoObserver = observable.Observer[pb.OutboundGroupList]{}
var mainOutboundsInfoObserver = observable.Observer[pb.OutboundGroupList]{}
var systemInfoObserver = NewObserver[pb.SystemInfo](10)
var outboundsInfoObserver = NewObserver[pb.OutboundGroupList](10)
var mainOutboundsInfoObserver = NewObserver[pb.OutboundGroupList](10)
var (
statusClient *libbox.CommandClient
@@ -19,7 +18,7 @@ var (
groupInfoOnlyClient *libbox.CommandClient
)
func (s *server) GetSystemInfo(stream pb.Hiddify_GetSystemInfoServer) error {
func (s *CoreService) GetSystemInfo(stream pb.Core_GetSystemInfoServer) error {
if statusClient == nil {
statusClient = libbox.NewCommandClient(
&CommandClientHandler{},
@@ -55,7 +54,7 @@ func (s *server) GetSystemInfo(stream pb.Hiddify_GetSystemInfoServer) error {
}
}
func (s *server) OutboundsInfo(stream pb.Hiddify_OutboundsInfoServer) error {
func (s *CoreService) OutboundsInfo(stream pb.Core_OutboundsInfoServer) error {
if groupClient == nil {
groupClient = libbox.NewCommandClient(
&CommandClientHandler{},
@@ -91,7 +90,7 @@ func (s *server) OutboundsInfo(stream pb.Hiddify_OutboundsInfoServer) error {
}
}
func (s *server) MainOutboundsInfo(stream pb.Hiddify_MainOutboundsInfoServer) error {
func (s *CoreService) MainOutboundsInfo(stream pb.Core_MainOutboundsInfoServer) error {
if groupInfoOnlyClient == nil {
groupInfoOnlyClient = libbox.NewCommandClient(
&CommandClientHandler{},
@@ -127,8 +126,10 @@ func (s *server) MainOutboundsInfo(stream pb.Hiddify_MainOutboundsInfoServer) er
}
}
// Implement the SelectOutbound method
func (s *server) SelectOutbound(ctx context.Context, in *pb.SelectOutboundRequest) (*pb.Response, error) {
func (s *CoreService) SelectOutbound(ctx context.Context, in *pb.SelectOutboundRequest) (*pb.Response, error) {
return SelectOutbound(in)
}
func SelectOutbound(in *pb.SelectOutboundRequest) (*pb.Response, error) {
err := libbox.NewStandaloneCommandClient().SelectOutbound(in.GroupTag, in.OutboundTag)
if err != nil {
@@ -144,8 +145,10 @@ func (s *server) SelectOutbound(ctx context.Context, in *pb.SelectOutboundReques
}, nil
}
// Implement the UrlTest method
func (s *server) UrlTest(ctx context.Context, in *pb.UrlTestRequest) (*pb.Response, error) {
func (s *CoreService) UrlTest(ctx context.Context, in *pb.UrlTestRequest) (*pb.Response, error) {
return UrlTest(in)
}
func UrlTest(in *pb.UrlTestRequest) (*pb.Response, error) {
err := libbox.NewStandaloneCommandClient().URLTest(in.GroupTag)
if err != nil {

View File

@@ -1,16 +1,18 @@
package v2
import (
"encoding/json"
"time"
"github.com/hiddify/libcore/bridge"
pb "github.com/hiddify/libcore/hiddifyrpc"
"github.com/sagernet/sing/common/observable"
)
var coreInfoObserver = observable.Observer[pb.CoreInfoResponse]{}
var coreInfoObserver = NewObserver[pb.CoreInfoResponse](10)
var CoreState = pb.CoreState_STOPPED
func SetCoreStatus(state pb.CoreState, msgType pb.MessageType, message string) pb.CoreInfoResponse {
Log(pb.LogLevel_INFO, pb.LogType_CORE, message)
CoreState = state
info := pb.CoreInfoResponse{
CoreState: state,
@@ -18,11 +20,13 @@ func SetCoreStatus(state pb.CoreState, msgType pb.MessageType, message string) p
Message: message,
}
coreInfoObserver.Emit(info)
msg, _ := json.Marshal(StatusMessage{Status: convert2OldState(CoreState)})
bridge.SendStringToPort(statusPropagationPort, string(msg))
return info
}
func (s *server) CoreInfoListener(stream pb.Hiddify_CoreInfoListenerServer) error {
func (s *CoreService) CoreInfoListener(stream pb.Core_CoreInfoListenerServer) error {
coreSub, _, _ := coreInfoObserver.Subscribe()
defer coreInfoObserver.UnSubscribe(coreSub)
stopch := make(chan int)

View File

@@ -2,11 +2,13 @@ package v2
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
"github.com/hiddify/libcore/bridge"
"github.com/hiddify/libcore/config"
pb "github.com/hiddify/libcore/hiddifyrpc"
"github.com/sagernet/sing-box/experimental/libbox"
@@ -22,19 +24,26 @@ var logFactory *log.Factory
func StopAndAlert(msgType pb.MessageType, message string) {
SetCoreStatus(pb.CoreState_STOPPED, msgType, message)
config.DeactivateTunnelService()
// if commandServer != nil {
// commandServer.SetService(nil)
// }
if commandServer != nil {
commandServer.SetService(nil)
}
if Box != nil {
Box.Close()
Box = nil
}
// if commandServer != nil {
// commandServer.Close()
// }
if commandServer != nil {
commandServer.Close()
}
alert := msgType.String()
msg, _ := json.Marshal(StatusMessage{Status: convert2OldState(CoreState), Alert: &alert, Message: &message})
bridge.SendStringToPort(statusPropagationPort, string(msg))
}
func (s *server) Start(ctx context.Context, in *pb.StartRequest) (*pb.CoreInfoResponse, error) {
func (s *CoreService) Start(ctx context.Context, in *pb.StartRequest) (*pb.CoreInfoResponse, error) {
return Start(in)
}
func Start(in *pb.StartRequest) (*pb.CoreInfoResponse, error) {
defer config.DeferPanicToError("start", func(err error) {
Log(pb.LogLevel_FATAL, pb.LogType_CORE, err.Error())
StopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error())
@@ -49,17 +58,23 @@ func (s *server) Start(ctx context.Context, in *pb.StartRequest) (*pb.CoreInfoRe
SetCoreStatus(pb.CoreState_STARTING, pb.MessageType_EMPTY, "")
libbox.SetMemoryLimit(!in.DisableMemoryLimit)
resp, err := s.StartService(ctx, in)
resp, err := StartService(in)
return resp, err
}
// Implement the StartService method
func (s *server) StartService(ctx context.Context, in *pb.StartRequest) (*pb.CoreInfoResponse, error) {
func (s *CoreService) StartService(ctx context.Context, in *pb.StartRequest) (*pb.CoreInfoResponse, error) {
return StartService(in)
}
func StartService(in *pb.StartRequest) (*pb.CoreInfoResponse, error) {
content := in.ConfigContent
if content != "" {
if content == "" {
if in.ConfigPath != "" {
activeConfigPath = &in.ConfigPath
}
fileContent, err := os.ReadFile(*activeConfigPath)
if err != nil {
Log(pb.LogLevel_FATAL, pb.LogType_CORE, err.Error())
resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_ERROR_READING_CONFIG, err.Error())
return &resp, err
}
@@ -68,26 +83,31 @@ func (s *server) StartService(ctx context.Context, in *pb.StartRequest) (*pb.Cor
parsedContent, err := parseConfig(content)
if err != nil {
Log(pb.LogLevel_FATAL, pb.LogType_CORE, err.Error())
resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_ERROR_PARSING_CONFIG, err.Error())
return &resp, err
}
var patchedOptions *option.Options
patchedOptions, err = config.BuildConfig(*configOptions, parsedContent)
if err != nil {
Log(pb.LogLevel_FATAL, pb.LogType_CORE, err.Error())
resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_ERROR_BUILDING_CONFIG, err.Error())
return &resp, err
}
config.SaveCurrentConfig(filepath.Join(sWorkingPath, "current-config.json"), *patchedOptions)
// err = startCommandServer(*logFactory)
// if err != nil {
// resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_START_COMMAND_SERVER, err.Error())
// return &resp, err
// }
if in.EnableOldCommandServer {
err = startCommandServer(*logFactory)
if err != nil {
Log(pb.LogLevel_FATAL, pb.LogType_CORE, err.Error())
resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_START_COMMAND_SERVER, err.Error())
return &resp, err
}
}
instance, err := NewService(*patchedOptions)
if err != nil {
Log(pb.LogLevel_FATAL, pb.LogType_CORE, err.Error())
resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_CREATE_SERVICE, err.Error())
return &resp, err
}
@@ -98,30 +118,55 @@ func (s *server) StartService(ctx context.Context, in *pb.StartRequest) (*pb.Cor
err = instance.Start()
if err != nil {
Log(pb.LogLevel_FATAL, pb.LogType_CORE, err.Error())
resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_START_SERVICE, err.Error())
return &resp, err
}
Box = instance
// commandServer.SetService(box)
if in.EnableOldCommandServer {
commandServer.SetService(Box)
}
resp := SetCoreStatus(pb.CoreState_STARTED, pb.MessageType_EMPTY, "")
return &resp, nil
}
func (s *server) Parse(ctx context.Context, in *pb.ParseRequest) (*pb.ParseResponse, error) {
func (s *CoreService) Parse(ctx context.Context, in *pb.ParseRequest) (*pb.ParseResponse, error) {
return Parse(in)
}
func Parse(in *pb.ParseRequest) (*pb.ParseResponse, error) {
defer config.DeferPanicToError("parse", func(err error) {
Log(pb.LogLevel_FATAL, pb.LogType_CONFIG, err.Error())
StopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error())
})
content := in.Content
if in.TempPath != "" {
contentBytes, err := os.ReadFile(in.TempPath)
content = string(contentBytes)
os.Chdir(filepath.Dir(in.ConfigPath))
if err != nil {
return nil, err
}
config, err := config.ParseConfigContent(in.Content, true)
}
config, err := config.ParseConfigContent(content, true)
if err != nil {
return &pb.ParseResponse{
ResponseCode: pb.ResponseCode_FAILED,
Message: err.Error(),
}, err
}
if in.ConfigPath != "" {
err = os.WriteFile(in.ConfigPath, config, 0644)
if err != nil {
return &pb.ParseResponse{
ResponseCode: pb.ResponseCode_FAILED,
Message: err.Error(),
}, err
}
}
return &pb.ParseResponse{
ResponseCode: pb.ResponseCode_OK,
Content: string(config),
@@ -129,31 +174,67 @@ func (s *server) Parse(ctx context.Context, in *pb.ParseRequest) (*pb.ParseRespo
}, err
}
// func (s *server) ChangeConfigOptions(ctx context.Context, in *pb.ChangeConfigOptionsRequest) (*pb.CoreInfoResponse, error) {
// // Implement your change config options logic
// // Return a CoreInfoResponse
// }
func (s *CoreService) ChangeConfigOptions(ctx context.Context, in *pb.ChangeConfigOptionsRequest) (*pb.CoreInfoResponse, error) {
return ChangeConfigOptions(in)
}
// func (s *server) GenerateConfig(ctx context.Context, in *pb.GenerateConfigRequest) (*pb.GenerateConfigResponse, error) {
// defer config.DeferPanicToError("generateConfig", func(err error) {
// Log(pb.LogLevel_FATAL, pb.LogType_CONFIG, err.Error())
// StopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error())
// })
func ChangeConfigOptions(in *pb.ChangeConfigOptionsRequest) (*pb.CoreInfoResponse, error) {
configOptions = &config.ConfigOptions{}
err := json.Unmarshal([]byte(in.ConfigOptionsJson), configOptions)
if err != nil {
return nil, err
}
if configOptions.Warp.WireguardConfigStr != "" {
err := json.Unmarshal([]byte(configOptions.Warp.WireguardConfigStr), &configOptions.Warp.WireguardConfig)
if err != nil {
return nil, err
}
}
return &pb.CoreInfoResponse{}, nil
}
func (s *CoreService) GenerateConfig(ctx context.Context, in *pb.GenerateConfigRequest) (*pb.GenerateConfigResponse, error) {
return GenerateConfig(in)
}
func GenerateConfig(in *pb.GenerateConfigRequest) (*pb.GenerateConfigResponse, error) {
defer config.DeferPanicToError("generateConfig", func(err error) {
Log(pb.LogLevel_FATAL, pb.LogType_CONFIG, err.Error())
StopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error())
})
// config, err := generateConfigFromFile(C.GoString(path), *configOptions)
// if err != nil {
// return C.CString("error" + err.Error())
// }
// return C.CString(config)
// }
config, err := generateConfigFromFile(in.Path, *configOptions)
if err != nil {
return nil, err
}
return &pb.GenerateConfigResponse{
ConfigContent: config,
}, nil
}
func generateConfigFromFile(path string, configOpt config.ConfigOptions) (string, error) {
os.Chdir(filepath.Dir(path))
content, err := os.ReadFile(path)
if err != nil {
return "", err
}
options, err := parseConfig(string(content))
if err != nil {
return "", err
}
config, err := config.BuildConfigJson(configOpt, options)
if err != nil {
return "", err
}
return config, nil
}
// Implement the Stop method
func (s *server) Stop(ctx context.Context, empty *pb.Empty) (*pb.CoreInfoResponse, error) {
func (s *CoreService) Stop(ctx context.Context, empty *pb.Empty) (*pb.CoreInfoResponse, error) {
return Stop()
}
func Stop() (*pb.CoreInfoResponse, error) {
defer config.DeferPanicToError("stop", func(err error) {
Log(pb.LogLevel_FATAL, pb.LogType_CORE, err.Error())
StopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error())
})
config.DeactivateTunnelService()
if CoreState != pb.CoreState_STARTED {
Log(pb.LogLevel_FATAL, pb.LogType_CORE, "Core is not started")
return &pb.CoreInfoResponse{
@@ -170,7 +251,10 @@ func (s *server) Stop(ctx context.Context, empty *pb.Empty) (*pb.CoreInfoRespons
}, fmt.Errorf("instance not found")
}
SetCoreStatus(pb.CoreState_STOPPING, pb.MessageType_EMPTY, "")
// commandServer.SetService(nil)
config.DeactivateTunnelService()
if commandServer != nil {
commandServer.SetService(nil)
}
err := Box.Close()
if err != nil {
@@ -181,22 +265,25 @@ func (s *server) Stop(ctx context.Context, empty *pb.Empty) (*pb.CoreInfoRespons
}, fmt.Errorf("Error while stopping the service.")
}
Box = nil
// err = commandServer.Close()
// if err != nil {
// return &pb.CoreInfoResponse{
// CoreState: CoreState,
// MessageType: pb.MessageType_UNEXPECTED_ERROR,
// Message: "Error while Closing the comand server.",
// }, fmt.Errorf("Error while Closing the comand server.")
// }
// commandServer = nil
if commandServer != nil {
err = commandServer.Close()
if err != nil {
return &pb.CoreInfoResponse{
CoreState: CoreState,
MessageType: pb.MessageType_UNEXPECTED_ERROR,
Message: "Error while Closing the comand server.",
}, fmt.Errorf("Error while Closing the comand server.")
}
commandServer = nil
}
resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_EMPTY, "")
return &resp, nil
}
func (s *server) Restart(ctx context.Context, in *pb.StartRequest) (*pb.CoreInfoResponse, error) {
func (s *CoreService) Restart(ctx context.Context, in *pb.StartRequest) (*pb.CoreInfoResponse, error) {
return Restart(in)
}
func Restart(in *pb.StartRequest) (*pb.CoreInfoResponse, error) {
defer config.DeferPanicToError("restart", func(err error) {
Log(pb.LogLevel_FATAL, pb.LogType_CORE, err.Error())
StopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error())
@@ -218,7 +305,7 @@ func (s *server) Restart(ctx context.Context, in *pb.StartRequest) (*pb.CoreInfo
}, fmt.Errorf("instance not found")
}
resp, err := s.Stop(ctx, &pb.Empty{})
resp, err := Stop()
if err != nil {
return resp, err
}
@@ -227,6 +314,6 @@ func (s *server) Restart(ctx context.Context, in *pb.StartRequest) (*pb.CoreInfo
<-time.After(250 * time.Millisecond)
libbox.SetMemoryLimit(!in.DisableMemoryLimit)
resp, gErr := s.StartService(ctx, in)
resp, gErr := StartService(in)
return resp, gErr
}

View File

@@ -21,7 +21,7 @@ func main() {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewHiddifyClient(conn)
c := pb.NewHelloClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()

View File

@@ -11,7 +11,7 @@ import (
func main() {
// defer C.free(unsafe.Pointer(port))
v2.StartGrpcServerGo("127.0.0.1:50051")
v2.StartGrpcServer("127.0.0.1:50051")
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan

View File

@@ -13,24 +13,18 @@ import (
"google.golang.org/grpc"
)
type server struct {
pb.UnimplementedHiddifyServer
type HelloService struct {
pb.UnimplementedHelloServer
}
type CoreService struct {
pb.UnimplementedCoreServer
}
//export StartGrpcServer
func StartGrpcServer(listenAddress *C.char) (CErr *C.char) {
//Example Listen Address: "127.0.0.1:50051"
err := StartGrpcServerGo(C.GoString(listenAddress))
if err != nil {
return C.CString(err.Error())
}
return nil
type TunnelService struct {
pb.UnimplementedTunnelServiceServer
}
func StartGrpcServerGo(listenAddressG string) error {
//Example Listen Address: "127.0.0.1:50051"
// defer C.free(unsafe.Pointer(CErr)) // free the C string when it's no longer needed
// defer C.free(unsafe.Pointer(listenAddress)) // free the C string when it's no longer needed
func StartGrpcServer(listenAddressG string, service string) error {
lis, err := net.Listen("tcp", listenAddressG)
if err != nil {
@@ -38,7 +32,13 @@ func StartGrpcServerGo(listenAddressG string) error {
return err
}
s := grpc.NewServer()
pb.RegisterHiddifyServer(s, &server{})
if service == "core" {
pb.RegisterCoreServer(s, &CoreService{})
} else if service == "hello" {
pb.RegisterHelloServer(s, &HelloService{})
} else if service == "tunnel" {
pb.RegisterTunnelServiceServer(s, &TunnelService{})
}
log.Printf("Server listening on %s", listenAddressG)
go func() {
if err := s.Serve(lis); err != nil {
@@ -47,3 +47,14 @@ func StartGrpcServerGo(listenAddressG string) error {
}()
return nil
}
func StartCoreGrpcServer(listenAddressG string) error {
return StartGrpcServer(listenAddressG, "core")
}
func StartHelloGrpcServer(listenAddressG string) error {
return StartGrpcServer(listenAddressG, "hello")
}
func StartTunnelGrpcServer(listenAddressG string) error {
return StartGrpcServer(listenAddressG, "tunnel")
}

View File

@@ -8,10 +8,10 @@ import (
pb "github.com/hiddify/libcore/hiddifyrpc"
)
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
func (s *HelloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
return &pb.HelloResponse{Message: "Hello, " + in.Name}, nil
}
func (s *server) SayHelloStream(stream pb.Hiddify_SayHelloStreamServer) error {
func (s *HelloService) SayHelloStream(stream pb.Hello_SayHelloStreamServer) error {
for {
req, err := stream.Recv()

View File

@@ -1,15 +1,21 @@
package v2
import (
"fmt"
"time"
pb "github.com/hiddify/libcore/hiddifyrpc"
"github.com/sagernet/sing/common/observable"
)
var logObserver = observable.Observer[pb.LogMessage]{}
func NewObserver[T any](listenerBufferSize int) *observable.Observer[T] {
return observable.NewObserver[T](&observable.Subscriber[T]{}, listenerBufferSize)
}
var logObserver = NewObserver[pb.LogMessage](10)
func Log(level pb.LogLevel, typ pb.LogType, message string) {
fmt.Printf("%s %s %s\n", level, typ, message)
logObserver.Emit(pb.LogMessage{
Level: level,
Type: typ,
@@ -18,7 +24,7 @@ func Log(level pb.LogLevel, typ pb.LogType, message string) {
}
func (s *server) LogListener(stream pb.Hiddify_LogListenerServer) error {
func (s *CoreService) LogListener(stream pb.Core_LogListenerServer) error {
logSub, _, _ := logObserver.Subscribe()
defer logObserver.UnSubscribe(logSub)
@@ -30,9 +36,7 @@ func (s *server) LogListener(stream pb.Hiddify_LogListenerServer) error {
for {
select {
case <-stream.Context().Done():
break
case <-stopch:
break
return nil
case info := <-logSub:
stream.Send(&info)
case <-time.After(500 * time.Millisecond):

105
v2/old_command_client.go Normal file
View File

@@ -0,0 +1,105 @@
package v2
import (
"encoding/json"
"fmt"
"github.com/hiddify/libcore/bridge"
"github.com/sagernet/sing-box/experimental/libbox"
)
var (
_ libbox.CommandClientHandler = (*OldCommandClientHandler)(nil)
)
type OldCommandClientHandler struct {
port int64
// logger log.Logger
}
func (cch *OldCommandClientHandler) Connected() {
// cch.logger.Debug("CONNECTED")
}
func (cch *OldCommandClientHandler) Disconnected(message string) {
// cch.logger.Debug("DISCONNECTED: ", message)
}
func (cch *OldCommandClientHandler) ClearLog() {
// cch.logger.Debug("clear log")
}
func (cch *OldCommandClientHandler) WriteLog(message string) {
// cch.logger.Debug("log: ", message)
}
func (cch *OldCommandClientHandler) WriteStatus(message *libbox.StatusMessage) {
msg, err := json.Marshal(
map[string]int64{
"connections-in": int64(message.ConnectionsIn),
"connections-out": int64(message.ConnectionsOut),
"uplink": message.Uplink,
"downlink": message.Downlink,
"uplink-total": message.UplinkTotal,
"downlink-total": message.DownlinkTotal,
},
)
// cch.logger.Debug("Memory: ", libbox.FormatBytes(message.Memory), ", Goroutines: ", message.Goroutines)
if err != nil {
bridge.SendStringToPort(cch.port, fmt.Sprintf("error: %e", err))
} else {
bridge.SendStringToPort(cch.port, string(msg))
}
}
func (cch *OldCommandClientHandler) WriteGroups(message libbox.OutboundGroupIterator) {
if message == nil {
return
}
groups := []*OutboundGroup{}
for message.HasNext() {
group := message.Next()
items := group.GetItems()
groupItems := []*OutboundGroupItem{}
for items.HasNext() {
item := items.Next()
groupItems = append(groupItems,
&OutboundGroupItem{
Tag: item.Tag,
Type: item.Type,
URLTestTime: item.URLTestTime,
URLTestDelay: item.URLTestDelay,
},
)
}
groups = append(groups, &OutboundGroup{Tag: group.Tag, Type: group.Type, Selected: group.Selected, Items: groupItems})
}
response, err := json.Marshal(groups)
if err != nil {
bridge.SendStringToPort(cch.port, fmt.Sprintf("error: %e", err))
} else {
bridge.SendStringToPort(cch.port, string(response))
}
}
func (cch *OldCommandClientHandler) InitializeClashMode(modeList libbox.StringIterator, currentMode string) {
// cch.logger.Debug("initial clash mode: ", currentMode)
}
func (cch *OldCommandClientHandler) UpdateClashMode(newMode string) {
// cch.logger.Debug("update clash mode: ", newMode)
}
type OutboundGroup struct {
Tag string `json:"tag"`
Type string `json:"type"`
Selected string `json:"selected"`
Items []*OutboundGroupItem `json:"items"`
}
type OutboundGroupItem struct {
Tag string `json:"tag"`
Type string `json:"type"`
URLTestTime int64 `json:"url-test-time"`
URLTestDelay int32 `json:"url-test-delay"`
}

50
v2/old_command_server.go Normal file
View File

@@ -0,0 +1,50 @@
package v2
import (
pb "github.com/hiddify/libcore/hiddifyrpc"
"github.com/sagernet/sing-box/experimental/libbox"
"github.com/sagernet/sing-box/log"
)
var commandServer *libbox.CommandServer
type CommandServerHandler struct {
logger log.Logger
}
func (csh *CommandServerHandler) ServiceReload() error {
csh.logger.Trace("Reloading service")
SetCoreStatus(pb.CoreState_STARTING, pb.MessageType_EMPTY, "")
if commandServer != nil {
commandServer.SetService(nil)
commandServer = nil
}
if Box != nil {
Box.Close()
Box = nil
}
_, err := StartService(&pb.StartRequest{
EnableOldCommandServer: true,
DelayStart: true,
})
return err
}
func (csh *CommandServerHandler) GetSystemProxyStatus() *libbox.SystemProxyStatus {
csh.logger.Trace("Getting system proxy status")
return &libbox.SystemProxyStatus{Available: true, Enabled: false}
}
func (csh *CommandServerHandler) SetSystemProxyEnabled(isEnabled bool) error {
csh.logger.Trace("Setting system proxy status, enabled? ", isEnabled)
return csh.ServiceReload()
}
func startCommandServer(logFactory log.Factory) error {
logger := logFactory.NewLogger("[Command Server Handler]")
logger.Trace("Starting command server")
commandServer = libbox.NewCommandServer(&CommandServerHandler{logger: logger}, 300)
return commandServer.Start()
}

71
v2/old_commands.go Normal file
View File

@@ -0,0 +1,71 @@
package v2
import (
"github.com/sagernet/sing-box/experimental/libbox"
)
var (
oldStatusClient *libbox.CommandClient
oldGroupClient *libbox.CommandClient
oldGroupInfoOnlyClient *libbox.CommandClient
)
func StartCommand(command int32, port int64) error {
switch command {
case libbox.CommandStatus:
oldStatusClient = libbox.NewCommandClient(
&OldCommandClientHandler{
port: port,
// logger: logFactory.NewLogger("[Status Command Client]"),
},
&libbox.CommandClientOptions{
Command: libbox.CommandStatus,
StatusInterval: 1000000000, //1000ms debounce
},
)
return oldStatusClient.Connect()
case libbox.CommandGroup:
oldGroupClient = libbox.NewCommandClient(
&OldCommandClientHandler{
port: port,
// logger: logFactory.NewLogger("[Group Command Client]"),
},
&libbox.CommandClientOptions{
Command: libbox.CommandGroup,
StatusInterval: 300000000, //300ms debounce
},
)
return oldGroupClient.Connect()
case libbox.CommandGroupInfoOnly:
oldGroupInfoOnlyClient = libbox.NewCommandClient(
&OldCommandClientHandler{
port: port,
// logger: logFactory.NewLogger("[GroupInfoOnly Command Client]"),
},
&libbox.CommandClientOptions{
Command: libbox.CommandGroupInfoOnly,
StatusInterval: 300000000, //300ms debounce
},
)
return oldGroupInfoOnlyClient.Connect()
}
return nil
}
func StopCommand(command int32) error {
switch command {
case libbox.CommandStatus:
err := oldStatusClient.Disconnect()
oldStatusClient = nil
return err
case libbox.CommandGroup:
err := oldGroupClient.Disconnect()
oldGroupClient = nil
return err
case libbox.CommandGroupInfoOnly:
err := oldGroupInfoOnlyClient.Disconnect()
oldGroupInfoOnlyClient = nil
return err
}
return nil
}

38
v2/old_constant.go Normal file
View File

@@ -0,0 +1,38 @@
package v2
import pb "github.com/hiddify/libcore/hiddifyrpc"
const (
Stopped = "Stopped"
Starting = "Starting"
Started = "Started"
Stopping = "Stopping"
)
const (
EmptyConfiguration = "EmptyConfiguration"
StartCommandServer = "StartCommandServer"
CreateService = "CreateService"
)
func convert2OldState(newStatus pb.CoreState) string {
if newStatus == pb.CoreState_STOPPED {
return Stopped
}
if newStatus == pb.CoreState_STARTED {
return Started
}
if newStatus == pb.CoreState_STARTING {
return Starting
}
if newStatus == pb.CoreState_STOPPING {
return Stopping
}
return "Invalid"
}
type StatusMessage struct {
Status string `json:"status"`
Alert *string `json:"alert"`
Message *string `json:"message"`
}

View File

@@ -2,13 +2,16 @@ package v2
import (
"context"
"io"
"os"
"runtime"
runtimeDebug "runtime/debug"
"time"
B "github.com/sagernet/sing-box"
"github.com/sagernet/sing-box/common/urltest"
"github.com/sagernet/sing-box/experimental/libbox"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/service"
@@ -17,13 +20,15 @@ import (
)
var (
sWorkingPath string
sTempPath string
sUserID int
sGroupID int
sWorkingPath string
sTempPath string
sUserID int
sGroupID int
statusPropagationPort int64
)
func Setup(basePath string, workingPath string, tempPath string) {
func Setup(basePath string, workingPath string, tempPath string, statusPort int64, debug bool) error {
statusPropagationPort = int64(statusPort)
tcpConn := runtime.GOOS == "windows" //TODO add TVOS
libbox.Setup(basePath, workingPath, tempPath, tcpConn)
sWorkingPath = workingPath
@@ -31,6 +36,19 @@ func Setup(basePath string, workingPath string, tempPath string) {
sTempPath = tempPath
sUserID = os.Getuid()
sGroupID = os.Getgid()
var defaultWriter io.Writer
if !debug {
defaultWriter = io.Discard
}
factory, err := log.New(
log.Options{
DefaultWriter: defaultWriter,
BaseTime: time.Now(),
Observable: false,
})
logFactory = &factory
return err
}
func NewService(options option.Options) (*libbox.BoxService, error) {

44
v2/system_proxy.go Normal file
View File

@@ -0,0 +1,44 @@
package v2
import (
"context"
pb "github.com/hiddify/libcore/hiddifyrpc"
"github.com/sagernet/sing-box/experimental/libbox"
)
func (s *CoreService) GetSystemProxyStatus(ctx context.Context, empty *pb.Empty) (*pb.SystemProxyStatus, error) {
return GetSystemProxyStatus(ctx, empty)
}
func GetSystemProxyStatus(ctx context.Context, empty *pb.Empty) (*pb.SystemProxyStatus, error) {
status, err := libbox.NewStandaloneCommandClient().GetSystemProxyStatus()
if err != nil {
return nil, err
}
return &pb.SystemProxyStatus{
Available: status.Available,
Enabled: status.Enabled,
}, nil
}
func (s *CoreService) SetSystemProxyEnabled(ctx context.Context, in *pb.SetSystemProxyEnabledRequest) (*pb.Response, error) {
return SetSystemProxyEnabled(ctx, in)
}
func SetSystemProxyEnabled(ctx context.Context, in *pb.SetSystemProxyEnabledRequest) (*pb.Response, error) {
err := libbox.NewStandaloneCommandClient().SetSystemProxyEnabled(in.IsEnabled)
if err != nil {
return &pb.Response{
ResponseCode: pb.ResponseCode_FAILED,
Message: err.Error(),
}, err
}
return &pb.Response{
ResponseCode: pb.ResponseCode_OK,
Message: "",
}, nil
}

View File

@@ -5,10 +5,12 @@ import (
"github.com/hiddify/libcore/config"
pb "github.com/hiddify/libcore/hiddifyrpc"
"github.com/sagernet/sing-box/experimental/libbox"
)
func (s *server) GenerateWarpConfig(ctx context.Context, in *pb.GenerateWarpConfigRequest) (*pb.WarpGenerationResponse, error) {
func (s *CoreService) GenerateWarpConfig(ctx context.Context, in *pb.GenerateWarpConfigRequest) (*pb.WarpGenerationResponse, error) {
return GenerateWarpConfig(in)
}
func GenerateWarpConfig(in *pb.GenerateWarpConfigRequest) (*pb.WarpGenerationResponse, error) {
account, log, wg, err := config.GenerateWarpInfo(in.LicenseKey, in.AccountId, in.AccessToken)
if err != nil {
return nil, err
@@ -27,39 +29,3 @@ func (s *server) GenerateWarpConfig(ctx context.Context, in *pb.GenerateWarpConf
Log: log,
}, nil
}
// Implement the GetSystemProxyStatus method
func (s *server) GetSystemProxyStatus(ctx context.Context, empty *pb.Empty) (*pb.SystemProxyStatus, error) {
status, err := libbox.NewStandaloneCommandClient().GetSystemProxyStatus()
if err != nil {
return nil, err
}
return &pb.SystemProxyStatus{
Available: status.Available,
Enabled: status.Enabled,
}, nil
}
// Implement the SetSystemProxyEnabled method
func (s *server) SetSystemProxyEnabled(ctx context.Context, in *pb.SetSystemProxyEnabledRequest) (*pb.Response, error) {
err := libbox.NewStandaloneCommandClient().SetSystemProxyEnabled(in.IsEnabled)
if err != nil {
return nil, err
}
if err != nil {
return &pb.Response{
ResponseCode: pb.ResponseCode_FAILED,
Message: err.Error(),
}, err
}
return &pb.Response{
ResponseCode: pb.ResponseCode_OK,
Message: "",
}, nil
}