package cli import ( "bufio" "bytes" "encoding/json" "fmt" "net/http" "os" "path/filepath" "strings" "syscall" "github.com/spf13/cobra" "golang.org/x/term" ) const apiURL = "http://localhost:8080" type Config struct { Token string `json:"token"` } var rootCmd = &cobra.Command{ Use: "termbox", Short: "Termbox CLI - A secure messaging platform", Long: "Termbox CLI allows you to register, login, and send messages securely.", } var authCmd = &cobra.Command{ Use: "auth", Short: "Authentication commands", Long: "Commands for user authentication including register, login, and logout.", } var registerCmd = &cobra.Command{ Use: "register", Short: "Register a new account", Long: "Register a new account with username, email, and password. Includes email verification.", Run: runRegister, } var loginCmd = &cobra.Command{ Use: "login", Short: "Login to your account", Long: "Login to your account with username and password.", Run: runLogin, } var logoutCmd = &cobra.Command{ Use: "logout", Short: "Logout from your account", Long: "Clear stored authentication token.", Run: runLogout, } func init() { authCmd.AddCommand(registerCmd) authCmd.AddCommand(loginCmd) authCmd.AddCommand(logoutCmd) rootCmd.AddCommand(authCmd) } func Execute() error { return rootCmd.Execute() } func getConfigPath() string { homeDir, _ := os.UserHomeDir() return filepath.Join(homeDir, ".termbox", "config.json") } func loadConfig() (*Config, error) { configPath := getConfigPath() if _, err := os.Stat(configPath); os.IsNotExist(err) { return &Config{}, nil } data, err := os.ReadFile(configPath) if err != nil { return nil, err } var config Config err = json.Unmarshal(data, &config) if err != nil { return nil, err } return &config, nil } func saveConfig(config *Config) error { configPath := getConfigPath() configDir := filepath.Dir(configPath) if err := os.MkdirAll(configDir, 0755); err != nil { return err } data, err := json.MarshalIndent(config, "", " ") if err != nil { return err } return os.WriteFile(configPath, data, 0600) } func runRegister(cmd *cobra.Command, args []string) { fmt.Print("Username: ") reader := bufio.NewReader(os.Stdin) username, _ := reader.ReadString('\n') username = strings.TrimSpace(username) fmt.Print("Email: ") email, _ := reader.ReadString('\n') email = strings.TrimSpace(email) fmt.Print("Password: ") passwordBytes, err := term.ReadPassword(int(syscall.Stdin)) if err != nil { fmt.Printf("Error reading password: %v\n", err) return } password := string(passwordBytes) fmt.Println() requestBody, _ := json.Marshal(map[string]string{ "username": username, "email": email, "password": password, }) resp, err := http.Post(apiURL+"/auth/register", "application/json", bytes.NewBuffer(requestBody)) if err != nil { fmt.Printf("Error registering: %v\n", err) return } defer resp.Body.Close() var response map[string]interface{} json.NewDecoder(resp.Body).Decode(&response) if resp.StatusCode == http.StatusCreated { fmt.Printf("Registration successful! Check your email (%s) for verification code.\n", email) fmt.Print("Verification code: ") code, _ := reader.ReadString('\n') code = strings.TrimSpace(code) verifyBody, _ := json.Marshal(map[string]string{ "email": email, "code": code, }) verifyResp, err := http.Post(apiURL+"/auth/verify", "application/json", bytes.NewBuffer(verifyBody)) if err != nil { fmt.Printf("Error verifying: %v\n", err) return } defer verifyResp.Body.Close() var verifyResponse map[string]interface{} json.NewDecoder(verifyResp.Body).Decode(&verifyResponse) if verifyResp.StatusCode == http.StatusOK { fmt.Println("Account verified successfully! You can now login.") } else { fmt.Printf("Verification failed: %s\n", verifyResponse["error"]) } } else { fmt.Printf("Registration failed: %s\n", response["error"]) } } func runLogin(cmd *cobra.Command, args []string) { fmt.Print("Username: ") reader := bufio.NewReader(os.Stdin) username, _ := reader.ReadString('\n') username = strings.TrimSpace(username) fmt.Print("Password: ") passwordBytes, err := term.ReadPassword(int(syscall.Stdin)) if err != nil { fmt.Printf("Error reading password: %v\n", err) return } password := string(passwordBytes) fmt.Println() requestBody, _ := json.Marshal(map[string]string{ "username": username, "password": password, }) resp, err := http.Post(apiURL+"/auth/login", "application/json", bytes.NewBuffer(requestBody)) if err != nil { fmt.Printf("Error logging in: %v\n", err) return } defer resp.Body.Close() var response map[string]interface{} json.NewDecoder(resp.Body).Decode(&response) if resp.StatusCode == http.StatusOK { user := response["user"].(map[string]interface{}) token := response["token"].(string) config := &Config{Token: token} if err := saveConfig(config); err != nil { fmt.Printf("Warning: Failed to save login token: %v\n", err) } fmt.Printf("Login successful! Welcome back, %s!\n", user["username"]) } else { fmt.Printf("Login failed: %s\n", response["error"]) } } func runLogout(cmd *cobra.Command, args []string) { config := &Config{} if err := saveConfig(config); err != nil { fmt.Printf("Error clearing token: %v\n", err) return } fmt.Println("Logged out successfully.") }