github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/storage/storage.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 package storage 7 8 import ( 9 "context" 10 "errors" 11 "fmt" 12 "io" 13 "net/url" 14 "os" 15 16 "github.com/gitbundle/modules/log" 17 "github.com/gitbundle/modules/setting" 18 ) 19 20 // ErrURLNotSupported represents url is not supported 21 var ErrURLNotSupported = errors.New("url method not supported") 22 23 // ErrInvalidConfiguration is called when there is invalid configuration for a storage 24 type ErrInvalidConfiguration struct { 25 cfg interface{} 26 err error 27 } 28 29 func (err ErrInvalidConfiguration) Error() string { 30 if err.err != nil { 31 return fmt.Sprintf("Invalid Configuration Argument: %v: Error: %v", err.cfg, err.err) 32 } 33 return fmt.Sprintf("Invalid Configuration Argument: %v", err.cfg) 34 } 35 36 // IsErrInvalidConfiguration checks if an error is an ErrInvalidConfiguration 37 func IsErrInvalidConfiguration(err error) bool { 38 _, ok := err.(ErrInvalidConfiguration) 39 return ok 40 } 41 42 // Type is a type of Storage 43 type Type string 44 45 // NewStorageFunc is a function that creates a storage 46 type NewStorageFunc func(ctx context.Context, cfg interface{}) (ObjectStorage, error) 47 48 var storageMap = map[Type]NewStorageFunc{} 49 50 // RegisterStorageType registers a provided storage type with a function to create it 51 func RegisterStorageType(typ Type, fn func(ctx context.Context, cfg interface{}) (ObjectStorage, error)) { 52 storageMap[typ] = fn 53 } 54 55 // Object represents the object on the storage 56 type Object interface { 57 io.ReadCloser 58 io.Seeker 59 Stat() (os.FileInfo, error) 60 } 61 62 // ObjectStorage represents an object storage to handle a bucket and files 63 type ObjectStorage interface { 64 Open(path string) (Object, error) 65 // Save store a object, if size is unknown set -1 66 Save(path string, r io.Reader, size int64) (int64, error) 67 Stat(path string) (os.FileInfo, error) 68 Delete(path string) error 69 URL(path, name string) (*url.URL, error) 70 IterateObjects(func(path string, obj Object) error) error 71 } 72 73 // Copy copies a file from source ObjectStorage to dest ObjectStorage 74 func Copy(dstStorage ObjectStorage, dstPath string, srcStorage ObjectStorage, srcPath string) (int64, error) { 75 f, err := srcStorage.Open(srcPath) 76 if err != nil { 77 return 0, err 78 } 79 defer f.Close() 80 81 size := int64(-1) 82 fsinfo, err := f.Stat() 83 if err == nil { 84 size = fsinfo.Size() 85 } 86 87 return dstStorage.Save(dstPath, f, size) 88 } 89 90 // Clean delete all the objects in this storage 91 func Clean(storage ObjectStorage) error { 92 return storage.IterateObjects(func(path string, obj Object) error { 93 _ = obj.Close() 94 return storage.Delete(path) 95 }) 96 } 97 98 // SaveFrom saves data to the ObjectStorage with path p from the callback 99 func SaveFrom(objStorage ObjectStorage, p string, callback func(w io.Writer) error) error { 100 pr, pw := io.Pipe() 101 defer pr.Close() 102 go func() { 103 defer pw.Close() 104 if err := callback(pw); err != nil { 105 _ = pw.CloseWithError(err) 106 } 107 }() 108 109 _, err := objStorage.Save(p, pr, -1) 110 return err 111 } 112 113 var ( 114 // Attachments represents attachments storage 115 Attachments ObjectStorage 116 117 // LFS represents lfs storage 118 LFS ObjectStorage 119 120 // Avatars represents user avatars storage 121 Avatars ObjectStorage 122 // RepoAvatars represents repository avatars storage 123 RepoAvatars ObjectStorage 124 125 // RepoArchives represents repository archives storage 126 RepoArchives ObjectStorage 127 128 // Packages represents packages storage 129 Packages ObjectStorage 130 ) 131 132 // Init init the stoarge 133 func Init() error { 134 if err := initAttachments(); err != nil { 135 return err 136 } 137 138 if err := initAvatars(); err != nil { 139 return err 140 } 141 142 if err := initRepoAvatars(); err != nil { 143 return err 144 } 145 146 if err := initLFS(); err != nil { 147 return err 148 } 149 150 if err := initRepoArchives(); err != nil { 151 return err 152 } 153 154 return initPackages() 155 } 156 157 // NewStorage takes a storage type and some config and returns an ObjectStorage or an error 158 func NewStorage(typStr string, cfg interface{}) (ObjectStorage, error) { 159 if len(typStr) == 0 { 160 typStr = string(LocalStorageType) 161 } 162 fn, ok := storageMap[Type(typStr)] 163 if !ok { 164 return nil, fmt.Errorf("Unsupported storage type: %s", typStr) 165 } 166 167 return fn(context.Background(), cfg) 168 } 169 170 func initAvatars() (err error) { 171 log.Info("Initialising Avatar storage with type: %s", setting.Avatar.Storage.Type) 172 Avatars, err = NewStorage(setting.Avatar.Storage.Type, &setting.Avatar.Storage) 173 return 174 } 175 176 func initAttachments() (err error) { 177 log.Info("Initialising Attachment storage with type: %s", setting.Attachment.Storage.Type) 178 Attachments, err = NewStorage(setting.Attachment.Storage.Type, &setting.Attachment.Storage) 179 return 180 } 181 182 func initLFS() (err error) { 183 log.Info("Initialising LFS storage with type: %s", setting.LFS.Storage.Type) 184 LFS, err = NewStorage(setting.LFS.Storage.Type, &setting.LFS.Storage) 185 return 186 } 187 188 func initRepoAvatars() (err error) { 189 log.Info("Initialising Repository Avatar storage with type: %s", setting.RepoAvatar.Storage.Type) 190 RepoAvatars, err = NewStorage(setting.RepoAvatar.Storage.Type, &setting.RepoAvatar.Storage) 191 return 192 } 193 194 func initRepoArchives() (err error) { 195 log.Info("Initialising Repository Archive storage with type: %s", setting.RepoArchive.Storage.Type) 196 RepoArchives, err = NewStorage(setting.RepoArchive.Storage.Type, &setting.RepoArchive.Storage) 197 return 198 } 199 200 func initPackages() (err error) { 201 log.Info("Initialising Packages storage with type: %s", setting.Packages.Storage.Type) 202 Packages, err = NewStorage(setting.Packages.Storage.Type, &setting.Packages.Storage) 203 return 204 }