diff --git a/cmd/configure.go b/cmd/configure.go index 1d0c93b..0eae650 100644 --- a/cmd/configure.go +++ b/cmd/configure.go @@ -1,52 +1,65 @@ package cmd import ( + "crypto/tls" "encoding/json" "fmt" + "github.com/AlecAivazis/survey/v2" + "github.com/AlecAivazis/survey/v2/terminal" + "github.com/creasty/defaults" + "github.com/pterodactyl/wings/config" + "github.com/spf13/cobra" "io/ioutil" "net/http" "net/url" "os" "path" - - "github.com/AlecAivazis/survey/v2" - "github.com/AlecAivazis/survey/v2/terminal" - "github.com/pterodactyl/wings/config" - "github.com/spf13/cobra" + "regexp" + "time" ) var ( configureArgs struct { - PanelURL string - Token string - Override bool + PanelURL string + Token string + Node string + Override bool + AllowInsecure bool } ) +var nodeIdRegex = regexp.MustCompile(`^(\d+)$`) + var configureCmd = &cobra.Command{ Use: "configure", Short: "Use a token to configure wings automatically", - - Run: configureCmdRun, + Run: configureCmdRun, } func init() { - configureCmd.PersistentFlags().StringVarP(&configureArgs.PanelURL, "panel-url", "p", "", "the baseurl of the pterodactyl panel to fetch the configuration from") - configureCmd.PersistentFlags().StringVarP(&configureArgs.Token, "token", "t", "", "the auto-deploy token to use") - configureCmd.PersistentFlags().BoolVar(&configureArgs.Override, "override", false, "override an existing configuration") + configureCmd.PersistentFlags().StringVarP(&configureArgs.PanelURL, "panel-url", "p", "", "The base URL for this daemon's panel") + configureCmd.PersistentFlags().StringVarP(&configureArgs.Token, "token", "t", "", "The API key to use for fetching node information") + configureCmd.PersistentFlags().StringVarP(&configureArgs.Node, "node", "n", "", "The ID of the node which will be connected to this daemon") + configureCmd.PersistentFlags().BoolVar(&configureArgs.Override, "override", false, "Set to true to override an existing configuration for this node") + configureCmd.PersistentFlags().BoolVar(&configureArgs.AllowInsecure, "allow-insecure", false, "Set to true to disable certificate checking") } func configureCmdRun(cmd *cobra.Command, args []string) { - _, err := os.Stat("config.yml") - if err != os.ErrNotExist && !configureArgs.Override { - survey.AskOne(&survey.Confirm{Message: "Override existing configuration file"}, &configureArgs.Override) - if !configureArgs.Override { - fmt.Println("Aborted.") - os.Exit(1) - } + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{ + InsecureSkipVerify: true, } - questions := []*survey.Question{} + if _, err := os.Stat("config.yml"); err == nil && !configureArgs.Override { + survey.AskOne(&survey.Confirm{Message: "Override existing configuration file"}, &configureArgs.Override) + if !configureArgs.Override { + fmt.Println("Aborting process; a configuration file already exists for this node.") + os.Exit(1) + } + } else if err != nil && !os.IsNotExist(err) { + panic(err) + } + + var questions []*survey.Question if configureArgs.PanelURL == "" { questions = append(questions, &survey.Question{ Name: "PanelURL", @@ -60,14 +73,15 @@ func configureCmdRun(cmd *cobra.Command, args []string) { }, }) } + if configureArgs.Token == "" { questions = append(questions, &survey.Question{ Name: "Token", - Prompt: &survey.Input{Message: "Token: "}, + Prompt: &survey.Input{Message: "API Token: "}, Validate: func(ans interface{}) error { if str, ok := ans.(string); ok { - if len(str) != 32 { - return fmt.Errorf("the token needs to have 32 characters") + if len(str) == 0 { + return fmt.Errorf("please provide a valid authentication token") } } return nil @@ -75,39 +89,85 @@ func configureCmdRun(cmd *cobra.Command, args []string) { }) } - err = survey.Ask(questions, &configureArgs) - if err == terminal.InterruptErr { - return + if configureArgs.Node == "" { + questions = append(questions, &survey.Question{ + Name: "Node", + Prompt: &survey.Input{Message: "Node ID: "}, + Validate: func(ans interface{}) error { + if str, ok := ans.(string); ok { + if !nodeIdRegex.Match([]byte(str)) { + return fmt.Errorf("please provide a valid authentication token") + } + } + return nil + }, + }) } + + if err := survey.Ask(questions, &configureArgs); err != nil { + if err == terminal.InterruptErr { + return + } + + panic(err) + } + + c := &http.Client{ + Timeout: time.Second * 30, + } + + req, err := getRequest() if err != nil { panic(err) } - url, err := url.Parse(configureArgs.PanelURL) - url.Path = path.Join(url.Path, "daemon/configure", configureArgs.Token) - req, err := http.NewRequest("GET", url.String(), nil) + res, err := c.Do(req) if err != nil { - panic(err) - } - req.Header.Add("Accept", "application/json") - res, err := http.DefaultClient.Do(req) - if err != nil { - fmt.Println("Couldn't fetch configuration from panel.\n", err.Error()) + fmt.Println("Failed to fetch configuration from the panel.\n", err.Error()) os.Exit(1) } defer res.Body.Close() + if res.StatusCode == http.StatusForbidden { - fmt.Println("The provided token is invalid.") + fmt.Println("The authentication credentials provided were not valid.") + os.Exit(1) } - configJSON, err := ioutil.ReadAll(res.Body) - cfg := config.Configuration{} - json.Unmarshal(configJSON, &cfg) - err = cfg.WriteToDisk() - if err != nil { + b, err := ioutil.ReadAll(res.Body) + + cfg := new(config.Configuration) + if err := defaults.Set(cfg); err != nil { + panic(err) + } + + if err := json.Unmarshal(b, &cfg); err != nil { + panic(err) + } + + if err = cfg.WriteToDisk(); err != nil { panic(err) } fmt.Println("Successfully configured wings.") } + +func getRequest() (*http.Request, error) { + u, err := url.Parse(configureArgs.PanelURL) + if err != nil { + panic(err) + } + + u.Path = path.Join(u.Path, fmt.Sprintf("api/application/nodes/%s/configuration", configureArgs.Node)) + + r, err := http.NewRequest(http.MethodGet, u.String(), nil) + if err != nil { + return nil, err + } + + r.Header.Set("Accept", "application/vnd.pterodactyl.v1+json") + r.Header.Set("Content-Type", "application/json") + r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", configureArgs.Token)) + + return r, nil +}