package cli import ( "context" "fmt" "os" "text/tabwriter" "github.com/spf13/cobra" ) func NewBucketCmd() *cobra.Command { cmd := &cobra.Command{ Use: "bucket", Short: "Manage storage buckets", Long: "Create, list, and manage your storage buckets", } cmd.AddCommand(newBucketCreateCmd()) cmd.AddCommand(newBucketListCmd()) cmd.AddCommand(newBucketDeleteCmd()) cmd.AddCommand(newBucketInfoCmd()) return cmd } func newBucketCreateCmd() *cobra.Command { return &cobra.Command{ Use: "create ", Short: "Create a new bucket", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { config, err := LoadConfig() if err != nil { return fmt.Errorf("failed to load config: %w", err) } if !config.IsAuthenticated() { return fmt.Errorf("not logged in. Use 'tcman account login' or 'tcman account create'") } bucketName := args[0] client := NewClient(config.ServerURL, config.AccessToken) ctx := context.Background() bucket, err := client.CreateBucket(ctx, bucketName) if err != nil { return fmt.Errorf("failed to create bucket: %w", err) } fmt.Printf("Bucket '%s' created successfully!\n", bucket.Name) fmt.Printf("ID: %d\n", bucket.ID) fmt.Printf("Storage Used: %d bytes\n", bucket.StorageUsedBytes) fmt.Printf("Created: %s\n", bucket.CreatedAt.Format("2006-01-02 15:04:05")) return nil }, } } func newBucketListCmd() *cobra.Command { return &cobra.Command{ Use: "list", Short: "List all buckets", RunE: func(cmd *cobra.Command, args []string) error { config, err := LoadConfig() if err != nil { return fmt.Errorf("failed to load config: %w", err) } if !config.IsAuthenticated() { return fmt.Errorf("not logged in. Use 'tcman account login' or 'tcman account create'") } client := NewClient(config.ServerURL, config.AccessToken) ctx := context.Background() buckets, err := client.ListBuckets(ctx) if err != nil { return fmt.Errorf("failed to list buckets: %w", err) } if len(buckets) == 0 { fmt.Printf("No buckets found.\n") return nil } w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) fmt.Fprintln(w, "Name\tStorage Used\tCreated") fmt.Fprintln(w, "----\t------------\t-------") for _, bucket := range buckets { storageStr := formatBytes(bucket.StorageUsedBytes) fmt.Fprintf(w, "%s\t%s\t%s\n", bucket.Name, storageStr, bucket.CreatedAt.Format("2006-01-02 15:04"), ) } w.Flush() return nil }, } } func newBucketInfoCmd() *cobra.Command { return &cobra.Command{ Use: "info ", Short: "Show bucket information", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { config, err := LoadConfig() if err != nil { return fmt.Errorf("failed to load config: %w", err) } if !config.IsAuthenticated() { return fmt.Errorf("not logged in. Use 'tcman account login' or 'tcman account create'") } bucketName := args[0] client := NewClient(config.ServerURL, config.AccessToken) ctx := context.Background() bucket, err := client.GetBucket(ctx, bucketName) if err != nil { return fmt.Errorf("failed to get bucket info: %w", err) } fmt.Printf("Bucket Information:\n") fmt.Printf(" Name: %s\n", bucket.Name) fmt.Printf(" ID: %d\n", bucket.ID) fmt.Printf(" Storage Used: %s\n", formatBytes(bucket.StorageUsedBytes)) fmt.Printf(" Created: %s\n", bucket.CreatedAt.Format("2006-01-02 15:04:05")) objects, err := client.ListObjects(ctx, bucketName) if err != nil { fmt.Printf(" Objects: Error loading (%v)\n", err) } else { fmt.Printf(" Objects: %d\n", len(objects)) } return nil }, } } func newBucketDeleteCmd() *cobra.Command { var force bool cmd := &cobra.Command{ Use: "delete ", Short: "Delete a bucket", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { config, err := LoadConfig() if err != nil { return fmt.Errorf("failed to load config: %w", err) } if !config.IsAuthenticated() { return fmt.Errorf("not logged in. Use 'tcman account login' or 'tcman account create'") } bucketName := args[0] client := NewClient(config.ServerURL, config.AccessToken) ctx := context.Background() if !force { objects, err := client.ListObjects(ctx, bucketName) if err != nil { return fmt.Errorf("failed to check bucket contents: %w", err) } if len(objects) > 0 { return fmt.Errorf("bucket '%s' contains %d objects. Use --force to delete anyway", bucketName, len(objects)) } fmt.Printf("Are you sure you want to delete bucket '%s'? (y/N): ", bucketName) var response string fmt.Scanln(&response) if response != "y" && response != "yes" && response != "Y" && response != "YES" { fmt.Printf("Cancelled.\n") return nil } } err = client.DeleteBucket(ctx, bucketName) if err != nil { return fmt.Errorf("failed to delete bucket: %w", err) } fmt.Printf("Bucket '%s' deleted successfully.\n", bucketName) return nil }, } cmd.Flags().BoolVar(&force, "force", false, "Force deletion without confirmation") return cmd } func formatBytes(bytes int64) string { if bytes == 0 { return "0 B" } const unit = 1024 if bytes < unit { return fmt.Sprintf("%d B", bytes) } div, exp := int64(unit), 0 for n := bytes / unit; n >= unit; n /= unit { div *= unit exp++ } units := []string{"KB", "MB", "GB", "TB"} return fmt.Sprintf("%.1f %s", float64(bytes)/float64(div), units[exp]) }