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 }