git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/storage/filesystem/filesystem.go (about)

     1  package filesystem
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  )
    11  
    12  type FilesystemStorage struct {
    13  	basePath string
    14  }
    15  
    16  type Config struct {
    17  	BaseDirectory string
    18  }
    19  
    20  var (
    21  	ErrKeyIsNotValid    = errors.New("storage key is not valid")
    22  	ErrPrefixIsNotValid = errors.New("storage prefix is not valid")
    23  )
    24  
    25  func NewFilesystemStorage(config Config) *FilesystemStorage {
    26  	return &FilesystemStorage{
    27  		basePath: config.BaseDirectory,
    28  	}
    29  }
    30  
    31  func (storage *FilesystemStorage) BasePath() string {
    32  	return storage.basePath
    33  }
    34  
    35  func (storage *FilesystemStorage) CopyObject(ctx context.Context, from string, to string) (err error) {
    36  	if strings.Contains(from, "..") || strings.Contains(to, "..") {
    37  		err = ErrKeyIsNotValid
    38  		return
    39  	}
    40  
    41  	from = filepath.Join(storage.basePath, from)
    42  	source, err := os.Open(from)
    43  	if err != nil {
    44  		return
    45  	}
    46  	defer source.Close()
    47  
    48  	to = filepath.Join(storage.basePath, to)
    49  	destination, err := os.Create(to)
    50  	if err != nil {
    51  		return
    52  	}
    53  	defer destination.Close()
    54  
    55  	_, err = io.Copy(destination, source)
    56  	return
    57  }
    58  
    59  func (storage *FilesystemStorage) DeleteObject(ctx context.Context, key string) (err error) {
    60  	if strings.Contains(key, "..") {
    61  		err = ErrKeyIsNotValid
    62  		return
    63  	}
    64  
    65  	filePath := filepath.Join(storage.basePath, key)
    66  	return os.Remove(filePath)
    67  }
    68  
    69  func (storage *FilesystemStorage) GetObject(ctx context.Context, key string) (file io.ReadCloser, err error) {
    70  	if strings.Contains(key, "..") {
    71  		err = ErrKeyIsNotValid
    72  		return
    73  	}
    74  
    75  	filePath := filepath.Join(storage.basePath, key)
    76  	file, err = os.OpenFile(filePath, os.O_RDONLY, os.ModePerm)
    77  	return
    78  }
    79  
    80  func (storage *FilesystemStorage) GetObjectSize(ctx context.Context, key string) (ret int64, err error) {
    81  	if strings.Contains(key, "..") {
    82  		err = ErrKeyIsNotValid
    83  		return
    84  	}
    85  
    86  	filePath := filepath.Join(storage.basePath, key)
    87  
    88  	fileStat, err := os.Stat(filePath)
    89  	if err != nil {
    90  		return
    91  	}
    92  
    93  	ret = fileStat.Size()
    94  	return
    95  }
    96  
    97  // func (storage *FilesystemStorage) GetPresignedUploadUrl(ctx context.Context, key string, size uint64) (string, error) {
    98  // 	panic("not implemented") // TODO: Implement
    99  // }
   100  
   101  func (storage *FilesystemStorage) PutObject(ctx context.Context, key string, contentType string, size int64, object io.Reader) (err error) {
   102  	if strings.Contains(key, "..") {
   103  		err = ErrKeyIsNotValid
   104  		return
   105  	}
   106  
   107  	filePath := filepath.Join(storage.basePath, key)
   108  	directory := filepath.Dir(filePath)
   109  
   110  	err = os.MkdirAll(directory, os.ModePerm)
   111  	if err != nil {
   112  		return
   113  	}
   114  
   115  	destination, err := os.Create(filePath)
   116  	if err != nil {
   117  		return
   118  	}
   119  	defer destination.Close()
   120  
   121  	_, err = io.Copy(destination, object)
   122  	return
   123  }
   124  
   125  func (storage *FilesystemStorage) DeleteObjectsWithPrefix(ctx context.Context, prefix string) (err error) {
   126  	if strings.Contains(prefix, "..") {
   127  		err = ErrPrefixIsNotValid
   128  		return
   129  	}
   130  
   131  	folder := filepath.Join(storage.basePath, prefix)
   132  
   133  	err = os.RemoveAll(folder)
   134  	return
   135  }