From f2575c570f3a1d5d968922c33b358a44ea158570 Mon Sep 17 00:00:00 2001 From: jonaustin09 Date: Thu, 11 May 2023 04:11:21 +0400 Subject: [PATCH] feat: add gofiber add gofiber add ListBuckets,PutBucket,DeleteBucket,ListObjects,ListObjectsV2,DeleteObject,DeleteObjects,CopyObject actions --- backend/backend.go | 8 +++- cmd/scoutgw/main.go | 15 +++++++- go.mod | 20 +++++++++- go.sum | 57 ++++++++++++++++++++++++++++ internal/type.go | 3 ++ s3api/router.go | 91 +++++++++++++++++++++++++++++++++++++++++++++ s3api/server.go | 34 +++++++++++++---- s3err/s3err.go | 51 ++++++++++++++++++++++++- 8 files changed, 267 insertions(+), 12 deletions(-) create mode 100644 internal/type.go create mode 100644 s3api/router.go diff --git a/backend/backend.go b/backend/backend.go index fab1ee9..979d440 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -32,6 +32,7 @@ type Backend interface { ListObjects(bucket, prefix, marker, delim string, maxkeys int) (*s3response.ListBucketResult, s3err.ErrorCode) ListObjectsV2(bucket, prefix, marker, delim string, maxkeys int) (*s3response.ListBucketResultV2, s3err.ErrorCode) DeleteObject(bucket, object string) s3err.ErrorCode + DeleteObjects(bucket string, objects []string) s3err.ErrorCode IsTaggingSupported() bool GetTags(bucket, object string) (map[string]string, error) @@ -41,7 +42,9 @@ type Backend interface { type BackendUnsupported struct{} -var _ Backend = BackendUnsupported{} +func New() Backend { + return &BackendUnsupported{} +} func (BackendUnsupported) GetIAMConfig() ([]byte, error) { return nil, fmt.Errorf("not supported") @@ -90,6 +93,9 @@ func (BackendUnsupported) PutObject(buket, object string, r io.Reader) (string, func (BackendUnsupported) DeleteObject(bucket, object string) s3err.ErrorCode { return s3err.ErrNotImplemented } +func (BackendUnsupported) DeleteObjects(bucket string, object []string) s3err.ErrorCode { + return s3err.ErrNotImplemented +} func (BackendUnsupported) GetObject(bucket, object string, startOffset, length int64, writer io.Writer, etag string) (*s3response.GetObjectResponse, s3err.ErrorCode) { return nil, s3err.ErrNotImplemented } diff --git a/cmd/scoutgw/main.go b/cmd/scoutgw/main.go index 7905807..5b42a5d 100644 --- a/cmd/scoutgw/main.go +++ b/cmd/scoutgw/main.go @@ -1,5 +1,18 @@ package main -func main() { +import ( + "github.com/gofiber/fiber/v2" + "github.com/versity/scoutgw/backend" + "github.com/versity/scoutgw/s3api" + "log" +) +func main() { + app := fiber.New(fiber.Config{}) + back := backend.New() + if api, err := s3api.New(app, back, ":7070"); err != nil { + log.Fatalln(err) + } else if err = api.Serve(); err != nil { + log.Fatalln(err) + } } diff --git a/go.mod b/go.mod index 45b0905..f21e7fc 100644 --- a/go.mod +++ b/go.mod @@ -4,4 +4,22 @@ go 1.20 require github.com/aws/aws-sdk-go v1.44.258 -require github.com/jmespath/go-jmespath v0.4.0 // indirect +require ( + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/gofiber/fiber/v2 v2.45.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/klauspost/compress v1.16.3 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/philhofer/fwd v1.1.2 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect + github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect + github.com/tinylib/msgp v1.1.8 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.47.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/sys v0.8.0 // indirect +) diff --git a/go.sum b/go.sum index 913bfbc..b22b39f 100644 --- a/go.sum +++ b/go.sum @@ -1,44 +1,101 @@ +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/aws/aws-sdk-go v1.44.258 h1:JVk1lgpsTnb1kvUw3eGhPLcTpEBp6HeSf1fxcYDs2Ho= github.com/aws/aws-sdk-go v1.44.258/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gofiber/fiber/v2 v2.45.0 h1:p4RpkJT9GAW6parBSbcNFH2ApnAuW3OzaQzbOCoDu+s= +github.com/gofiber/fiber/v2 v2.45.0/go.mod h1:DNl0/c37WLe0g92U6lx1VMQuxGUQY5V7EIaVoEsUffc= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= +github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= +github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4= +github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8= +github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= +github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= +github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= +github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/type.go b/internal/type.go new file mode 100644 index 0000000..18f5f02 --- /dev/null +++ b/internal/type.go @@ -0,0 +1,3 @@ +package internal + +type Any any diff --git a/s3api/router.go b/s3api/router.go new file mode 100644 index 0000000..dd1c3eb --- /dev/null +++ b/s3api/router.go @@ -0,0 +1,91 @@ +package s3api + +import ( + "encoding/xml" + "errors" + "fmt" + "github.com/gofiber/fiber/v2" + "github.com/versity/scoutgw/backend" + "github.com/versity/scoutgw/internal" + "github.com/versity/scoutgw/s3err" + "github.com/versity/scoutgw/s3response" + "strings" +) + +type S3ApiRouter struct { + app *fiber.App + api fiber.Router +} + +func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend) { + // ListBuckets action + app.Get("/", func(ctx *fiber.Ctx) error { + res, code := be.ListBuckets() + return responce[internal.Any](ctx, res, code) + }) + + // PutBucket action + app.Put("/:bucket", func(ctx *fiber.Ctx) error { + code := be.PutBucket(ctx.Params("bucket")) + return responce[internal.Any](ctx, nil, code) + }) + // DeleteBucket action + app.Delete("/:bucket", func(ctx *fiber.Ctx) error { + code := be.DeleteBucket(ctx.Params("bucket")) + return responce[internal.Any](ctx, nil, code) + }) + + // ListObjects action + // ListObjectsV2 action + app.Get("/:bucket", func(ctx *fiber.Ctx) error { + listType := ctx.QueryInt("list-type") + + if listType == 2 { + res, code := be.ListObjectsV2(ctx.Params("bucket"), "", "", "", 1) + return responce[*s3response.ListBucketResultV2](ctx, res, code) + } else { + res, code := be.ListObjects(ctx.Params("bucket"), "", "", "", 1) + return responce[*s3response.ListBucketResult](ctx, res, code) + } + }) + // DeleteObject action + app.Delete("/:bucket/:key", func(ctx *fiber.Ctx) error { + code := be.DeleteObject(ctx.Params("bucket"), ctx.Params("key")) + return responce[internal.Any](ctx, nil, code) + }) + // DeleteObjects action + app.Post("/:bucket", func(ctx *fiber.Ctx) error { + body := ctx.Body() + + fmt.Println(string(body)) + //todo: create type for body and pass to function parsed structure + code := be.DeleteObjects(ctx.Params("bucket"), []string{}) + return responce[internal.Any](ctx, nil, code) + }) + // CopyObject action + app.Put("/:dstBucket/:dstKey/*", func(ctx *fiber.Ctx) error { + copySource := strings.Split(ctx.Get("X-Amz-Copy-Source"), "/") + if len(copySource) < 2 { + return errors.New("wrong api call") + } + srcBucket, srcObject := copySource[0], copySource[1:] + dstBucket, dstKeyStart, dstKeyEnd := ctx.Params("dstBucket"), ctx.Params("dstKey"), ctx.Params("*1") + if dstKeyEnd != "" { + dstKeyStart = strings.Join([]string{dstKeyStart, dstKeyEnd}, "/") + } + res, code := be.CopyObject(srcBucket, strings.Join(srcObject, "/"), dstBucket, dstKeyStart) + return responce[*s3response.CopyObjectResponse](ctx, res, code) + }) +} + +func responce[R comparable](ctx *fiber.Ctx, resp R, code s3err.ErrorCode) error { + if code != 0 { + err := s3err.GetAPIError(code) + ctx.Status(err.HTTPStatusCode) + return ctx.Send(s3err.GetAPIErrorResponse(err, "", "", "")) + } else if b, err := xml.Marshal(resp); err != nil { + return err + } else { + return ctx.Send(b) + } +} diff --git a/s3api/server.go b/s3api/server.go index 2d2d29b..120b4a0 100644 --- a/s3api/server.go +++ b/s3api/server.go @@ -1,19 +1,37 @@ package s3api import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/logger" "github.com/versity/scoutgw/backend" ) type S3ApiServer struct { - be backend.Backend - port int + app *fiber.App + backend backend.Backend + router *S3ApiRouter + port string } -func New(be backend.Backend, port int) (s3ApiServer *S3ApiServer, err error) { - s3ApiServer = &S3ApiServer{ - be: be, - port: port, - } +func New(app *fiber.App, be backend.Backend, port string) (s3ApiServer *S3ApiServer, err error) { + s3ApiServer = &S3ApiServer{app, be, new(S3ApiRouter), port} - return s3ApiServer, nil + app.Use(logger.New()) + + s3ApiServer.router.Init(app, be) + //app.All("/*", func(ctx *fiber.Ctx) error { + // + // fmt.Println(ctx.Method()) + // listBucket := new(s3response.ListBucket) + // if b, err := xml.Marshal(listBucket); err != nil { + // return err + // } else { + // return ctx.Send(b) + // } + //}) + return +} + +func (sa *S3ApiServer) Serve() (err error) { + return sa.app.Listen(sa.port) } diff --git a/s3err/s3err.go b/s3err/s3err.go index aaf6d4d..ca16eb0 100644 --- a/s3err/s3err.go +++ b/s3err/s3err.go @@ -1,6 +1,10 @@ package s3err -import "net/http" +import ( + "bytes" + "encoding/xml" + "net/http" +) // APIError structure type APIError struct { @@ -9,6 +13,27 @@ type APIError struct { HTTPStatusCode int } +// APIErrorResponse - error response format +type APIErrorResponse struct { + XMLName xml.Name `xml:"Error" json:"-"` + Code string + Message string + Key string `xml:"Key,omitempty" json:"Key,omitempty"` + BucketName string `xml:"BucketName,omitempty" json:"BucketName,omitempty"` + Resource string + Region string `xml:"Region,omitempty" json:"Region,omitempty"` + RequestID string `xml:"RequestId" json:"RequestId"` + HostID string `xml:"HostId" json:"HostId"` +} + +func (A APIError) Error() []byte { + var bytesBuffer bytes.Buffer + bytesBuffer.WriteString(xml.Header) + e := xml.NewEncoder(&bytesBuffer) + _ = e.Encode(A) + return bytesBuffer.Bytes() +} + // ErrorCode type of error status. type ErrorCode int @@ -345,3 +370,27 @@ var errorCodeResponse = map[ErrorCode]APIError{ func GetAPIError(code ErrorCode) APIError { return errorCodeResponse[code] } + +// getErrorResponse gets in standard error and resource value and +// provides a encodable populated response values +func GetAPIErrorResponse(err APIError, resource, requestID, hostID string) []byte { + return encodeResponse(APIErrorResponse{ + Code: err.Code, + Message: err.Description, + BucketName: "", + Key: "", + Resource: resource, + Region: "", + RequestID: requestID, + HostID: hostID, + }) +} + +// Encodes the response headers into XML format. +func encodeResponse(response interface{}) []byte { + var bytesBuffer bytes.Buffer + bytesBuffer.WriteString(xml.Header) + e := xml.NewEncoder(&bytesBuffer) + e.Encode(response) + return bytesBuffer.Bytes() +}