diff --git a/cmd/frpp/cmd.go b/cmd/frpp/cmd.go index 351aa9a..3f48833 100644 --- a/cmd/frpp/cmd.go +++ b/cmd/frpp/cmd.go @@ -2,12 +2,18 @@ package main import ( "context" + "errors" "fmt" "os" + "github.com/VaalaCat/frp-panel/common" "github.com/VaalaCat/frp-panel/conf" "github.com/VaalaCat/frp-panel/logger" + "github.com/VaalaCat/frp-panel/pb" + "github.com/VaalaCat/frp-panel/rpc" "github.com/VaalaCat/frp-panel/utils" + "github.com/joho/godotenv" + "github.com/spf13/cast" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -20,6 +26,17 @@ var ( ) func initCommand() { + rootCmd = &cobra.Command{ + Use: "frp-panel", + Short: "frp-panel is a frp panel QwQ", + } + CmdListWithFlag := initCmdWithFlag() + CmdListWithoutFlag := initCmdWithoutFlag() + rootCmd.AddCommand(CmdListWithFlag...) + rootCmd.AddCommand(CmdListWithoutFlag...) +} + +func initCmdWithFlag() []*cobra.Command { var ( clientSecret string clientID string @@ -28,6 +45,7 @@ func initCommand() { rpcPort int apiPort int apiScheme string + joinToken string ) clientCmd = &cobra.Command{ @@ -47,6 +65,7 @@ func initCommand() { } }, } + serverCmd = &cobra.Command{ Use: "server [-s client secret] [-i client id] [-a app secret] [-r rpc host] [-c rpc port] [-p api port]", Short: "run managed frps", @@ -64,6 +83,43 @@ func initCommand() { } }, } + + joinCmd := &cobra.Command{ + Use: "join [-j join token] [-r rpc host] [-p api port] [-e api scheme]", + Short: "join extra params", + Run: func(cmd *cobra.Command, args []string) { + pullRunConfig(joinToken, appSecret, rpcHost, apiScheme, rpcPort, apiPort) + }, + } + + clientCmd.Flags().StringVarP(&clientSecret, "secret", "s", "", "client secret") + serverCmd.Flags().StringVarP(&clientSecret, "secret", "s", "", "client secret") + clientCmd.Flags().StringVarP(&clientID, "id", "i", "", "client id") + serverCmd.Flags().StringVarP(&clientID, "id", "i", "", "client id") + clientCmd.Flags().StringVarP(&rpcHost, "rpc", "r", "", "rpc host, canbe ip or domain") + serverCmd.Flags().StringVarP(&rpcHost, "rpc", "r", "", "rpc host, canbe ip or domain") + clientCmd.Flags().StringVarP(&appSecret, "app", "a", "", "app secret") + serverCmd.Flags().StringVarP(&appSecret, "app", "a", "", "app secret") + + clientCmd.Flags().IntVarP(&rpcPort, "rpc-port", "c", 0, "rpc port, master rpc port, scheme is grpc") + serverCmd.Flags().IntVarP(&rpcPort, "rpc-port", "c", 0, "rpc port, master rpc port, scheme is grpc") + clientCmd.Flags().IntVarP(&apiPort, "api-port", "p", 0, "api port, master api port, scheme is http/https") + serverCmd.Flags().IntVarP(&apiPort, "api-port", "p", 0, "api port, master api port, scheme is http/https") + + clientCmd.Flags().StringVarP(&apiScheme, "api-scheme", "e", "", "api scheme, master api scheme, scheme is http/https") + serverCmd.Flags().StringVarP(&apiScheme, "api-scheme", "e", "", "api scheme, master api scheme, scheme is http/https") + + joinCmd.Flags().IntVarP(&rpcPort, "rpc-port", "c", 0, "rpc port, master rpc port, scheme is grpc") + joinCmd.Flags().IntVarP(&apiPort, "api-port", "p", 0, "api port, master api port, scheme is http/https") + joinCmd.Flags().StringVarP(&appSecret, "app", "a", "", "app secret") + joinCmd.Flags().StringVarP(&joinToken, "join-token", "j", "", "join token") + joinCmd.Flags().StringVarP(&rpcHost, "rpc", "r", "", "rpc host, canbe ip or domain") + joinCmd.Flags().StringVarP(&apiScheme, "api-scheme", "e", "", "api scheme, master api scheme, scheme is http/https") + + return []*cobra.Command{clientCmd, serverCmd, joinCmd} +} + +func initCmdWithoutFlag() []*cobra.Command { masterCmd = &cobra.Command{ Use: "master", Short: "run frp-panel manager", @@ -75,10 +131,6 @@ func initCommand() { } }, } - rootCmd = &cobra.Command{ - Use: "frp-panel", - Short: "frp-panel is a frp panel QwQ", - } installServiceCmd := &cobra.Command{ Use: "install", @@ -138,27 +190,15 @@ func initCommand() { fmt.Println(conf.GetVersion().String()) }, } - - rootCmd.AddCommand(clientCmd, serverCmd, masterCmd, versionCmd, - installServiceCmd, uninstallServiceCmd, - startServiceCmd, stopServiceCmd, restartServiceCmd) - - clientCmd.Flags().StringVarP(&clientSecret, "secret", "s", "", "client secret") - serverCmd.Flags().StringVarP(&clientSecret, "secret", "s", "", "client secret") - clientCmd.Flags().StringVarP(&clientID, "id", "i", "", "client id") - serverCmd.Flags().StringVarP(&clientID, "id", "i", "", "client id") - clientCmd.Flags().StringVarP(&rpcHost, "rpc", "r", "", "rpc host, canbe ip or domain") - serverCmd.Flags().StringVarP(&rpcHost, "rpc", "r", "", "rpc host, canbe ip or domain") - clientCmd.Flags().StringVarP(&appSecret, "app", "a", "", "app secret") - serverCmd.Flags().StringVarP(&appSecret, "app", "a", "", "app secret") - - clientCmd.Flags().IntVarP(&rpcPort, "rpc-port", "c", 0, "rpc port, master rpc port, scheme is grpc") - serverCmd.Flags().IntVarP(&rpcPort, "rpc-port", "c", 0, "rpc port, master rpc port, scheme is grpc") - clientCmd.Flags().IntVarP(&apiPort, "api-port", "p", 0, "api port, master api port, scheme is http/https") - serverCmd.Flags().IntVarP(&apiPort, "api-port", "p", 0, "api port, master api port, scheme is http/https") - - clientCmd.Flags().StringVarP(&apiScheme, "api-scheme", "e", "", "api scheme, master api scheme, scheme is http/https") - serverCmd.Flags().StringVarP(&apiScheme, "api-scheme", "e", "", "api scheme, master api scheme, scheme is http/https") + return []*cobra.Command{ + masterCmd, + installServiceCmd, + uninstallServiceCmd, + startServiceCmd, + stopServiceCmd, + restartServiceCmd, + versionCmd, + } } func initLogger() { @@ -202,3 +242,93 @@ func setMasterCommandIfNonePresent() { rootCmd.SetArgs(args) } } + +func pullRunConfig(joinToken, appSecret, rpcHost, apiScheme string, rpcPort, apiPort int) { + c := context.Background() + if err := checkPullParams(joinToken, rpcHost, apiScheme, apiPort); err != nil { + logger.Logger(c).Errorf("check pull params failed: %s", err.Error()) + return + } + + if err := utils.EnsureDirectoryExists(common.SysEnvPath); err != nil { + logger.Logger(c).Errorf("ensure directory failed: %s", err.Error()) + return + } + + // 设置一下调接口需要的参数 + clientID := utils.GetHostnameWithIP() + clientID = utils.MakeClientIDPermited(clientID) + patchConfig(rpcHost, appSecret, "", "", apiScheme, rpcPort, apiPort) + + initResp, err := rpc.InitClient(clientID, joinToken) + if err != nil { + logger.Logger(c).Errorf("init client failed: %s", err.Error()) + return + } + if initResp == nil { + logger.Logger(c).Errorf("init resp is nil") + return + } + if initResp.GetStatus().GetCode() != pb.RespCode_RESP_CODE_SUCCESS { + logger.Logger(c).Errorf("init resp code is not success: %s", initResp.GetStatus().GetMessage()) + return + } + + clientID = initResp.GetClientId() + clientResp, err := rpc.GetClient(clientID, joinToken) + if err != nil { + logger.Logger(c).Errorf("get client failed: %s", err.Error()) + return + } + if clientResp == nil { + logger.Logger(c).Errorf("client resp is nil") + return + } + if clientResp.GetStatus().GetCode() != pb.RespCode_RESP_CODE_SUCCESS { + logger.Logger(c).Errorf("client resp code is not success: %s", clientResp.GetStatus().GetMessage()) + return + } + + client := clientResp.GetClient() + if client == nil { + logger.Logger(c).Errorf("client is nil") + return + } + + envMap, err := godotenv.Read(common.SysEnvPath) + if err != nil { + envMap = make(map[string]string) + logger.Logger(c).Errorf("read env file failed, try to create: %s", err.Error()) + } + + envMap[common.EnvAppSecret] = appSecret + envMap[common.EnvClientID] = clientID + envMap[common.EnvClientSecret] = client.GetSecret() + envMap[common.EnvMasterRPCHost] = rpcHost + envMap[common.EnvMasterRPCPort] = cast.ToString(rpcPort) + envMap[common.EnvMasterAPIHost] = rpcHost + envMap[common.EnvMasterAPIPort] = cast.ToString(apiPort) + envMap[common.EnvMasterAPIScheme] = apiScheme + + if err = godotenv.Write(envMap, common.SysEnvPath); err != nil { + logger.Logger(c).Errorf("write env file failed: %s", err.Error()) + return + } + logger.Logger(c).Info("run config saved to env file") +} + +func checkPullParams(joinToken, rpcHost, apiScheme string, apiPort int) error { + if len(joinToken) == 0 { + return errors.New("join token is empty") + } + if len(rpcHost) == 0 { + return errors.New("rpc host is empty") + } + if len(apiScheme) == 0 { + return errors.New("api scheme is empty") + } + if apiPort == 0 { + return errors.New("api port is empty") + } + return nil +} diff --git a/common/const.go b/common/const.go index c28cfa5..21e4640 100644 --- a/common/const.go +++ b/common/const.go @@ -1,6 +1,8 @@ package common -import "time" +import ( + "time" +) const ( AuthorizationKey = "authorization" @@ -61,3 +63,16 @@ const ( PullConfigDuration = 30 * time.Second PushProxyInfoDuration = 30 * time.Second ) + +const ( + CurEnvPath = ".env" + SysEnvPath = "/etc/frpp/.env" + EnvAppSecret = "APP_SECRET" + EnvClientID = "CLIENT_ID" + EnvClientSecret = "CLIENT_SECRET" + EnvMasterRPCHost = "MASTER_RPC_HOST" + EnvMasterAPIHost = "MASTER_API_HOST" + EnvMasterRPCPort = "MASTER_RPC_PORT" + EnvMasterAPIPort = "MASTER_API_PORT" + EnvMasterAPIScheme = "MASTER_API_SCHEME" +) diff --git a/conf/settings.go b/conf/settings.go index 5b8f06c..739444f 100644 --- a/conf/settings.go +++ b/conf/settings.go @@ -5,6 +5,7 @@ import ( "fmt" "os" + "github.com/VaalaCat/frp-panel/common" "github.com/VaalaCat/frp-panel/logger" "github.com/ilyakaznacheev/cleanenv" "github.com/joho/godotenv" @@ -69,20 +70,16 @@ func InitConfig() { ctx = context.Background() ) + // 越前面优先级越高,后面的不会覆盖前面的 envFiles := []string{ - ".env", - "/etc/frpp/.env", + common.CurEnvPath, + common.SysEnvPath, } - for i, envFile := range envFiles { + for _, envFile := range envFiles { if err = godotenv.Load(envFile); err == nil { logger.Logger(ctx).Infof("load env file success: %s", envFile) useEnvFile = true - break - } - if i == len(envFiles)-1 { - useEnvFile = false - break } } diff --git a/go.mod b/go.mod index 700d43f..045135c 100644 --- a/go.mod +++ b/go.mod @@ -18,15 +18,18 @@ require ( github.com/iamacarpet/go-winpty v1.0.4 github.com/ilyakaznacheev/cleanenv v1.5.0 github.com/imroc/req/v3 v3.42.3 + github.com/jackpal/gateway v1.0.16 github.com/joho/godotenv v1.5.1 github.com/kardianos/service v1.2.2 github.com/samber/lo v1.39.0 github.com/shirou/gopsutil/v4 v4.24.11 github.com/sirupsen/logrus v1.9.3 github.com/sourcegraph/conc v0.3.0 + github.com/spf13/cast v1.7.1 github.com/spf13/cobra v1.8.0 + github.com/spf13/pflag v1.0.5 github.com/tiendc/go-deepcopy v1.2.0 - golang.org/x/crypto v0.25.0 + golang.org/x/crypto v0.31.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.35.2 gorm.io/driver/mysql v1.5.2 @@ -102,7 +105,8 @@ require ( github.com/refraction-networking/utls v1.6.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/robfig/cron/v3 v3.0.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.9.0 // indirect github.com/templexxx/cpu v0.1.1 // indirect github.com/templexxx/xorsimd v0.4.3 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect @@ -118,11 +122,11 @@ require ( golang.org/x/arch v0.3.0 // indirect golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.27.0 // indirect + golang.org/x/net v0.33.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d // indirect diff --git a/go.sum b/go.sum index d9cd8e2..5a12739 100644 --- a/go.sum +++ b/go.sum @@ -49,6 +49,8 @@ github.com/fatedier/frp v0.59.0 h1:4Nai/PMVvAQLQFJ1FijyKkNzXm4V6M+9IoJ8nobKojY= github.com/fatedier/frp v0.59.0/go.mod h1:8lwoMcR+f8Shnx7lFWhN0JLhGspl8j0Tt/3cRiRVHgc= github.com/fatedier/golib v0.5.0 h1:hNcH7hgfIFqVWbP+YojCCAj4eO94pPf4dEF8lmq2jWs= github.com/fatedier/golib v0.5.0/go.mod h1:W6kIYkIFxHsTzbgqg5piCxIiDo4LzwgTY6R5W8l9NFQ= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -140,6 +142,8 @@ github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/ github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= +github.com/jackpal/gateway v1.0.16 h1:mTBRuHSW8qviVqX7kXnxKevqlfS/OA01ys6k6fxSX7w= +github.com/jackpal/gateway v1.0.16/go.mod h1:IOn1OUbso/cGYmnCBZbCEqhNCLSz0xxdtIpUpri5/nA= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= @@ -234,6 +238,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -241,6 +247,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -296,8 +303,8 @@ golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= @@ -321,8 +328,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -331,8 +338,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -351,16 +358,16 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -368,8 +375,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/rpc/master.go b/rpc/master.go index 12adeec..046fef7 100644 --- a/rpc/master.go +++ b/rpc/master.go @@ -2,7 +2,9 @@ package rpc import ( "context" + "fmt" + "github.com/VaalaCat/frp-panel/common" "github.com/VaalaCat/frp-panel/conf" "github.com/VaalaCat/frp-panel/pb" "github.com/imroc/req/v3" @@ -56,3 +58,54 @@ func GetClientCert(clientID, clientSecret string, clientType pb.ClientType) []by } return resp.Cert } + +func InitClient(clientID, joinToken string) (*pb.InitClientResponse, error) { + apiEndpoint := conf.GetAPIURL() + c := req.C() + rawReq, err := proto.Marshal(&pb.InitClientRequest{ + ClientId: &clientID, + }) + if err != nil { + return nil, err + } + + r, err := c.R().SetHeader("Content-Type", "application/x-protobuf"). + SetHeader(common.AuthorizationKey, joinToken). + SetBodyBytes(rawReq).Post(apiEndpoint + "/api/v1/client/init") + if err != nil { + return nil, err + } + + resp := &pb.InitClientResponse{} + err = proto.Unmarshal(r.Bytes(), resp) + if err != nil { + return nil, err + } + return resp, nil +} + +func GetClient(clientID, joinToken string) (*pb.GetClientResponse, error) { + apiEndpoint := conf.GetAPIURL() + c := req.C() + rawReq, err := proto.Marshal(&pb.GetClientRequest{ + ClientId: &clientID, + }) + if err != nil { + return nil, err + } + + r, err := c.R().SetHeader("Content-Type", "application/x-protobuf"). + SetHeader(common.AuthorizationKey, joinToken). + SetBodyBytes(rawReq).Post(apiEndpoint + "/api/v1/client/get") + if err != nil { + return nil, err + } + fmt.Printf("resp: %s\n", r.String()) + + resp := &pb.GetClientResponse{} + err = proto.Unmarshal(r.Bytes(), resp) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/utils/misc.go b/utils/misc.go index e304f61..68d9a25 100644 --- a/utils/misc.go +++ b/utils/misc.go @@ -1,9 +1,56 @@ package utils import ( + "fmt" + "net" + "os" "time" + + "github.com/jackpal/gateway" ) func IsSameDay(first time.Time, second time.Time) bool { return first.YearDay() == second.YearDay() && first.Year() == second.Year() } + +func GetHostnameWithIP() string { + hostname, _ := os.Hostname() + interfaces, err := net.Interfaces() + if err != nil { + return hostname + } + ipGateway, err := gateway.DiscoverGateway() + if err != nil { + return hostname + } + + stop := false + for _, iface := range interfaces { + if stop { + break + } + + if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 { + continue + } + + addrs, err := iface.Addrs() + if err != nil { + continue + } + + for _, addr := range addrs { + addrIP, ok := addr.(*net.IPNet) + if !ok || addrIP == nil { + continue + } + if !addrIP.Contains(ipGateway) { + continue + } + hostname = fmt.Sprintf("%s-%s", hostname, addrIP.IP.String()) + stop = true + break + } + } + return hostname +} diff --git a/utils/validate.go b/utils/validate.go index 7a8d164..42eaa68 100644 --- a/utils/validate.go +++ b/utils/validate.go @@ -1,7 +1,10 @@ package utils +const ( + whiteListChar = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" +) + func IsClientIDPermited(clientID string) bool { - whiteListChar := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" if len(clientID) == 0 { return false } @@ -19,3 +22,18 @@ func IsClientIDPermited(clientID string) bool { return true } + +func MakeClientIDPermited(clientID string) string { + input := []rune(clientID) + output := input + chrMap := make(map[rune]bool) + for _, chr := range whiteListChar { + chrMap[chr] = true + } + for idx, chr := range input { + if !chrMap[chr] { + output[idx] = '-' + } + } + return string(output) +}