termcloud/internal/cli/bucket.go
2025-08-07 19:10:13 +01:00

222 lines
5.5 KiB
Go

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 <bucket-name>",
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 <bucket-name>",
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 <bucket-name>",
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])
}