11package supabase
22
33import (
4+
5+ "bufio"
46 "bytes"
57 "context"
68 "encoding/json"
7- // "errors"
9+ "io"
10+ "regexp"
11+ "strconv"
812 "fmt"
913 "net/http"
1014)
1115
1216type Storage struct {
1317 client * Client
14- // *bucket
15- // *file
18+
1619}
1720
21+ // Storage buckets methods
22+
1823type bucket struct {
1924 Name string `json:"name"`
2025}
@@ -28,7 +33,6 @@ type bucketResponse struct {
2833type bucketMessage struct {
2934 Message string `json:"message"`
3035}
31- // type file struct{}
3236
3337type bucketOption struct {
3438 Id string `json:"id"`
@@ -42,6 +46,7 @@ type storageError struct {
4246 Message string `json:"message"`
4347}
4448
49+
4550// CreateBucket creates a new storage bucket
4651// @param: option: a bucketOption with the name and id of the bucket you want to create
4752// @returns: bucket: a response with the details of the bucket of the bucket created
@@ -174,3 +179,351 @@ func (s *Storage) DeleteBucket(ctx context.Context, id string) (*bucketResponse,
174179 return & res , nil
175180}
176181
182+
183+ func (s Storage ) From (bucketId string ) * file {
184+ return & file {BucketId : bucketId }
185+ }
186+
187+ // Storage Objects methods
188+
189+ type file struct {
190+ BucketId string
191+ header http.Header
192+ }
193+
194+ type SortBy struct {
195+ Column string `json:"column"`
196+ Order string `json:"order"`
197+ }
198+
199+ type FileResponse struct {
200+ Key string `json:"key"`
201+ Message string `json:"message"`
202+ }
203+
204+ type FileSearchOptions struct {
205+ Limit int `json:"limit"`
206+ Offset int `json:"offset"`
207+ SortBy SortBy `json:"sortBy"`
208+ }
209+
210+ type FileObject struct {
211+ Name string `json:"name"`
212+ BucketId string `json:"bucket_id"`
213+ Owner string `json:"owner"`
214+ Id string `json:"id"`
215+ UpdatedAt string `json:"updated_at"`
216+ CreatedAt string `json:"created_at"`
217+ LastAccessedAt string `json:"last_accessed_at"`
218+ Metadata interface {} `json:"metadata"`
219+ Buckets bucket `json:"buckets"`
220+ }
221+
222+ type ListFileRequest struct {
223+ Limit int `json:"limit"`
224+ Offset int `json:"offset"`
225+ SortBy SortBy `json:"sortBy"`
226+ Prefix string `json:"prefix"`
227+ }
228+
229+ type SignedUrlResponse struct {
230+ SignedUrl string `json:"signedURL"`
231+ }
232+
233+ const (
234+ defaultLimit = 100
235+ defaultOffset = 0
236+ defaultFileCacheControl = "3600"
237+ defaultFileContent = "text/pain;charset=UTF-8"
238+ defaultFileUpsert = false
239+ defaultSortColumn = "name"
240+ defaultSortOrder = "asc"
241+ )
242+
243+ func (f * file ) UploadOrUpdate (path string , data io.Reader , update bool ) FileResponse {
244+ s := & Storage {}
245+ f .header .Set ("cache-control" , defaultFileCacheControl )
246+ f .header .Set ("content-type" , defaultFileContent )
247+ f .header .Set ("x-upsert" , strconv .FormatBool (defaultFileUpsert ))
248+
249+ body := bufio .NewReader (data )
250+ _path := removeEmptyFolder (f .BucketId + "/" + path )
251+ client := & http.Client {}
252+
253+
254+ var (
255+ res * http.Response
256+ err error
257+ )
258+
259+ if update {
260+ var req * http.Request
261+ req , err = http .NewRequest (http .MethodPut , s .client .BaseURL + "/object/" + _path , body )
262+ injectAuthorizationHeader (req , s .client .apiKey )
263+ if err != nil {
264+ panic (err )
265+ }
266+ res , err = client .Do (req )
267+ }else {
268+ var req * http.Request
269+ req , err = http .NewRequest (http .MethodPost , s .client .BaseURL + "/object/" + _path , body )
270+ f .header .Set ("content-type" , defaultFileContent )
271+ injectAuthorizationHeader (req , s .client .apiKey )
272+ if err != nil {
273+ panic (err )
274+ }
275+ res , err = client .Do (req )
276+ if err != nil {
277+ panic (err )
278+ }
279+ }
280+ if err != nil {
281+ panic (err )
282+ }
283+
284+ resBody , err := io .ReadAll (res .Body )
285+ if err != nil {
286+ panic (err )
287+ }
288+ var response FileResponse
289+ if err = json .Unmarshal (resBody , & response ); err != nil {
290+ panic (err )
291+ }
292+
293+ return response
294+ }
295+
296+ // Update updates a file object in a storage bucket
297+ func (f * file ) Update (path string , data io.Reader ) FileResponse {
298+ return f .UploadOrUpdate (path , data , true )
299+ }
300+
301+ // Upload uploads a file object to a storage bucket
302+ func (f * file ) Upload (path string , data io.Reader ) FileResponse {
303+ return f .UploadOrUpdate (path , data , false )
304+ }
305+
306+ // Move moves a file object
307+ func (f * file ) Move (fromPath string , toPath string ) FileResponse {
308+ _json , _ := json .Marshal (map [string ]interface {}{
309+ "bucketId" : f .BucketId ,
310+ "sourceKey" : fromPath ,
311+ "destintionKey" : toPath ,
312+ })
313+ s := & Storage {}
314+ req , err := http .NewRequest (http .MethodPost , s .client .BaseURL + "/object/move" , bytes .NewBuffer (_json ))
315+ injectAuthorizationHeader (req , s .client .apiKey )
316+ if err != nil {
317+ panic (err )
318+ }
319+ client := & http.Client {}
320+ res , err := client .Do (req )
321+ if err != nil {
322+ panic (err )
323+ }
324+
325+ body , err := io .ReadAll (res .Body )
326+ if err != nil {
327+ panic (err )
328+ }
329+
330+ var response FileResponse
331+ if err := json .Unmarshal (body , & response ); err != nil {
332+ panic (err )
333+ }
334+
335+ return response
336+ }
337+
338+ // CreatSignedUrl create a signed url for a file object
339+ func (f * file ) CreatSignedUrl (filePath string , expiresIn int ) SignedUrlResponse {
340+ _json , _ := json .Marshal (map [string ]interface {}{
341+ "expiresIn" : expiresIn ,
342+ })
343+ s := & Storage {}
344+ req , err := http .NewRequest (http .MethodPost , s .client .BaseURL + "/object/sign/" + f .BucketId + "/" + filePath , bytes .NewBuffer (_json ))
345+ injectAuthorizationHeader (req , s .client .apiKey )
346+
347+ if err != nil {
348+ panic (err )
349+ }
350+ client := & http.Client {}
351+ res , err := client .Do (req )
352+ if err != nil {
353+ panic (err )
354+ }
355+
356+ body , err := io .ReadAll (res .Body )
357+ if err != nil {
358+ panic (err )
359+ }
360+
361+ var response SignedUrlResponse
362+ if err := json .Unmarshal (body , & response ); err != nil {
363+ panic (err )
364+ }
365+ response .SignedUrl = s .client .BaseURL + response .SignedUrl
366+
367+ return response
368+ }
369+
370+ // GetPublicUrl get a public signed url of a file object
371+ func (f * file ) GetPublicUrl (filePath string ) SignedUrlResponse {
372+ s := & Storage {}
373+ var response SignedUrlResponse
374+ response .SignedUrl = s .client .BaseURL + "/object/public" + f .BucketId + "/" + filePath
375+
376+ return response
377+ }
378+
379+ // Remove deletes a file object
380+ func (f * file ) Remove (filePaths []string ) FileResponse {
381+ _json , _ := json .Marshal (map [string ]interface {}{
382+ "prefixex" : filePaths ,
383+ })
384+ s := & Storage {}
385+ req , err := http .NewRequest (http .MethodPost , s .client .BaseURL + "/object/" + f .BucketId , bytes .NewBuffer (_json ))
386+ injectAuthorizationHeader (req , s .client .apiKey )
387+
388+ if err != nil {
389+ panic (err )
390+ }
391+ client := & http.Client {}
392+ res , err := client .Do (req )
393+ if err != nil {
394+ panic (err )
395+ }
396+
397+ body , err := io .ReadAll (res .Body )
398+ if err != nil {
399+ panic (err )
400+ }
401+
402+ var response FileResponse
403+ if err := json .Unmarshal (body , & response ); err != nil {
404+ panic (err )
405+ }
406+
407+ return response
408+ }
409+
410+ // List list all file object
411+ func (f * file ) List (queryPath string , options FileSearchOptions ) []FileObject {
412+ if options .Limit == 0 {
413+ options .Limit = defaultLimit
414+ }
415+ if options .Offset == 0 {
416+ options .Offset = defaultOffset
417+ }
418+ if options .SortBy .Order == "" {
419+ options .SortBy .Order = defaultSortOrder
420+ }
421+ if options .SortBy .Column == "" {
422+ options .SortBy .Column = defaultSortColumn
423+ }
424+
425+ _body := ListFileRequest {
426+ Limit : options .Limit ,
427+ Offset : options .Offset ,
428+ SortBy : SortBy {
429+ Column : options .SortBy .Column ,
430+ Order : options .SortBy .Order ,
431+ },
432+ Prefix : queryPath ,
433+ }
434+
435+ _json , _ := json .Marshal (_body )
436+ s := & Storage {}
437+ req , err := http .NewRequest (http .MethodPost , s .client .BaseURL + "/object/list/" + f .BucketId , bytes .NewBuffer (_json ))
438+ injectAuthorizationHeader (req , s .client .apiKey )
439+
440+ if err != nil {
441+ panic (err )
442+ }
443+ client := & http.Client {}
444+ res , err := client .Do (req )
445+ if err != nil {
446+ panic (err )
447+ }
448+
449+ body , err := io .ReadAll (res .Body )
450+ if err != nil {
451+ panic (err )
452+ }
453+
454+ var response []FileObject
455+ if err := json .Unmarshal (body , & response ); err != nil {
456+ panic (err )
457+ }
458+
459+ return response
460+
461+ }
462+
463+ // Copy copies a file object
464+ func (f * file ) Copy (fromPath , toPath string ) FileResponse {
465+ _json , _ := json .Marshal (map [string ]interface {}{
466+ "bucketId" : f .BucketId ,
467+ "sourceKey" : fromPath ,
468+ "destintionKey" : toPath ,
469+ })
470+ s := & Storage {}
471+ req , err := http .NewRequest (http .MethodPost , s .client .BaseURL + "/object/copy" , bytes .NewBuffer (_json ))
472+ injectAuthorizationHeader (req , s .client .apiKey )
473+
474+ if err != nil {
475+ panic (err )
476+ }
477+ client := & http.Client {}
478+ res , err := client .Do (req )
479+ if err != nil {
480+ panic (err )
481+ }
482+
483+ body , err := io .ReadAll (res .Body )
484+ if err != nil {
485+ panic (err )
486+ }
487+
488+ var response FileResponse
489+ if err := json .Unmarshal (body , & response ); err != nil {
490+ panic (err )
491+ }
492+
493+ return response
494+ }
495+
496+ // Download retrieves a file object
497+ func (f * file ) Download (filePath string ) FileResponse {
498+ // _json, _ := json.Marshal(map[string]interface{}{
499+ // })
500+ s := & Storage {}
501+ req , err := http .NewRequest (http .MethodGet , s .client .BaseURL + "/object/" + f .BucketId + "/" + filePath , nil )
502+
503+ injectAuthorizationHeader (req , s .client .apiKey )
504+ if err != nil {
505+ panic (err )
506+ }
507+ client := & http.Client {}
508+ res , err := client .Do (req )
509+ if err != nil {
510+ panic (err )
511+ }
512+
513+ body , err := io .ReadAll (res .Body )
514+ if err != nil {
515+ panic (err )
516+ }
517+
518+ var response FileResponse
519+ if err := json .Unmarshal (body , & response ); err != nil {
520+ panic (err )
521+ }
522+
523+ return response
524+ }
525+
526+ func removeEmptyFolder (filePath string ) string {
527+ return regexp .MustCompile (`\/\/` ).ReplaceAllString (filePath , "/" )
528+ }
529+
0 commit comments