diff --git a/cmd/app/subcommands/reset.go b/cmd/app/subcommands/reset.go index 58c96f4..8724eb2 100644 --- a/cmd/app/subcommands/reset.go +++ b/cmd/app/subcommands/reset.go @@ -24,7 +24,7 @@ var ResetAppCmd = &cobra.Command{ spinner.Start() // Reset app - err := api.ApiRequest(path, "POST", 5 * time.Minute) + response, err := api.ApiRequest(path, "POST", 5 * time.Minute) if err != nil { spinner.Fail("Failed to reset app") @@ -33,6 +33,8 @@ var ResetAppCmd = &cobra.Command{ os.Exit(1) } + defer response.Body.Close() + // Succeed spinner.Succeed("App reset succeessfully") spinner.Stop() diff --git a/cmd/app/subcommands/restart.go b/cmd/app/subcommands/restart.go index 556ab52..54a29da 100644 --- a/cmd/app/subcommands/restart.go +++ b/cmd/app/subcommands/restart.go @@ -24,7 +24,7 @@ var RestartAppCmd = &cobra.Command{ spinner.Start() // Restart app - err := api.ApiRequest(path, "POST", 5 * time.Minute) + response, err := api.ApiRequest(path, "POST", 5 * time.Minute) if err != nil { spinner.Fail("Failed to restart app") @@ -33,6 +33,8 @@ var RestartAppCmd = &cobra.Command{ os.Exit(1) } + defer response.Body.Close() + // Succeed spinner.Succeed("App restarted succeessfully") spinner.Stop() diff --git a/cmd/app/subcommands/start-all.go b/cmd/app/subcommands/start-all.go index 5ed6fd1..ecf2cc2 100644 --- a/cmd/app/subcommands/start-all.go +++ b/cmd/app/subcommands/start-all.go @@ -24,7 +24,7 @@ var StartAllCmd = &cobra.Command{ spinner.Start() // Start apps - err := api.ApiRequest(path, "POST", 15 * time.Minute) + response, err := api.ApiRequest(path, "POST", 15 * time.Minute) if err != nil { spinner.Fail("Failed to start apps") @@ -33,6 +33,8 @@ var StartAllCmd = &cobra.Command{ os.Exit(1) } + defer response.Body.Close() + // Succeed spinner.Succeed("Apps succeessfully") spinner.Stop() diff --git a/cmd/app/subcommands/start.go b/cmd/app/subcommands/start.go index 0b857b5..de34f9b 100644 --- a/cmd/app/subcommands/start.go +++ b/cmd/app/subcommands/start.go @@ -24,7 +24,7 @@ var StartAppCmd = &cobra.Command{ spinner.Start() // Start app - err := api.ApiRequest(path, "POST", 5 * time.Minute) + response, err := api.ApiRequest(path, "POST", 5 * time.Minute) if err != nil { spinner.Fail("Failed to start app") @@ -33,6 +33,8 @@ var StartAppCmd = &cobra.Command{ os.Exit(1) } + defer response.Body.Close() + // Succeed spinner.Succeed("App started succeessfully") spinner.Stop() diff --git a/cmd/app/subcommands/stop.go b/cmd/app/subcommands/stop.go index c77aab9..ce65eaa 100644 --- a/cmd/app/subcommands/stop.go +++ b/cmd/app/subcommands/stop.go @@ -24,7 +24,7 @@ var StopAppCmd = &cobra.Command{ spinner.Start() // Stop app - err := api.ApiRequest(path, "POST", 5 * time.Minute) + response, err := api.ApiRequest(path, "POST", 5 * time.Minute) if err != nil { spinner.Fail("Failed to stop app") @@ -33,6 +33,8 @@ var StopAppCmd = &cobra.Command{ os.Exit(1) } + defer response.Body.Close() + // Succeed spinner.Succeed("App stopped succeessfully") spinner.Stop() diff --git a/cmd/app/subcommands/uninstall.go b/cmd/app/subcommands/uninstall.go index 1f2fb13..060dcda 100644 --- a/cmd/app/subcommands/uninstall.go +++ b/cmd/app/subcommands/uninstall.go @@ -24,7 +24,7 @@ var UninstallAppCmd = &cobra.Command{ spinner.Start() // Uninstall app - err := api.ApiRequest(path, "POST", 5 * time.Minute) + response, err := api.ApiRequest(path, "POST", 5 * time.Minute) if err != nil { spinner.Fail("Failed to uninstall app") @@ -33,6 +33,8 @@ var UninstallAppCmd = &cobra.Command{ os.Exit(1) } + defer response.Body.Close() + // Succeed spinner.Succeed("App uninstalled succeessfully") spinner.Stop() diff --git a/cmd/app/subcommands/update.go b/cmd/app/subcommands/update.go index c41a05c..746bd95 100644 --- a/cmd/app/subcommands/update.go +++ b/cmd/app/subcommands/update.go @@ -24,7 +24,7 @@ var UpdateAppCmd = &cobra.Command{ spinner.Start() // Updating app - err := api.ApiRequest(path, "POST", 15 * time.Minute) + response, err := api.ApiRequest(path, "POST", 15 * time.Minute) if err != nil { spinner.Fail("Failed to update app") @@ -33,6 +33,8 @@ var UpdateAppCmd = &cobra.Command{ os.Exit(1) } + defer response.Body.Close() + // Succeed spinner.Succeed("App updated succeessfully") spinner.Stop() diff --git a/cmd/root.go b/cmd/root.go index 9ebae7f..d0eec56 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -6,6 +6,7 @@ import ( "github.com/spf13/cobra" "github.com/steveiliop56/runtipi-cli-go/cmd/app" + "github.com/steveiliop56/runtipi-cli-go/cmd/system" ) var noPermissions bool @@ -29,4 +30,5 @@ func Execute() { func init() { fmt.Println("Welcome to Runtipi CLI in Go ✨") rootCmd.AddCommand(app.AppCmd()) + rootCmd.AddCommand(system.SystemCmd()) } \ No newline at end of file diff --git a/cmd/system/subcommands/healthcheck.go b/cmd/system/subcommands/healthcheck.go new file mode 100644 index 0000000..057894f --- /dev/null +++ b/cmd/system/subcommands/healthcheck.go @@ -0,0 +1,41 @@ +package subcommands + +import ( + "fmt" + "os" + "time" + + "github.com/spf13/cobra" + "github.com/steveiliop56/runtipi-cli-go/internal/api" + "github.com/steveiliop56/runtipi-cli-go/internal/spinner" +) + +var HealthCheckCmd = &cobra.Command{ + Use: "healthcheck", + Short: "Checks if the Runtipi system is up and running use the API", + Long: "Checks if the Runtipi system is up and running using the worker API", + Run: func(cmd *cobra.Command, args []string) { + // Define Path + path := "healthcheck" + + // Start Spinner + spinner.SetMessage("Checking system") + spinner.Start() + + // Do Check + response, err := api.ApiRequest(path, "GET", 1 * time.Minute) + + if err != nil { + spinner.Fail("Failed to check system, is runtipi running?") + spinner.Stop() + fmt.Printf("Error: %s\n", err) + os.Exit(1) + } + + defer response.Body.Close() + + // Succeed + spinner.Succeed("Runtipi system up and running") + spinner.Stop() + }, +} \ No newline at end of file diff --git a/cmd/system/subcommands/status.go b/cmd/system/subcommands/status.go new file mode 100644 index 0000000..41ca448 --- /dev/null +++ b/cmd/system/subcommands/status.go @@ -0,0 +1,62 @@ +package subcommands + +import ( + "encoding/json" + "fmt" + "os" + "time" + + "github.com/spf13/cobra" + "github.com/steveiliop56/runtipi-cli-go/internal/api" + "github.com/steveiliop56/runtipi-cli-go/internal/constants" + "github.com/steveiliop56/runtipi-cli-go/internal/schemas" + "github.com/steveiliop56/runtipi-cli-go/internal/spinner" +) + +var StatusCmd = &cobra.Command{ + Use: "status", + Short: "Gets system readings from the Runtipi API", + Long: "Shows the system readings (e.g. cpu usage, disk usage) from the Runtipi worker API", + Run: func(cmd *cobra.Command, args []string) { + // Define Path + path := "system-status" + + // Start Spinner + spinner.SetMessage("Getting system status") + spinner.Start() + + // Do Check + response, err := api.ApiRequest(path, "GET", 1 * time.Minute) + + if err != nil { + spinner.Fail("Failed to get system status, is runtipi running?") + spinner.Stop() + fmt.Printf("Error: %s\n", err) + os.Exit(1) + } + + // Parse Json + + status := new(schemas.SystemStatusApi) + + jsonErr := json.NewDecoder(response.Body).Decode(&status) + + if jsonErr != nil { + spinner.Fail("Failed to decode system status json") + spinner.Stop() + fmt.Printf("Error: %s\n", jsonErr) + os.Exit(1) + } + + defer response.Body.Close() + + // Succeed + spinner.Succeed("Successfully got system status") + spinner.Stop() + + // Print status + fmt.Printf("Your CPU usage is %s %% \n", constants.Blue(fmt.Sprintf("%.2f", status.Data.CpuLoad))) + fmt.Printf("Your Disk size is %s GB, you are using %s GB which is %s %% \n", constants.Blue(status.Data.DiskSize), constants.Blue(status.Data.DiskUsed), constants.Blue(status.Data.PercentUsed)) + fmt.Printf("Your Memory size is %s GB and you are using %s %% \n", constants.Blue(status.Data.MemoryTotal), constants.Blue(status.Data.PercentUsedMemory)) + }, +} \ No newline at end of file diff --git a/cmd/system/system.go b/cmd/system/system.go new file mode 100644 index 0000000..a3c2573 --- /dev/null +++ b/cmd/system/system.go @@ -0,0 +1,17 @@ +package system + +import ( + "github.com/spf13/cobra" + "github.com/steveiliop56/runtipi-cli-go/cmd/system/subcommands" +) + +func SystemCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "system", + Short: "System commands", + Long: "Control your Runtipi system through the CLI", + } + cmd.AddCommand(subcommands.HealthCheckCmd) + cmd.AddCommand(subcommands.StatusCmd) + return cmd +} \ No newline at end of file diff --git a/cmd/version.go b/cmd/version.go index 9101993..fb165cd 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -17,6 +17,6 @@ var versionCmd = &cobra.Command{ Long: "This command prints the current Runtipi CLI version", Run: func(cmd *cobra.Command, args []string) { fmt.Printf("Current Runtipi version: %s\n", constants.Blue(constants.RuntipiVersion)) - fmt.Print("Current CLI version: %s\n", constants.Blue(constants.CliVersion)) + fmt.Printf("Current CLI version: %s\n", constants.Blue(constants.CliVersion)) }, } \ No newline at end of file diff --git a/internal/api/api.go b/internal/api/api.go index d650579..1b7af99 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -30,17 +30,17 @@ func GenerateJWT() (string, error) { return tokenString, nil } -func ApiRequest(path string, method string, timeout time.Duration) (error) { +func ApiRequest(path string, method string, timeout time.Duration) (*http.Response, error) { token, tokenErr := GenerateJWT() if tokenErr != nil { - return tokenErr + return nil, tokenErr } port, portErr := env.GetEnvValue("NGINX_PORT") if portErr != nil { - return portErr + return nil, portErr } apiUrl := fmt.Sprintf("http://localhost:%s/worker-api/%s", port, path) @@ -48,7 +48,7 @@ func ApiRequest(path string, method string, timeout time.Duration) (error) { request, requestErr := http.NewRequest(method, apiUrl, bytes.NewBuffer([]byte(""))) if requestErr != nil { - return requestErr + return nil, requestErr } request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token)) @@ -60,10 +60,8 @@ func ApiRequest(path string, method string, timeout time.Duration) (error) { response, clientErr := client.Do(request) if clientErr != nil { - return clientErr + return nil, clientErr } - - defer response.Body.Close() - return nil -} \ No newline at end of file + return response, nil +} diff --git a/internal/release/release.go b/internal/release/release.go index ddcc425..c555352 100644 --- a/internal/release/release.go +++ b/internal/release/release.go @@ -11,14 +11,10 @@ import ( "strconv" "strings" + "github.com/steveiliop56/runtipi-cli-go/internal/schemas" "github.com/steveiliop56/runtipi-cli-go/internal/system" ) -type GithubRelease struct { - TagName string `json:"tag_name"` - Status string `json:"status"` -} - func IsMajorBump(newVersion string, currentVersion string) (bool, error) { newVersionMajor := strings.Split(strings.Replace(newVersion, "v", "", 1), ".")[0] currentVersionMajor := strings.Split(strings.Replace(currentVersion, "v", "", 1), ".")[0] @@ -53,7 +49,7 @@ func GetLatestVersion() (string, error) { defer response.Body.Close() - release := new(GithubRelease) + release := new(schemas.GithubRelease) jsonErr := json.NewDecoder(response.Body).Decode(&release) @@ -75,7 +71,7 @@ func ValidateVersion(version string) (bool, error) { defer response.Body.Close() - release := new(GithubRelease) + release := new(schemas.GithubRelease) jsonErr := json.NewDecoder(response.Body).Decode(&release) diff --git a/internal/schemas/schemas.go b/internal/schemas/schemas.go index 4c0b356..4cd34af 100644 --- a/internal/schemas/schemas.go +++ b/internal/schemas/schemas.go @@ -1,15 +1,46 @@ package schemas type Settings struct { - InternalIp string `json:"internalIp"` + // Traefik config Port any `json:"port"` SSLPort any `json:"sslPort"` + InternalIp string `json:"internalIp"` + Domain string `json:"domain"` + LocalDomain string `json:"localDomain"` // Deprecated StoragePath string `json:"storagePath"` + + // App data path (storagePath) AppDataPath string `json:"appDataPath"` + // Postgres PostgresPort any `json:"postgresPort"` - Domain string `json:"domain"` - LocalDomain string `json:"localDomain"` +} + +type SystemStatus struct { + // Disk + DiskUsed float64 `json:"diskUsed"` + DiskSize float64 `json:"diskSize"` + PercentUsed float64 `json:"percentUsed"` + + // Cpu + CpuLoad float64 `json:"cpuLoad"` + + // Memory + MemoryTotal int `json:"memoryTotal"` + PercentUsedMemory float64 `json:"percentUsedMemory"` +} + +type SystemStatusApi struct { + // Data + Data SystemStatus `json:"data"` +} + +type GithubRelease struct { + // Tag Name + TagName string `json:"tag_name"` + + // Status Code + Status string `json:"status"` } \ No newline at end of file