package cli import ( "context" "fmt" "mime" "os" "path/filepath" "strings" "text/tabwriter" "github.com/spf13/cobra" ) func NewFileCmd() *cobra.Command { cmd := &cobra.Command{ Use: "file", Short: "Manage files in buckets", Long: "Upload, download, list, and manage files in your buckets", } cmd.AddCommand(newFileUploadCmd()) cmd.AddCommand(newFileDownloadCmd()) cmd.AddCommand(newFileListCmd()) cmd.AddCommand(newFileDeleteCmd()) return cmd } func newFileUploadCmd() *cobra.Command { var contentType string cmd := &cobra.Command{ Use: "upload [remote-key]", Short: "Upload a file to a bucket", Args: cobra.RangeArgs(2, 3), 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] localFile := args[1] remoteKey := filepath.Base(localFile) if len(args) == 3 { remoteKey = args[2] } data, err := os.ReadFile(localFile) if err != nil { return fmt.Errorf("failed to read file: %w", err) } if contentType == "" { contentType = mime.TypeByExtension(filepath.Ext(localFile)) } client := NewClient(config.ServerURL, config.AccessToken) ctx := context.Background() fmt.Printf("Uploading %s to %s/%s (%s)...\n", localFile, bucketName, remoteKey, formatBytes(int64(len(data)))) err = client.PutObject(ctx, bucketName, remoteKey, data, contentType) if err != nil { return fmt.Errorf("failed to upload file: %w", err) } fmt.Printf("File uploaded successfully!\n") fmt.Printf("Bucket: %s\n", bucketName) fmt.Printf("Key: %s\n", remoteKey) fmt.Printf("Size: %s\n", formatBytes(int64(len(data)))) if contentType != "" { fmt.Printf("Content-Type: %s\n", contentType) } return nil }, } cmd.Flags().StringVar(&contentType, "content-type", "", "Override content type") return cmd } func newFileDownloadCmd() *cobra.Command { return &cobra.Command{ Use: "download [local-file]", Short: "Download a file from a bucket", Args: cobra.RangeArgs(2, 3), 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] remoteKey := args[1] localFile := filepath.Base(remoteKey) if len(args) == 3 { localFile = args[2] } client := NewClient(config.ServerURL, config.AccessToken) ctx := context.Background() fmt.Printf("Downloading %s/%s to %s...\n", bucketName, remoteKey, localFile) data, err := client.GetObject(ctx, bucketName, remoteKey) if err != nil { return fmt.Errorf("failed to download file: %w", err) } err = os.WriteFile(localFile, data, 0644) if err != nil { return fmt.Errorf("failed to write file: %w", err) } fmt.Printf("File downloaded successfully!\n") fmt.Printf("Size: %s\n", formatBytes(int64(len(data)))) fmt.Printf("Saved to: %s\n", localFile) return nil }, } } func newFileListCmd() *cobra.Command { return &cobra.Command{ Use: "list ", Short: "List files in 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() objects, err := client.ListObjects(ctx, bucketName) if err != nil { return fmt.Errorf("failed to list objects: %w", err) } if len(objects) == 0 { fmt.Printf("No files found in bucket '%s'.\n", bucketName) return nil } w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) fmt.Fprintln(w, "Key\tSize\tContent-Type\tLast Modified") fmt.Fprintln(w, "---\t----\t------------\t-------------") for _, obj := range objects { contentType := obj.ContentType if contentType == "" { contentType = "application/octet-stream" } fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", obj.Key, formatBytes(obj.SizeBytes), contentType, obj.LastModified.Format("2006-01-02 15:04:05"), ) } w.Flush() return nil }, } } func newFileDeleteCmd() *cobra.Command { var force bool cmd := &cobra.Command{ Use: "delete ", Short: "Delete a file from a bucket", Args: cobra.ExactArgs(2), 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] remoteKey := args[1] client := NewClient(config.ServerURL, config.AccessToken) ctx := context.Background() if !force { fmt.Printf("Are you sure you want to delete '%s' from bucket '%s'? (y/N): ", remoteKey, bucketName) var response string fmt.Scanln(&response) response = strings.ToLower(response) if response != "y" && response != "yes" { fmt.Printf("Cancelled.\n") return nil } } err = client.DeleteObject(ctx, bucketName, remoteKey) if err != nil { return fmt.Errorf("failed to delete file: %w", err) } fmt.Printf("File '%s' deleted from bucket '%s'.\n", remoteKey, bucketName) return nil }, } cmd.Flags().BoolVar(&force, "force", false, "Force deletion without confirmation") return cmd }