github.com/supabase/cli@v1.168.1/internal/storage/ls/ls.go (about)

     1  package ls
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path"
     8  	"strings"
     9  
    10  	"github.com/spf13/afero"
    11  	"github.com/supabase/cli/internal/storage/client"
    12  	"github.com/supabase/cli/internal/utils/flags"
    13  	"github.com/supabase/cli/pkg/storage"
    14  )
    15  
    16  func Run(ctx context.Context, objectPath string, recursive bool, fsys afero.Fs) error {
    17  	remotePath, err := client.ParseStorageURL(objectPath)
    18  	if err != nil {
    19  		return err
    20  	}
    21  	callback := func(objectPath string) error {
    22  		fmt.Println(objectPath)
    23  		return nil
    24  	}
    25  	api, err := client.NewStorageAPI(ctx, flags.ProjectRef)
    26  	if err != nil {
    27  		return err
    28  	}
    29  	if recursive {
    30  		return IterateStoragePathsAll(ctx, api, remotePath, callback)
    31  	}
    32  	return IterateStoragePaths(ctx, api, remotePath, callback)
    33  }
    34  
    35  func ListStoragePaths(ctx context.Context, api storage.StorageAPI, remotePath string) ([]string, error) {
    36  	var result []string
    37  	err := IterateStoragePaths(ctx, api, remotePath, func(objectName string) error {
    38  		result = append(result, objectName)
    39  		return nil
    40  	})
    41  	return result, err
    42  }
    43  
    44  func IterateStoragePaths(ctx context.Context, api storage.StorageAPI, remotePath string, callback func(objectName string) error) error {
    45  	bucket, prefix := client.SplitBucketPrefix(remotePath)
    46  	if len(bucket) == 0 || (len(prefix) == 0 && !strings.HasSuffix(remotePath, "/")) {
    47  		buckets, err := api.ListBuckets(ctx)
    48  		if err != nil {
    49  			return err
    50  		}
    51  		for _, b := range buckets {
    52  			if strings.HasPrefix(b.Name, bucket) {
    53  				if err := callback(b.Name + "/"); err != nil {
    54  					return err
    55  				}
    56  			}
    57  		}
    58  	} else {
    59  		pages := 1
    60  		for i := 0; i < pages; i++ {
    61  			objects, err := api.ListObjects(ctx, bucket, prefix, i)
    62  			if err != nil {
    63  				return err
    64  			}
    65  			for _, o := range objects {
    66  				name := o.Name
    67  				if o.Id == nil {
    68  					name += "/"
    69  				}
    70  				if err := callback(name); err != nil {
    71  					return err
    72  				}
    73  			}
    74  			if len(objects) == storage.PAGE_LIMIT {
    75  				// TODO: show interactive prompt?
    76  				fmt.Fprintln(os.Stderr, "Loading page:", pages)
    77  				pages++
    78  			}
    79  		}
    80  	}
    81  	return nil
    82  }
    83  
    84  // Expects remotePath to be terminated by "/"
    85  func ListStoragePathsAll(ctx context.Context, api storage.StorageAPI, remotePath string) ([]string, error) {
    86  	var result []string
    87  	err := IterateStoragePathsAll(ctx, api, remotePath, func(objectPath string) error {
    88  		result = append(result, objectPath)
    89  		return nil
    90  	})
    91  	return result, err
    92  }
    93  
    94  func IterateStoragePathsAll(ctx context.Context, api storage.StorageAPI, remotePath string, callback func(objectPath string) error) error {
    95  	basePath := remotePath
    96  	if !strings.HasSuffix(remotePath, "/") {
    97  		basePath, _ = path.Split(remotePath)
    98  	}
    99  	// BFS so we can list paths in increasing depth
   100  	dirQueue := make([]string, 0)
   101  	// We don't know if user passed in a directory or file, so query storage first.
   102  	if err := IterateStoragePaths(ctx, api, remotePath, func(objectName string) error {
   103  		objectPath := basePath + objectName
   104  		if strings.HasSuffix(objectName, "/") {
   105  			dirQueue = append(dirQueue, objectPath)
   106  			return nil
   107  		}
   108  		return callback(objectPath)
   109  	}); err != nil {
   110  		return err
   111  	}
   112  	for len(dirQueue) > 0 {
   113  		dirPath := dirQueue[len(dirQueue)-1]
   114  		dirQueue = dirQueue[:len(dirQueue)-1]
   115  		empty := true
   116  		if err := IterateStoragePaths(ctx, api, dirPath, func(objectName string) error {
   117  			empty = false
   118  			objectPath := dirPath + objectName
   119  			if strings.HasSuffix(objectName, "/") {
   120  				dirQueue = append(dirQueue, objectPath)
   121  				return nil
   122  			}
   123  			return callback(objectPath)
   124  		}); err != nil {
   125  			return err
   126  		}
   127  		// Also report empty buckets
   128  		bucket, prefix := client.SplitBucketPrefix(dirPath)
   129  		if empty && len(prefix) == 0 {
   130  			if err := callback(bucket + "/"); err != nil {
   131  				return err
   132  			}
   133  		}
   134  	}
   135  	return nil
   136  }