termbox/internal/cli/root.go

272 lines
6.4 KiB
Go

package cli
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"path/filepath"
"strconv"
"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,
}
var inboxCmd = &cobra.Command{
Use: "inbox",
Short: "View your termail inbox",
Long: "View, search, and manage your termail inbox.",
Run: runInbox,
}
var inboxReadCmd = &cobra.Command{
Use: "read [termail_id]",
Short: "Mark termail as read",
Long: "Mark a specific termail as read by ID.",
Args: cobra.ExactArgs(1),
Run: runInboxRead,
}
var inboxDeleteCmd = &cobra.Command{
Use: "delete [termail_id]",
Short: "Delete termail",
Long: "Delete a specific termail by ID.",
Args: cobra.ExactArgs(1),
Run: runInboxDelete,
}
var sendCmd = &cobra.Command{
Use: "send [username]",
Short: "Send termail to a user",
Long: "Send termail to a specific user by username.",
Args: cobra.ExactArgs(1),
Run: runSend,
}
func init() {
inboxCmd.Flags().StringP("search", "s", "", "Search termails by content, subject, or sender")
inboxCmd.Flags().IntP("limit", "l", 10, "Number of termails to show")
inboxCmd.Flags().IntP("offset", "o", 0, "Number of termails to skip")
inboxCmd.Flags().Bool("unread", false, "Show only unread termails")
inboxCmd.AddCommand(inboxReadCmd)
inboxCmd.AddCommand(inboxDeleteCmd)
authCmd.AddCommand(registerCmd)
authCmd.AddCommand(loginCmd)
authCmd.AddCommand(logoutCmd)
rootCmd.AddCommand(authCmd)
rootCmd.AddCommand(inboxCmd)
rootCmd.AddCommand(sendCmd)
}
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.")
}