11package supabase
22
33import (
4-
54 "bufio"
65 "bytes"
76 "context"
87 "encoding/json"
8+ "errors"
9+ "fmt"
910 "io"
11+ "net/http"
1012 "regexp"
1113 "strconv"
12- "fmt"
13- "net/http"
1414)
1515
1616type Storage struct {
1717 client * Client
18-
1918}
2019
2120// Storage buckets methods
@@ -46,6 +45,8 @@ type storageError struct {
4645 Message string `json:"message"`
4746}
4847
48+ var ErrNotFound = errors .New ("file not found" )
49+
4950
5051// CreateBucket creates a new storage bucket
5152// @param: option: a bucketOption with the name and id of the bucket you want to create
@@ -180,15 +181,15 @@ func (s *Storage) DeleteBucket(ctx context.Context, id string) (*bucketResponse,
180181}
181182
182183
183- func (s Storage ) From (bucketId string ) * file {
184- return & file {BucketId : bucketId }
184+ func (s * Storage ) From (bucketId string ) * file {
185+ return & file {BucketId : bucketId , storage : s }
185186}
186187
187188// Storage Objects methods
188189
189190type file struct {
190191 BucketId string
191- header http. Header
192+ storage * Storage
192193}
193194
194195type SortBy struct {
@@ -201,6 +202,15 @@ type FileResponse struct {
201202 Message string `json:"message"`
202203}
203204
205+ type FileErrorResponse struct {
206+ Status string `json:"statusCode"`
207+ ShortError string `json:"error"`
208+ Message string `json:"message"`
209+ }
210+ func (err * FileErrorResponse ) Error () string {
211+ return err .ShortError + ": " + err .Message
212+ }
213+
204214type FileSearchOptions struct {
205215 Limit int `json:"limit"`
206216 Offset int `json:"offset"`
@@ -241,42 +251,38 @@ const (
241251)
242252
243253func (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-
249254 body := bufio .NewReader (data )
250255 _path := removeEmptyFolder (f .BucketId + "/" + path )
251256 client := & http.Client {}
252-
253257
254258 var (
259+ method string
260+ req * http.Request
255261 res * http.Response
256262 err error
257263 )
258264
259265 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- }
266+ method = http .MethodPut
267+ } else {
268+ method = http .MethodPost
279269 }
270+
271+ reqURL := fmt .Sprintf ("%s/%s/object/%s" , f .storage .client .BaseURL , StorageEndpoint , _path )
272+ req , err = http .NewRequest (method , reqURL , body )
273+ if err != nil {
274+ panic (err )
275+ }
276+
277+ injectAuthorizationHeader (req , f .storage .client .apiKey )
278+ req .Header .Set ("cache-control" , defaultFileCacheControl )
279+ req .Header .Set ("content-type" , defaultFileContent )
280+ req .Header .Set ("x-upsert" , strconv .FormatBool (defaultFileUpsert ))
281+ if ! update {
282+ req .Header .Set ("content-type" , defaultFileContent )
283+ }
284+
285+ res , err = client .Do (req )
280286 if err != nil {
281287 panic (err )
282288 }
@@ -285,6 +291,7 @@ func (f *file) UploadOrUpdate(path string, data io.Reader, update bool) FileResp
285291 if err != nil {
286292 panic (err )
287293 }
294+
288295 var response FileResponse
289296 if err = json .Unmarshal (resBody , & response ); err != nil {
290297 panic (err )
@@ -310,12 +317,15 @@ func (f *file) Move(fromPath string, toPath string) FileResponse {
310317 "sourceKey" : fromPath ,
311318 "destintionKey" : toPath ,
312319 })
313- s := & Storage {}
314- req , err := http . NewRequest ( http . MethodPost , s . client . BaseURL + "/ object/move" , bytes . NewBuffer ( _json ) )
315- injectAuthorizationHeader ( req , s . client . apiKey )
320+
321+ reqURL := fmt . Sprintf ( "%s/%s/ object/move" , f . storage . client . BaseURL , StorageEndpoint )
322+ req , err := http . NewRequest ( http . MethodPost , reqURL , bytes . NewBuffer ( _json ) )
316323 if err != nil {
317324 panic (err )
318325 }
326+
327+ injectAuthorizationHeader (req , f .storage .client .apiKey )
328+
319329 client := & http.Client {}
320330 res , err := client .Do (req )
321331 if err != nil {
@@ -340,13 +350,15 @@ func (f *file) CreatSignedUrl(filePath string, expiresIn int) SignedUrlResponse
340350 _json , _ := json .Marshal (map [string ]interface {}{
341351 "expiresIn" : expiresIn ,
342352 })
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-
353+
354+ reqURL := fmt .Sprintf ("%s/%s/object/sign/%s/%s" , f .storage .client .BaseURL , StorageEndpoint , f .BucketId , filePath )
355+ req , err := http .NewRequest (http .MethodPost , reqURL , bytes .NewBuffer (_json ))
347356 if err != nil {
348357 panic (err )
349358 }
359+
360+ injectAuthorizationHeader (req , f .storage .client .apiKey )
361+
350362 client := & http.Client {}
351363 res , err := client .Do (req )
352364 if err != nil {
@@ -362,32 +374,32 @@ func (f *file) CreatSignedUrl(filePath string, expiresIn int) SignedUrlResponse
362374 if err := json .Unmarshal (body , & response ); err != nil {
363375 panic (err )
364376 }
365- response .SignedUrl = s .client .BaseURL + response .SignedUrl
377+ response .SignedUrl = f . storage .client .BaseURL + response .SignedUrl
366378
367379 return response
368380}
369381
370382// GetPublicUrl get a public signed url of a file object
371383func (f * file ) GetPublicUrl (filePath string ) SignedUrlResponse {
372- s := & Storage {}
373384 var response SignedUrlResponse
374- response .SignedUrl = s .client .BaseURL + "/object/public" + f .BucketId + "/" + filePath
375-
385+ response .SignedUrl = fmt .Sprintf ("%s/%s/object/public/%s/%s" , f .storage .client .BaseURL , StorageEndpoint , f .BucketId , filePath )
376386 return response
377387}
378388
379389// Remove deletes a file object
380390func (f * file ) Remove (filePaths []string ) FileResponse {
381391 _json , _ := json .Marshal (map [string ]interface {}{
382- "prefixex" : filePaths ,
392+ "prefixex" : filePaths ,
383393 })
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-
394+
395+ reqURL := fmt .Sprintf ("%s/%s/object/%s" , f .storage .client .BaseURL , StorageEndpoint , f .BucketId )
396+ req , err := http .NewRequest (http .MethodPost , reqURL , bytes .NewBuffer (_json ))
388397 if err != nil {
389398 panic (err )
390399 }
400+
401+ injectAuthorizationHeader (req , f .storage .client .apiKey )
402+
391403 client := & http.Client {}
392404 res , err := client .Do (req )
393405 if err != nil {
@@ -433,13 +445,15 @@ func (f *file) List(queryPath string, options FileSearchOptions) []FileObject {
433445 }
434446
435447 _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 )
439448
449+ reqURL := fmt .Sprintf ("%s/%s/object/list/%s" , f .storage .client .BaseURL , StorageEndpoint , f .BucketId )
450+ req , err := http .NewRequest (http .MethodPost , reqURL , bytes .NewBuffer (_json ))
440451 if err != nil {
441452 panic (err )
442453 }
454+
455+ injectAuthorizationHeader (req , f .storage .client .apiKey )
456+
443457 client := & http.Client {}
444458 res , err := client .Do (req )
445459 if err != nil {
@@ -457,7 +471,6 @@ func (f *file) List(queryPath string, options FileSearchOptions) []FileObject {
457471 }
458472
459473 return response
460-
461474}
462475
463476// Copy copies a file object
@@ -467,13 +480,15 @@ func (f *file) Copy(fromPath, toPath string) FileResponse {
467480 "sourceKey" : fromPath ,
468481 "destintionKey" : toPath ,
469482 })
470- s := & Storage {}
471- req , err := http .NewRequest (http .MethodPost , s .client .BaseURL + "/object/copy" , bytes .NewBuffer (_json ))
472- injectAuthorizationHeader (req , s .client .apiKey )
473483
484+ reqURL := fmt .Sprintf ("%s/%s/object/copy" , f .storage .client .BaseURL , StorageEndpoint , f .BucketId )
485+ req , err := http .NewRequest (http .MethodPost , reqURL , bytes .NewBuffer (_json ))
474486 if err != nil {
475487 panic (err )
476488 }
489+
490+ injectAuthorizationHeader (req , f .storage .client .apiKey )
491+
477492 client := & http.Client {}
478493 res , err := client .Do (req )
479494 if err != nil {
@@ -493,17 +508,16 @@ func (f *file) Copy(fromPath, toPath string) FileResponse {
493508 return response
494509}
495510
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 )
511+ // Download retrieves a file object, if it exists, otherwise return file response
512+ func (f * file ) Download (filePath string ) ([]byte , error ) {
513+ reqURL := fmt .Sprintf ("%s/%s/object/authenticated/%s/%s" , f .storage .client .BaseURL , StorageEndpoint , f .BucketId , filePath )
514+ req , err := http .NewRequest (http .MethodGet , reqURL , nil )
504515 if err != nil {
505516 panic (err )
506517 }
518+
519+ injectAuthorizationHeader (req , f .storage .client .apiKey )
520+
507521 client := & http.Client {}
508522 res , err := client .Do (req )
509523 if err != nil {
@@ -515,12 +529,21 @@ func (f *file) Download(filePath string) FileResponse {
515529 panic (err )
516530 }
517531
518- var response FileResponse
519- if err := json .Unmarshal (body , & response ); err != nil {
520- panic (err )
532+ // when not success, supabase will return json insted of file
533+ if res .StatusCode != 200 {
534+ var resErr * FileErrorResponse
535+ if err := json .Unmarshal (body , & resErr ); err != nil {
536+ panic (err )
537+ }
538+
539+ if resErr .Status == "404" {
540+ return nil , ErrNotFound
541+ }
542+
543+ return nil , resErr
521544 }
522545
523- return response
546+ return body , nil
524547}
525548
526549func removeEmptyFolder (filePath string ) string {
0 commit comments