Skip to content

Commit

Permalink
Refactor login logic to use workspace URL and improve user input hand…
Browse files Browse the repository at this point in the history
…ling
  • Loading branch information
royroyee committed Oct 25, 2024
1 parent 01bc342 commit f5df2d0
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 63 deletions.
13 changes: 4 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,18 @@ To access and utilize all features of `Alpacon CLI`, first authenticate with the
```bash
$ alpacon login

$ alpacon login -s [SERVER URL] -u [USERNAME] -p [PASSWORD]
$ alpacon login [WORKSPACE_URL] -u [USERNAME] -p [PASSWORD]

# Log in via API token
$ alpacon login -s [SERVER URL] -t [TOKEN KEY]
$ alpacon login -w [WORKSPACE_URL] -t [TOKEN_KEY]

# Logout
$ alpacon logout
```
A successful login generates a `config.json` file in `~/.alpacon`, which includes the server address, API token, and token expiration time (approximately 1 week).
A successful login generates a `config.json` file in `~/.alpacon`, which includes the workspace url, API token, and token expiration time (approximately 1 week).
This file is crucial for executing commands, and you will need to log in again once the token expires.

Upon re-login, the Alpacon CLI will automatically reuse the server address from `config.json`, unless you provide all the flags (-s, -u, -p).
If you need to connect to a different server or change the server address, you can either directly modify the `config.json` file in `~/.alpacon` or provide all flags to specify a new server URL.

#### Default Server URL
If you do not explicitly specify the server URL (-s) in the command, the default value `https://alpacon.io` is used.
Therefore, you only need to use the `-s` option to specify a server URL if you wish to connect to a server other than the default one.
Upon re-login, the Alpacon CLI will automatically reuse the workspace URL from `config.json`, unless you provide a workspace URL as an argument.

## Usage
Explore Alpacon CLI's capabilities with the `-h` or `help` command.
Expand Down
14 changes: 7 additions & 7 deletions api/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,27 @@ const (
func LoginAndSaveCredentials(loginReq *LoginRequest, token string) error {
if token != "" {

client := &client.AlpaconClient{
alpaconClient := &client.AlpaconClient{
HTTPClient: &http.Client{},
BaseURL: loginReq.ServerAddress,
BaseURL: loginReq.WorkspaceURL,
Token: token,
UserAgent: utils.SetUserAgent(),
}

_, err := client.SendGetRequest(statusURL)
_, err := alpaconClient.SendGetRequest(statusURL)
if err != nil {
return err
}

err = config.CreateConfig(loginReq.ServerAddress, token, "")
err = config.CreateConfig(loginReq.WorkspaceURL, token, "")
if err != nil {
return err
}

return nil
}

serverAddress := loginReq.ServerAddress
workspaceURL := loginReq.WorkspaceURL

reqBody, err := json.Marshal(loginReq)
if err != nil {
Expand All @@ -54,7 +54,7 @@ func LoginAndSaveCredentials(loginReq *LoginRequest, token string) error {
httpClient := &http.Client{}

// Log in to Alpacon server
httpReq, err := http.NewRequest("POST", serverAddress+loginURL, bytes.NewBuffer(reqBody))
httpReq, err := http.NewRequest("POST", workspaceURL+loginURL, bytes.NewBuffer(reqBody))
if err != nil {
return err
}
Expand Down Expand Up @@ -83,7 +83,7 @@ func LoginAndSaveCredentials(loginReq *LoginRequest, token string) error {
return err
}

err = config.CreateConfig(serverAddress, loginResponse.Token, loginResponse.ExpiresAt)
err = config.CreateConfig(workspaceURL, loginResponse.Token, loginResponse.ExpiresAt)
if err != nil {
return err
}
Expand Down
6 changes: 3 additions & 3 deletions api/auth/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package auth
import "time"

type LoginRequest struct {
ServerAddress string `json:"server_address"`
Username string `json:"username"`
Password string `json:"password"`
WorkspaceURL string `json:"workspace_url"`
Username string `json:"username"`
Password string `json:"password"`
}

type LoginResponse struct {
Expand Down
6 changes: 3 additions & 3 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ const (
)

func NewAlpaconAPIClient() (*AlpaconClient, error) {
config, err := config.LoadConfig()
validConfig, err := config.LoadConfig()
if err != nil {
return nil, err
}

client := &AlpaconClient{
HTTPClient: &http.Client{},
BaseURL: config.ServerAddress,
Token: config.Token,
BaseURL: validConfig.WorkspaceURL,
Token: validConfig.Token,
UserAgent: utils.SetUserAgent(),
}

Expand Down
83 changes: 55 additions & 28 deletions cmd/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,51 @@ import (
"github.com/alpacanetworks/alpacon-cli/config"
"github.com/alpacanetworks/alpacon-cli/utils"
"github.com/spf13/cobra"
"strings"
)

var (
loginRequest auth.LoginRequest
)

const defaultServerURL = "https://alpacon.io"

var loginCmd = &cobra.Command{
Use: "login",
Short: "Log in to Alpacon Server",
Long: "Log in to Alpacon Server. To access Alpacon Server, server address is must specified",
Short: "Log in to Alpacon",
Long: "Log in to Alpacon. To access Alpacon, workspace url is must specified",
Example: `
alpacon login
alpacon login [WORKSPACE_URL] -u [USERNAME] -p [PASSWORD]
alpacon login example.alpacon.io
# Login via API Token
alpacon login -w [WORKSPACE_URL] -t [TOKEN_KEY]
`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
var workspaceURL string
if len(args) > 0 {
workspaceURL = args[0]
} else {
workspaceURL = ""
}

username, _ := cmd.Flags().GetString("username")
password, _ := cmd.Flags().GetString("password")
token, _ := cmd.Flags().GetString("token")

if (loginRequest.Username == "" || loginRequest.Password == "" || loginRequest.ServerAddress == "") && token == "" {
promptForCredentials()
if (workspaceURL == "" || username == "" || password == "") && token == "" {
workspaceURL, username, password = promptForCredentials(workspaceURL, username, password)
}

err := auth.LoginAndSaveCredentials(&loginRequest, token)
if !strings.HasPrefix(workspaceURL, "https") {
workspaceURL = "https://" + workspaceURL
}

loginRequest := &auth.LoginRequest{
WorkspaceURL: workspaceURL,
Username: username,
Password: password,
}

fmt.Printf("Logging in to %s...\n", workspaceURL)
err := auth.LoginAndSaveCredentials(loginRequest, token)
if err != nil {
utils.CliError("Login failed %v. Please check your credentials and try again.\n", err)
}
Expand All @@ -35,35 +60,37 @@ var loginCmd = &cobra.Command{
if err != nil {
utils.CliError("Connection to Alpacon API failed: %s. Consider re-logging.", err)
}

fmt.Println("Login succeeded!")
},
}

func init() {
var token string
var username, password, token string

loginCmd.Flags().StringVarP(&loginRequest.ServerAddress, "server", "s", "defaultServerURL", "URL of the server to login, default: https://alpacon.io")
loginCmd.Flags().StringVarP(&loginRequest.Username, "username", "u", "", "Username for login")
loginCmd.Flags().StringVarP(&loginRequest.Password, "password", "p", "", "Password for login")
loginCmd.Flags().StringVarP(&username, "username", "u", "", "Username for login")
loginCmd.Flags().StringVarP(&password, "password", "p", "", "Password for login")
loginCmd.Flags().StringVarP(&token, "token", "t", "", "API token for login")
}

func promptForCredentials() {
configFile, err := config.LoadConfig()
if err == nil && configFile.ServerAddress != "" {
loginRequest.ServerAddress = configFile.ServerAddress
fmt.Println("Using server address from config:", configFile.ServerAddress, ". Modify it in `~/.alpacon/config.json` or use all flags (-s, -u, -p) for changes.")
} else {
loginRequest.ServerAddress = utils.PromptForInput("Server Address[https://alpacon.io]: ")
if loginRequest.ServerAddress == "" {
loginRequest.ServerAddress = defaultServerURL
func promptForCredentials(workspaceURL, username, password string) (string, string, string) {
if workspaceURL == "" {
configFile, err := config.LoadConfig()
if err == nil && configFile.WorkspaceURL != "" {
workspaceURL = configFile.WorkspaceURL
fmt.Printf("Using Workspace URL %s from config file.\n", configFile.WorkspaceURL)
fmt.Println("If you want to change the workspace, specify workspace url: alpacon login [WORKSPACE_URL] -u [USERNAME] -p [PASSWORD]")
fmt.Println()
}
}

if loginRequest.Username == "" {
loginRequest.Username = utils.PromptForRequiredInput("Username: ")
if username == "" {
username = utils.PromptForRequiredInput("Username: ")
}
if loginRequest.Password == "" {
loginRequest.Password = utils.PromptForPassword("Password: ")

if password == "" {
password = utils.PromptForPassword("Password: ")
}

return workspaceURL, username, password
}
11 changes: 7 additions & 4 deletions cmd/logout.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@ import (

var logoutCmd = &cobra.Command{
Use: "logout",
Short: "Log out of Alpacon Server",
Long: "Log out of Alpacon Server. This command removes your authentication credentials stored locally on your system.",
Short: "Log out of Alpacon",
Long: "Log out of Alpacon. This command removes your authentication credentials stored locally on your system.",
Example: `
alpacon logout
`,
Run: func(cmd *cobra.Command, args []string) {

alpaconClient, err := client.NewAlpaconAPIClient()
if err != nil {
utils.CliError("Connection to Alpacon API failed: %s. Consider re-logging.", err)
utils.CliError("Connection to Alpacon failed: %s. Consider re-logging.", err)
}

err = auth.LogoutAndDeleteCredentials(alpaconClient)
if err != nil {
utils.CliError("Connection to Alpacon API failed: %s. Consider re-logging.", err)
utils.CliError("Connection to Alpacon failed: %s. Consider re-logging.", err)
}
fmt.Println("Logout succeeded!")
},
Expand Down
18 changes: 9 additions & 9 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ import (

// Config describes the configuration for Alpacon-CLI
type Config struct {
ServerAddress string `json:"server_address"`
Token string `json:"token"`
ExpiresAt string `json:"expires_at"`
WorkspaceURL string `json:"workspace_url"`
Token string `json:"token"`
ExpiresAt string `json:"expires_at"`
}

const (
ConfigFileName = "config.json"
ConfigFileDir = ".alpacon"
)

func CreateConfig(serverAddress string, token string, expiresAt string) error {
func CreateConfig(workspaceURL string, token string, expiresAt string) error {
config := Config{
ServerAddress: serverAddress,
Token: token,
ExpiresAt: expiresAt,
WorkspaceURL: workspaceURL,
Token: token,
ExpiresAt: expiresAt,
}

return saveConfig(&config)
Expand All @@ -45,7 +45,7 @@ func saveConfig(config *Config) error {
if err != nil {
return fmt.Errorf("failed to create config file: %v", err)
}
defer file.Close()
defer func() { _ = file.Close() }()

encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
Expand Down Expand Up @@ -89,7 +89,7 @@ func LoadConfig() (Config, error) {
}
return Config{}, fmt.Errorf("failed to open config file: %v", err)
}
defer file.Close()
defer func() { _ = file.Close() }()

var config Config
decoder := json.NewDecoder(file)
Expand Down

0 comments on commit f5df2d0

Please sign in to comment.