From c86b5ccb0dd266e83645519293db57bf60a99b5a Mon Sep 17 00:00:00 2001 From: Hiddify Date: Sun, 10 Mar 2024 19:45:03 +0100 Subject: [PATCH] release: version 0.17.7 --- bridge/bridge_stub.go | 22 +- cli/main.go | 22 +- custom/cmd_interface.go | 52 ++--- global/standalone.go | 446 ++++++++++++++++++------------------ hiddifyrpc/hiddify.proto | 428 +++++++++++++++++------------------ v2/commands.go | 326 +++++++++++++------------- v2/coreinfo.go | 88 ++++---- v2/custom.go | 464 +++++++++++++++++++------------------- v2/example_client/main.go | 124 +++++----- v2/example_server/main.go | 36 +-- v2/grpc_server.go | 98 ++++---- v2/hello.go | 72 +++--- v2/logproto.go | 82 +++---- v2/warp.go | 130 +++++------ 14 files changed, 1195 insertions(+), 1195 deletions(-) diff --git a/bridge/bridge_stub.go b/bridge/bridge_stub.go index be021a9..343166c 100644 --- a/bridge/bridge_stub.go +++ b/bridge/bridge_stub.go @@ -1,11 +1,11 @@ -//go:build !cgo -// +build !cgo - -package bridge - -import "unsafe" - -func InitializeDartApi(api unsafe.Pointer) { -} -func SendStringToPort(port int64, msg string) { -} +//go:build !cgo +// +build !cgo + +package bridge + +import "unsafe" + +func InitializeDartApi(api unsafe.Pointer) { +} +func SendStringToPort(port int64, msg string) { +} diff --git a/cli/main.go b/cli/main.go index 9ecd69f..9f41717 100644 --- a/cli/main.go +++ b/cli/main.go @@ -1,11 +1,11 @@ -package main - -import ( - "os" - - "github.com/hiddify/libcore/cmd" -) - -func main() { - cmd.ParseCli(os.Args[1:]) -} +package main + +import ( + "os" + + "github.com/hiddify/libcore/cmd" +) + +func main() { + cmd.ParseCli(os.Args[1:]) +} diff --git a/custom/cmd_interface.go b/custom/cmd_interface.go index e9055a0..7ab77ae 100644 --- a/custom/cmd_interface.go +++ b/custom/cmd_interface.go @@ -1,26 +1,26 @@ -package main - -/* -#include -*/ -import "C" -import ( - "unsafe" - - "github.com/hiddify/libcore/cmd" -) - -//export parseCli -func parseCli(argc C.int, argv **C.char) *C.char { - args := make([]string, argc) - for i := 0; i < int(argc); i++ { - // fmt.Println("parseCli", C.GoString(*argv)) - args[i] = C.GoString(*argv) - argv = (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + uintptr(unsafe.Sizeof(*argv)))) - } - err := cmd.ParseCli(args[1:]) - if err != nil { - return C.CString(err.Error()) - } - return C.CString("") -} +package main + +/* +#include +*/ +import "C" +import ( + "unsafe" + + "github.com/hiddify/libcore/cmd" +) + +//export parseCli +func parseCli(argc C.int, argv **C.char) *C.char { + args := make([]string, argc) + for i := 0; i < int(argc); i++ { + // fmt.Println("parseCli", C.GoString(*argv)) + args[i] = C.GoString(*argv) + argv = (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + uintptr(unsafe.Sizeof(*argv)))) + } + err := cmd.ParseCli(args[1:]) + if err != nil { + return C.CString(err.Error()) + } + return C.CString("") +} diff --git a/global/standalone.go b/global/standalone.go index f291227..5cc803e 100644 --- a/global/standalone.go +++ b/global/standalone.go @@ -1,223 +1,223 @@ -package global - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "os" - "os/signal" - "runtime" - "strconv" - "strings" - "syscall" - "time" - - "github.com/hiddify/libcore/config" - - "github.com/sagernet/sing-box/option" -) - -func RunStandalone(hiddifySettingPath string, configPath string) error { - fmt.Println("Running in standalone mode") - current, err := readAndBuildConfig(hiddifySettingPath, configPath) - if err != nil { - fmt.Printf("Error in read and build config %v", err) - return err - } - - go StartServiceC(false, current.Config) - go updateConfigInterval(current, hiddifySettingPath, configPath) - fmt.Printf("Press CTRL+C to stop\n") - fmt.Printf("Open http://localhost:6756/?secret=hiddify in your browser\n") - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) - - <-sigChan - err = StopServiceC() - - return err -} - -type ConfigResult struct { - Config string - RefreshInterval int -} - -func readAndBuildConfig(hiddifySettingPath string, configPath string) (ConfigResult, error) { - var result ConfigResult - - result, err := readConfigContent(configPath) - if err != nil { - return result, err - } - hiddifyconfig := config.DefaultConfigOptions() - if hiddifySettingPath != "" { - hiddifyconfig, err = readConfigOptionsAt(hiddifySettingPath) - if err != nil { - return result, err - } - } - result.Config, err = buildConfig(result.Config, *hiddifyconfig) - if err != nil { - return result, err - } - - return result, nil -} - -func readConfigContent(configPath string) (ConfigResult, error) { - var content string - var refreshInterval int - - if strings.HasPrefix(configPath, "http://") || strings.HasPrefix(configPath, "https://") { - client := &http.Client{} - - // Create a new request - req, err := http.NewRequest("GET", configPath, nil) - if err != nil { - fmt.Println("Error creating request:", err) - return ConfigResult{}, err - } - req.Header.Set("User-Agent", "HiddifyNext/17.5.0 ("+runtime.GOOS+") like ClashMeta v2ray sing-box") - resp, err := client.Do(req) - if err != nil { - fmt.Println("Error making GET request:", err) - return ConfigResult{}, err - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return ConfigResult{}, fmt.Errorf("failed to read config body: %w", err) - } - content = string(body) - refreshInterval, _ = extractRefreshInterval(resp.Header, content) - fmt.Printf("Refresh interval: %d\n", refreshInterval) - } else { - data, err := ioutil.ReadFile(configPath) - if err != nil { - return ConfigResult{}, fmt.Errorf("failed to read config file: %w", err) - } - content = string(data) - } - - return ConfigResult{ - Config: content, - RefreshInterval: refreshInterval, - }, nil -} - -func extractRefreshInterval(header http.Header, bodyStr string) (int, error) { - refreshIntervalStr := header.Get("profile-update-interval") - if refreshIntervalStr != "" { - refreshInterval, err := strconv.Atoi(refreshIntervalStr) - if err != nil { - return 0, fmt.Errorf("failed to parse refresh interval from header: %w", err) - } - return refreshInterval, nil - } - - lines := strings.Split(bodyStr, "\n") - for _, line := range lines { - line = strings.TrimSpace(line) - if strings.HasPrefix(line, "//profile-update-interval:") || strings.HasPrefix(line, "#profile-update-interval:") { - parts := strings.SplitN(line, ":", 2) - str := strings.TrimSpace(parts[1]) - refreshInterval, err := strconv.Atoi(str) - if err != nil { - return 0, fmt.Errorf("failed to parse refresh interval from body: %w", err) - } - return refreshInterval, nil - } - } - return 0, nil -} -func buildConfig(configContent string, options config.ConfigOptions) (string, error) { - parsedContent, err := config.ParseConfigContent(configContent, true) - if err != nil { - return "", fmt.Errorf("failed to parse config content: %w", err) - } - singconfigs, err := readConfigBytes([]byte(parsedContent)) - if err != nil { - return "", err - } - - finalconfig, err := config.BuildConfig(options, *singconfigs) - if err != nil { - return "", fmt.Errorf("failed to build config: %w", err) - } - - finalconfig.Log.Output = "" - finalconfig.Experimental.ClashAPI.ExternalUI = "webui" - if options.AllowConnectionFromLAN { - finalconfig.Experimental.ClashAPI.ExternalController = "0.0.0.0:6756" - } else { - finalconfig.Experimental.ClashAPI.ExternalController = "127.0.0.1:6756" - } - - if finalconfig.Experimental.ClashAPI.Secret == "" { - // finalconfig.Experimental.ClashAPI.Secret = "hiddify" - } - - if err := SetupC("./", "./", "./tmp", false); err != nil { - return "", fmt.Errorf("failed to set up global configuration: %w", err) - } - - configStr, err := config.ToJson(*finalconfig) - if err != nil { - return "", fmt.Errorf("failed to convert config to JSON: %w", err) - } - - return configStr, nil -} - -func updateConfigInterval(current ConfigResult, hiddifySettingPath string, configPath string) { - if current.RefreshInterval <= 0 { - return - } - - for { - <-time.After(time.Duration(current.RefreshInterval) * time.Hour) - new, err := readAndBuildConfig(hiddifySettingPath, configPath) - if err != nil { - continue - } - if new.Config != current.Config { - go StopServiceC() - go StartServiceC(false, new.Config) - } - current = new - } - -} - -func readConfigBytes(content []byte) (*option.Options, error) { - var options option.Options - err := options.UnmarshalJSON(content) - if err != nil { - return nil, err - } - return &options, nil -} - -func readConfigOptionsAt(path string) (*config.ConfigOptions, error) { - content, err := os.ReadFile(path) - if err != nil { - return nil, err - } - var options config.ConfigOptions - err = json.Unmarshal(content, &options) - - if err != nil { - return nil, err - } - if options.Warp.WireguardConfigStr != "" { - err := json.Unmarshal([]byte(options.Warp.WireguardConfigStr), &options.Warp.WireguardConfig) - if err != nil { - return nil, err - } - } - - return &options, nil -} +package global + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "os/signal" + "runtime" + "strconv" + "strings" + "syscall" + "time" + + "github.com/hiddify/libcore/config" + + "github.com/sagernet/sing-box/option" +) + +func RunStandalone(hiddifySettingPath string, configPath string) error { + fmt.Println("Running in standalone mode") + current, err := readAndBuildConfig(hiddifySettingPath, configPath) + if err != nil { + fmt.Printf("Error in read and build config %v", err) + return err + } + + go StartServiceC(false, current.Config) + go updateConfigInterval(current, hiddifySettingPath, configPath) + fmt.Printf("Press CTRL+C to stop\n") + fmt.Printf("Open http://localhost:6756/?secret=hiddify in your browser\n") + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) + + <-sigChan + err = StopServiceC() + + return err +} + +type ConfigResult struct { + Config string + RefreshInterval int +} + +func readAndBuildConfig(hiddifySettingPath string, configPath string) (ConfigResult, error) { + var result ConfigResult + + result, err := readConfigContent(configPath) + if err != nil { + return result, err + } + hiddifyconfig := config.DefaultConfigOptions() + if hiddifySettingPath != "" { + hiddifyconfig, err = readConfigOptionsAt(hiddifySettingPath) + if err != nil { + return result, err + } + } + result.Config, err = buildConfig(result.Config, *hiddifyconfig) + if err != nil { + return result, err + } + + return result, nil +} + +func readConfigContent(configPath string) (ConfigResult, error) { + var content string + var refreshInterval int + + if strings.HasPrefix(configPath, "http://") || strings.HasPrefix(configPath, "https://") { + client := &http.Client{} + + // Create a new request + req, err := http.NewRequest("GET", configPath, nil) + if err != nil { + fmt.Println("Error creating request:", err) + return ConfigResult{}, err + } + req.Header.Set("User-Agent", "HiddifyNext/17.5.0 ("+runtime.GOOS+") like ClashMeta v2ray sing-box") + resp, err := client.Do(req) + if err != nil { + fmt.Println("Error making GET request:", err) + return ConfigResult{}, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ConfigResult{}, fmt.Errorf("failed to read config body: %w", err) + } + content = string(body) + refreshInterval, _ = extractRefreshInterval(resp.Header, content) + fmt.Printf("Refresh interval: %d\n", refreshInterval) + } else { + data, err := ioutil.ReadFile(configPath) + if err != nil { + return ConfigResult{}, fmt.Errorf("failed to read config file: %w", err) + } + content = string(data) + } + + return ConfigResult{ + Config: content, + RefreshInterval: refreshInterval, + }, nil +} + +func extractRefreshInterval(header http.Header, bodyStr string) (int, error) { + refreshIntervalStr := header.Get("profile-update-interval") + if refreshIntervalStr != "" { + refreshInterval, err := strconv.Atoi(refreshIntervalStr) + if err != nil { + return 0, fmt.Errorf("failed to parse refresh interval from header: %w", err) + } + return refreshInterval, nil + } + + lines := strings.Split(bodyStr, "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "//profile-update-interval:") || strings.HasPrefix(line, "#profile-update-interval:") { + parts := strings.SplitN(line, ":", 2) + str := strings.TrimSpace(parts[1]) + refreshInterval, err := strconv.Atoi(str) + if err != nil { + return 0, fmt.Errorf("failed to parse refresh interval from body: %w", err) + } + return refreshInterval, nil + } + } + return 0, nil +} +func buildConfig(configContent string, options config.ConfigOptions) (string, error) { + parsedContent, err := config.ParseConfigContent(configContent, true) + if err != nil { + return "", fmt.Errorf("failed to parse config content: %w", err) + } + singconfigs, err := readConfigBytes([]byte(parsedContent)) + if err != nil { + return "", err + } + + finalconfig, err := config.BuildConfig(options, *singconfigs) + if err != nil { + return "", fmt.Errorf("failed to build config: %w", err) + } + + finalconfig.Log.Output = "" + finalconfig.Experimental.ClashAPI.ExternalUI = "webui" + if options.AllowConnectionFromLAN { + finalconfig.Experimental.ClashAPI.ExternalController = "0.0.0.0:6756" + } else { + finalconfig.Experimental.ClashAPI.ExternalController = "127.0.0.1:6756" + } + + if finalconfig.Experimental.ClashAPI.Secret == "" { + // finalconfig.Experimental.ClashAPI.Secret = "hiddify" + } + + if err := SetupC("./", "./", "./tmp", false); err != nil { + return "", fmt.Errorf("failed to set up global configuration: %w", err) + } + + configStr, err := config.ToJson(*finalconfig) + if err != nil { + return "", fmt.Errorf("failed to convert config to JSON: %w", err) + } + + return configStr, nil +} + +func updateConfigInterval(current ConfigResult, hiddifySettingPath string, configPath string) { + if current.RefreshInterval <= 0 { + return + } + + for { + <-time.After(time.Duration(current.RefreshInterval) * time.Hour) + new, err := readAndBuildConfig(hiddifySettingPath, configPath) + if err != nil { + continue + } + if new.Config != current.Config { + go StopServiceC() + go StartServiceC(false, new.Config) + } + current = new + } + +} + +func readConfigBytes(content []byte) (*option.Options, error) { + var options option.Options + err := options.UnmarshalJSON(content) + if err != nil { + return nil, err + } + return &options, nil +} + +func readConfigOptionsAt(path string) (*config.ConfigOptions, error) { + content, err := os.ReadFile(path) + if err != nil { + return nil, err + } + var options config.ConfigOptions + err = json.Unmarshal(content, &options) + + if err != nil { + return nil, err + } + if options.Warp.WireguardConfigStr != "" { + err := json.Unmarshal([]byte(options.Warp.WireguardConfigStr), &options.Warp.WireguardConfig) + if err != nil { + return nil, err + } + } + + return &options, nil +} diff --git a/hiddifyrpc/hiddify.proto b/hiddifyrpc/hiddify.proto index 4cfec46..aadeaa3 100644 --- a/hiddifyrpc/hiddify.proto +++ b/hiddifyrpc/hiddify.proto @@ -1,214 +1,214 @@ -syntax = "proto3"; - -package hiddifyrpc; - -option go_package = "./hiddifyrpc"; - -enum ResponseCode { - OK = 0; - FAILED = 1; -} - -enum CoreState { - STOPPED = 0; - STARTING = 1; - STARTED = 2; - STOPPING = 3; -} - -enum MessageType { - EMPTY=0; - EMPTY_CONFIGURATION = 1; - START_COMMAND_SERVER = 2; - CREATE_SERVICE = 3; - START_SERVICE = 4; - UNEXPECTED_ERROR = 5; - ALREADY_STARTED = 6; - ALREADY_STOPPED = 7; - INSTANCE_NOT_FOUND = 8; - INSTANCE_NOT_STOPPED = 9; - INSTANCE_NOT_STARTED = 10; - ERROR_BUILDING_CONFIG = 11; - ERROR_PARSING_CONFIG = 12; - ERROR_READING_CONFIG = 13; -} - -message CoreInfoResponse { - CoreState core_state = 1; - MessageType message_type = 2; - string message = 3; -} - -message StartRequest { - string config_path = 1; - string config_content = 2; // Optional if configPath is not provided. - bool disable_memory_limit = 3; - bool delay_start = 4; -} - -message SetupRequest { - string base_path = 1; - string working_path = 2; - string temp_path = 3; -} - -message Response { - ResponseCode response_code = 1; - string message = 2; -} - - -message HelloRequest { - string name = 1; -} - -message HelloResponse { - string message = 1; -} - -message Empty { -} - -message SystemInfo { - int64 memory = 1; - int32 goroutines = 2; - int32 connections_in = 3; - int32 connections_out = 4; - bool traffic_available = 5; - int64 uplink = 6; - int64 downlink = 7; - int64 uplink_total = 8; - int64 downlink_total = 9; -} - -message OutboundGroupItem { - string tag = 1; - string type = 2; - int64 url_test_time = 3; - int32 url_test_delay = 4; -} - -message OutboundGroup { - string tag = 1; - string type = 2; - string selected=3; - repeated OutboundGroupItem items = 4; - -} -message OutboundGroupList{ - repeated OutboundGroup items = 1; -} - -message WarpAccount { - string account_id = 1; - string access_token = 2; -} - -message WarpWireguardConfig { - string private_key = 1; - string local_address_ipv4 = 2; - string local_address_ipv6 = 3; - string peer_public_key = 4; -} - -message WarpGenerationResponse { - WarpAccount account = 1; - string log = 2; - WarpWireguardConfig config = 3; -} - -message SystemProxyStatus { - bool available = 1; - bool enabled = 2; -} - -message ParseRequest { - string content = 1; - bool debug = 2; -} - -message ParseResponse { - ResponseCode response_code = 1; - string content = 2; - string message = 3; -} - -message ChangeConfigOptionsRequest { - string config_options_json = 1; -} - -message GenerateConfigRequest { - string path = 1; - string temp_path = 2; - bool debug = 3; -} - -message GenerateConfigResponse { - string config_content = 1; -} - - - -message SelectOutboundRequest { - string group_tag = 1; - string outbound_tag = 2; -} - -message UrlTestRequest { - string group_tag = 1; -} - -message GenerateWarpConfigRequest { - string license_key = 1; - string account_id = 2; - string access_token = 3; -} - -message SetSystemProxyEnabledRequest { - bool is_enabled = 1; -} - -enum LogLevel { - DEBUG = 0; - INFO = 1; - WARNING = 2; - ERROR = 3; - FATAL = 4; -} -enum LogType { - CORE = 0; - SERVICE = 1; - CONFIG = 2; -} -message LogMessage { - LogLevel level = 1; - LogType type = 2; - string message = 3; -} - -message StopRequest{ -} - -service Hiddify { - rpc SayHello (HelloRequest) returns (HelloResponse); - rpc SayHelloStream (stream HelloRequest) returns (stream HelloResponse); - rpc Start (StartRequest) returns (CoreInfoResponse); - rpc CoreInfoListener (stream StopRequest) returns (stream CoreInfoResponse); - rpc OutboundsInfo (stream StopRequest) returns (stream OutboundGroupList); - rpc MainOutboundsInfo (stream StopRequest) returns (stream OutboundGroupList); - rpc GetSystemInfo (stream StopRequest) returns (stream SystemInfo); - rpc Setup (SetupRequest) returns (Response); - rpc Parse (ParseRequest) returns (ParseResponse); - //rpc ChangeConfigOptions (ChangeConfigOptionsRequest) returns (CoreInfoResponse); - //rpc GenerateConfig (GenerateConfigRequest) returns (GenerateConfigResponse); - rpc StartService (StartRequest) returns (CoreInfoResponse); - rpc Stop (Empty) returns (CoreInfoResponse); - rpc Restart (StartRequest) returns (CoreInfoResponse); - rpc SelectOutbound (SelectOutboundRequest) returns (Response); - rpc UrlTest (UrlTestRequest) returns (Response); - rpc GenerateWarpConfig (GenerateWarpConfigRequest) returns (WarpGenerationResponse); - rpc GetSystemProxyStatus (Empty) returns (SystemProxyStatus); - rpc SetSystemProxyEnabled (SetSystemProxyEnabledRequest) returns (Response); - rpc LogListener (stream StopRequest) returns (stream LogMessage); - -} +syntax = "proto3"; + +package hiddifyrpc; + +option go_package = "./hiddifyrpc"; + +enum ResponseCode { + OK = 0; + FAILED = 1; +} + +enum CoreState { + STOPPED = 0; + STARTING = 1; + STARTED = 2; + STOPPING = 3; +} + +enum MessageType { + EMPTY=0; + EMPTY_CONFIGURATION = 1; + START_COMMAND_SERVER = 2; + CREATE_SERVICE = 3; + START_SERVICE = 4; + UNEXPECTED_ERROR = 5; + ALREADY_STARTED = 6; + ALREADY_STOPPED = 7; + INSTANCE_NOT_FOUND = 8; + INSTANCE_NOT_STOPPED = 9; + INSTANCE_NOT_STARTED = 10; + ERROR_BUILDING_CONFIG = 11; + ERROR_PARSING_CONFIG = 12; + ERROR_READING_CONFIG = 13; +} + +message CoreInfoResponse { + CoreState core_state = 1; + MessageType message_type = 2; + string message = 3; +} + +message StartRequest { + string config_path = 1; + string config_content = 2; // Optional if configPath is not provided. + bool disable_memory_limit = 3; + bool delay_start = 4; +} + +message SetupRequest { + string base_path = 1; + string working_path = 2; + string temp_path = 3; +} + +message Response { + ResponseCode response_code = 1; + string message = 2; +} + + +message HelloRequest { + string name = 1; +} + +message HelloResponse { + string message = 1; +} + +message Empty { +} + +message SystemInfo { + int64 memory = 1; + int32 goroutines = 2; + int32 connections_in = 3; + int32 connections_out = 4; + bool traffic_available = 5; + int64 uplink = 6; + int64 downlink = 7; + int64 uplink_total = 8; + int64 downlink_total = 9; +} + +message OutboundGroupItem { + string tag = 1; + string type = 2; + int64 url_test_time = 3; + int32 url_test_delay = 4; +} + +message OutboundGroup { + string tag = 1; + string type = 2; + string selected=3; + repeated OutboundGroupItem items = 4; + +} +message OutboundGroupList{ + repeated OutboundGroup items = 1; +} + +message WarpAccount { + string account_id = 1; + string access_token = 2; +} + +message WarpWireguardConfig { + string private_key = 1; + string local_address_ipv4 = 2; + string local_address_ipv6 = 3; + string peer_public_key = 4; +} + +message WarpGenerationResponse { + WarpAccount account = 1; + string log = 2; + WarpWireguardConfig config = 3; +} + +message SystemProxyStatus { + bool available = 1; + bool enabled = 2; +} + +message ParseRequest { + string content = 1; + bool debug = 2; +} + +message ParseResponse { + ResponseCode response_code = 1; + string content = 2; + string message = 3; +} + +message ChangeConfigOptionsRequest { + string config_options_json = 1; +} + +message GenerateConfigRequest { + string path = 1; + string temp_path = 2; + bool debug = 3; +} + +message GenerateConfigResponse { + string config_content = 1; +} + + + +message SelectOutboundRequest { + string group_tag = 1; + string outbound_tag = 2; +} + +message UrlTestRequest { + string group_tag = 1; +} + +message GenerateWarpConfigRequest { + string license_key = 1; + string account_id = 2; + string access_token = 3; +} + +message SetSystemProxyEnabledRequest { + bool is_enabled = 1; +} + +enum LogLevel { + DEBUG = 0; + INFO = 1; + WARNING = 2; + ERROR = 3; + FATAL = 4; +} +enum LogType { + CORE = 0; + SERVICE = 1; + CONFIG = 2; +} +message LogMessage { + LogLevel level = 1; + LogType type = 2; + string message = 3; +} + +message StopRequest{ +} + +service Hiddify { + rpc SayHello (HelloRequest) returns (HelloResponse); + rpc SayHelloStream (stream HelloRequest) returns (stream HelloResponse); + rpc Start (StartRequest) returns (CoreInfoResponse); + rpc CoreInfoListener (stream StopRequest) returns (stream CoreInfoResponse); + rpc OutboundsInfo (stream StopRequest) returns (stream OutboundGroupList); + rpc MainOutboundsInfo (stream StopRequest) returns (stream OutboundGroupList); + rpc GetSystemInfo (stream StopRequest) returns (stream SystemInfo); + rpc Setup (SetupRequest) returns (Response); + rpc Parse (ParseRequest) returns (ParseResponse); + //rpc ChangeConfigOptions (ChangeConfigOptionsRequest) returns (CoreInfoResponse); + //rpc GenerateConfig (GenerateConfigRequest) returns (GenerateConfigResponse); + rpc StartService (StartRequest) returns (CoreInfoResponse); + rpc Stop (Empty) returns (CoreInfoResponse); + rpc Restart (StartRequest) returns (CoreInfoResponse); + rpc SelectOutbound (SelectOutboundRequest) returns (Response); + rpc UrlTest (UrlTestRequest) returns (Response); + rpc GenerateWarpConfig (GenerateWarpConfigRequest) returns (WarpGenerationResponse); + rpc GetSystemProxyStatus (Empty) returns (SystemProxyStatus); + rpc SetSystemProxyEnabled (SetSystemProxyEnabledRequest) returns (Response); + rpc LogListener (stream StopRequest) returns (stream LogMessage); + +} diff --git a/v2/commands.go b/v2/commands.go index 7a4c4c1..4341b4c 100644 --- a/v2/commands.go +++ b/v2/commands.go @@ -1,163 +1,163 @@ -package v2 - -import ( - "context" - "time" - - 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 ( - statusClient *libbox.CommandClient - groupClient *libbox.CommandClient - groupInfoOnlyClient *libbox.CommandClient -) - -func (s *server) GetSystemInfo(stream pb.Hiddify_GetSystemInfoServer) error { - if statusClient == nil { - statusClient = libbox.NewCommandClient( - &CommandClientHandler{}, - &libbox.CommandClientOptions{ - Command: libbox.CommandStatus, - StatusInterval: 1000000000, //1000ms debounce - }, - ) - - defer func() { - statusClient.Disconnect() - statusClient = nil - }() - statusClient.Connect() - } - - sub, _, _ := systemInfoObserver.Subscribe() - stopch := make(chan int) - go func() { - stream.Recv() - close(stopch) - }() - for { - select { - case <-stream.Context().Done(): - break - case <-stopch: - break - case info := <-sub: - stream.Send(&info) - case <-time.After(1000 * time.Millisecond): - } - } -} - -func (s *server) OutboundsInfo(stream pb.Hiddify_OutboundsInfoServer) error { - if groupClient == nil { - groupClient = libbox.NewCommandClient( - &CommandClientHandler{}, - &libbox.CommandClientOptions{ - Command: libbox.CommandGroup, - StatusInterval: 500000000, //500ms debounce - }, - ) - - defer func() { - groupClient.Disconnect() - groupClient = nil - }() - groupClient.Connect() - } - - sub, _, _ := outboundsInfoObserver.Subscribe() - stopch := make(chan int) - go func() { - stream.Recv() - close(stopch) - }() - for { - select { - case <-stream.Context().Done(): - break - case <-stopch: - break - case info := <-sub: - stream.Send(&info) - case <-time.After(500 * time.Millisecond): - } - } -} - -func (s *server) MainOutboundsInfo(stream pb.Hiddify_MainOutboundsInfoServer) error { - if groupInfoOnlyClient == nil { - groupInfoOnlyClient = libbox.NewCommandClient( - &CommandClientHandler{}, - &libbox.CommandClientOptions{ - Command: libbox.CommandGroupInfoOnly, - StatusInterval: 500000000, //500ms debounce - }, - ) - - defer func() { - groupInfoOnlyClient.Disconnect() - groupInfoOnlyClient = nil - }() - groupInfoOnlyClient.Connect() - } - - sub, _, _ := mainOutboundsInfoObserver.Subscribe() - stopch := make(chan int) - go func() { - stream.Recv() - close(stopch) - }() - for { - select { - case <-stream.Context().Done(): - break - case <-stopch: - break - case info := <-sub: - stream.Send(&info) - case <-time.After(500 * time.Millisecond): - } - } -} - -// Implement the SelectOutbound method -func (s *server) SelectOutbound(ctx context.Context, in *pb.SelectOutboundRequest) (*pb.Response, error) { - err := libbox.NewStandaloneCommandClient().SelectOutbound(in.GroupTag, in.OutboundTag) - - if err != nil { - return &pb.Response{ - ResponseCode: pb.ResponseCode_FAILED, - Message: err.Error(), - }, err - } - - return &pb.Response{ - ResponseCode: pb.ResponseCode_OK, - Message: "", - }, nil -} - -// Implement the UrlTest method -func (s *server) UrlTest(ctx context.Context, in *pb.UrlTestRequest) (*pb.Response, error) { - err := libbox.NewStandaloneCommandClient().URLTest(in.GroupTag) - - if err != nil { - return &pb.Response{ - ResponseCode: pb.ResponseCode_FAILED, - Message: err.Error(), - }, err - } - - return &pb.Response{ - ResponseCode: pb.ResponseCode_OK, - Message: "", - }, nil - -} +package v2 + +import ( + "context" + "time" + + 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 ( + statusClient *libbox.CommandClient + groupClient *libbox.CommandClient + groupInfoOnlyClient *libbox.CommandClient +) + +func (s *server) GetSystemInfo(stream pb.Hiddify_GetSystemInfoServer) error { + if statusClient == nil { + statusClient = libbox.NewCommandClient( + &CommandClientHandler{}, + &libbox.CommandClientOptions{ + Command: libbox.CommandStatus, + StatusInterval: 1000000000, //1000ms debounce + }, + ) + + defer func() { + statusClient.Disconnect() + statusClient = nil + }() + statusClient.Connect() + } + + sub, _, _ := systemInfoObserver.Subscribe() + stopch := make(chan int) + go func() { + stream.Recv() + close(stopch) + }() + for { + select { + case <-stream.Context().Done(): + break + case <-stopch: + break + case info := <-sub: + stream.Send(&info) + case <-time.After(1000 * time.Millisecond): + } + } +} + +func (s *server) OutboundsInfo(stream pb.Hiddify_OutboundsInfoServer) error { + if groupClient == nil { + groupClient = libbox.NewCommandClient( + &CommandClientHandler{}, + &libbox.CommandClientOptions{ + Command: libbox.CommandGroup, + StatusInterval: 500000000, //500ms debounce + }, + ) + + defer func() { + groupClient.Disconnect() + groupClient = nil + }() + groupClient.Connect() + } + + sub, _, _ := outboundsInfoObserver.Subscribe() + stopch := make(chan int) + go func() { + stream.Recv() + close(stopch) + }() + for { + select { + case <-stream.Context().Done(): + break + case <-stopch: + break + case info := <-sub: + stream.Send(&info) + case <-time.After(500 * time.Millisecond): + } + } +} + +func (s *server) MainOutboundsInfo(stream pb.Hiddify_MainOutboundsInfoServer) error { + if groupInfoOnlyClient == nil { + groupInfoOnlyClient = libbox.NewCommandClient( + &CommandClientHandler{}, + &libbox.CommandClientOptions{ + Command: libbox.CommandGroupInfoOnly, + StatusInterval: 500000000, //500ms debounce + }, + ) + + defer func() { + groupInfoOnlyClient.Disconnect() + groupInfoOnlyClient = nil + }() + groupInfoOnlyClient.Connect() + } + + sub, _, _ := mainOutboundsInfoObserver.Subscribe() + stopch := make(chan int) + go func() { + stream.Recv() + close(stopch) + }() + for { + select { + case <-stream.Context().Done(): + break + case <-stopch: + break + case info := <-sub: + stream.Send(&info) + case <-time.After(500 * time.Millisecond): + } + } +} + +// Implement the SelectOutbound method +func (s *server) SelectOutbound(ctx context.Context, in *pb.SelectOutboundRequest) (*pb.Response, error) { + err := libbox.NewStandaloneCommandClient().SelectOutbound(in.GroupTag, in.OutboundTag) + + if err != nil { + return &pb.Response{ + ResponseCode: pb.ResponseCode_FAILED, + Message: err.Error(), + }, err + } + + return &pb.Response{ + ResponseCode: pb.ResponseCode_OK, + Message: "", + }, nil +} + +// Implement the UrlTest method +func (s *server) UrlTest(ctx context.Context, in *pb.UrlTestRequest) (*pb.Response, error) { + err := libbox.NewStandaloneCommandClient().URLTest(in.GroupTag) + + if err != nil { + return &pb.Response{ + ResponseCode: pb.ResponseCode_FAILED, + Message: err.Error(), + }, err + } + + return &pb.Response{ + ResponseCode: pb.ResponseCode_OK, + Message: "", + }, nil + +} diff --git a/v2/coreinfo.go b/v2/coreinfo.go index 7c59312..696e286 100644 --- a/v2/coreinfo.go +++ b/v2/coreinfo.go @@ -1,44 +1,44 @@ -package v2 - -import ( - "time" - - pb "github.com/hiddify/libcore/hiddifyrpc" - "github.com/sagernet/sing/common/observable" -) - -var coreInfoObserver = observable.Observer[pb.CoreInfoResponse]{} -var CoreState = pb.CoreState_STOPPED - -func SetCoreStatus(state pb.CoreState, msgType pb.MessageType, message string) pb.CoreInfoResponse { - CoreState = state - info := pb.CoreInfoResponse{ - CoreState: state, - MessageType: msgType, - Message: message, - } - coreInfoObserver.Emit(info) - return info - -} - -func (s *server) CoreInfoListener(stream pb.Hiddify_CoreInfoListenerServer) error { - coreSub, _, _ := coreInfoObserver.Subscribe() - defer coreInfoObserver.UnSubscribe(coreSub) - stopch := make(chan int) - go func() { - stream.Recv() - close(stopch) - }() - for { - select { - case <-stream.Context().Done(): - break - case <-stopch: - break - case info := <-coreSub: - stream.Send(&info) - case <-time.After(500 * time.Millisecond): - } - } -} +package v2 + +import ( + "time" + + pb "github.com/hiddify/libcore/hiddifyrpc" + "github.com/sagernet/sing/common/observable" +) + +var coreInfoObserver = observable.Observer[pb.CoreInfoResponse]{} +var CoreState = pb.CoreState_STOPPED + +func SetCoreStatus(state pb.CoreState, msgType pb.MessageType, message string) pb.CoreInfoResponse { + CoreState = state + info := pb.CoreInfoResponse{ + CoreState: state, + MessageType: msgType, + Message: message, + } + coreInfoObserver.Emit(info) + return info + +} + +func (s *server) CoreInfoListener(stream pb.Hiddify_CoreInfoListenerServer) error { + coreSub, _, _ := coreInfoObserver.Subscribe() + defer coreInfoObserver.UnSubscribe(coreSub) + stopch := make(chan int) + go func() { + stream.Recv() + close(stopch) + }() + for { + select { + case <-stream.Context().Done(): + break + case <-stopch: + break + case info := <-coreSub: + stream.Send(&info) + case <-time.After(500 * time.Millisecond): + } + } +} diff --git a/v2/custom.go b/v2/custom.go index 571c5f6..57cdf2e 100644 --- a/v2/custom.go +++ b/v2/custom.go @@ -1,232 +1,232 @@ -package v2 - -import ( - "context" - "fmt" - "os" - "path/filepath" - "time" - - "github.com/hiddify/libcore/config" - pb "github.com/hiddify/libcore/hiddifyrpc" - "github.com/sagernet/sing-box/experimental/libbox" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-box/option" -) - -var Box *libbox.BoxService -var configOptions *config.ConfigOptions -var activeConfigPath *string -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 Box != nil { - Box.Close() - Box = nil - } - // if commandServer != nil { - // commandServer.Close() - // } -} - -func (s *server) Start(ctx context.Context, 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()) - }) - - if CoreState != pb.CoreState_STOPPED { - return &pb.CoreInfoResponse{ - CoreState: CoreState, - MessageType: pb.MessageType_INSTANCE_NOT_STOPPED, - }, fmt.Errorf("instance not stopped") - } - SetCoreStatus(pb.CoreState_STARTING, pb.MessageType_EMPTY, "") - - libbox.SetMemoryLimit(!in.DisableMemoryLimit) - resp, err := s.StartService(ctx, in) - return resp, err -} - -// Implement the StartService method -func (s *server) StartService(ctx context.Context, in *pb.StartRequest) (*pb.CoreInfoResponse, error) { - - content := in.ConfigContent - if content != "" { - fileContent, err := os.ReadFile(*activeConfigPath) - if err != nil { - resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_ERROR_READING_CONFIG, err.Error()) - return &resp, err - } - content = string(fileContent) - } - - parsedContent, err := parseConfig(content) - if err != nil { - 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 { - 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 - // } - - instance, err := NewService(*patchedOptions) - if err != nil { - resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_CREATE_SERVICE, err.Error()) - return &resp, err - } - - if in.DelayStart { - <-time.After(250 * time.Millisecond) - } - - err = instance.Start() - if err != nil { - resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_START_SERVICE, err.Error()) - return &resp, err - } - Box = instance - // 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) { - defer config.DeferPanicToError("parse", func(err error) { - Log(pb.LogLevel_FATAL, pb.LogType_CONFIG, err.Error()) - StopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error()) - }) - - config, err := config.ParseConfigContent(in.Content, true) - if err != nil { - return &pb.ParseResponse{ - ResponseCode: pb.ResponseCode_FAILED, - Message: err.Error(), - }, err - } - return &pb.ParseResponse{ - ResponseCode: pb.ResponseCode_OK, - Content: string(config), - Message: "", - }, 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 *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()) -// }) - -// config, err := generateConfigFromFile(C.GoString(path), *configOptions) -// if err != nil { -// return C.CString("error" + err.Error()) -// } -// return C.CString(config) -// } - -// Implement the Stop method -func (s *server) Stop(ctx context.Context, empty *pb.Empty) (*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{ - CoreState: CoreState, - MessageType: pb.MessageType_INSTANCE_NOT_STARTED, - Message: "instance is not started", - }, fmt.Errorf("instance not started") - } - if Box == nil { - return &pb.CoreInfoResponse{ - CoreState: CoreState, - MessageType: pb.MessageType_INSTANCE_NOT_FOUND, - Message: "instance is not found", - }, fmt.Errorf("instance not found") - } - SetCoreStatus(pb.CoreState_STOPPING, pb.MessageType_EMPTY, "") - // commandServer.SetService(nil) - - err := Box.Close() - if err != nil { - return &pb.CoreInfoResponse{ - CoreState: CoreState, - MessageType: pb.MessageType_UNEXPECTED_ERROR, - Message: "Error while stopping the service.", - }, 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 - resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_EMPTY, "") - return &resp, nil - -} - -func (s *server) Restart(ctx context.Context, 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()) - }) - log.Debug("[Service] Restarting") - - if CoreState != pb.CoreState_STARTED { - return &pb.CoreInfoResponse{ - CoreState: CoreState, - MessageType: pb.MessageType_INSTANCE_NOT_STARTED, - Message: "instance is not started", - }, fmt.Errorf("instance not started") - } - if Box == nil { - return &pb.CoreInfoResponse{ - CoreState: CoreState, - MessageType: pb.MessageType_INSTANCE_NOT_FOUND, - Message: "instance is not found", - }, fmt.Errorf("instance not found") - } - - resp, err := s.Stop(ctx, &pb.Empty{}) - if err != nil { - return resp, err - } - - SetCoreStatus(pb.CoreState_STARTING, pb.MessageType_EMPTY, "") - <-time.After(250 * time.Millisecond) - - libbox.SetMemoryLimit(!in.DisableMemoryLimit) - resp, gErr := s.StartService(ctx, in) - return resp, gErr -} +package v2 + +import ( + "context" + "fmt" + "os" + "path/filepath" + "time" + + "github.com/hiddify/libcore/config" + pb "github.com/hiddify/libcore/hiddifyrpc" + "github.com/sagernet/sing-box/experimental/libbox" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" +) + +var Box *libbox.BoxService +var configOptions *config.ConfigOptions +var activeConfigPath *string +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 Box != nil { + Box.Close() + Box = nil + } + // if commandServer != nil { + // commandServer.Close() + // } +} + +func (s *server) Start(ctx context.Context, 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()) + }) + + if CoreState != pb.CoreState_STOPPED { + return &pb.CoreInfoResponse{ + CoreState: CoreState, + MessageType: pb.MessageType_INSTANCE_NOT_STOPPED, + }, fmt.Errorf("instance not stopped") + } + SetCoreStatus(pb.CoreState_STARTING, pb.MessageType_EMPTY, "") + + libbox.SetMemoryLimit(!in.DisableMemoryLimit) + resp, err := s.StartService(ctx, in) + return resp, err +} + +// Implement the StartService method +func (s *server) StartService(ctx context.Context, in *pb.StartRequest) (*pb.CoreInfoResponse, error) { + + content := in.ConfigContent + if content != "" { + fileContent, err := os.ReadFile(*activeConfigPath) + if err != nil { + resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_ERROR_READING_CONFIG, err.Error()) + return &resp, err + } + content = string(fileContent) + } + + parsedContent, err := parseConfig(content) + if err != nil { + 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 { + 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 + // } + + instance, err := NewService(*patchedOptions) + if err != nil { + resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_CREATE_SERVICE, err.Error()) + return &resp, err + } + + if in.DelayStart { + <-time.After(250 * time.Millisecond) + } + + err = instance.Start() + if err != nil { + resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_START_SERVICE, err.Error()) + return &resp, err + } + Box = instance + // 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) { + defer config.DeferPanicToError("parse", func(err error) { + Log(pb.LogLevel_FATAL, pb.LogType_CONFIG, err.Error()) + StopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error()) + }) + + config, err := config.ParseConfigContent(in.Content, true) + if err != nil { + return &pb.ParseResponse{ + ResponseCode: pb.ResponseCode_FAILED, + Message: err.Error(), + }, err + } + return &pb.ParseResponse{ + ResponseCode: pb.ResponseCode_OK, + Content: string(config), + Message: "", + }, 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 *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()) +// }) + +// config, err := generateConfigFromFile(C.GoString(path), *configOptions) +// if err != nil { +// return C.CString("error" + err.Error()) +// } +// return C.CString(config) +// } + +// Implement the Stop method +func (s *server) Stop(ctx context.Context, empty *pb.Empty) (*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{ + CoreState: CoreState, + MessageType: pb.MessageType_INSTANCE_NOT_STARTED, + Message: "instance is not started", + }, fmt.Errorf("instance not started") + } + if Box == nil { + return &pb.CoreInfoResponse{ + CoreState: CoreState, + MessageType: pb.MessageType_INSTANCE_NOT_FOUND, + Message: "instance is not found", + }, fmt.Errorf("instance not found") + } + SetCoreStatus(pb.CoreState_STOPPING, pb.MessageType_EMPTY, "") + // commandServer.SetService(nil) + + err := Box.Close() + if err != nil { + return &pb.CoreInfoResponse{ + CoreState: CoreState, + MessageType: pb.MessageType_UNEXPECTED_ERROR, + Message: "Error while stopping the service.", + }, 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 + resp := SetCoreStatus(pb.CoreState_STOPPED, pb.MessageType_EMPTY, "") + return &resp, nil + +} + +func (s *server) Restart(ctx context.Context, 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()) + }) + log.Debug("[Service] Restarting") + + if CoreState != pb.CoreState_STARTED { + return &pb.CoreInfoResponse{ + CoreState: CoreState, + MessageType: pb.MessageType_INSTANCE_NOT_STARTED, + Message: "instance is not started", + }, fmt.Errorf("instance not started") + } + if Box == nil { + return &pb.CoreInfoResponse{ + CoreState: CoreState, + MessageType: pb.MessageType_INSTANCE_NOT_FOUND, + Message: "instance is not found", + }, fmt.Errorf("instance not found") + } + + resp, err := s.Stop(ctx, &pb.Empty{}) + if err != nil { + return resp, err + } + + SetCoreStatus(pb.CoreState_STARTING, pb.MessageType_EMPTY, "") + <-time.After(250 * time.Millisecond) + + libbox.SetMemoryLimit(!in.DisableMemoryLimit) + resp, gErr := s.StartService(ctx, in) + return resp, gErr +} diff --git a/v2/example_client/main.go b/v2/example_client/main.go index a3ae9e7..88ecb7a 100644 --- a/v2/example_client/main.go +++ b/v2/example_client/main.go @@ -1,62 +1,62 @@ -package main - -import ( - "context" - "log" - "time" - - pb "github.com/hiddify/libcore/hiddifyrpc" - - "google.golang.org/grpc" -) - -const ( - address = "localhost:50051" - defaultName = "world" -) - -func main() { - conn, err := grpc.Dial(address, grpc.WithInsecure()) - if err != nil { - log.Fatalf("did not connect: %v", err) - } - defer conn.Close() - c := pb.NewHiddifyClient(conn) - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) - defer cancel() - - // SayHello - r, err := c.SayHello(ctx, &pb.HelloRequest{Name: defaultName}) - if err != nil { - log.Fatalf("could not greet: %v", err) - } - log.Printf("Greeting: %s", r.Message) - - // SayHelloStream - stream, err := c.SayHelloStream(ctx) - if err != nil { - log.Fatalf("could not stream: %v", err) - } - - names := []string{"Alice", "Bob", "Charlie"} - - for _, name := range names { - err := stream.Send(&pb.HelloRequest{Name: name}) - if err != nil { - log.Fatalf("could not send: %v", err) - } - r, err := stream.Recv() - if err != nil { - log.Fatalf("could not receive: %v", err) - } - log.Printf("Received1: %s", r.Message) - r2, err2 := stream.Recv() - if err2 != nil { - log.Fatalf("could not receive2: %v", err2) - } - log.Printf("Received: %s", r2.Message) - time.Sleep(1 * time.Second) - } - -} +package main + +import ( + "context" + "log" + "time" + + pb "github.com/hiddify/libcore/hiddifyrpc" + + "google.golang.org/grpc" +) + +const ( + address = "localhost:50051" + defaultName = "world" +) + +func main() { + conn, err := grpc.Dial(address, grpc.WithInsecure()) + if err != nil { + log.Fatalf("did not connect: %v", err) + } + defer conn.Close() + c := pb.NewHiddifyClient(conn) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + // SayHello + r, err := c.SayHello(ctx, &pb.HelloRequest{Name: defaultName}) + if err != nil { + log.Fatalf("could not greet: %v", err) + } + log.Printf("Greeting: %s", r.Message) + + // SayHelloStream + stream, err := c.SayHelloStream(ctx) + if err != nil { + log.Fatalf("could not stream: %v", err) + } + + names := []string{"Alice", "Bob", "Charlie"} + + for _, name := range names { + err := stream.Send(&pb.HelloRequest{Name: name}) + if err != nil { + log.Fatalf("could not send: %v", err) + } + r, err := stream.Recv() + if err != nil { + log.Fatalf("could not receive: %v", err) + } + log.Printf("Received1: %s", r.Message) + r2, err2 := stream.Recv() + if err2 != nil { + log.Fatalf("could not receive2: %v", err2) + } + log.Printf("Received: %s", r2.Message) + time.Sleep(1 * time.Second) + } + +} diff --git a/v2/example_server/main.go b/v2/example_server/main.go index f4ae948..5bb0a15 100644 --- a/v2/example_server/main.go +++ b/v2/example_server/main.go @@ -1,18 +1,18 @@ -package main - -import ( - "os" - "os/signal" - "syscall" - - v2 "github.com/hiddify/libcore/v2" -) - -func main() { - - // defer C.free(unsafe.Pointer(port)) - v2.StartGrpcServerGo("127.0.0.1:50051") - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) - <-sigChan -} +package main + +import ( + "os" + "os/signal" + "syscall" + + v2 "github.com/hiddify/libcore/v2" +) + +func main() { + + // defer C.free(unsafe.Pointer(port)) + v2.StartGrpcServerGo("127.0.0.1:50051") + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) + <-sigChan +} diff --git a/v2/grpc_server.go b/v2/grpc_server.go index 231fae4..a0f6f2e 100644 --- a/v2/grpc_server.go +++ b/v2/grpc_server.go @@ -1,49 +1,49 @@ -package v2 - -/* -#include "stdint.h" -*/ - -import "C" -import ( - "log" - "net" - - pb "github.com/hiddify/libcore/hiddifyrpc" - "google.golang.org/grpc" -) - -type server struct { - pb.UnimplementedHiddifyServer -} - -//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 -} - -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 - - lis, err := net.Listen("tcp", listenAddressG) - if err != nil { - log.Printf("failed to listen: %v", err) - return err - } - s := grpc.NewServer() - pb.RegisterHiddifyServer(s, &server{}) - log.Printf("Server listening on %s", listenAddressG) - go func() { - if err := s.Serve(lis); err != nil { - log.Printf("failed to serve: %v", err) - } - }() - return nil -} +package v2 + +/* +#include "stdint.h" +*/ + +import "C" +import ( + "log" + "net" + + pb "github.com/hiddify/libcore/hiddifyrpc" + "google.golang.org/grpc" +) + +type server struct { + pb.UnimplementedHiddifyServer +} + +//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 +} + +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 + + lis, err := net.Listen("tcp", listenAddressG) + if err != nil { + log.Printf("failed to listen: %v", err) + return err + } + s := grpc.NewServer() + pb.RegisterHiddifyServer(s, &server{}) + log.Printf("Server listening on %s", listenAddressG) + go func() { + if err := s.Serve(lis); err != nil { + log.Printf("failed to serve: %v", err) + } + }() + return nil +} diff --git a/v2/hello.go b/v2/hello.go index 30aad98..e29965b 100644 --- a/v2/hello.go +++ b/v2/hello.go @@ -1,36 +1,36 @@ -package v2 - -import ( - "context" - "log" - "time" - - pb "github.com/hiddify/libcore/hiddifyrpc" -) - -func (s *server) 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 { - - for { - req, err := stream.Recv() - if err != nil { - log.Printf("stream.Recv() failed: %v", err) - break - } - log.Printf("Received: %v", req.Name) - time.Sleep(1 * time.Second) - err = stream.Send(&pb.HelloResponse{Message: "Hello, " + req.Name}) - if err != nil { - log.Printf("stream.Send() failed: %v", err) - break - } - err = stream.Send(&pb.HelloResponse{Message: "Hello again, " + req.Name}) - if err != nil { - log.Printf("stream.Send() failed: %v", err) - break - } - } - return nil -} +package v2 + +import ( + "context" + "log" + "time" + + pb "github.com/hiddify/libcore/hiddifyrpc" +) + +func (s *server) 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 { + + for { + req, err := stream.Recv() + if err != nil { + log.Printf("stream.Recv() failed: %v", err) + break + } + log.Printf("Received: %v", req.Name) + time.Sleep(1 * time.Second) + err = stream.Send(&pb.HelloResponse{Message: "Hello, " + req.Name}) + if err != nil { + log.Printf("stream.Send() failed: %v", err) + break + } + err = stream.Send(&pb.HelloResponse{Message: "Hello again, " + req.Name}) + if err != nil { + log.Printf("stream.Send() failed: %v", err) + break + } + } + return nil +} diff --git a/v2/logproto.go b/v2/logproto.go index 8667940..e418ae6 100644 --- a/v2/logproto.go +++ b/v2/logproto.go @@ -1,41 +1,41 @@ -package v2 - -import ( - "time" - - pb "github.com/hiddify/libcore/hiddifyrpc" - "github.com/sagernet/sing/common/observable" -) - -var logObserver = observable.Observer[pb.LogMessage]{} - -func Log(level pb.LogLevel, typ pb.LogType, message string) { - logObserver.Emit(pb.LogMessage{ - Level: level, - Type: typ, - Message: message, - }) - -} - -func (s *server) LogListener(stream pb.Hiddify_LogListenerServer) error { - logSub, _, _ := logObserver.Subscribe() - defer logObserver.UnSubscribe(logSub) - - stopch := make(chan int) - go func() { - stream.Recv() - close(stopch) - }() - for { - select { - case <-stream.Context().Done(): - break - case <-stopch: - break - case info := <-logSub: - stream.Send(&info) - case <-time.After(500 * time.Millisecond): - } - } -} +package v2 + +import ( + "time" + + pb "github.com/hiddify/libcore/hiddifyrpc" + "github.com/sagernet/sing/common/observable" +) + +var logObserver = observable.Observer[pb.LogMessage]{} + +func Log(level pb.LogLevel, typ pb.LogType, message string) { + logObserver.Emit(pb.LogMessage{ + Level: level, + Type: typ, + Message: message, + }) + +} + +func (s *server) LogListener(stream pb.Hiddify_LogListenerServer) error { + logSub, _, _ := logObserver.Subscribe() + defer logObserver.UnSubscribe(logSub) + + stopch := make(chan int) + go func() { + stream.Recv() + close(stopch) + }() + for { + select { + case <-stream.Context().Done(): + break + case <-stopch: + break + case info := <-logSub: + stream.Send(&info) + case <-time.After(500 * time.Millisecond): + } + } +} diff --git a/v2/warp.go b/v2/warp.go index 32c2afb..ffc91b8 100644 --- a/v2/warp.go +++ b/v2/warp.go @@ -1,65 +1,65 @@ -package v2 - -import ( - "context" - - "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) { - account, log, wg, err := config.GenerateWarpInfo(in.LicenseKey, in.AccountId, in.AccessToken) - if err != nil { - return nil, err - } - return &pb.WarpGenerationResponse{ - Account: &pb.WarpAccount{ - AccountId: account.AccountID, - AccessToken: account.AccessToken, - }, - Config: &pb.WarpWireguardConfig{ - PrivateKey: wg.PrivateKey, - LocalAddressIpv4: wg.LocalAddressIPv4, - LocalAddressIpv6: wg.LocalAddressIPv6, - PeerPublicKey: wg.PeerPublicKey, - }, - 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 - -} +package v2 + +import ( + "context" + + "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) { + account, log, wg, err := config.GenerateWarpInfo(in.LicenseKey, in.AccountId, in.AccessToken) + if err != nil { + return nil, err + } + return &pb.WarpGenerationResponse{ + Account: &pb.WarpAccount{ + AccountId: account.AccountID, + AccessToken: account.AccessToken, + }, + Config: &pb.WarpWireguardConfig{ + PrivateKey: wg.PrivateKey, + LocalAddressIpv4: wg.LocalAddressIPv4, + LocalAddressIpv6: wg.LocalAddressIPv6, + PeerPublicKey: wg.PeerPublicKey, + }, + 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 + +}