diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml
index bcf54196..d707fbac 100644
--- a/.github/workflows/security.yml
+++ b/.github/workflows/security.yml
@@ -1,72 +1,76 @@
-on: [ push, pull_request ]
-name: Security
-jobs:
- Gosec:
- runs-on: ubuntu-latest
- steps:
- - name: Fetch Repository
- uses: actions/checkout@v3
- - name: Install Go
- uses: actions/setup-go@v3
- with:
- go-version: '^1.17.6'
- - name: Install Gosec
- run: |
- export PATH=${PATH}:`go env GOPATH`/bin
- go install github.com/securego/gosec/v2/cmd/gosec@latest
- - name: Run Gosec (root)
- working-directory: .
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal -exclude-dir=arangodb -exclude-dir=badger -exclude-dir=dynamodb -exclude-dir=memcache -exclude-dir=memory -exclude-dir=mongodb -exclude-dir=mysql -exclude-dir=postgres -exclude-dir=redis -exclude-dir=ristretto -exclude-dir=sqlite3 -exclude-dir=s3 -exclude-dir=bbolt ./..."
- # -----
- - name: Run Gosec (arangodb)
- working-directory: ./arangodb
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
- # -----
- - name: Run Gosec (badger)
- working-directory: ./badger
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
- # -----
- - name: Run Gosec (dynamodb)
- working-directory: ./dynamodb
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
- # -----
- - name: Run Gosec (memcache)
- working-directory: ./memcache
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
- # -----
- - name: Run Gosec (memory)
- working-directory: ./memory
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
- # -----
- - name: Run Gosec (mongodb)
- working-directory: ./mongodb
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
- # -----
- - name: Run Gosec (mysql)
- working-directory: ./mysql
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
- # -----
- - name: Run Gosec (postgres)
- working-directory: ./postgres
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
- # -----
- - name: Run Gosec (redis)
- working-directory: ./redis
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
- # -----
- - name: Run Gosec (sqlite3)
- working-directory: ./sqlite3
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
- # -----
- - name: Run Gosec (s3)
- working-directory: ./s3
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
- # -----
- - name: Run Gosec (ristretto)
- working-directory: ./ristretto
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
- # -----
- - name: Run Gosec (bbolt)
- working-directory: ./bbolt
- run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
- # -----
+on: [ push, pull_request ]
+name: Security
+jobs:
+ Gosec:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Fetch Repository
+ uses: actions/checkout@v3
+ - name: Install Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: '^1.17.6'
+ - name: Install Gosec
+ run: |
+ export PATH=${PATH}:`go env GOPATH`/bin
+ go install github.com/securego/gosec/v2/cmd/gosec@latest
+ - name: Run Gosec (root)
+ working-directory: .
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal -exclude-dir=arangodb -exclude-dir=badger -exclude-dir=dynamodb -exclude-dir=memcache -exclude-dir=memory -exclude-dir=mongodb -exclude-dir=mysql -exclude-dir=postgres -exclude-dir=redis -exclude-dir=ristretto -exclude-dir=sqlite3 -exclude-dir=s3 -exclude-dir=bbolt -exclude-dir=azureblob ./..."
+ # -----
+ - name: Run Gosec (arangodb)
+ working-directory: ./arangodb
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
+ - name: Run Gosec (badger)
+ working-directory: ./badger
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
+ - name: Run Gosec (dynamodb)
+ working-directory: ./dynamodb
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
+ - name: Run Gosec (memcache)
+ working-directory: ./memcache
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
+ - name: Run Gosec (memory)
+ working-directory: ./memory
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
+ - name: Run Gosec (mongodb)
+ working-directory: ./mongodb
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
+ - name: Run Gosec (mysql)
+ working-directory: ./mysql
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
+ - name: Run Gosec (postgres)
+ working-directory: ./postgres
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
+ - name: Run Gosec (redis)
+ working-directory: ./redis
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
+ - name: Run Gosec (sqlite3)
+ working-directory: ./sqlite3
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
+ - name: Run Gosec (s3)
+ working-directory: ./s3
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
+ - name: Run Gosec (ristretto)
+ working-directory: ./ristretto
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
+ - name: Run Gosec (bbolt)
+ working-directory: ./bbolt
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
+ - name: Run Gosec (azureblob)
+ working-directory: ./azureblob
+ run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
+ # -----
diff --git a/.github/workflows/test-azureblob.yml b/.github/workflows/test-azureblob.yml
new file mode 100644
index 00000000..d4d672da
--- /dev/null
+++ b/.github/workflows/test-azureblob.yml
@@ -0,0 +1,46 @@
+on:
+ push:
+ branches:
+ - master
+ - main
+ pull_request:
+name: "Tests Azure Blob"
+jobs:
+ Tests:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ go-version:
+ - 1.18.x
+ - 1.19.x
+ platform:
+ - ubuntu-latest
+ - windows-latest
+ steps:
+ - name: Install Azurite
+ run: |
+ docker run -d -p 10000:10000 -e AZURITE_ACCOUNTS="azurite:YXp1cml0ZWtleQo=" mcr.microsoft.com/azure-storage/azurite azurite-blob --blobHost 0.0.0.0 --blobPort 10000
+ - name: Install Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: '${{ matrix.go-version }}'
+ - name: Setup Golang caches
+ uses: actions/cache@v3
+ with:
+ # In order:
+ # * Module download cache
+ # * Build cache (Linux)
+ # * Build cache (Mac)
+ # * Build cache (Windows)
+ path: |
+ ~/go/pkg/mod
+ ~/.cache/go-build
+ ~/Library/Caches/go-build
+ ~\AppData\Local\go-build
+ key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }}
+ restore-keys: |
+ ${{ runner.os }}-go-${{ matrix.go-version }}-
+ - name: Fetch Repository
+ uses: actions/checkout@v3
+ - name: Run Test
+ run: cd ./azureblob && go test ./... -v -race
diff --git a/README.md b/README.md
index d71f89e2..6da6b40f 100644
--- a/README.md
+++ b/README.md
@@ -1,89 +1,92 @@
-
-
-
-
- # 📦 Storage
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Premade storage drivers that implement the [`Storage`](https://github.com/gofiber/storage/blob/main/storage.go) interface, designed to be used with various [Fiber middlewares](https://github.com/gofiber/fiber/tree/master/middleware).
-
-```go
-// Storage interface for communicating with different database/key-value
-// providers. Visit https://github.com/gofiber/storage for more info.
-type Storage interface {
- // Get gets the value for the given key.
- // `nil, nil` is returned when the key does not exist
- Get(key string) ([]byte, error)
-
- // Set stores the given value for the given key along
- // with an expiration value, 0 means no expiration.
- // Empty key or value will be ignored without an error.
- Set(key string, val []byte, exp time.Duration) error
-
- // Delete deletes the value for the given key.
- // It returns no error if the storage does not contain the key,
- Delete(key string) error
-
- // Reset resets the storage and delete all keys.
- Reset() error
-
- // Close closes the storage and will stop any running garbage
- // collectors and open connections.
- Close() error
-}
-```
-
-## 📑 Storage Implementations
-
-* [ArangoDB](/arangodb)
-
-
-* [Badger](/badger)
-
-
-* [Bbolt](/bbolt)
-
-
-* [DynamoDB](/dynamodb)
-
-
-* [Memcache](/memcache)
-
-
-* [Memory](/memory)
-
-
-* [MongoDB](/mongodb)
-
-
-* [MySQL](/mysql)
-
-
-* [Postgres](/postgres)
-
-
-* [Redis](/redis)
-
-
-* [SQLite3](/sqlite3)
-
-
-* [S3](/s3)
-
+
+
+
+
+ # 📦 Storage
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Premade storage drivers that implement the [`Storage`](https://github.com/gofiber/storage/blob/main/storage.go) interface, designed to be used with various [Fiber middlewares](https://github.com/gofiber/fiber/tree/master/middleware).
+
+```go
+// Storage interface for communicating with different database/key-value
+// providers. Visit https://github.com/gofiber/storage for more info.
+type Storage interface {
+ // Get gets the value for the given key.
+ // `nil, nil` is returned when the key does not exist
+ Get(key string) ([]byte, error)
+
+ // Set stores the given value for the given key along
+ // with an expiration value, 0 means no expiration.
+ // Empty key or value will be ignored without an error.
+ Set(key string, val []byte, exp time.Duration) error
+
+ // Delete deletes the value for the given key.
+ // It returns no error if the storage does not contain the key,
+ Delete(key string) error
+
+ // Reset resets the storage and delete all keys.
+ Reset() error
+
+ // Close closes the storage and will stop any running garbage
+ // collectors and open connections.
+ Close() error
+}
+```
+
+## 📑 Storage Implementations
+
+* [ArangoDB](/arangodb)
+
+
+* [AzureBlob](/azureblob)
+
+
+* [Badger](/badger)
+
+
+* [Bbolt](/bbolt)
+
+
+* [DynamoDB](/dynamodb)
+
+
+* [Memcache](/memcache)
+
+
+* [Memory](/memory)
+
+
+* [MongoDB](/mongodb)
+
+
+* [MySQL](/mysql)
+
+
+* [Postgres](/postgres)
+
+
+* [Redis](/redis)
+
+
+* [SQLite3](/sqlite3)
+
+
+* [S3](/s3)
+
\ No newline at end of file
diff --git a/azureblob/README.md b/azureblob/README.md
new file mode 100644
index 00000000..9af5273b
--- /dev/null
+++ b/azureblob/README.md
@@ -0,0 +1,102 @@
+# Azure blob
+
+[Azure Blob storage](https://azure.microsoft.com/en-us/products/storage/blobs/#overview) is Microsoft's object storage solution for the cloud.
+
+> NOTE: Go **1.18** or later is required. Source: [link](https://github.com/Azure/azure-sdk-for-go/blob/main/README.md)
+
+### Table of Contents
+
+- [Signatures](#signatures)
+- [Installation](#installation)
+- [Examples](#examples)
+- [Config](#config)
+- [Default Config](#default-config)
+
+### Signatures
+
+```go
+func New(config ...Config) Storage
+func (s *Storage) Get(key string) ([]byte, error)
+func (s *Storage) Set(key string, val []byte, exp time.Duration) error
+func (s *Storage) Delete(key string) error
+func (s *Storage) Reset() error
+func (s *Storage) Close() error
+```
+
+### Installation
+
+Azure blob storage driver is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet:
+
+```bash
+go mod init github.com//
+```
+
+And then install the azure blob implementation:
+
+```bash
+go get github.com/gofiber/storage/azureblob
+```
+
+### Examples
+
+Import the storage package.
+
+```go
+import "github.com/gofiber/storage/azureblob"
+```
+
+You can use the following possibilities to create a storage:
+
+```go
+// Initialize default config
+store := azureblob.New()
+
+// Initialize custom config
+store := azureblob.New(azureblob.Config{
+ Account: "test",
+ Container: "test",
+ Credentials: Credentials{
+ Account: "test",
+ Key: "YXp1cml0ZWtleQo=",
+ },
+})
+```
+
+### Config
+
+```go
+type Config struct {
+ // Storage account name.
+ Account string
+ // Container name.
+ Container string
+ // Storage endpoint.
+ // Optional. Default: "https://STORAGEACCOUNTNAME.blob.core.windows.net"
+ Endpoint string
+ // Request timeout.
+ // Optional. Default is 0 (no timeout)
+ RequestTimeout time.Duration
+ // Reset clears any existing keys in existing container.
+ // Optional. Default is false
+ Reset bool
+ // Credentials overrides AWS access key and AWS secret access key. Not recommended.
+ // Optional. Default is Credentials{}
+ Credentials Credentials
+ // The maximum number of times requests that encounter retryable failures should be attempted.
+ // Optional. Default is 3
+ MaxAttempts int
+}
+```
+
+### Default Config
+
+```go
+var ConfigDefault = Config{
+ Account: "",
+ Container: "",
+ Endpoint: "",
+ RequestTimeout: 0,
+ Reset: false,
+ MaxAttempts: 3,
+}
+```
diff --git a/azureblob/azureblob.go b/azureblob/azureblob.go
new file mode 100644
index 00000000..6bbc4bb6
--- /dev/null
+++ b/azureblob/azureblob.go
@@ -0,0 +1,133 @@
+package azureblob
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
+ "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
+ "io"
+ "time"
+)
+
+// Storage interface that is implemented by storage providers
+type Storage struct {
+ client *azblob.Client
+ container string
+ requestTimeout time.Duration
+}
+
+// New creates a new storage
+func New(config ...Config) *Storage {
+ // Set default config
+ cfg := configure(config...)
+ // Set the azure credentials
+ cred, err := azblob.NewSharedKeyCredential(cfg.Credentials.Account, cfg.Credentials.Key)
+ handleError(err)
+ client, err := azblob.NewClientWithSharedKeyCredential(cfg.Endpoint, cred, nil)
+ handleError(err)
+ _, err = client.CreateContainer(context.TODO(), cfg.Container, nil)
+ if err != nil {
+ if !bloberror.HasCode(err, bloberror.ContainerAlreadyExists) {
+ panic(fmt.Sprintf("invalid config:, %v", err))
+ }
+ }
+ storage := &Storage{
+ client: client,
+ container: cfg.Container,
+ requestTimeout: cfg.RequestTimeout,
+ }
+
+ // Reset all entries if set to true
+ if cfg.Reset {
+ if err := storage.Reset(); err != nil {
+ panic(err)
+ }
+ }
+
+ return storage
+}
+
+// Get value by key
+func (s *Storage) Get(key string) ([]byte, error) {
+ if len(key) <= 0 {
+ return nil, nil
+ }
+ ctx, cancel := s.requestContext()
+ defer cancel()
+ resp, err := s.client.DownloadStream(ctx, s.container, key, nil)
+ if err != nil {
+ return []byte{}, err
+ }
+ data, err := io.ReadAll(resp.Body)
+ return data, err
+}
+
+// Set key with value
+func (s *Storage) Set(key string, val []byte, exp time.Duration) error {
+ if len(key) <= 0 {
+ return nil
+ }
+ ctx, cancel := s.requestContext()
+ defer cancel()
+ _, err := s.client.UploadBuffer(ctx, s.container, key, val, nil)
+ return err
+}
+
+// Delete entry by key
+func (s *Storage) Delete(key string) error {
+ if len(key) <= 0 {
+ return nil
+ }
+
+ ctx, cancel := s.requestContext()
+ defer cancel()
+ _, err := s.client.DeleteBlob(ctx, s.container, key, nil)
+ return err
+}
+
+// Reset all entries
+func (s *Storage) Reset() error {
+ ctx, cancel := s.requestContext()
+ defer cancel()
+ //_, err := s.client.DeleteContainer(ctx, s.container, nil)
+ //return err
+ pager := s.client.NewListBlobsFlatPager(s.container, nil)
+ errCounter := 0
+ for pager.More() {
+ resp, err := pager.NextPage(ctx)
+ if err != nil {
+ errCounter = errCounter + 1
+ }
+ for _, v := range resp.Segment.BlobItems {
+ _, err = s.client.DeleteBlob(ctx, s.container, *v.Name, nil)
+ if err != nil {
+ errCounter = errCounter + 1
+ }
+ }
+ }
+ if errCounter > 0 {
+ return errors.New(fmt.Sprintf("%d errors occured while resetting", errCounter))
+ }
+ return nil
+}
+
+// Close the storage connextion
+func (s *Storage) Close() error {
+ return nil
+}
+
+// Context for making requests will timeout if a non-zero timeout is configured
+func (s *Storage) requestContext() (context.Context, context.CancelFunc) {
+ if s.requestTimeout > 0 {
+ return context.WithTimeout(context.Background(), s.requestTimeout)
+ }
+ return context.Background(), func() {}
+}
+
+// handleError is a helper to panic on error
+func handleError(err error) {
+ if err != nil {
+ panic(fmt.Sprintf("invalid config:, %v", err))
+ }
+}
diff --git a/azureblob/azureblob_test.go b/azureblob/azureblob_test.go
new file mode 100644
index 00000000..91e3c5dd
--- /dev/null
+++ b/azureblob/azureblob_test.go
@@ -0,0 +1,132 @@
+package azureblob
+
+import (
+ "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
+ "github.com/gofiber/fiber/v2/utils"
+ "testing"
+)
+
+func newStore() *Storage {
+ return New(Config{
+ Account: "azurite",
+ Container: "test",
+ Endpoint: "http://127.0.0.1:10000/azurite",
+ Credentials: Credentials{
+ Account: "azurite",
+ Key: "YXp1cml0ZWtleQo=",
+ },
+ })
+}
+func Test_AzureBlob_Get(t *testing.T) {
+ var (
+ key = "john"
+ val = []byte("doe")
+ )
+ testStore := newStore()
+
+ err := testStore.Set(key, val, 0)
+ utils.AssertEqual(t, nil, err)
+
+ result, err := testStore.Get(key)
+ utils.AssertEqual(t, nil, err)
+ utils.AssertEqual(t, val, result)
+}
+
+func Test_AzureBlob_Set(t *testing.T) {
+ var (
+ key = "john"
+ val = []byte("doe")
+ )
+
+ testStore := newStore()
+ err := testStore.Set(key, val, 0)
+ utils.AssertEqual(t, nil, err)
+}
+
+func Test_AzureBlob_Delete(t *testing.T) {
+ var (
+ key = "john"
+ val = []byte("doe")
+ )
+ testStore := newStore()
+
+ err := testStore.Set(key, val, 0)
+ utils.AssertEqual(t, nil, err)
+
+ err = testStore.Delete(key)
+ utils.AssertEqual(t, nil, err)
+
+ result, err := testStore.Get(key)
+ if err != nil {
+ if bloberror.HasCode(err, bloberror.BlobNotFound) {
+ err = nil
+ }
+ }
+ utils.AssertEqual(t, nil, err)
+ utils.AssertEqual(t, true, len(result) == 0)
+}
+
+func Test_AzureBlob_Override(t *testing.T) {
+ var (
+ key = "john"
+ val = []byte("doe")
+ )
+ testStore := newStore()
+
+ err := testStore.Set(key, val, 0)
+ utils.AssertEqual(t, nil, err)
+
+ err = testStore.Set(key, val, 0)
+ utils.AssertEqual(t, nil, err)
+}
+
+func Test_AzureBlob_Get_NotExist(t *testing.T) {
+ testStore := newStore()
+ result, err := testStore.Get("notexist")
+ if err != nil {
+ if bloberror.HasCode(err, bloberror.BlobNotFound) {
+ err = nil
+ }
+ }
+ utils.AssertEqual(t, nil, err)
+ utils.AssertEqual(t, true, len(result) == 0)
+}
+
+func Test_AzureBlob_Reset(t *testing.T) {
+ var (
+ val = []byte("doe")
+ )
+ testStore := newStore()
+
+ err := testStore.Set("john1", val, 0)
+ utils.AssertEqual(t, nil, err)
+
+ err = testStore.Set("john2", val, 0)
+ utils.AssertEqual(t, nil, err)
+
+ err = testStore.Reset()
+ utils.AssertEqual(t, nil, err)
+
+ result, err := testStore.Get("john1")
+ if err != nil {
+ if bloberror.HasCode(err, bloberror.BlobNotFound) {
+ err = nil
+ }
+ }
+ utils.AssertEqual(t, nil, err)
+ utils.AssertEqual(t, true, len(result) == 0)
+
+ result, err = testStore.Get("john2")
+ if err != nil {
+ if bloberror.HasCode(err, bloberror.BlobNotFound) {
+ err = nil
+ }
+ }
+ utils.AssertEqual(t, nil, err)
+ utils.AssertEqual(t, true, len(result) == 0)
+}
+
+func Test_AzureBlob_Close(t *testing.T) {
+ testStore := newStore()
+ utils.AssertEqual(t, nil, testStore.Close())
+}
diff --git a/azureblob/config.go b/azureblob/config.go
new file mode 100644
index 00000000..4b7e3c90
--- /dev/null
+++ b/azureblob/config.go
@@ -0,0 +1,80 @@
+package azureblob
+
+import (
+ "errors"
+ "fmt"
+ "time"
+)
+
+// Config defines the config for storage.
+type Config struct {
+ // Storage account name.
+ Account string
+ // Container name.
+ Container string
+ // Storage endpoint.
+ // Optional. Default: "https://STORAGEACCOUNTNAME.blob.core.windows.net"
+ Endpoint string
+ // Request timeout.
+ // Optional. Default is 0 (no timeout)
+ RequestTimeout time.Duration
+ // Reset clears any existing keys in existing container.
+ // Optional. Default is false
+ Reset bool
+ // Credentials overrides AWS access key and AWS secret access key. Not recommended.
+ // Optional. Default is Credentials{}
+ Credentials Credentials
+ // The maximum number of times requests that encounter retryable failures should be attempted.
+ // Optional. Default is 3
+ MaxAttempts int
+}
+
+// Credentials are the azure storage account access keys
+type Credentials struct {
+ Account string
+ Key string
+}
+
+// ConfigDefault is the default config
+var ConfigDefault = Config{
+ Account: "",
+ Container: "",
+ Endpoint: "",
+ RequestTimeout: 0,
+ Reset: false,
+ MaxAttempts: 3,
+}
+
+// Helper function to set default values
+func configure(config ...Config) Config {
+ // Return default config if nothing provided
+ if len(config) < 1 {
+ return ConfigDefault
+ }
+ // Override default config
+ cfg := config[0]
+ valid, err := validateConfig(cfg)
+ if err != nil || !valid {
+ panic(fmt.Sprintf("invalid config:, %v", err))
+ }
+ if cfg.Endpoint == "" {
+ cfg.Endpoint = "https://" + cfg.Account + ".blob.core.windows.net"
+ }
+ return cfg
+}
+
+func validateConfig(config Config) (bool, error) {
+ if config.Credentials.Account == "" || config.Credentials.Key == "" {
+ err := errors.New("credentials must not be empty")
+ return false, err
+ }
+ if config.Account == "" || config.Container == "" {
+ err := errors.New("invalid account information provided")
+ return false, err
+ }
+ if config.Account != config.Credentials.Account {
+ err := errors.New("account configuration mismatch")
+ return false, err
+ }
+ return true, nil
+}
diff --git a/azureblob/go.mod b/azureblob/go.mod
new file mode 100644
index 00000000..62ab9ae6
--- /dev/null
+++ b/azureblob/go.mod
@@ -0,0 +1,16 @@
+module github.com/gofiber/storage/azureblob
+
+go 1.19
+
+require (
+ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1
+ github.com/gofiber/fiber/v2 v2.39.0
+)
+
+require (
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 // indirect
+ github.com/google/uuid v1.3.0 // indirect
+ golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect
+ golang.org/x/text v0.3.7 // indirect
+)
diff --git a/azureblob/go.sum b/azureblob/go.sum
new file mode 100644
index 00000000..25b10dda
--- /dev/null
+++ b/azureblob/go.sum
@@ -0,0 +1,27 @@
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 h1:pqrAR74b6EoR4kcxF7L7Wg2B8Jgil9UUZtMvxhEFqWo=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 h1:XUNQ4mw+zJmaA2KXzP9JlQiecy1SI+Eog7xVkPiqIbg=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1 h1:BMTdr+ib5ljLa9MxTJK8x/Ds0MbBb4MfuW5BL0zMJnI=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1/go.mod h1:c6WvOhtmjNUWbLfOG1qxM/q0SPvQNSVJvolm+C52dIU=
+github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1 h1:BWe8a+f/t+7KY7zH2mqygeUD0t8hNFXe08p1Pb3/jKE=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c=
+github.com/gofiber/fiber/v2 v2.39.0 h1:uhWpYQ6EHN8J7FOPYbI2hrdBD/KNZBC5CjbuOd4QUt4=
+github.com/gofiber/fiber/v2 v2.39.0/go.mod h1:Cmuu+elPYGqlvQvdKyjtYsjGMi69PDp8a1AY2I5B2gM=
+github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
+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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
+golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 h1:Tgea0cVUD0ivh5ADBX4WwuI12DUd2to3nCYe2eayMIw=
+golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
+golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=